Hook memory line lead; command-only followed
Epic context (Task 6294, verbatim): Implement the five improvements from the Project Memory audit (Task 6283). The audit scanned 251 memory-ON runner sessions (2026-10-11..14): only 21 distinct non-codemap units ever reached a prompt, codemap chunks were 42% of injected bytes with zero reads, 24 of 74 search/read tool calls failed on argument names, 16 accepted runner proposals never became injectable, and memory_feedback was called once. The five changes below are one coherent flywheel fix; each is independently shippable.
Full settled inputs: the BA PRD (R-*/AC-*) and the Architecture doc of epic 6294 (sections cited below). This body carries everything this child needs.
## Requirement (verbatim from the epic)
4 (part). For the hook tier: carry a short lead (first sentence, <=200 chars) of each unit's body in the bundle (bundle.py _entry ~84-91) and render it on the [memory] line (tools/project_memory_tier.py ~348-352) so a surfacing informs without a memory_read round trip. Respect the router's existing 60-line cap.
5 (part). Mint `followed` from real use (the unit's own command string appearing in a Bash call, or its mem-N in the settle summary), not from path overlap (tools/context_router.py ~806-815). [This child owns the command-string signal; the settle-summary signal is a sibling child.]
BA requirements (verbatim):
- R-D3: Each bundle entry carries `lead`. `lead` is the first sentence of the body. A sentence ends at the first `.`, `!` or `?` followed by whitespace or end of text, or at the first newline. It is trimmed to ≤200 characters and cut on a word boundary with `…` when longer. It is sanitized the way the title is. The `[memory]` hook line renders it on the same line after the title (for example ` mem-N │ gotcha │ <title> — <lead>`). One unit stays one line, so the existing cap math holds. The total injected block never exceeds the router's 60-line cap. When the room is short, whole units are dropped (never a half line), as today. The footer keeps pointing at `memory_read` for the full body. A unit whose lead equals its title (or is empty) renders without the `— lead` suffix.
- R-E2 (a): `followed` is minted when a unit with a non-empty `commands` list was injected or surfaced to the session, and a later Bash call's command line contains one of the unit's command strings. Path overlap alone, and keyword overlap, no longer mint `followed`. The existing PR-body `cited` provisional path is unchanged.
## Approach (settled by the Architect)
- CP `bundle.py`: `lead(body) -> str`: strip fenced code blocks and a leading `Repro:` line (architect deviation: a fence/command is not a readable sentence); take text to the first newline or first `.`/`!`/`?` followed by whitespace or end; collapse whitespace; pass through `sanitize_title`'s sanitiser; if >200 chars cut at the last space ≤199 and append `…`; return `""` when it equals the title case-folded. `_entry` adds `"lead": lead(unit.body)`. NO `BUNDLE_VERSION` bump (old nodes read keys by name, project_memory_tier.py:173-192; a bump makes them drop the bundle, :225).
- Node `tools/project_memory_tier.py`: `_unit` reads `lead` and re-sanitises it (CP data is untrusted); `render` emits `f" {token} │ {kind} │ {title}" + (f" — {lead}" if lead else "")` on one line. Cap math in `_take_memory` unchanged.
- E2(a): in `match` (:297-325) `followable` for non-codemap units becomes `cmd_hit` only (no `path_hit`); codemap units are `followable=False`. `_take_memory`'s followed rule (context_router.py:807) still requires the unit banked `hook`/`prompt` first. Extend `_command_hit` to strip leading `env [-u NAME|NAME=VAL]...` and `NAME=VAL` assignments from each segment before the `startswith` match.
- Report the dev bundle's byte size before and after in the PR (`admit`, bundle.py:135-143, already caps by size).
Credentials & reach:
| Oracle | Credential / identity / environment | Status |
|---|---|---|
| All unit and integration tests (AC-A*, B*, C*, D*, E1-E3, X1, X2) | None beyond the worktree. Postgres comes from the slot's `./dc` stack under `./dc check full` | verified: the worktree and `./dc` exist in every coder slot. The BA noted Postgres is absent in analysis slots, so tests run in the coder's gate |
| Dev flows AC-B2, AC-D3, AC-E1 (a memory-ON **dev** runner's `memory_search`, `[memory]` line, settle votes) | `jaah/debug/main/operator-login` on `https://app.dev.jaah.ai`. The coder signs in to the dev console as operator, dispatches a short probe task in a memory-ON project on dev, and reads that task's transcript in Audit › Sessions. This follows the hub rule "operate Jaah only through the UI". The coder's own session is NOT relied on: it may be dispatched by the prod CP and so run prod code | verified: credential on the roster and `task_credential_get` succeeded (value not recorded); dev console `GET /` → 200 and `/api/health` → 200 from node `h2`. The probe task itself is created at build time, not here |
| Deploy watch (operator directive: Jaah Deploy of each PR) | `jaah-forge` (pre-authenticated per task) | verified by construction: provided to every build runner; no credential to fetch |
Out of scope: Do NOT bump `BUNDLE_VERSION`; do NOT change the router's 60-line cap or `CODEMAP_BUNDLE_SHARE`; do NOT change the PR-body provisional `cited` path or the codemap directory-match rule; the settle-summary `followed` signal belongs to a sibling child.
Epic out of scope (verbatim): "the two immediate data fixes (purging mem-7834's login triple and retiring mem-2/mem-3) are operator actions in Project > Memory, not code in this epic; profiling the node-side codemap job and curate/nightly cron cost; changing the OFF-arm behaviour (memory OFF must remain byte-identical, see apps/api/tests/memory_off_fixture.py); any new memory kind; org-scoped memory."
Do NOT edit `apps/api/tests/memory_off_*.golden.json`, the `memory_off_fixture.py` pins, or `portfolio/fleet/runner-charter.md`. No new kind, feed, flag, setting or env var. Schema changes additive only (coding-standards migration-overlap rule).
Done means:
- AC-D3 — A bundle entry carries `lead` ≤200 chars, which is the first sentence. A 400-char single-sentence body is cut on a word boundary with `…`. A hook surfacing renders `mem-N │ kind │ title — lead` on one line. With 70 candidate lines, the injected block is ≤60 lines and contains no partial unit line. — `pytest tools/tests/test_context_router_memory.py -k lead` + bundle test in `apps/api/tests/test_project_memory.py` (400-char cut, fence stripped, lead equal to title → empty); observe pass. Dev flow: in a memory-ON dev runner, a Bash call matching a unit shows the lead text on the `[memory]` line.
- AC-E2 (a) — A hook-surfaced unit with `commands:["./dc check full"]` gets `followed` after a Bash call `env -u X ./dc check full`. A unit matched only by path overlap gets NO `followed`. (Codemap → no `followed`.) — `pytest tools/tests/test_context_router_memory.py -k followed`; observe pass
- Quality floor: the hook lead must be a readable sentence, never a mid-word cut and never raw markdown fences.
- AC-X1 — Memory-OFF parity: `memory_off_prompt.golden.json` and `memory_off_lease_payload.golden.json` are unchanged (no diff in the PR), and the OFF golden test passes. — `cd apps/api && pytest tests/test_project_memory_dispatch.py -k memory_off_prompt_golden`; `git diff --stat origin/develop -- apps/api/tests/memory_off_*.golden.json` is empty
- AC-X2 — Whole gate green. — `./dc check full` exits 0
- AC-X3 — Availability: every capability above is ON for any project whose memory arm is ON, with no new flag, setting or env var gating it, and it is live on the dev control plane after deploy. — Dev flow AC-D3 observed on `dev.jaah.ai`
- The post-merge Jaah Deploy run on `develop` succeeds in GitHub (operator directive; a canceled/superseded run is left alone); cite the run.
Outcome
Bundle entries carry a first-sentence `lead`, shown as `mem-N │ kind │ title — lead`; `followed` now comes only from the unit's own command (env prefixes stripped). Full gate green, PR #5438 merged, Jaah Deploy succeeded. Dev-flow probe (dev task 5007) never dispatched: unverified. mem-2 used.
Artifacts
Activity
- Draft
- Ready
- Runner started
- Runner ended (done)