Mint memory confirms only on evidence

Task 6673 · Done · Coder · created · updated

Epic 6659: Project memory, open the pipe, honest trust. This is child C3 (improvement 3, PRD S3). It also owns PRD S5-4's demotion test, which the Architect moved here. It builds on C0's plan gate. Sources: PRD (Task 6660) §3.3 and §3.5 S5-4, and Architecture (Task 6661) §1 C3, §1 C5 ("Unused demotion moves to C3") and §2.3. ## Original requirement (verbatim) "It is important that all memory related mechanism stays behind the feature flag on the billing plan." … "Each later subtask ships its change inside this gate and adds a test proving the mechanism is inert with the term off." "3. `memory_used` becomes an evidence field. `record_settle_votes` mints a confirm only when the task has a `read`/`followed`/non-provisional `cited` event for the unit, or its `mem-N` appears in the summary or PR body; other listed units are recorded as \"seen\" (no vote; still counts against unused demotion). Reword the prompt line (`dispatch_compose.py` ~3430) to \"list a unit only if you read it, ran its command, or cite it\". Done means: a settle listing only prompt ids mints zero confirm votes; one listing a searched-and-read unit mints one." Also from improvement 5, verbatim: "count a unit surfaced in 20 sessions and never read as unused, the same demotion the prompt channel uses." ## Acceptance criteria (verbatim, PRD) - **S3-1** `record_settle_votes` mints a confirm for a `memory_used` token only when that task has a read, followed or non-provisional cited event for the unit, or the unit's `mem-N` appears in the settle summary or the task's PR body. Every other listed token is recorded as **seen**: no vote, and it does not count as use. `use_pairs` and unused demotion must not count a seen record, or a confirm minted without evidence, as use. `memory_wrong` behaviour is unchanged. - **S3-2** The prompt line in `_PROJECT_MEMORY_GUIDANCE` says, in substance, "list a unit in `memory_used` only if you read it, ran its command, or cite it in your summary". It stays one line and keeps the `memory_wrong` clause. - **S3-3** The Project › Memory "Used" column counts only real use, never seen records. - **S3-4 Inert** (S0-7). - **S5-4 Hook-only units can be demoted.** A unit surfaced by the hook in 20 distinct sessions/tasks and never read, followed or cited is demoted as unused (`injectable=false`, reason "unused after 20 injections"), like the prompt channel. ## Approach (Architect §1 C3; settled) - **Signatures.** `routes/agent.py:5517` `_record_settle_memory_votes` and `routes/agent_memory.py:318` `record_settle_votes` both gain `summary`. The call site `routes/agent.py:5833` passes `body.summary`. Run `lsp_find_references` first. - **Evidence per used token** is any one of: - (a) a `read` or `followed` event for (task, unit); - (b) a non-provisional `cited` event; - (c) the token in `summary`, matched with `\bmem-\d+\b`. For (c), first record a non-provisional `cited` event on channel `summary` via `events.record_events`, then vote. - With evidence, cast a `confirm` (`source="settle"`). Without it, cast no vote and count the token as `seen`. The function returns and logs `{"confirmed": n, "seen": m, "flagged": k}`. No seen row is persisted, and no new event kind is added. - **PR body.** A PR-body `mem-N` arrives from the node as a provisional `cited/pr` event, which becomes non-provisional at merge. Nightly `promote_citations` then mints the `citation`-source confirm. Do not fetch PR bodies from the forge at settle. - **`use_pairs`** (`events.py:102-117`): the vote branch keeps only `confirm` votes with `source in ('tool','citation')`. Settle confirms count as use only through their evidence events. - **"Used" column.** Verify the console count query reads `use_pairs`/`used_tasks`, not `confirms`, and fix it if not. - **Prompt line.** `dispatch_compose.py:3430-3431` becomes: "on `task_settle`, list a unit in `memory_used` only if you read it, ran its command, or cite it in your summary; list wrong ones in `memory_wrong`". It stays one line. - Pinned seam with C2 (Architect §2.3): every C3 confirm has a use event, so it weighs 1.0 under C2's `vote_weight_clause`. C2 owns weighting; C3 owns `use_pairs`. Shared edit points (rebase expected): `routes/agent_memory.py` (C2 and C4 also edit it), `events.use_pairs`, `dispatch_compose.py` guidance, and `nightly.demote_unused` callers. Credentials & reach (verbatim rows from Architecture §4): - All unit/API/web tests, `./dc check full`, and the "local stack" oracles (S3-1 local) | Nothing beyond the worktree and its `./dc` stack's Postgres | verified: worktree present, gate runs in-slot. **Decided:** the "local stack" oracles run as API integration tests. They drive the real agent routes through the FastAPI test client against the slot DB, with a test-minted node caller. `jaah-call` never reaches the `./dc` stack. - Dev console checks (S3-2/3, S5-4), operator sign-in on `https://app.dev.jaah.ai` | `jaah/debug/main/operator-login` | verified: on the roster, `task_credential_get` succeeded, and the dev health probe returned 200. - PR merge and Jaah Deploy watch (E1) | `jaah-forge` (authenticated per task) | verified. Out of scope: - From the epic, verbatim: "configuring the production memory model credential (an operator action in Settings › Runners › Project memory, not code); retiring mem-2/mem-3 and purging mem-7834 (operator does it in the console); any change to the OMC notepad/project-memory tier; new embedding providers." - Do NOT change `memory_wrong` handling. - Do NOT change vote weights or trust formula (that is C2). - Do NOT fetch PR bodies from a forge. - Make no migration and no new event kind. - Do NOT change hook-tier matching (that is C5). Done means: - S3-1 — an API test against the slot DB through the real settle route shows: - (a) a settle listing only prompt-injected ids mints 0 confirms and N seen; - (b) a settle listing a unit the task searched and then read via `memory_read` mints exactly 1 confirm; - (c) a unit cited by `mem-N` in the summary mints 1 confirm and a non-provisional `cited/summary` event; - `memory_wrong` still flags. - S3-2 — the dispatch_compose test asserts the new one-line text. On dev, open a newly dispatched memory-ON runner's prompt (Task › session transcript) and read the line. - S3-3 — a web/API test asserts Used counts only real use. On dev, a unit listed only reflexively shows no Used increment. - S5-4 — a nightly test seeds 20 hook-channel `injected` events plus reflexive `settle` confirms with no use event, and asserts the unit is demoted with reason "unused after 20 injections". The test fails before the `use_pairs` change and passes after it. - S3-4 — an inert test using `plan_off_project` shows the settle mints no vote and records nothing. Name it in the PR body. - E1 — the PR is merged to develop through auto-merge with `./dc check full` green, and the Jaah Deploy run succeeded (or was superseded).

Outcome

Settle confirms memory only on use evidence (read/followed/cited or mem-N in summary); others seen. use_pairs ignores settle confirms, fixing Used and hook demotion. PR #5612 merged, deploy OK. Full gate green bar 1 unrelated flake. Dev runner-prompt/Used legs unverified (tests cover). Draft #6711.

Pull request

Artifacts

Activity

  • Draft
  • Ready
  • Runner started
  • Runner ended (done)