Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 135 additions & 0 deletions docs/todos/2026-06-18-issue-495-viewer-token-savings/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# Viewer Token Savings Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Correct the Viewer dashboard Token Savings calculation so it matches the CLI status formula.

**Architecture:** Keep the source-of-truth behavior local to the dashboard render path, matching the existing CLI arithmetic exactly. Add a VM-based Viewer test using the existing `loadViewerSandbox()` helper so the rendered stat card is asserted without a browser server.

**Tech Stack:** TypeScript, Vitest, Node VM sandbox, static `src/viewer/index.html` script.

---

## Files

- Modify: `src/viewer/index.html`
- Create: `test/viewer-token-savings.test.ts`
- Modify: `docs/todos/2026-06-18-issue-495-viewer-token-savings/todo.md`

### Task 1: Add Red Regression Coverage

- [ ] **Step 1: Create `test/viewer-token-savings.test.ts`**

```ts
import { describe, expect, it } from "vitest";
import { loadViewerSandbox } from "./helpers/viewer-sandbox.js";

describe("viewer token savings", () => {
it("matches the CLI injected-token estimate for dashboard savings", () => {
const { sandbox, getElement } = loadViewerSandbox();
const sessions = Array.from({ length: 26 }, (_, index) => ({
id: `session-${index}`,
status: index === 0 ? "active" : "completed",
observationCount: index < 20 ? 2 : 1,
}));

sandbox.state.dashboard = {
health: { status: "healthy" },
sessions,
memories: [],
lessons: [],
crystals: [],
graphStats: {},
recentAudit: [],
};

sandbox.renderDashboard();

const html = getElement("view-dashboard").innerHTML;
expect(html).toContain('<div class="label">Token Savings</div><div class="value">53%</div>');
expect(html).toContain("~1,932 tokens");
});
});
```

- [ ] **Step 2: Run focused test and confirm RED**

Run:

```bash
corepack pnpm exec vitest run test/viewer-token-savings.test.ts
```

Expected: fail because current Viewer renders `0%` and `~0 tokens` for 46 observations across 26 sessions.

### Task 2: Apply Minimal Viewer Formula Fix

- [ ] **Step 1: Change `src/viewer/index.html` formula**

Replace:

```js
var tokenBudget = parseInt(new URLSearchParams(window.location.search).get('tokenBudget') || '2000', 10) || 2000;
var estFull = totalObs * 80;
var estInjected = sessions.length * tokenBudget;
```

with:

```js
var estFull = totalObs * 80;
var estInjected = Math.min(totalObs, 50) * 38;
```

- [ ] **Step 2: Run focused test and confirm GREEN**

Run:

```bash
corepack pnpm exec vitest run test/viewer-token-savings.test.ts
```

Expected: pass, proving the Viewer renders `53%` and `~1,932 tokens` for the issue sample.

### Task 3: Verify And Prepare PR

- [ ] **Step 1: Run focused Viewer tests**

Run:

```bash
corepack pnpm exec vitest run test/viewer-token-savings.test.ts test/viewer-layout.test.ts test/viewer-security.test.ts
```

Expected: pass.

- [ ] **Step 2: Run full repo test suite**

Run:

```bash
corepack pnpm test
```

Expected: pass. If dependency hardening blocks pnpm, run `corepack pnpm install --frozen-lockfile --ignore-scripts`, then retry.

- [ ] **Step 3: Run required pre-commit/security gates**

Run relevant project or fallback commands after staging:

```bash
gitleaks protect --staged --redact
semgrep scan --config p/default --error --metrics=off .
```

Expected: no findings. OSV is not required unless dependency/package surfaces changed.

- [ ] **Step 4: Commit, push, PR, CI, merge**

Commit only task-owned files, push `issue/495-viewer-token-savings` to `origin`, create a PR against `main`, wait for CI, merge if checks pass, then verify final state and close/update Issue #495 as appropriate.

## Plan Self-Review

- Spec coverage: covers Issue #495 validation, Viewer formula, focused regression, branch/PR flow.
- Placeholder scan: no TBD/TODO placeholders.
- Type consistency: uses existing `loadViewerSandbox()` helper and existing Viewer globals.
87 changes: 87 additions & 0 deletions docs/todos/2026-06-18-issue-495-viewer-token-savings/todo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Issue 495 Viewer Token Savings

Scope: repo `agentmemory`, viewer dashboard display and focused VM test coverage.

## Sprint Contract

Goal: make the Viewer dashboard's Token Savings calculation match the CLI `agentmemory status` calculation for the same session observation counts.

Scope:
- Validate GitHub Issue #495 against local code and upstream PR #721.
- Add a focused Viewer VM regression test for the CLI-compatible formula.
- Change only the Viewer dashboard formula if the issue is valid.
- Prepare a PR branch against `origin/main`.

Non-goals:
- Do not change CLI status behavior.
- Do not change dashboard layout, cost rate, REST/MCP APIs, persistence, auth, dependencies, or upstream PR target.
- Do not merge into local `main`.

Acceptance criteria:
- Viewer uses `Math.min(totalObs, 50) * 38` for estimated injected tokens, matching `src/cli.ts`.
- Regression test covers the sample from the issue: 46 observations across 26 sessions renders `53%` and `~1,932 tokens`.
- Focused viewer test passes.
- Repo-native verification and required security/secret gates are run or blockers are recorded.
- Branch is pushed to `origin`, PR targets `origin/main`, CI is checked, and merge is attempted only after passing verification.

Intended verification:
- `corepack pnpm exec vitest run test/viewer-token-savings.test.ts`
- `corepack pnpm test`
- Security/secret gates required before commit/merge as applicable.

Known boundaries:
- Remote writes are authorized by the delegated user request for this issue flow, but only to `origin`/GitHub for branch, PR, issue workflow, and merge.
- `upstream` is read-only context only; no branch, PR, or merge targets upstream.
- No externally consumed API, auth, persistence, schema, dependency, or toolchain behavior changes are intended.

Stop conditions:
- Viewer and CLI formulas already match after local inspection.
- Tests show the issue cannot be reproduced.
- Required remote auth, CI, or security gates fail and cannot be fixed within scope.
- A fix would require behavior outside the accepted scope.

## Evidence

- Issue #495 is open in `wbugitlab1/agentmemory` and mirrors upstream PR #721.
- Upstream PR #721 is open, one-file change in `src/viewer/index.html`.
- CLI source of truth: `src/cli.ts` computes `estInjectedTokens = Math.min(obsCount, 50) * 38`.
- Current Viewer code computes `estInjected = sessions.length * tokenBudget`.

## Feature / Verification Matrix

| Change | Verification method | Status | Evidence |
|---|---|---|---|
| Validate issue and formula mismatch | Local code inspection plus upstream/issue metadata | Done | CLI and Viewer formulas differ before edit |
| Viewer formula matches CLI | TDD red/green focused VM test | Done | RED failed as expected before edit; GREEN passed after Viewer formula change |
| Full regression safety | Repo-native test suite | Done | `corepack pnpm test`: 199 files, 2768 tests passed |
| PR readiness gates | GitHub push prepare review/security/verification flow | Done locally | Lint, build, Semgrep, staged Gitleaks, and read-only final review passed; remote push/PR/CI/merge pending |

## Subagent Ledger

| Workstream | Scope | Edits allowed | Expected output | Result | Residual risk |
|---|---|---:|---|---|---|
| Read-only Explorer | Issue #495 formula mismatch, CLI/Viewer/tests | No | Formula evidence and minimal test recommendation | Done: confirmed Viewer diverges from CLI and recommended VM dashboard coverage | Main agent verified CLI/Viewer formulas before editing |

## Progress

- Created from detached `HEAD` based on `origin/main` `a029b7e117db99c65201ee3d9abb6bb93ff2e173`.
- Working branch: `issue/495-viewer-token-savings`.
- Explorer validated the mismatch independently.
- Added RED regression test for the issue sample before changing Viewer production code.
- RED command: `corepack pnpm exec vitest run test/viewer-token-savings.test.ts` failed as expected before the Viewer edit.
- GREEN command: `corepack pnpm exec vitest run test/viewer-token-savings.test.ts` passed after changing Viewer injected-token estimate to `Math.min(totalObs, 50) * 38`.
- Focused Viewer command passed: `corepack pnpm exec vitest run test/viewer-token-savings.test.ts test/viewer-session-id.test.ts test/viewer-layout.test.ts test/viewer-security.test.ts` (30 tests).
- Full suite passed: `corepack pnpm test` (199 test files, 2768 tests).
- Lint passed: `corepack pnpm run lint`.
- Semgrep passed: `semgrep scan --config p/default --error --metrics=off .` (0 findings).
- Build passed: `corepack pnpm run build`.
- Passive frontend security review found no new risky sink; the diff removes URL query parsing and uses static arithmetic.
- Read-only final implementation reviewer returned `NO FINDINGS`.
- Staged secret scan passed: `gitleaks protect --staged --redact`.

## Final Review Notes Before Commit

- Sprint Contract status: local implementation acceptance criteria are met; remote PR/CI/merge acceptance criteria remain in progress.
- Feature / Verification Matrix: all local rows passed; remote row continues after commit/push/PR.
- Residual risks: none found in local review. The only remaining uncertainty is remote CI/merge outcome.
- Caveats: initial `corepack pnpm exec vitest` attempted an install and was blocked by pnpm ignored-build hardening; resolved with documented `corepack pnpm install --frozen-lockfile --ignore-scripts` and reran tests.
3 changes: 1 addition & 2 deletions src/viewer/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1695,9 +1695,8 @@ <h1>agentmemory</h1>
html += '<div class="sub">' + (cb.failures || 0) + ' failures</div></div>';
}
var totalObs = sessions.reduce(function(a, s) { return a + (s.observationCount || 0); }, 0);
var tokenBudget = parseInt(new URLSearchParams(window.location.search).get('tokenBudget') || '2000', 10) || 2000;
var estFull = totalObs * 80;
var estInjected = sessions.length * tokenBudget;
var estInjected = Math.min(totalObs, 50) * 38;
var savings = estFull > 0 ? Math.round((1 - estInjected / Math.max(estFull, 1)) * 100) : 0;
if (savings < 0) savings = 0;
var tokensSaved = Math.max(0, estFull - estInjected);
Expand Down
32 changes: 32 additions & 0 deletions test/viewer-token-savings.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { describe, expect, it } from "vitest";
import { loadViewerSandbox } from "./helpers/viewer-sandbox.js";

describe("viewer token savings", () => {
it("matches the CLI injected-token estimate for dashboard savings", () => {
const { sandbox, getElement } = loadViewerSandbox();
const sessions = Array.from({ length: 26 }, (_, index) => ({
id: `session-${index}`,
status: index === 0 ? "active" : "completed",
observationCount: index < 20 ? 2 : 1,
}));

sandbox.state.dashboard = {
loaded: true,
health: { status: "healthy", health: {} },
sessions,
memories: [],
lessons: [],
crystals: [],
graphStats: {},
recentAudit: [],
};

sandbox.renderDashboard();

const dashboard = getElement("view-dashboard").innerHTML;
expect(dashboard).toContain(
'<div class="label">Token Savings</div><div class="value">53%</div>',
);
expect(dashboard).toContain("~1,932 tokens");
});
});
Loading