@treeseed/dsh-chapters 0.1.24 → 0.2.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/README.md +2 -1
- package/cordis.patch.yml +49 -1
- package/docs/architecture.md +1 -496
- package/docs/backlog.md +234 -4
- package/docs/contract.md +127 -1
- package/docs/development.md +248 -12
- package/docs/host-compaction-seam.md +108 -0
- package/docs/knowledge-repo.md +532 -16
- package/docs/project-state.md +323 -0
- package/docs/verify.md +266 -0
- package/lib/archive.d.ts +49 -1
- package/lib/archive.js +56 -2
- package/lib/archive.js.map +1 -1
- package/lib/arrival.js +24 -10
- package/lib/arrival.js.map +1 -1
- package/lib/attach.d.ts +46 -0
- package/lib/attach.js +120 -0
- package/lib/attach.js.map +1 -0
- package/lib/auto-save.d.ts +74 -0
- package/lib/auto-save.js +175 -26
- package/lib/auto-save.js.map +1 -1
- package/lib/client.js +413 -11
- package/lib/client.js.map +1 -1
- package/lib/commands.d.ts +8 -1
- package/lib/commands.js +135 -7
- package/lib/commands.js.map +1 -1
- package/lib/compose.js +25 -6
- package/lib/compose.js.map +1 -1
- package/lib/continue-core.d.ts +24 -1
- package/lib/continue-core.js +41 -9
- package/lib/continue-core.js.map +1 -1
- package/lib/diag.d.ts +55 -0
- package/lib/diag.js +186 -0
- package/lib/diag.js.map +1 -0
- package/lib/engine-core.d.ts +53 -1
- package/lib/engine-core.js +64 -7
- package/lib/engine-core.js.map +1 -1
- package/lib/engine.d.ts +99 -0
- package/lib/engine.js +488 -36
- package/lib/engine.js.map +1 -1
- package/lib/enrich-queue.js +28 -2
- package/lib/enrich-queue.js.map +1 -1
- package/lib/enrich-wire.d.ts +92 -2
- package/lib/enrich-wire.js +135 -17
- package/lib/enrich-wire.js.map +1 -1
- package/lib/enrich.d.ts +27 -2
- package/lib/enrich.js +114 -18
- package/lib/enrich.js.map +1 -1
- package/lib/index.d.ts +52 -0
- package/lib/index.js +182 -2
- package/lib/index.js.map +1 -1
- package/lib/indexing.d.ts +7 -0
- package/lib/indexing.js.map +1 -1
- package/lib/injections.d.ts +1 -0
- package/lib/injections.js +38 -0
- package/lib/injections.js.map +1 -1
- package/lib/last-resort.d.ts +24 -0
- package/lib/last-resort.js +27 -0
- package/lib/last-resort.js.map +1 -0
- package/lib/prompt.d.ts +130 -0
- package/lib/prompt.js +255 -0
- package/lib/prompt.js.map +1 -0
- package/lib/registry.d.ts +39 -1
- package/lib/registry.js +76 -9
- package/lib/registry.js.map +1 -1
- package/lib/render.d.ts +31 -3
- package/lib/render.js +75 -3
- package/lib/render.js.map +1 -1
- package/lib/repair-run.d.ts +49 -0
- package/lib/repair-run.js +423 -0
- package/lib/repair-run.js.map +1 -0
- package/lib/repair.d.ts +200 -0
- package/lib/repair.js +354 -0
- package/lib/repair.js.map +1 -0
- package/lib/rules-commands.d.ts +22 -0
- package/lib/rules-commands.js +177 -20
- package/lib/rules-commands.js.map +1 -1
- package/lib/rules.d.ts +184 -7
- package/lib/rules.js +312 -27
- package/lib/rules.js.map +1 -1
- package/lib/save-core.d.ts +31 -1
- package/lib/save-core.js +153 -17
- package/lib/save-core.js.map +1 -1
- package/lib/session-rules.d.ts +222 -0
- package/lib/session-rules.js +454 -0
- package/lib/session-rules.js.map +1 -0
- package/lib/store.d.ts +22 -1
- package/lib/store.js +94 -17
- package/lib/store.js.map +1 -1
- package/lib/sync.js +13 -1
- package/lib/sync.js.map +1 -1
- package/lib/tools.d.ts +14 -0
- package/lib/tools.js +321 -32
- package/lib/tools.js.map +1 -1
- package/lib/types.d.ts +12 -1
- package/lib/types.js.map +1 -1
- package/lib/version.d.ts +9 -0
- package/lib/version.js +10 -0
- package/lib/version.js.map +1 -0
- package/lib/warnings.d.ts +10 -0
- package/lib/warnings.js +18 -0
- package/lib/warnings.js.map +1 -0
- package/package.json +9 -3
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
# Project state — the history of what was built and measured
|
|
2
|
+
|
|
3
|
+
**Moved out of `AGENTS.md` verbatim on 2026-10-22.** `AGENTS.md` is loaded into every agent session
|
|
4
|
+
against a workspace instruction budget, and it had grown to 70,401 bytes — over the limit, so the file was
|
|
5
|
+
being silently TRUNCATED when delivered: the rules near the top survived and the ones at the bottom did not,
|
|
6
|
+
which is worse than a smaller honest file. This is the project's record of what happened, what was measured,
|
|
7
|
+
and what remains unverified. It is reference material, not a rule surface: **read it when you need the
|
|
8
|
+
history of a decision, not before every task.** The rules that lived inside it are in
|
|
9
|
+
`AGENTS.md` § Rig and metering rules, and the load-bearing spec stays in `docs/`.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Start Here
|
|
14
|
+
## 2026-10-22/23 — the host 0.2.0-rc.2 acceptance sweep (#56)
|
|
15
|
+
|
|
16
|
+
**What actually happened, in order.** The pinned host moved to 0.2.x; the tape corpus stayed at
|
|
17
|
+
0.1.5-rc.1, so `CORPUS_HOST` closed the acceptance chain and no e2e claim was current (#56/#57). While
|
|
18
|
+
re-anchoring it, seven distinct failures turned out to be **one root cause plus six real defects**: the
|
|
19
|
+
preset pin had been written to a row id the host retired (`agent-presets` → `agent-preset-registry`), so
|
|
20
|
+
every browser boot since the move ran the STANDARD realm — no `compaction/*`, no arrival artifacting, no
|
|
21
|
+
`chapters_rules`. The legs were measuring an absence. Fixing the pin is what made the other six visible.
|
|
22
|
+
|
|
23
|
+
**Defects found by measurement, each with red-first rows, on `feature/56-reanchor-tape-corpus`:**
|
|
24
|
+
- **#77** — the continuation door called `sessions.open`, a verb that does not exist; the real seam is
|
|
25
|
+
`uiWorkspace.openSession` (`docs/contract.md` § Client Sessions Service). Three evenings of "the client
|
|
26
|
+
refuses to catalogue plugin-created children" were a door that never switched anything, while the tool
|
|
27
|
+
reported success. The door now verifies the switch from live state and degrades to
|
|
28
|
+
`switched:false, note:…` rather than claiming it.
|
|
29
|
+
- **The flattened 0.2.x `tool/result` shape** silently disabled arrival artifacting chain-wide (the
|
|
30
|
+
probe decoded `data.message` at the top level, where 0.2.x puts `data.message.message`). Arrival
|
|
31
|
+
artifacted nothing because the predicate was never true — `src/arrival.ts` now takes both dialects,
|
|
32
|
+
with a row that rebuilds the assertion against the host's real event shape.
|
|
33
|
+
- **#79** — the composer ghost ran `command/run` on **every render** (measured 4–26 per session, into
|
|
34
|
+
the transcript this plugin archives) because a `useEffect` listed `[props]` under a comment claiming
|
|
35
|
+
"once per mount"; and it kept pulling suggestions from a disabled feature.
|
|
36
|
+
- **#78** — relief that declines while the surface is over the trigger said nothing. It now prints
|
|
37
|
+
`surface=/trigger=/window=/ratio=` plus the offending node count; observed live:
|
|
38
|
+
`relief declined … (surface=33442 trigger=16000 window=32000 ratio=0.5)`. The engine deliberately does
|
|
39
|
+
**not** override the operator's arrival floor (AGENTS calls that switch a behavior switch, and a rig
|
|
40
|
+
that reinterprets config is the same dishonesty as a pin that is collected and never applied).
|
|
41
|
+
- **#81/#84** — `typeComposer` decided "did my send work?" from the editor's own emptiness, which the
|
|
42
|
+
host clears asynchronously: one session received the same instruction twice (seq 40 and 47). Then the
|
|
43
|
+
guard, matching an 80-character prefix, **swallowed** a genuinely different second instruction. Delivery
|
|
44
|
+
is now decided from the durable log, by the full instruction, after a bounded grace — and a drift row
|
|
45
|
+
fails if any composer call decides from the editor again.
|
|
46
|
+
- **#85** — replay booted a **different realm than record** because the rig's regime lived in three
|
|
47
|
+
places (distill tables, the chain script's inline env, boot defaults) and Playwright project `env`
|
|
48
|
+
reaches only workers, so `globalSetup` used defaults. One table now (`tests/e2e/rig-pins.ts`), read by
|
|
49
|
+
both the config and the boot.
|
|
50
|
+
- **#87** — the deepest one, and the last to become visible: **relief shadows the newest human
|
|
51
|
+
instruction.** Measured from one session's durable log: 13 compaction summaries, 108 distinct shadowed
|
|
52
|
+
seqs, `STILL-HERE` sent eleven times, **ten of them shadowed away**, final assistant text empty.
|
|
53
|
+
`retainRatio` reserves a token fraction as a verbatim tail, so an oversized node in the tail consumes
|
|
54
|
+
it and the question disappears with no error anywhere. The fix is inside our seam and honors
|
|
55
|
+
invariant 1: `carryOpenRequest` appends the newest human turn **verbatim** into the summary we author
|
|
56
|
+
(`openRequestCarryMaxChars`, default 4000; truncation is stated, never silent; injected scaffolding is
|
|
57
|
+
excluded by the one canonical predicate, `isInjectedProse`).
|
|
58
|
+
|
|
59
|
+
**The instruments earned their keep, and four bugs lived in the instruments.** `pending` never
|
|
60
|
+
matched a JSON-escaped newline; `ids.size > 0` disabled every #77 candidate; a report helper resolved
|
|
61
|
+
logs against the import-time home so its own gate could never pass; an unbounded
|
|
62
|
+
`scrollIntoViewIfNeeded` hung a leg for 17 minutes; my own MISS dump was swallowed by its own `catch`
|
|
63
|
+
for a whole run; and a second injected-prose list was caught by the #59 drift row within minutes.
|
|
64
|
+
`docs/verify.md` now carries a release gate shaped by these failures ("an unexercised assertion is a
|
|
65
|
+
rumor of an assertion"), and `npm run typecheck:e2e` exposes **77 errors in a browser plane that has
|
|
66
|
+
never been type-checked** (#83) — after I subtracted the ones my own new code caused.
|
|
67
|
+
|
|
68
|
+
**Still open as this lands:** #86 (a session with the arrival floor off can livelock on one oversized
|
|
69
|
+
result — 89 requests in 20 minutes, three outcomes for the same leg minutes apart), #80 (plugin command
|
|
70
|
+
results are durable but render nothing operator-visible), #82 (a distill and the deterministic suite share
|
|
71
|
+
the corpus directory), #81 criteria 3-4, and the #78 A/B product decision, which is the maintainer's.
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
**Phase 0 passed.** Across nine probe boots on an isolated instance, a plugin created a session seeded with
|
|
75
|
+
exactly one synthetic `user/message`, and that session was **listed** by `sessionQuery.listSessions`,
|
|
76
|
+
**resumable** via `agents.resume({ resumeSessionId })` after disposing the create handle, **durable across a
|
|
77
|
+
process kill**, and its TOC text round-tripped **byte-identical**. Structurally the head is bounded: ~102
|
|
78
|
+
tokens of notice versus ~2,476 for the equivalent transcript. Full measurements and the seed contract:
|
|
79
|
+
[spikes/probe/FINDINGS.md](spikes/probe/FINDINGS.md).
|
|
80
|
+
|
|
81
|
+
**Phase 0b passed (rounds 12-17): the compaction seam is real.** A plugin-subclassed `BasicCompactionEngine`
|
|
82
|
+
with a deterministic `summarize()` ran both the manual (`/compact`) and automatic (`agent/pre-step`)
|
|
83
|
+
paths to durable commit — surface 504→206 tokens, **zero summarization tokens**, log append-only
|
|
84
|
+
throughout. The live engine slot is the **agent-preset realm**, not the host plane; double-provisioning
|
|
85
|
+
one plane fails boot loudly; and the Phase 1 mount is a shipped preset naming `dsh-chapters` +
|
|
86
|
+
`setup`-mounted creation (all of it measured, cited, in
|
|
87
|
+
[docs/host-compaction-seam.md](docs/host-compaction-seam.md) and FINDINGS).
|
|
88
|
+
|
|
89
|
+
**Phase 1 stages 0-4 landed (rounds 18-22): the whole loop works with the real parts.** Registry/store/engine/
|
|
90
|
+
preset-install/tools pass — a realm row names our subpath export (`dsh-chapters/engine`), the cascade
|
|
91
|
+
finalizes post-commit, `presets/chapters/` installs itself into `.agent-presets` at boot (profile
|
|
92
|
+
`!!js` has no `require`, copying was the delivery mechanism) — and since 0.1.16 that copy validates the
|
|
93
|
+
WHOLE shipped file set and repairs missing files additively: the guard once tested only for `preset.yml`
|
|
94
|
+
while `agent.cordis.yml` is the file that mounts the engine, so a partial wipe was a permanent silent
|
|
95
|
+
failure (never clobber a user's edits; DO restore what is absent), and r22 closed the MVP loop: parent →
|
|
96
|
+
`chapters_segment` → `chapters_continue` (ranges only; plugin renders) → TOC-only child with the composed
|
|
97
|
+
preset (15K head) → the CHILD `read` its chapter and echoed its title back. Two real bugs caught and
|
|
98
|
+
fixed en route: finalization must run on the rejection path too (the retry loop can commit, then throw),
|
|
99
|
+
and `validateRanges` refuses before any file is written.
|
|
100
|
+
|
|
101
|
+
**Host 0.1.7-alpha.1 retired the preset mechanism that paragraph describes — do not carry it forward as
|
|
102
|
+
current.** Nothing reads `$DSH_HOME/.agent-presets` any more; a preset is an `@deepseek-ai/dsh-agent-preset`
|
|
103
|
+
*declaration row*, and the chapters preset IS one now: `preset-chapters`, inserted by our own bundle patch,
|
|
104
|
+
with the preset definition in its `config` — so it is a visible Loader entry you can inspect, override, or
|
|
105
|
+
`disabled: true` from a profile patch, exactly like the host's `preset-standard`. **Its row module is ours**
|
|
106
|
+
(`@treeseed/dsh-chapters/preset`, `src/preset.ts`), not the host package: it mirrors the host's plugin line
|
|
107
|
+
for line (`inject: ['agentPresets']`, `static [EntryGroup.key] = true` so child `!!js` expressions survive
|
|
108
|
+
until the child activates, `[Service.init]` yielding the registry disposer so **Cordis** owns the
|
|
109
|
+
registration's lifetime) and adds drop-what-will-not-load. Why not name the host package: one unresolvable
|
|
110
|
+
row aborts the plugin tree of a host predating declaration rows, and one unresolvable *child* makes the
|
|
111
|
+
whole preset `broken` — measured both ways; our module resolves wherever our package runs, and pruning a
|
|
112
|
+
missing child keeps the preset usable instead of declaring it unusable. **The lifecycle is the trap that
|
|
113
|
+
took longest**: registering in `apply()` and disposing through `ctx.effect` un-registered the preset the
|
|
114
|
+
moment activation finished (`register()` reported success while the roster came back WITHOUT `chapters`) —
|
|
115
|
+
same apply-fiber trap r35 caught for the storage domain; a row's own init is the only place with the right
|
|
116
|
+
lifetime. **Support floor is 0.2.0** (user decision 2026-10-16): the legacy `.agent-presets` copy, the
|
|
117
|
+
`presets/chapters/` tree and `preset-install.test.ts` are DELETED, and a pre-0.2.0 host logs
|
|
118
|
+
`this host cannot register preset rows` while its tools keep working. The tape acceptance chain needs
|
|
119
|
+
`dev/dsh-host-lock` moved to 0.2.x plus a re-distill. **#54 did the move: the lock now
|
|
120
|
+
resolves 0.2.0-rc.2 — chosen by WITNESS (the host this box runs the plugin on), not by npm's
|
|
121
|
+
latest — the refusal has lifted itself, and `hostSupportsPresetRows()` is what lifts it (a
|
|
122
|
+
version test in `tests/e2e/boot.ts`, because it runs before any host exists; silently skipping
|
|
123
|
+
the pins would make every segment pass a run that never exercised the preset).** Three 0.2.x
|
|
124
|
+
drifts were found by booting rather than guessing, all rig-side (and a fourth was NOT the host:
|
|
125
|
+
the acceptance chain's remaining failure is a **stale tape corpus**, now gated by
|
|
126
|
+
`tests/fixtures/model-tape/CORPUS_HOST` — `scripts/corpus-anchor.mjs` refuses replay by name and CI
|
|
127
|
+
skips the acceptance job with a tracked reason, issues #56/#57; a gate that blocks `record` too
|
|
128
|
+
would strand itself, so record stays allowed), all the kind that yields a
|
|
129
|
+
WRONG conclusion rather than an error: the durable log is `session.v4.jsonl.zstd` (five hardcoded
|
|
130
|
+
`v3` paths made every log assertion decode nothing and report "the archive never landed" —
|
|
131
|
+
`sessionLogFileFor()` resolves the highest now, pinned by a row); the host opens a **Preview
|
|
132
|
+
Notice**, a focus trap like the 2026-09-18 `Internal Testing Notice`, and dismissal must target
|
|
133
|
+
`[role=dialog]`-shaped elements (matching "any div mentioning a notice" matched the app wrapper
|
|
134
|
+
and pressed an unrelated 'Close'); and **provider config rides `cordis.patch.yml`** — a
|
|
135
|
+
profile-dir `settings.yaml` is read by nothing on 0.2.x, so without an `llm-pi-ai` block the
|
|
136
|
+
host has no `local` provider and the browser opens on "Add an API key to get started" with the
|
|
137
|
+
composer never reachable. Distillation no longer needs the GPU box: `E2E_UPSTREAM[_KEY|_MODEL]`
|
|
138
|
+
makes the tape proxy translate on the way out while the fixture keeps its pinned model id and a
|
|
139
|
+
key-free profile (the tape RECORDS the profile's name — a fixture of this rig, not of the
|
|
140
|
+
service that distilled it). Remaining: re-distill all eight legacy projects (the ninth, `prompt`, was distilled ON 0.2.0-rc.2 in #71 and needs no re-record), then a strict replay and a
|
|
141
|
+
fresh `coverage:e2e` ledger — until then no browser-plane claim is verified.** Health is still the host ROSTER's answer, asked AFTER activation, plus
|
|
142
|
+
`compositionInventory()` rows — **file presence is not preset existence, and "declared" is not "usable"**.
|
|
143
|
+
A session's preset id is durable state (`agent-preset/selected`), so an unregistered id makes every session
|
|
144
|
+
that chose it fail to *open* (`agent-preset/not-found`) while its log sits there intact — and meanwhile the
|
|
145
|
+
plugin boots, registers its tools, and Settings says "enabled and running". Facts with file:line:
|
|
146
|
+
`docs/contract.md` § Presets are declaration rows; the gate that turns the next host rename into a red row
|
|
147
|
+
instead of a broken user session is `tests/unit/preset-declaration.test.ts` (15 rows, each verified to fail
|
|
148
|
+
on the mutation it guards: ralph re-disabled, an unknown pruner key, `toolFilter` restored, `{{cwd}}`
|
|
149
|
+
restored, the preset id renamed).
|
|
150
|
+
Rounds 23-28 then: E3 measured (per-machine — see the Cache section, never cross the numbers),
|
|
151
|
+
`chapters_fork` (r24), the durable title (r25), the 20-check mechanical pass (r26/26b: 18/20 proven
|
|
152
|
+
on-scratch, the rest human-only), the target-hardware verdict (r28: zero-token compaction confirmed at
|
|
153
|
+
llama.cpp counters, ~2 s steady-state turns), and a full-install product bug found-and-fixed by the
|
|
154
|
+
same run — one domain, one opener, `acquireChapterStore` (e0e67d0).
|
|
155
|
+
**MVP state: engine + tools + preset verified ON THE REAL MODEL, and the message-level fork AND
|
|
156
|
+
continue BUTTONS are shipped and e2e-green** (both rows come from one table + one runner in
|
|
157
|
+
`src/client/doors.ts` — adding a third door must extend that table, never copy the body; a test refuses a
|
|
158
|
+
second runner) (`conversation.chat.assistant-actions` → `remote.commands` → watermark
|
|
159
|
+
fork → `sessions.open` after `refresh`; `tests/{unit,integration,e2e}`, Playwright with
|
|
160
|
+
workspace-cached chromium — client-plane facts are now measured, never guessed).
|
|
161
|
+
**Long-context mechanics (architecture amendments, 2026-09-19) are BUILT**: arrival-time
|
|
162
|
+
artifacting — tool results ≥ `toolResultArtifactTokens` never enter the surface (stub +
|
|
163
|
+
content-addressed artifact, queried via `chapters_artifact` toc/search/read; measured: 373KB
|
|
164
|
+
book, zero blob prefills, cacheRead monotone across the stub) — and plot carriage (persona
|
|
165
|
+
`PLOT:` discipline carried through in-place checkpoints; bounded elicited fallback behind
|
|
166
|
+
`elicitedPlot`). e2e is now TWELVE specs in NINE replay projects, each with its own boot (#71 adds the prompt-ghost journey, `prompt` @ 0.99 + NO_AUX_MODEL + E2E_PROMPT=1, scripted-distilled like plans — suite = knowledge-loop +
|
|
167
|
+
discovery @ 0.75 of the pinned 32K/15K stress regime; heavy @ 0.5 with the arrival floor OFF —
|
|
168
|
+
measured: a floor below the chunk size arrival-stubs the surface and silently prevents
|
|
169
|
+
compaction from ever being needed; arrival @ production 0.9 + floor 500; fanout @ 0.75 — the
|
|
170
|
+
chains its child sessions write are the deepest on this hardware and must be distilled in
|
|
171
|
+
isolation, and its child prompt caps readers at SIX 300-line reads (an uncapped qwen looped
|
|
172
|
+
past 30 exchanges; the cap still crosses the threshold inside every child); enrich @ 0.9 with
|
|
173
|
+
enrichmentEnabled; rules @ 0.9; fork @ suite pins — carved out of the shared suite boot 2026-09-25
|
|
174
|
+
when knowledge's fork child became the SPA's restore target for later specs (the fanout lesson
|
|
175
|
+
generalized: one journey, one boot); explore.spec is an on-demand selector-archaeology DEV
|
|
176
|
+
project, never in the CI chain) against port-keyed throwaway homes `var/e2e-home-<port>` — the dev
|
|
177
|
+
home is SHARED with live agents and must never be the test ledger (FINDINGS 2026-09-19: run 13
|
|
178
|
+
asserted against an agent transcript; draft identity, YAML colon-in-plain-scalar, worker-bound
|
|
179
|
+
setupFiles, and same-home sibling boots all bit; identity is now verified via localStorage + the
|
|
180
|
+
log's own header event). ACCEPTANCE RUNS OFF THE MODEL TAPE by default:
|
|
181
|
+
`npm run test:e2e:replay` serves every model turn from `tests/fixtures/model-tape/<project>` (proxy
|
|
182
|
+
`tests/e2e/model-proxy.ts`; explore 14.7 s replayed vs 1.7 min live; recorded usage keeps the
|
|
183
|
+
token meter — and thus threshold behavior — bit-identical); `npm run test:e2e:record` distills
|
|
184
|
+
tapes from the real model (once per scenario; re-record when PRODUCT prompts change —
|
|
185
|
+
persona, tool descriptions, thresholds — but NOT the notice: measured 2026-10-11 that no committed tape prefix contains a seeded notice, so notice prose is currently free; that is coverage luck (no journey makes a continuation child speak), not a guarantee. Workspace docs (AGENTS.md itself,
|
|
186
|
+
skill catalogs) are masked out of matching by the AGENTS-BLIND rule (user decision
|
|
187
|
+
2026-09-20: development artifacts, every real project ships its own — doc edits are
|
|
188
|
+
FREE, no re-record; reaffirmed 2026-10-01 with no exceptions: the tapes stand in for
|
|
189
|
+
ANOTHER project, not development of this one, so no doc edit — near-threshold
|
|
190
|
+
compaction summaries included — ever couples a tape to this repo's docs).
|
|
191
|
+
`rm -rf tests/fixtures/model-tape` for a clean corpus. The normalization
|
|
192
|
+
pipeline is CLOSED — escape/whitespace classes, volatile rules, tool-result content and
|
|
193
|
+
injected instruction blocks matched out; the AGENTS-blind addition was the last change
|
|
194
|
+
and required one final re-record; 2026-09-23 added two DERIVED-COUNT collapses to the
|
|
195
|
+
same family — `(N est tokens)` in chapter index lines and the char count inside
|
|
196
|
+
`⟦omitted:host-injected …⟧` markers (both move with doc/render drift, both identified
|
|
197
|
+
already by their line — pinning them made heavy/enrich tapes brittle across builds). Replay chain verified green — ALL SEVEN segments strict 2026-09-25 and — for the first time — GREEN ON HOSTED CI (GitHub Actions, no model, no GPU): the tape is machine-neutral by contract now — injections mask to fixed tokens everywhere (skill catalogs incl. the 'catalog changed' notice), marker RUNS collapse (a dev box injects more spans than a runner), digest message-tallies collapse to <MSGCOUNT> (they count injections), the enricher's input defuses markers before its cap (hosted divergence at char 758), WS-PATH/DSH-CHECKOUT blind the system prompt's identity lines, the sessions-dir slug is COMPUTED from ROOT (a hardcoded /home/adrian one made CI poll an invisible log for 7 minutes per spec), zstd is a runner dep, the host comes from dev/dsh-host-lock's hermetic lockfile (npm global mode rewrites the plugin tree; rc-ranges float), workspaces boot PRISTINE (sessionIds cleared — restore races ended), and an unborn-mirror push is an honest no-op, not local-only (pristine-store discovery: the old knowledge assertion passed only on accumulated dev state). All 464 deterministic + 85/85/85 gate green on the hosted runner too. The `rules` browser scenario was UN-PARKED — the r39 diagnosis: the spec read zstd-compressed session directories as flat files and hardcoded a rule id; the product was always right. The enrich e2e earned its keep: it exposed two real product bugs invisible to every deterministic layer (verify.md T9/T10 — the body-hash anchor convention mismatch that silently blocked enrichment since P2, and the missing host-plane route) (r38 lesson: the DRAFT screen turns composer text into a first MESSAGE, not a command — establish a session before exercising command surfaces; and RR2=137: a chatty live loop that records mid-test can be OOM-killed — keep e2e prompts tool-free in WORDS the model obeys; and 2026-09-23: a tape records the CONTEXT it was distilled in — re-record whole projects, never one spec of a shared boot, and never run two GPU suites at once). 608 unit/integration, zero skips when the TreeDX container is up (live-gate fails loudly when configured-but-unreachable; skips only with no configuration at all), under a GATED coverage ledger: `npm run coverage:ci` (c8 --check-coverage) enforces ≥85% statements/branches/functions/lines in CI — current 97.94 / 86.47 / 93.82; `npm run coverage:e2e` remains the browser-plane ledger (eight replays since the plans journey joined, boot-level V8 profiles remapped through REAL source maps; current 79.92/64.64/76.28 — reported, not tuned; first ledger 80/62 before the audit). The 2026-09-23 coverage audit closed its own gap list the same way: providerKindFor policy rows, credential migration, the git-command surface incl. every refusal text (T13), the TreeDX client's failure vocabulary (timeout/bad_json/envelope), git-plane auth (T11), the push retry/divergence paths, scheduler pull rows, listing pagination over a paginating stub (T12), overlay-delete, rule-number walk-past by prefix (id-uniqueness), the composer busy-retry budget, and the coverage instrumentation itself; then the deterministic plane was swept to the gate — engine-lifecycle (real cordis Context + real base engine: engine.ts 40→91 stmts), the tool wrappers' full success/refusal surface, the TreeDX error taxonomy over stub faults + parser-shape drift via injected fetchImpl, the local-path git transport, host-command error arms, and the pure planners (vocabulary/enrich/indexing/search/arrival/archive) — catching one REAL bug (canonicalizeRemote carried embedded credentials into the project key: same repo with/without creds forked the pool; fixed + pinned). CI/CD shipped 2026-09-24 and went fully green 2026-09-25: .github/workflows/ci.yml (deterministic gate + seven-segment tape-replay acceptance with NO LLM/GPU/key — tape misses fail loud, hosted runs ship diff-dumps sorted by shared prefix) and release.yml (tag = package version → suite-gated npm publish --provenance; NPM_TOKEN strategy chosen, wired for the treeseed-ai org move); the pristine-runner dress rehearsal caught the scaffold-clobbering patch guard, the credential-less-provider death, and the unregistered-workspace home — plus the profile is now built with plain npm (dsh plugin add was a pnpm forwarder — nothing in this repo touches pnpm, and it is not and never will be a monorepo; the host lock is a standalone manifest). docs/development.md § Hosted CI.
|
|
198
|
+
Git history was rewritten 2026-09-20 (test-results/.dsh purged, .git 7M→840K) and force-pushed —
|
|
199
|
+
clone fresh or `git reset --hard origin/main` after pulling.
|
|
200
|
+
**Knowledge layer P1 (record §13) is complete and live-proven**: signatures collect at real
|
|
201
|
+
`turn/end`s (r35j), the fork composes topics live (r36 — same-file turns merge, topic changes
|
|
202
|
+
split), the index + `chapters_search` + redaction + `/chapters-link`/`/chapters-status` ride a
|
|
203
|
+
sync loop that degrades to local-only honestly and rebuilds the mirror on divergence, with the
|
|
204
|
+
two-machine exit criterion automated over a real git smart-HTTP remote (integration + the
|
|
205
|
+
`knowledge.spec.ts` browser journey). **P2 is BUILT (2026-09-20)**: the idle/after-push
|
|
206
|
+
enrichment ladder with validation-before-commit, provenance chains (`generated:` newest-first),
|
|
207
|
+
the body-hash guard as the only sanctioned chapter-file mutation, `/chapters-enrich
|
|
208
|
+
run|model|report`; shadow-by-default emergent vocabulary writing git-visible `topic-alias`
|
|
209
|
+
curation + derived `topics/vocabulary.json`; and index-level fragment stitching (adjacent
|
|
210
|
+
legacy fragments cohere into ONE search entry citing all members — nothing merged on disk). Two-machine exit criterion proven — catching r37 on the way: stageAllAndCommit compared the workdir against itself, making modifications to tracked files permanently invisible to commits (additive P1 hid it; enrichment would never have traveled). **P3 is BUILT (2026-09-20, §15 amendment: approval = per-machine curation fact, never a file rewrite)**: write-once proposed rule files, /chapters-rule add|list|approve|revoke, chapters_rule_propose tool, verbatim core block in the continuation notice (byte-identical ABSENT, golden-tested), refuse-with-numbers overflow budget, category-aware search with a per-machine core bonus, and the rules lifecycle proven transport-grade over a real pool with three machines. **P3c — two-tier rules is BUILT, both capture speeds (#50; record §7.1 + §7.3–§7.9, amendments 2026-10-20 and 2026-10-21). Two things remain unverified, for different reasons: the **browser plane** (the host pin moved to 0.2.0-rc.2 in #54 and the boot's named refusal lifted, but the corpus still has to be re-distilled on the new host before ANY browser-plane claim is real — this feature added a tool AND changed the preset persona, both prompt bytes), and **mining quality beyond n=3** — `npm run mining:probe` did run against the real session model (`qwen3.8-flash`, regional token-plan door; the GPU box was unnecessary) on three of this repo's own chapters: 9 rules, 6 stated / 3 inferred, all inferences confined to the chapter with no human text. See `docs/verify.md § Two-Tier Rules` R5 and `docs/backlog.md` 1b/1c.** The hole was concrete, not vague: `planSummarize` composes `leadSections = [planSection, plotSection]`, so a plan survives an in-place compaction and a plot survives it, and a **rule does not**. Session rules (`scope: session`, local, never staged — one filter in `planStoreToRepo`) captured by a deterministic `ADD RULE:` parse at the existing `turn/end` listener (zero model tokens, invariant 1) plus enrichment capture (`rulesEnrichment`, ON) that MINES standing orders the human never said and lets them govern on arrival — `rulesAutoActivate: true` is the default, and the earlier design that required a verbatim human quote before a mined rule could apply was REJECTED by the maintainer and removed (record Amendment 2026-10-21): demanding proof that a human already said it makes the tier redundant with the deterministic marker, and the premise of the tier is that the human may not know. Visibility replaced permission: each mined rule renders with its warrant (`stated by you, mined from ch.N` / `inferred from ch.N — nobody said it`), the agent must NAME the session rules that shaped its reply, at most 3 per chapter plus per-tier display caps, and `reviewOnly` + `approve` is the opt-in review-before-trust posture; `rulesModel` gives mining a model of its own (one extra bounded call, and its answer REPLACES the annotation pass's rules). One auxiliary completion budget, `enrichmentMaxTokens` (default 4000), is the single source for that ceiling on both planes — the route measured here does not enforce `max_tokens` and counts reasoning tokens toward completion usage, so a small number is not safety, it is truncation on a stricter provider. What did not loosen: a session rule never overrides a project rule, and never becomes one without a human. Activation itself is unchanged: **DERIVED** from the ancestor path, never a `curation.jsonl` fact — the 2026-09-20 per-machine approval boundary is NOT relaxed, it still governs the entire shared tier. Rules are carried at the two moments a head is rebuilt — the continuation notice AND the in-place compaction checkpoint (`rulesCarry` is the third lead section in `planSummarize`, and `session-rules.renderRulesBlock` is the ONE builder both paths plus `chapters_rules` call: a source-shape row fails if either door starts picking rules itself) — **never injected per request**: invariant 3, and measured llama.cpp `cacheReuse 0` on a head-position replacement means a churning ruleset is a cache-wipe machine. `chapters_continue` carries both tiers, `chapters_fork` carries project only — the SAME `intent` axis plans already use (`docs/plans-carriage.md` §§ 9–13), not a new distinction. **#52 (same round, second half): rule ancestry ALSO crosses the delegation edge** —
|
|
211
|
+
`session-rules.ruleAncestorSet` walks registry `parentSession` ∪ the kernel header's
|
|
212
|
+
`parentSession`, transitively, READ-ONLY, upward only (a subagent inherits its delegator's orders; a
|
|
213
|
+
child's inferences never govern the parent), bounded at 64 expansions and LOUD when the cap bites;
|
|
214
|
+
every door, the checkpoint and `/chapters-rule active` call that ONE walk, and an anti-drift row
|
|
215
|
+
fails if a plane computes ancestry by hand again — hand-built ancestry is how delegated subagents
|
|
216
|
+
ended up with zero rules. And the `chapters` preset's **persona suffix** is the per-composition
|
|
217
|
+
channel that tells a session — fresh, continued, or delegated — to run `chapters_rules` before a
|
|
218
|
+
task and `chapters_search` for history: that is allowed because it is preset-scoped, not a
|
|
219
|
+
system-prompt contribution (invariant 3), and two couplings are pinned by behaviour: the suffix must
|
|
220
|
+
stay matched by the plot template-echo guard, and it may not introduce a `{{variable}}` the host has
|
|
221
|
+
not registered. Changing that suffix is a PRODUCT PROMPT change — the tape re-distill debt GROWS,
|
|
222
|
+
it does not shrink. Selection is pinned → topicality → index-only, via `rules.selectRules` — the head's selector, deliberately NOT `search.rankEntries`, which ranks the corpus (one implementation of each job, and bending a rebuilt head to read index shards was rejected); per-tier count caps config-driven, and the **withheld count is mandatory** — silent selection would rebuild "lost in the noise" inside the fix for it; the token ceiling still refuses with numbers, never clips. **No global tier was built, because none was needed**: `projectKey` is UUIDv5 over the canonicalized *remote* and `harnessId` defaults to hostname, so checkouts of one repository already share one rule namespace; what that round actually owes is loudness for the four ways the identity silently diverges (record §7.8) — a `path-derived` key, a fork listed first, TreeDX keying on the pool location, and an unlinked/un-synced clone, each of which must SAY it rendered zero rules: `/chapters-status` now prints an `Identity:` line and warns when the derived key differs from the bound one (the repo's own non-git test fixture trips it, so the warning is not theoretical).
|
|
223
|
+
One design lesson from that reversal, because it is the recurring mistake in this repo: a safety mechanism must block the harm, not the value. The quote gate blocked invented rules by also blocking every inferred one; the mechanisms that replaced it (a rendered warrant, the agent naming its rules, caps, one-command revoke) hold the same risk without amputating the feature. Suspicion of model authorship is a reason to make output visible and reversible, not a reason to demand human provenance.
|
|
224
|
+
Two local build facts, both learned the hard way: the deterministic suites need **Node ≥ 24** (`engines`), and on this box's Node 22 every test file fails in a way that looks like your diff; and until 2026-10-21 `npm run coverage:ci` intermittently reddened `git-auth-classification`/`sync-remote` with an EPIPE blamed on their `before` hook — **diagnosed as pre-existing, which was true and not the point**: the test git server wrote request bodies into a CGI child's `stdin` with no error listener, so a child that had already exited raised an async EPIPE nobody handled. Streams are guarded now (three consecutive combined runs: 800 pass / 0 fail). Recording the sequence because the mistake is the reusable part: proving a failure predates your diff is a step, not an answer.
|
|
225
|
+
**Knowledge transport is now a provider seam (2026-09-21, record §15: "transport is pluggable; git is one provider" + "host-injected context is not conversation")**: `src/provider.ts`'s six verbs carry every sync path; `kind:'git'` (default, every pre-existing record — zero behavior change) or `kind:'treedx'` (`/chapters-link treedx+<url>/<repo> <token>`, `src/treedx/`: workspace-create → overlay-write → commit transport, `.treedx-state.json` mirror memory). Search/rules/index/notice layers read the materialized mirror unchanged — §3.1 survives both backends; divergence routes through rebuild-from-the-store in both; TreeDX lease contention maps to the existing rejected→pull-retry, moved head to diverged. Contract + truth table in `docs/provider.md`; TreeDX API facts (doc-reads vs live-measured, with corrections) in `spikes/treedx/FINDINGS.md`; dev loop `scripts/treedx-local.sh` + `dev/treedx.compose.yaml` (the cloned repo's own compose is platform-bound and will not boot standalone — measured). Transport-grade proof: 24 tests over an in-process stub (`treedx-{provider,sync,link}.test.ts`); the real-container exit criterion (`treedx-live.test.ts`) ran and PASSED 2026-09-21 (5/5 against a booted dev-auth service; full suite 286/286 zero skips — the live run caught three parser shapes, the born-with-`.treedxkeep` phantom-deletion trap, the lease-survives-TTL close requirement, and a swallowed auth detail, all fixed and pinned). **Re-verification 2026-09-23 caught one more, in the wild:** `paths/list` PAGINATES (limit 100, base64 offset cursor in `page.nextCursor`) — reading one page silently truncated the corpus once the shared dev repo grew past 100 paths (machine B reported 'up to date' while missing real files). `corpusPaths` now follows the cursor to the end; the paginating stub + a 150-file row pin it (T12), and the live container proves it at the real service. **The same round landed the injection screen** (`src/injections.ts`): host-injected context (instruction files, runtime snapshots, skill catalogs, our own TOC notice — event-level and `<system-reminder>` spans inside human messages) is marked-and-counted, never archived; the conversation-bytes guarantee and TOC `(N msgs)` now say what is actually true, and the knowledge corpus carries no project's instruction bytes (H1 rows in `docs/verify.md`).
|
|
226
|
+
**Session-plan carriage is BUILT (2026-10-09, `docs/plans-carriage.md`)**: `src/plans.ts` pairs an
|
|
227
|
+
`exit_plan_mode` call with the `tool/result` that carries the human verdict (approved / rejected /
|
|
228
|
+
dismissed / pending / superseded / failed — the fifth class is the review never opening, which is not a
|
|
229
|
+
verdict); chapters carry a `## Plans in this chapter` block AFTER the verbatim transcript with the plan's
|
|
230
|
+
outline, bounded head and content-addressed full text; `plan/mode` folds last-value-wins into `planMode:`
|
|
231
|
+
instead of falling into `unrenderedSeqs`; the child's notice carries the operative plan (intent-aware
|
|
232
|
+
since the second round — see below; a pending one is marked unapproved); plans are forced chapter boundaries in
|
|
233
|
+
both segmenters and title the chapter they conclude. Two truths worth keeping: the token meter is NEVER
|
|
234
|
+
exempted (the block is counted by `preflight`, and a plan-less path renders zero bytes — which is why all
|
|
235
|
+
seven tapes still replay WITHOUT re-recording, verified 2026-10-09), and a plan whose review is still open
|
|
236
|
+
sits in an unfinished turn above the archive ceiling, so it is carried as live and un-stored. Fixed on the
|
|
237
|
+
way: chapter `artifacts/…` citations pointed at paths `chapters_artifact` could not resolve at all
|
|
238
|
+
(`writeArchive` writes inside the session tree; the resolver only looked at the store root) — true for
|
|
239
|
+
every deferred blob since arrival-stubbing shipped, now pinned (verify.md P10). The browser plane is IN the chain (project `plans`, verified 2026-10-10); its tape is distilled from
|
|
240
|
+
the scripted upstream `tests/e2e/stub-model.ts` because this box's model server drops the stream
|
|
241
|
+
mid-turn — read `tests/fixtures/model-tape/plans/PROVENANCE.md` before quoting what it proves (the
|
|
242
|
+
carriage yes, a model's choice to plan no). **Second round, same day, four decisions landed**
|
|
243
|
+
(`docs/plans-carriage.md` §§ 9–13, verify.md P12–P17):
|
|
244
|
+
- **Intent decides what a child is told.** A CONTINUATION (`chapters_continue`) extends the same stream
|
|
245
|
+
of work, so it gets `## Active plan` + `## Previous plans on this path` and, while the parent was still
|
|
246
|
+
planning, a seeded `plan/mode`. A BRANCH (`chapters_fork`, the fork button) splits a new direction, so it
|
|
247
|
+
gets **zero** plan bytes — its plans are history, in the cited chapters and as documents.
|
|
248
|
+
- **A plan is a document of a third kind**: `kind: plan`, written in `chapters/` as `plan-NNN-<slug>.md`
|
|
249
|
+
with its own registry counter (`nextPlanNumber`, allocated through the registry — never a directory
|
|
250
|
+
scan), so it never consumes a chapter number, is carried by the pool transport and the mirror rehydrate
|
|
251
|
+
for free, and is findable with `chapters_search` **as a plan**. Superseding writes a new document; the
|
|
252
|
+
old one keeps its bytes.
|
|
253
|
+
- **The enrichment ladder never touches a plan document.** Directives are not narrative; paraphrasing an
|
|
254
|
+
order is the drift this store exists to prevent. The guard reads the file's own frontmatter.
|
|
255
|
+
- **The operative plan is carried into every in-place checkpoint** (the plot mechanism, but chosen
|
|
256
|
+
session-wide and newest-first), bounded, with truncation announced.
|
|
257
|
+
- **Findability round (0.1.12, `docs/plans-carriage.md` § 14, knowledge-repo Amendment 2026-10-11): two
|
|
258
|
+
doors, ONE scorer (`search.rankEntries`) — the pool mirror when pulled, the workspace store otherwise
|
|
259
|
+
(and as a fallback when the pool has no match). A bare workspace used to answer `chapters_search` with
|
|
260
|
+
"link a remote" while its own chapters, `kind: plan` documents and rules sat two directories away; that
|
|
261
|
+
was the flaw the audit found. The answer must NAME ITS DOOR ("searched this machine's store, N docs …")
|
|
262
|
+
so "this machine only" is never read as "the whole project", and "nothing matched" ≠ "nothing archived
|
|
263
|
+
here" are reported apart (`documents`/`empty`). Artifacts are never indexed (they answer to
|
|
264
|
+
`chapters_artifact`; a second index is forbidden). The notice gains `## Finding things` naming
|
|
265
|
+
read/chapters_artifact/chapters_search (`retrievalHints`, default on) because an unmentioned tool does
|
|
266
|
+
not exist to a small model. Two pinned rows asserted the old contract and failed loudly — that is why
|
|
267
|
+
this is a fact rather than a guess; amended with the reason in the diff.
|
|
268
|
+
- **Kernel-drift gate (`tests/unit/host-seed-contract.test.ts`)**: the seeded `plan/mode` depends on the
|
|
269
|
+
host knowing the type and treating it as NOT surface-eligible. Read the kernel's OWN exported predicates
|
|
270
|
+
(`KNOWN_SESSION_EVENT_TYPES`, `isSurfaceEligibleType`, `isAppendSurfaceEvent`), never reimplement them —
|
|
271
|
+
a kernel change that silently ungates a child must show up as a red row, not as a passing suite.
|
|
272
|
+
- **Two deterministic doors, one builder (`/chapters-fork`, `/chapters-continue`)** — the load-bearing
|
|
273
|
+
lesson of attempt 5: **a tape cannot carry a model-authored range.** The recorded `chapters_continue`
|
|
274
|
+
said `endSeq: 30`, the runner's ceiling was 29, and `validateRanges` refused it — correctly. And a small
|
|
275
|
+
local model will not reliably chain `chapters_segment` → `chapters_continue` at all. So anything that must
|
|
276
|
+
be accepted repeatably goes through the plugin's OWN derivation (`deriveRanges`/`composeChapters`, zero
|
|
277
|
+
model involvement); the tool remains for the model's use but is never the only proof path. Both doors come
|
|
278
|
+
from `deterministicCommand(door)` and a row pins that they **archive identically** (same ranges, titles,
|
|
279
|
+
plan facts) — two implementations would drift. `intent` is the ONLY semantic difference: continue ⇒
|
|
280
|
+
`## Active plan` (+ seeded `plan/mode` while still planning); branch ⇒ zero directive bytes.
|
|
281
|
+
Also measured there: **the turn containing a plan submission does not end until the review is answered**,
|
|
282
|
+
so a session whose only turn is pending has no archive ceiling and the door refuses with its first guard.
|
|
283
|
+
- **The one exception to the injection screen**: the harness's `plan-mode` + `form:'notice'`
|
|
284
|
+
mode-flip sentence archives verbatim (named by producer AND form, so it cannot widen — v3 wrapper
|
|
285
|
+
`{kind:'plugin', plugin:'plan-mode'}` and v4 `{kind:'plan-mode'}` both match, issue #43).
|
|
286
|
+
`isHumanAuthored` stays strict on purpose — it drives topic signatures, and the r28 lesson was that
|
|
287
|
+
injected text moves chapter boundaries.
|
|
288
|
+
**Outstanding: the human verification rows (live `/compact` in a real browser, readability
|
|
289
|
+
judgment), the seeded-gate case OBSERVED in a browser (pinned deterministically; needs a second distilled session — `docs/plans-carriage.md` § 15), per-message anchoring (the button forks at the conversation's end today; `deriveRanges`
|
|
290
|
+
already supports any anchor), and the default-preset shipping policy (opt-in menu vs profile
|
|
291
|
+
default), which is the user's call, not ours.**
|
|
292
|
+
|
|
293
|
+
The fiber-eviction trap `dsh-session-fork/src/branch.ts:10-14` warns about did **not** bite — an unattached
|
|
294
|
+
session was the whole explanation for round 6's "not listed", fixed by
|
|
295
|
+
`workspaceRegistry.createCanonical(cwd)` → `workspace.attachSession(id)`.
|
|
296
|
+
|
|
297
|
+
**Cache behaviour measured (rounds 10-11, 13, 16, real provider).** A continuation builds its own cache —
|
|
298
|
+
0% hit cold, 74% warm — and the parent keeps hitting across the boundary, so **G3 and G2 are green**. G1's
|
|
299
|
+
head is now *measured* for the composed shape: r16's `setup`-mounted child's first request was **13,315
|
|
300
|
+
tokens (27-tool header, this roster)** — quote the older "≈ 8K" only as a projection. Steady state it
|
|
301
|
+
protects: 396,800 cached + 822 uncached (**99.79%**) live session. E3 measured — and it is
|
|
302
|
+
per-machine (r23/r28): on the cloud provider a replacement keeps ~7.4K of prefix cached (refill
|
|
303
|
+
halves, 13,658 → ~7,000); on llama.cpp (the target) the slot reuses NOTHING across a head-position
|
|
304
|
+
replacement (cacheReuse 0) — the local win is zero-token compaction (r28: 0 prompt tokens vs ~11 min
|
|
305
|
+
for an LLM summary on that box) plus ~2 s steady-state turns. **Never quote one machine's cache
|
|
306
|
+
number for the other.**
|
|
307
|
+
|
|
308
|
+
Metric formula, since getting it wrong produces 1721% hit rates: `totalPrompt = inputTokens +
|
|
309
|
+
cacheReadTokens`; `hit = cacheReadTokens / totalPrompt`. Judge "cache undisturbed" by
|
|
310
|
+
`cacheReadTokens` not collapsing, **not** by hit percentage, which falls innocently as the prompt grows
|
|
311
|
+
past a cache-block boundary.
|
|
312
|
+
|
|
313
|
+
Phase 1 follow-ups CLOSED: the title call is the *service* (`sessionController.rename`, live-handle
|
|
314
|
+
window — r25 durable), and the preset/composition note in invariant 2 ships in every child.
|
|
315
|
+
|
|
316
|
+
Coexistence with `dsh-session-fork` is currently **untestable, not disputed**: the shipped example fails
|
|
317
|
+
to boot the installed host ALONE (its rc.2-era code vs rc.1's `webServer` inject requirement — verified
|
|
318
|
+
by pair-boot then solo-boot, r26). Name-spaces are disjoint by inspection; re-run the pair-boot on a
|
|
319
|
+
version-matched host before claiming either way. The dev/test loop that avoids restarting the harness
|
|
320
|
+
being edited from is solved — see [docs/development.md](docs/development.md).
|
|
321
|
+
|
|
322
|
+
Then [docs/contract.md](docs/contract.md) for how to declare things, and
|
|
323
|
+
[docs/verify.md](docs/verify.md) before claiming any step passes.
|