@mmerterden/multi-agent-pipeline 15.9.1 → 15.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/package.json +2 -2
- package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +33 -4
- package/pipeline/multi-agent-refs/phases/phase-1-analysis.md +16 -5
- package/pipeline/multi-agent-refs/phases/phase-3-dev.md +4 -1
- package/pipeline/multi-agent-refs/phases/phase-4-review.md +19 -1
- package/pipeline/multi-agent-refs/prompt-assembly.md +12 -1
- package/pipeline/schemas/prefs.schema.json +137 -287
- package/pipeline/schemas/token-budget.json +2 -2
- package/pipeline/scripts/_retrieval.mjs +387 -0
- package/pipeline/scripts/gc-refs.sh +187 -0
- package/pipeline/scripts/learning-curve.mjs +21 -5
- package/pipeline/scripts/learnings-ledger.mjs +255 -29
- package/pipeline/scripts/memory-load.sh +58 -8
- package/pipeline/scripts/offload-ref.sh +195 -0
- package/pipeline/scripts/triage-memory.mjs +112 -99
- package/pipeline/scripts/uninstall.mjs +15 -5
package/CHANGELOG.md
CHANGED
|
@@ -16,6 +16,32 @@ Internal file-layout changes that don't affect the slash-command surface are sti
|
|
|
16
16
|
|
|
17
17
|
## [Unreleased]
|
|
18
18
|
|
|
19
|
+
## [15.10.1] - 2026-08-21
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
- **`uninstall --all-data` now removes the per-repo memory root.** `~/.claude/memory/multi-agent/` holds the learnings ledger and triage corpus: durable knowledge written in prose about the repos it was collected from, which on a corporate checkout is concrete information about the codebase. It survived even the full-cleanup mode, so "removes everything but your tokens" was not true in the one place a user is most likely to mean it. The default run still keeps it, alongside settings and logs, and both halves of that promise are now tested.
|
|
23
|
+
- **Offloaded tool payloads are reclaimable.** v15.10.0 added `offload-ref.sh` without a way to clean up after it. In worktree modes the payloads die with the worktree, but the `--local` modes write into the real checkout, and because `.multi-agent/refs/` is gitignored the files never appear in `git status` and nothing reclaims them. New `gc-refs.sh`, dispatched as a third phase of `/multi-agent:garbage-collect`, sweeps them with the same contract as the /tmp sweeper: dry-run until `--yes`, a root guard that refuses `/` and `$HOME`, a grace window so a sweep cannot pull a ref out from under a running phase, and node-id matching so a file the user put in that directory survives. `--all` sweeps every checkout under `$HOME`.
|
|
24
|
+
|
|
25
|
+
## [15.10.0] - 2026-08-21
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
- **Per-repo memory now recalls by relevance, not by recency.** Both memory stores ranked by something that was not relevance: `triage-memory.mjs query` scored a raw token overlap with no IDF, so a word present in every row ("view", "test", "error") counted as much as the one word that identified the bug, and `learnings-ledger.mjs brief` did not rank at all - it replayed the newest 20 entries. Past a few hundred rows both degrade the same way: the injected context stops being about the task in hand. New `pipeline/scripts/_retrieval.mjs` owns the arithmetic for both (the way `_cost.mjs` owns pricing): field-weighted BM25, exponential recency, and Reciprocal Rank Fusion with per-ranking damping so recency separates comparably relevant rows without ever promoting an unrelated one. The tokenizer indexes identifiers whole and split (`KeychainStore` is reachable from "keychain") and folds regular plurals, which is what lets a query phrased as prose reach a row that names a symbol. Zero dependencies: no embedding service, no vector store, no second model call. `prefs.global.memoryRecall.strategy: "legacy"` restores the old behaviour in one flag.
|
|
29
|
+
- **`learnings-ledger.mjs profile` and drill-down pointers.** Durable knowledge is now two blocks instead of one, because relevance and prompt-cache reuse pull against each other. `profile` emits a task-INDEPENDENT `<repo-profile>` ordered by confidence, then kind, then statement - byte-stable across runs, so it belongs at the head of a phase prompt where an unchanged prefix is served from cache and grows into an asset as a repo is learned. `brief --task` emits `<task-relevant-memory>`, ranked against the task, and goes after the task text where a per-run difference costs nothing. Every rendered line ends with an `L:<id>` pointer instead of spelling out its evidence; `learnings-ledger.mjs show --id` and `triage-memory.mjs show --id` return the full row. `multi-agent-refs/prompt-assembly.md` carries the placement contract.
|
|
30
|
+
- **`offload-ref.sh`: bulky tool payloads become a pointer plus a tail.** Phase 3 already teed its build output to a file, but nothing decided how much of that file reached the model, so in practice all of it did. The filter parks the full text at `.multi-agent/refs/<node_id>.md` (content-addressed, gitignored) and prints a `[[ref:<node_id>]]` stub with the last lines - where a failing build's error already is. The evidence gate keeps reading the whole log, so what counts as a verified pass is unchanged; only what reaches the prompt shrinks. Wired into Phase 3 builds and Phase 4 test output, opt-in via `prefs.global.contextOffload.enabled`, and a pass-through when off, so the pipe is always safe to write.
|
|
31
|
+
- **Recall precision is measured.** Phase 1 and Phase 4 emit `memory.injected` / `memory.hit`, and `learning-curve.mjs` trends the ratio alongside the existing KPIs. Without the pair, a ranking change that injects the right rows and one that injects noise are indistinguishable from outside: both return five hits and exit 0. `smoke-learnings-ledger.sh` holds the emitter and the consumer to the same event names, so renaming one side fails a gate instead of silently emptying the column.
|
|
32
|
+
|
|
33
|
+
### Security
|
|
34
|
+
- **Entry text cannot forge the boundary of the block it is injected into.** `from-triage` builds ledger statements out of a finding's own words, which are model output, so a statement is untrusted content placed inside a structure the reader parses. Statements are now stored as one line (the schema always said "in one line"; nothing enforced it), and the block delimiters are neutralised at render time in both the ledger blocks and `<repo-memory>`. Angle brackets that are not delimiters are untouched, so `Array<String>` still reads as itself.
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
- `memory-load.sh` takes optional task text and ranks the MEMORY.md pointers against it. The previous `head -30` was a truncation, not a summary - the thirty-first pointer was invisible however precisely it matched, so the block got less useful the longer a repo was worked on. With no task text the index order is unchanged.
|
|
38
|
+
- Phase 4 prior-art lookup and the rejected-preference brief are both ranked against the findings under triage; a finding whose wording matches nothing now returns nothing instead of the three newest rows.
|
|
39
|
+
- `prefs.global.contextOffload.minLines` and `tailLines` are read by `offload-ref.sh`. They shipped in the schema and were honoured by nothing, so a user who set `tailLines: 50` got 20 with no way to tell; config that does nothing documents a control that is not there. An explicit flag still beats the pref, and a non-numeric or zero value falls back to the shipped default.
|
|
40
|
+
- Phase-doc token budget: total 53350 -> 53950. The new prose was compressed twice first (1168 tokens down to 580) by keeping the reasoning in `prompt-assembly.md` and the `offload-ref.sh` header, both outside the budget. Phase 3 and Phase 4 are left amber on their warn lines on purpose - that is the signal that those two docs are next for structural compression rather than another bump.
|
|
41
|
+
|
|
42
|
+
### Tests
|
|
43
|
+
- `test/retrieval.test.mjs` (20 unit assertions on the ranking primitives), `pipeline/scripts/eval-recall.mjs` + `pipeline/eval/recall-cases.json` (8 end-to-end recall cases against a deliberately noisy corpus, each reporting what the pre-ranking scorer would have returned), `smoke-offload-ref.sh`, and new coverage in `smoke-learnings-ledger.sh` and `smoke-per-repo-memory.sh`.
|
|
44
|
+
|
|
19
45
|
## [15.9.1] - 2026-08-20
|
|
20
46
|
|
|
21
47
|
### Changed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mmerterden/multi-agent-pipeline",
|
|
3
|
-
"version": "15.
|
|
3
|
+
"version": "15.10.1",
|
|
4
4
|
"description": "8-phase AI development pipeline with full orchestration on Claude Code, Copilot CLI and Codex CLI. Analysis, planning, TDD, CLI-aware parallel review with consensus surfacing + Fable triage, default-FAIL evidence gates, secret + intent guards, per-phase cost ledger, persistent learnings memory, wiki generation, commit automation. Token-preserving uninstall.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
16
|
"start": "node index.js",
|
|
17
|
-
"test": "node --test test/*.test.mjs && node pipeline/scripts/run-smokes.mjs && node pipeline/scripts/lint-skills.mjs && node pipeline/scripts/lint-mcp-refs.mjs && node pipeline/scripts/eval-triage.mjs && node pipeline/scripts/eval-golden-tasks.mjs && node pipeline/scripts/eval-intent.mjs && node pipeline/scripts/validate-schemas.mjs && node pipeline/scripts/validate-prefs.mjs && node pipeline/scripts/scorecard.mjs",
|
|
17
|
+
"test": "node --test test/*.test.mjs && node pipeline/scripts/run-smokes.mjs && node pipeline/scripts/lint-skills.mjs && node pipeline/scripts/lint-mcp-refs.mjs && node pipeline/scripts/eval-triage.mjs && node pipeline/scripts/eval-golden-tasks.mjs && node pipeline/scripts/eval-intent.mjs && node pipeline/scripts/eval-recall.mjs && node pipeline/scripts/validate-schemas.mjs && node pipeline/scripts/validate-prefs.mjs && node pipeline/scripts/scorecard.mjs",
|
|
18
18
|
"test:unit": "node --test test/*.test.mjs",
|
|
19
19
|
"test:smoke": "node pipeline/scripts/run-smokes.mjs",
|
|
20
20
|
"lint:skills": "node pipeline/scripts/lint-skills.mjs",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: "Sweep leftover /tmp scratch (picker state, review diffs, channel payloads, analysis drafts)
|
|
3
|
-
description-tr: "Geçmiş çalışmalardan kalan /tmp scratch'ini (picker durumu, review diff'leri, kanal payload'ları, analiz taslakları)
|
|
2
|
+
description: "Sweep leftover /tmp scratch (picker state, review diffs, channel payloads, analysis drafts), worktree residue (orphan .worktrees dirs, gitlink index entries) and offloaded tool payloads under .multi-agent/refs/ from past runs. Dry-run first; confirms before deleting. Use when scratch files from earlier runs need clearing out."
|
|
3
|
+
description-tr: "Geçmiş çalışmalardan kalan /tmp scratch'ini (picker durumu, review diff'leri, kanal payload'ları, analiz taslakları), worktree kalıntılarını (sahipsiz .worktrees klasörleri, gitlink index kayıtları) ve .multi-agent/refs/ altındaki offload edilmiş araç çıktılarını süpürür. Önce dry-run; silmeden önce onay alır."
|
|
4
4
|
argument-hint: "[--older-than=<minutes>] [--yes] - dry-run unless confirmed"
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -72,6 +72,31 @@ deletes nothing until you confirm.
|
|
|
72
72
|
- On confirm, re-run with `--yes` and echo its summary line. If a gitlink
|
|
73
73
|
was unstaged, remind the user the removal still needs a commit.
|
|
74
74
|
|
|
75
|
+
7. **Offloaded payload sweep (third phase)** - when the current directory is a
|
|
76
|
+
git repo, also preview the offloaded tool payloads:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
bash $HOME/.claude/scripts/gc-refs.sh ${ARGUMENTS}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`offload-ref.sh` parks build logs, diffs and test output under
|
|
83
|
+
`.multi-agent/refs/<node_id>.md`. In worktree modes that directory dies with
|
|
84
|
+
the worktree; in the `--local` modes it lands in the real checkout, and
|
|
85
|
+
because it is gitignored it never shows up in `git status`. Only files
|
|
86
|
+
matching the node-id shape are touched - anything else in that directory is
|
|
87
|
+
left alone, and files inside the grace window are spared so a sweep cannot
|
|
88
|
+
pull a ref out from under a running phase.
|
|
89
|
+
|
|
90
|
+
- Output says `nothing to do` -> skip silently, no question.
|
|
91
|
+
- Otherwise surface a third `AskUserQuestion` (in `outputLanguage`):
|
|
92
|
+
`question`: "Delete the listed offloaded payloads?" (include the count and
|
|
93
|
+
freed size), `header`: "Payloads", options `{ label: "Delete payloads" }` /
|
|
94
|
+
`{ label: "Cancel", description: "Keep everything, change nothing" }`.
|
|
95
|
+
- On confirm, re-run with `--yes` and echo its summary line.
|
|
96
|
+
|
|
97
|
+
Pass `--all` to sweep every checkout under `$HOME` rather than only the
|
|
98
|
+
current repo.
|
|
99
|
+
|
|
75
100
|
## Notes
|
|
76
101
|
|
|
77
102
|
- An in-flight analysis keeps its drafts in `/tmp/analysis-<slug>-<timestamp>/`
|
|
@@ -79,6 +104,10 @@ deletes nothing until you confirm.
|
|
|
79
104
|
`awaiting_output_decision`, pass `--older-than=<minutes>` so fresh drafts are
|
|
80
105
|
spared, or finish the run first.
|
|
81
106
|
- The /tmp phase only touches `/tmp` scratch; the worktree phase only touches
|
|
82
|
-
the current repo's `.worktrees/` residue and index
|
|
107
|
+
the current repo's `.worktrees/` residue and index; the payload phase only
|
|
108
|
+
touches node-id files under `.multi-agent/refs/`. Persistent per-task logs
|
|
83
109
|
live under `~/.claude/logs/multi-agent/` and are cleaned by
|
|
84
|
-
`/multi-agent:prune-logs`.
|
|
110
|
+
`/multi-agent:prune-logs`. The per-repo memory stores under
|
|
111
|
+
`~/.claude/memory/multi-agent/` are durable knowledge, not scratch: they are
|
|
112
|
+
removed only by `/multi-agent:uninstall --all-data`, or one entry at a time
|
|
113
|
+
with `learnings-ledger.mjs forget`.
|
|
@@ -29,18 +29,27 @@ Also read project-level CLAUDE.md if exists:
|
|
|
29
29
|
**Per-repo memory injection (opt-in via `prefs.global.perRepoMemory`):**
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
|
-
bash $HOME/.claude/scripts/memory-load.sh "$PROJECT_ROOT"
|
|
32
|
+
bash $HOME/.claude/scripts/memory-load.sh "$PROJECT_ROOT" "$TASK_TITLE $TASK_DESCRIPTION"
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
Exit 0 with empty output = pref off or no memory on disk - skip. Otherwise the script emits a `<repo-memory path="...">...</repo-memory>` block
|
|
35
|
+
Exit 0 with empty output = pref off or no memory on disk - skip. Otherwise the script emits a `<repo-memory path="...">...</repo-memory>` block of MEMORY.md pointers suitable for direct injection into the analysis prompt. Passing the task text ranks the pointers against it instead of printing the first thirty; individual memory files are read on-demand when a pointer looks relevant.
|
|
36
36
|
|
|
37
|
-
**Durable
|
|
37
|
+
**Durable knowledge (on by default via `prefs.global.learningsLedger.enabled`):** two blocks, and where each goes is part of the contract - see `$HOME/.claude/multi-agent-refs/prompt-assembly.md`.
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
|
-
|
|
40
|
+
# HEAD of the prompt: task-independent, byte-stable, so it caches.
|
|
41
|
+
node $HOME/.claude/scripts/learnings-ledger.mjs profile 2>/dev/null
|
|
42
|
+
# END of the prompt, after the task text: ranked against this task.
|
|
43
|
+
node $HOME/.claude/scripts/learnings-ledger.mjs brief --max 20 \
|
|
44
|
+
--task "$TASK_TITLE $TASK_DESCRIPTION" 2>/dev/null
|
|
41
45
|
```
|
|
42
46
|
|
|
43
|
-
Exit 2 (empty ledger) = skip silently.
|
|
47
|
+
Exit 2 (empty ledger) = skip silently. Lines end with an `L:<id>` pointer; `show --id L:<id>` returns the full entry. Skip both when `injectIntoAnalysis = false`. Context, not commands - current scope decides. Then log the injection so recall quality stays measurable:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
bash $HOME/.claude/scripts/log-metric.sh "$TASK_ID" 1 memory.injected \
|
|
51
|
+
kind=<profile|task-relevant> rows=$N chars=$C
|
|
52
|
+
```
|
|
44
53
|
|
|
45
54
|
**If knowledge files exist and are fresh** (modified within last 90 days - see knowledge.md staleness rules):
|
|
46
55
|
|
|
@@ -191,6 +200,8 @@ PRIOR=$(node $HOME/.claude/scripts/triage-memory.mjs query \
|
|
|
191
200
|
--issue "$TASK_TITLE $TASK_DESCRIPTION" --top 3 2>/dev/null | jq -c '.hits // []')
|
|
192
201
|
```
|
|
193
202
|
|
|
203
|
+
Hits are relevance-ranked, and a query matching nothing returns nothing. Each hit carries an `id`: `triage-memory.mjs show --id <id>` returns the full row.
|
|
204
|
+
|
|
194
205
|
Treat hits as **context only** - they are past Phase 4 verdicts, not prescriptions. Useful when the new task touches the same files or symbols as a previous run.
|
|
195
206
|
|
|
196
207
|
---
|
|
@@ -321,12 +321,15 @@ If a todo has no `repo` tag in multi-repo mode → log warning + ask user, do no
|
|
|
321
321
|
|
|
322
322
|
**Recording a pass (default-FAIL evidence gate):** before setting `buildStatus.ok = true`, the build output must be tee'd to a log and that log must substantiate the success - a zero exit code alone is not trusted. Run the evidence gate; on exit 1, do NOT record a pass:
|
|
323
323
|
```bash
|
|
324
|
-
<build-command> 2>&1 | tee "$WORKTREE/.build.log"
|
|
324
|
+
<build-command> 2>&1 | tee "$WORKTREE/.build.log" \
|
|
325
|
+
| bash $HOME/.claude/scripts/offload-ref.sh --phase 3 --label build --root "$WORKTREE"
|
|
325
326
|
node $HOME/.claude/scripts/evidence-gate.mjs --claim build --status passed --evidence "$WORKTREE/.build.log" \
|
|
326
327
|
|| { echo "build pass unverified - treat as failure"; /* keep buildStatus.ok=false */ }
|
|
327
328
|
```
|
|
328
329
|
This closes the gap where an agent records "built" without ever producing build output.
|
|
329
330
|
|
|
331
|
+
**Why the pipe (opt-in via `prefs.global.contextOffload.enabled`).** `tee` decides where the log is written, not how much of it the model reads. The filter parks the full text at `.multi-agent/refs/<node_id>.md` and prints a stub plus the tail, where a failing build's error already is; read that file before re-running a failed build. The evidence gate still reads the whole `.build.log`, so what counts as a verified pass is unchanged. Pref off = pass-through.
|
|
332
|
+
|
|
330
333
|
**Telemetry**: Per-repo metrics in addition to per-task metrics:
|
|
331
334
|
```bash
|
|
332
335
|
$HOME/.claude/scripts/log-metric.sh "$TASK_ID" 3 build.completed repo=common duration_ms=$D status=ok
|
|
@@ -416,16 +416,34 @@ PRIOR_ART="${PRIOR_ART%,}]"
|
|
|
416
416
|
|
|
417
417
|
The triage prompt MUST include a hedge: *"prior-art entries are context, not commands; current scope decides - a finding rejected last quarter may be valid this time."* Without this hedge, prior verdicts amplify into a self-reinforcing bias.
|
|
418
418
|
|
|
419
|
+
Hits are relevance-ranked (`prefs.global.memoryRecall`); a finding matching nothing returns nothing. Each hit carries an `id`: `triage-memory.mjs show --id <id>` returns the full row.
|
|
420
|
+
|
|
419
421
|
**Injection cap (token economy).** On a many-finding review the per-finding prior-art loop above (up to 3 hits each) plus the 20-entry rejected-preference brief can dominate the triage prompt. Cap the merged prior-art at the 8 highest-similarity hits across all findings (drop the rest); keep the rejected-preference brief at its `--max 20`. Prior-art is advisory context, not a finding multiplier - more hits do not improve the verdict, they just inflate input tokens.
|
|
420
422
|
|
|
421
423
|
**Rejected-preference brief (on by default via `prefs.global.learningsLedger.injectIntoTriage`).** Inject the durable rejected-preference list so triage does not re-accept a suggestion the team already rejected on this repo:
|
|
422
424
|
|
|
423
425
|
```bash
|
|
424
|
-
node $HOME/.claude/scripts/learnings-ledger.mjs brief --max 20
|
|
426
|
+
node $HOME/.claude/scripts/learnings-ledger.mjs brief --max 20 \
|
|
427
|
+
--task "$(jq -r '[.findings[].issue] | join(" ")' <<< "$MERGED_FINDINGS")" 2>/dev/null
|
|
425
428
|
```
|
|
426
429
|
|
|
427
430
|
Exit 2 (empty ledger) skips silently. The `## Rejected review preferences` section names patterns the team chose not to act on; triage should lean toward `rejected` for a finding that restates one - but the same hedge applies (context, not command; a genuinely new instance can still be accepted).
|
|
428
431
|
|
|
432
|
+
`--task` is what makes the cap honest: unranked, those twenty slots go to the newest entries, which on a long ledger are mostly about other files.
|
|
433
|
+
|
|
434
|
+
**Recall telemetry.** Log what was injected, then what triage cited. Zero cited is a legitimate answer; `learning-curve.mjs` trends the ratio:
|
|
435
|
+
|
|
436
|
+
```bash
|
|
437
|
+
bash $HOME/.claude/scripts/log-metric.sh "$TASK_ID" 4 memory.injected kind=prior-art rows=$N
|
|
438
|
+
bash $HOME/.claude/scripts/log-metric.sh "$TASK_ID" 4 memory.hit rows=$CITED_COUNT
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
**Bulky payloads (opt-in via `prefs.global.contextOffload.enabled`).** Test output and whole-file diffs go through the offload filter, which leaves a `[[ref:<node_id>]]` line plus the tail in context and the full text under `.multi-agent/refs/`. Read that file when the tail is not enough; with the pref off it is a pass-through.
|
|
442
|
+
|
|
443
|
+
```bash
|
|
444
|
+
<test-command> 2>&1 | bash $HOME/.claude/scripts/offload-ref.sh --phase 4 --label tests
|
|
445
|
+
```
|
|
446
|
+
|
|
429
447
|
**Triage prompt skeleton:**
|
|
430
448
|
|
|
431
449
|
```
|
|
@@ -8,14 +8,24 @@ Assemble every phase prompt as two spans, in this order:
|
|
|
8
8
|
|
|
9
9
|
1. **Stable prefix (cacheable, repo-stable, changes rarely):**
|
|
10
10
|
- system / role instructions for the phase
|
|
11
|
-
- the repo
|
|
11
|
+
- the repo profile (`learnings-ledger.mjs profile` -> `<repo-profile>`)
|
|
12
12
|
- extracted conventions (Phase 1c) and the design/analysis doc when applicable
|
|
13
13
|
- repo structural evidence (the reuse-first buckets, Code Connect index)
|
|
14
14
|
2. **Volatile suffix (task-specific, changes every run):**
|
|
15
15
|
- the specific task / issue / diff
|
|
16
|
+
- the task-ranked memory (`learnings-ledger.mjs brief --task ...` -> `<task-relevant-memory>`, and any `triage-memory.mjs query` prior art)
|
|
16
17
|
- the current file(s) under edit
|
|
17
18
|
- per-run state and the immediate instruction
|
|
18
19
|
|
|
20
|
+
## Why durable knowledge is TWO blocks
|
|
21
|
+
|
|
22
|
+
Ranking the ledger against the task makes the injected knowledge relevant. It also makes it different on every run, and anything that differs cannot be a cached prefix. Those two goods are in direct conflict if there is one block, so there are two:
|
|
23
|
+
|
|
24
|
+
- `profile` is task-INDEPENDENT: that, and only that, is what makes it cacheable. Identical bytes across runs, so it sits at the head and is served from cache, and it grows as the repo is learned - the mechanism that makes knowing the repo cheaper rather than more expensive. Its order (confidence, then kind, then statement by code point, never recency) is a separate choice, made for truncation: when `--max` or `maxChars` cuts the block, the entries that survive should be the ones held with most confidence. The cost of that choice is that a new entry lands mid-block rather than at the end, so slightly less of the block's own prefix survives the run that adds it.
|
|
25
|
+
- `brief --task` is ranked against the task in hand and goes AFTER the task text, where a per-run difference costs nothing that was not already volatile.
|
|
26
|
+
|
|
27
|
+
Both blocks end each line with an `L:<id>` pointer instead of spelling out the evidence behind it. The detail is one `learnings-ledger.mjs show --id` away, and the block that would have carried it inline for every entry stays small. The same applies to prior art (`T:<id>`, `triage-memory.mjs show --id`) and to offloaded tool payloads (`[[ref:<node_id>]]`, `offload-ref.sh`): a pointer in context, the evidence on disk, nothing lost.
|
|
28
|
+
|
|
19
29
|
Put the stable material FIRST and the volatile material LAST. Reordering volatile content into the middle of the prefix invalidates the cache for everything after it, so keep the boundary clean.
|
|
20
30
|
|
|
21
31
|
## Why it matters
|
|
@@ -27,5 +37,6 @@ Put the stable material FIRST and the volatile material LAST. Reordering volatil
|
|
|
27
37
|
## Anti-patterns
|
|
28
38
|
|
|
29
39
|
- Interleaving the task text with conventions/learnings (breaks the prefix).
|
|
40
|
+
- Putting the task-ranked block (`brief --task`) in the prefix. It changes every run, so everything after it re-bills at full price - which costs more than the relevance was worth.
|
|
30
41
|
- Letting the stable prefix bloat past what the phase needs (context rot; cache a lean prefix, not everything - see the corpus merge/drop + progressive-disclosure discipline).
|
|
31
42
|
- Regenerating repo evidence from scratch each run instead of reusing cached/learned artifacts (defeats both the cache and the learning curve).
|