@treeseed/dsh-chapters 0.1.16 → 0.1.18
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/cordis.patch.yml +211 -8
- package/docs/backlog.md +79 -0
- package/docs/contract.md +93 -6
- package/docs/development.md +64 -1
- package/docs/plans-carriage.md +1 -1
- package/docs/verify.md +2 -1
- package/lib/index.d.ts +50 -32
- package/lib/index.js +146 -110
- package/lib/index.js.map +1 -1
- package/lib/preset.d.ts +186 -0
- package/lib/preset.js +212 -0
- package/lib/preset.js.map +1 -0
- package/package.json +9 -5
- package/presets/chapters/agent.cordis.yml +0 -272
- package/presets/chapters/preset.yml +0 -3
- package/presets/probe-roots/agent.cordis.yml +0 -11
- package/presets/probe-roots/preset.yml +0 -3
package/cordis.patch.yml
CHANGED
|
@@ -10,17 +10,41 @@
|
|
|
10
10
|
# Two planes, two surfaces:
|
|
11
11
|
# HOST PLANE — the `config:` block below (defaults shown commented; set any
|
|
12
12
|
# under your profile's plugin entry).
|
|
13
|
-
# REALM PLANE — the compaction ENGINE row
|
|
14
|
-
#
|
|
13
|
+
# REALM PLANE — the compaction ENGINE row inside the preset's composition (the
|
|
14
|
+
# `preset-chapters` row at the foot of this file); shared names behave per
|
|
15
15
|
# plane. Design record: docs/knowledge-repo.md §12.
|
|
16
16
|
#
|
|
17
|
+
# PRESET DELIVERY — the preset is a DECLARATION ROW, exactly like the host's own presets.
|
|
18
|
+
# `preset-chapters` (the id is the host convention `preset-<id>`) is inserted by this
|
|
19
|
+
# file, its `config` IS the preset definition (`id/name/description/order/plugins`), and
|
|
20
|
+
# because it is an ordinary Loader entry you can see it in the plugin list and the config
|
|
21
|
+
# editor, override it from your profile patch, or `disabled: true` it.
|
|
22
|
+
# Its row module is ours — '@treeseed/dsh-chapters/preset' (src/preset.ts) — not the
|
|
23
|
+
# host's '@deepseek-ai/dsh-agent-preset'. It mirrors that plugin line for line (injects
|
|
24
|
+
# `agentPresets`, preserves child `!!js` expressions, yields the registry disposer to
|
|
25
|
+
# Cordis) and adds one thing: rows this host cannot import are dropped at boot and NAMED,
|
|
26
|
+
# because a single unresolvable child row makes the WHOLE preset `broken` — which is the
|
|
27
|
+
# same user-visible failure as no preset at all (measured: `DECLARED BUT BROKEN —
|
|
28
|
+
# tool-schedule … never started` on a host where that package was never published). The
|
|
29
|
+
# host package cannot be named directly because a row that fails to import aborts the
|
|
30
|
+
# plugin tree of any host predating declaration rows; ours resolves everywhere our
|
|
31
|
+
# package runs. Support floor: 0.2.0.
|
|
32
|
+
# After activation the plugin then ASKS THE ROSTER, and the mounted rows: file presence
|
|
33
|
+
# is not preset existence, and "declared" is not "usable". The `dsh-chapters:` lines at
|
|
34
|
+
# startup say which of the four states you are in.
|
|
35
|
+
#
|
|
17
36
|
# config:
|
|
18
|
-
# # ---- identity
|
|
19
|
-
# installChaptersPreset: true #
|
|
20
|
-
# #
|
|
21
|
-
# #
|
|
22
|
-
#
|
|
23
|
-
# #
|
|
37
|
+
# # ---- identity -------------------------------------------------------
|
|
38
|
+
# installChaptersPreset: true # INERT since 0.1.18 — the preset is the `preset-chapters`
|
|
39
|
+
# # row above, so `disabled: true` on THAT row is the switch.
|
|
40
|
+
# # Kept so an existing profile patch still parses.
|
|
41
|
+
# compactionThresholdRatio: 0.9 # INERT since 0.1.18 for the same reason: it is
|
|
42
|
+
# # `thresholdRatio` on the preset's chapters-engine row now.
|
|
43
|
+
# # fallbackPreset: 'chapters' # preset mounted for children when the
|
|
44
|
+
# # caller's own preset cannot be resolved — and
|
|
45
|
+
# # the id the roster check looks for. This id is
|
|
46
|
+
# # DURABLE STATE: every child's log records it,
|
|
47
|
+
# # so renaming it orphans those sessions.
|
|
24
48
|
# harnessId: '<hostname>' # per-machine identity: git commit author,
|
|
25
49
|
# # token scoping, edits/<harness>/ curation
|
|
26
50
|
# # partition, rule-approval authorship
|
|
@@ -98,3 +122,182 @@
|
|
|
98
122
|
- insert:
|
|
99
123
|
- id: '@treeseed/dsh-chapters'
|
|
100
124
|
name: '@treeseed/dsh-chapters'
|
|
125
|
+
|
|
126
|
+
# ── the `chapters` agent preset — a DECLARATION ROW, exactly as @deepseek-ai/dsh-web-app
|
|
127
|
+
# ships its own presets/standard.patch.yml. Row id `preset-chapters` follows the host's
|
|
128
|
+
# `preset-<id>` convention; `config.id: chapters` is DURABLE STATE (every continuation
|
|
129
|
+
# child records it as `agent-preset/selected`, so renaming it orphans those sessions).
|
|
130
|
+
#
|
|
131
|
+
# The row's `name` is OUR plugin (`@treeseed/dsh-chapters/preset`), which mirrors
|
|
132
|
+
# @deepseek-ai/dsh-agent-preset line for line — `inject: ['agentPresets']`,
|
|
133
|
+
# `static [EntryGroup.key] = true` so child `!!js` expressions survive until the child
|
|
134
|
+
# activates, `[Service.init]` yields the registry disposer so Cordis owns its lifetime —
|
|
135
|
+
# and drops rows THIS host cannot import before registering. Why not the host package
|
|
136
|
+
# directly: a row naming @deepseek-ai/dsh-agent-preset is unresolvable on a pre-0.1.7
|
|
137
|
+
# host and an unresolvable row aborts that host's whole plugin tree, while one stale
|
|
138
|
+
# child row (e.g. @deepseek-ai/dsh-tool-schedule, unpublished at 0.2.0-rc.2) makes the
|
|
139
|
+
# ENTIRE preset `broken` — measured both ways. Same row, same visibility in
|
|
140
|
+
# `dsh --dump-default-config`/the config editor, no boot that it can brick.
|
|
141
|
+
#
|
|
142
|
+
# Four deltas from the host's own standard list, each ours deliberately:
|
|
143
|
+
# 1. compaction-basic -> chapters-engine (the zero-inference summarize())
|
|
144
|
+
# 2. tool-result-pruner.thresholdChars 8192 -> 1000000 (dormant by choice: chapters
|
|
145
|
+
# defer oversized results at ARCHIVE time; an in-surface prune is a prefix cliff
|
|
146
|
+
# on local hardware — and that plugin REJECTS unknown config keys, so the three
|
|
147
|
+
# keys below are the whole surface)
|
|
148
|
+
# 3. the PLOT: persona suffix, with NO {{cwd}} (hosts from 0.2.1-alpha.2 register only
|
|
149
|
+
# provider/model and an unregistered variable throws at prompt assembly, killing
|
|
150
|
+
# every turn of every chapters session)
|
|
151
|
+
# 4. tool-ralph ENABLED (the host standard list mounts it disabled)
|
|
152
|
+
# `noUnusedLocals`-equivalent discipline applies: keys the host dropped between versions
|
|
153
|
+
# are not carried here (toolFilter was one — alpha.4 throws on it).
|
|
154
|
+
- insert:
|
|
155
|
+
- id: preset-chapters
|
|
156
|
+
name: '@treeseed/dsh-chapters/preset'
|
|
157
|
+
config:
|
|
158
|
+
id: chapters
|
|
159
|
+
name: Chapters
|
|
160
|
+
description: Full coding agent; context relief via zero-inference chapter compaction.
|
|
161
|
+
order: 0
|
|
162
|
+
plugins:
|
|
163
|
+
- id: persona
|
|
164
|
+
name: '@deepseek-ai/dsh-persona'
|
|
165
|
+
config:
|
|
166
|
+
prefix: You are a coding agent powered by the {{model}} model.
|
|
167
|
+
suffix: "When a task spans multiple turns, end each reply with one line beginning 'PLOT:' (max 60 words — objective, current hypothesis, immediate next step). A checkpoint carries this plot forward; if it looks stale, revise it on your next plot line."
|
|
168
|
+
- id: agent-instructions
|
|
169
|
+
name: '@deepseek-ai/dsh-agent-instructions'
|
|
170
|
+
config:
|
|
171
|
+
maxBytes: 65536
|
|
172
|
+
- id: time-context
|
|
173
|
+
name: '@deepseek-ai/dsh-time-context'
|
|
174
|
+
- id: tool-bash
|
|
175
|
+
name: '@deepseek-ai/dsh-tool-bash'
|
|
176
|
+
disabled: !!js process.platform === 'win32'
|
|
177
|
+
- id: tool-pwsh
|
|
178
|
+
name: '@deepseek-ai/dsh-tool-pwsh'
|
|
179
|
+
disabled: !!js process.platform !== 'win32'
|
|
180
|
+
- id: tool-fs
|
|
181
|
+
name: '@deepseek-ai/dsh-tool-fs'
|
|
182
|
+
- id: tool-fs-search
|
|
183
|
+
name: '@deepseek-ai/dsh-tool-fs-search'
|
|
184
|
+
config:
|
|
185
|
+
sampleOverCapGlobResults: false
|
|
186
|
+
- id: tool-jobs
|
|
187
|
+
name: '@deepseek-ai/dsh-tool-jobs'
|
|
188
|
+
- id: tool-schedule
|
|
189
|
+
name: '@deepseek-ai/dsh-tool-schedule'
|
|
190
|
+
- id: skill-filesystem
|
|
191
|
+
name: '@deepseek-ai/dsh-skill-filesystem'
|
|
192
|
+
- id: tool-skill
|
|
193
|
+
name: '@deepseek-ai/dsh-tool-skill'
|
|
194
|
+
- id: command-goal
|
|
195
|
+
name: '@deepseek-ai/dsh-command-goal'
|
|
196
|
+
- id: tool-goal
|
|
197
|
+
name: '@deepseek-ai/dsh-tool-goal'
|
|
198
|
+
- id: planning
|
|
199
|
+
name: cordis:group
|
|
200
|
+
group: true
|
|
201
|
+
isolate:
|
|
202
|
+
planMode: true
|
|
203
|
+
config:
|
|
204
|
+
- id: plan-mode
|
|
205
|
+
name: '@deepseek-ai/dsh-plan-mode'
|
|
206
|
+
config:
|
|
207
|
+
section: |
|
|
208
|
+
You are in plan mode. Stay in plan mode until exit_plan_mode succeeds or the user switches the session mode. Imperative language to implement changes means plan the implementation, not execute it. A user's conversational agreement — including an answer confirming something you asked — approves nothing and does not end plan mode; fold the confirmed decision into the plan and submit it through exit_plan_mode.
|
|
209
|
+
|
|
210
|
+
Explore first. Use non-mutating reads, searches, static analysis, and checks to ground the plan in the actual repository. Do not edit or write files, change configuration, run formatters or code generation that rewrites tracked files, commit, or otherwise carry out the plan. Prefer existing functions and patterns over new machinery.
|
|
211
|
+
|
|
212
|
+
The tool catalog stays the same across modes for request-cache stability. These plan-mode rules override any later tool description or guidance that suggests using mutation tools; those tools remain listed to keep the tool catalog unchanged. Do not use todo_write to track this planning phase: it tracks implementation after an approved plan, while the plan itself belongs in exit_plan_mode.
|
|
213
|
+
|
|
214
|
+
Resolve discoverable facts by inspection. Use ask_user_question only for user-owned choices or material ambiguity that inspection cannot answer. Do not ask the user where code lives or how current behavior works when you can find out.
|
|
215
|
+
|
|
216
|
+
Make the plan decision-complete: state the goal and success criteria; group implementation changes by subsystem; identify public API, schema, and data-flow changes; cover edge cases, failure modes, tests, acceptance criteria, and explicit assumptions. Keep it concise enough to review but detailed enough that another engineer can implement it without making design decisions.
|
|
217
|
+
|
|
218
|
+
When ready, call exit_plan_mode with the complete plan markdown, starting with a # title. Make exit_plan_mode the only and final tool call in that assistant response: it presents the plan for approval, and implementation begins only in a later step after approval. Do not paste the final plan as a plain reply or ask "should I proceed?" through prose or ask_user_question. If review rejects it, incorporate the feedback and present again. If the review channel is unavailable or aborted, stay in plan mode and ask the user to switch modes manually; do not proceed with implementation.
|
|
219
|
+
- id: compaction
|
|
220
|
+
name: cordis:group
|
|
221
|
+
group: true
|
|
222
|
+
isolate:
|
|
223
|
+
compaction: true
|
|
224
|
+
toolResultPruner: true
|
|
225
|
+
config:
|
|
226
|
+
- id: chapters-engine
|
|
227
|
+
name: '@treeseed/dsh-chapters/engine'
|
|
228
|
+
config:
|
|
229
|
+
thresholdRatio: 0.9
|
|
230
|
+
- id: command-compact
|
|
231
|
+
name: '@deepseek-ai/dsh-command-compact'
|
|
232
|
+
- id: tool-result-pruner
|
|
233
|
+
name: '@deepseek-ai/dsh-compaction-tool-result-pruner'
|
|
234
|
+
config:
|
|
235
|
+
thresholdChars: 1000000
|
|
236
|
+
headChars: 4096
|
|
237
|
+
tailChars: 1024
|
|
238
|
+
- id: delegation
|
|
239
|
+
name: cordis:group
|
|
240
|
+
group: true
|
|
241
|
+
isolate:
|
|
242
|
+
workflowEngine: true
|
|
243
|
+
config:
|
|
244
|
+
- id: tool-subagent-control
|
|
245
|
+
name: '@deepseek-ai/dsh-tool-subagent-control'
|
|
246
|
+
- id: tool-subagent-list-agents
|
|
247
|
+
name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
|
|
248
|
+
- id: tool-subagent
|
|
249
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
250
|
+
config:
|
|
251
|
+
provider: spawn
|
|
252
|
+
toolName: subagent
|
|
253
|
+
modelSelectionSettings: true
|
|
254
|
+
backgroundMode: continuable
|
|
255
|
+
- id: tool-subagent-fork
|
|
256
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
257
|
+
config:
|
|
258
|
+
provider: fork
|
|
259
|
+
toolName: subagent_fork
|
|
260
|
+
backgroundMode: continuable
|
|
261
|
+
- id: tool-subagent-codex
|
|
262
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
263
|
+
disabled: true
|
|
264
|
+
config:
|
|
265
|
+
provider: codex
|
|
266
|
+
toolName: subagent_codex
|
|
267
|
+
backgroundMode: one-shot
|
|
268
|
+
maxDepth: provider-managed
|
|
269
|
+
- id: tool-subagent-claude-code
|
|
270
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
271
|
+
disabled: true
|
|
272
|
+
config:
|
|
273
|
+
provider: claude-code
|
|
274
|
+
toolName: subagent_claude_code
|
|
275
|
+
backgroundMode: one-shot
|
|
276
|
+
maxDepth: provider-managed
|
|
277
|
+
- id: workflow-ptc
|
|
278
|
+
name: '@deepseek-ai/dsh-workflow-ptc'
|
|
279
|
+
config:
|
|
280
|
+
provider: spawn
|
|
281
|
+
- id: tool-workflow
|
|
282
|
+
name: '@deepseek-ai/dsh-tool-workflow'
|
|
283
|
+
- id: tool-ralph
|
|
284
|
+
name: '@deepseek-ai/dsh-tool-ralph'
|
|
285
|
+
config:
|
|
286
|
+
subagentProvider: spawn
|
|
287
|
+
maxRounds: 64
|
|
288
|
+
- id: tool-ask-user
|
|
289
|
+
name: '@deepseek-ai/dsh-tool-ask-user'
|
|
290
|
+
- id: tool-todo
|
|
291
|
+
name: '@deepseek-ai/dsh-tool-todo'
|
|
292
|
+
config:
|
|
293
|
+
allowParallelInProgress: true
|
|
294
|
+
- id: tool-web
|
|
295
|
+
name: '@deepseek-ai/dsh-tool-web'
|
|
296
|
+
config:
|
|
297
|
+
fetch: true
|
|
298
|
+
searchTimeoutMs: 60000
|
|
299
|
+
- id: present
|
|
300
|
+
name: '@deepseek-ai/dsh-tool-present'
|
|
301
|
+
- id: tool-plugin-manager
|
|
302
|
+
name: '@deepseek-ai/dsh-plugin-manager/tools'
|
|
303
|
+
disabled: true
|
package/docs/backlog.md
CHANGED
|
@@ -95,3 +95,82 @@ about.
|
|
|
95
95
|
machine-only when you expected the pool, that note is the first thing to read.
|
|
96
96
|
- **`/chapters-continue` vs `/chapters-fork`** is the intent line: continue carries the operative plan
|
|
97
97
|
and may seed plan mode; fork carries nothing.
|
|
98
|
+
|
|
99
|
+
## Preset delivery on 0.1.7+/0.2.x — landed, and what it exposed (2026-10-16)
|
|
100
|
+
|
|
101
|
+
**Landed:** the `chapters` preset now EXISTS on a declaration-row host, by `agentPresets.register()` at
|
|
102
|
+
activation (`src/preset.ts`), with rows the host cannot import dropped and named, a held disposer, and a
|
|
103
|
+
deferred ROSTER check (`present` / `DECLARED BUT BROKEN` / `absent` / `unknown`) as the only health signal.
|
|
104
|
+
**Support floor is 0.2.0** (user decision 2026-10-16): the legacy `.agent-presets` copy, the
|
|
105
|
+
`presets/chapters/` tree, `installChaptersPreset`'s copy arm and `preset-install.test.ts` are DELETED; a
|
|
106
|
+
pre-0.2.0 host gets `delivery: REFUSED` with its tools intact. The engine row's `thresholdRatio` became
|
|
107
|
+
the host-plane config `compactionThresholdRatio` (no hardcoded tunables). Verified on real boots:
|
|
108
|
+
0.2.0-rc.2 → `register: pruned, roster: present, inventoryRows: 28, unusableRows: []`;
|
|
109
|
+
0.2.1-alpha.4 → `register: registered, roster: present, inventoryRows: 29, unusableRows: []`
|
|
110
|
+
(after dropping `toolFilter`, which alpha.4 THROWS on — see the config-key row of the drift test). `tests/unit/preset-declaration.test.ts`
|
|
111
|
+
(13 rows, `src/preset.ts` at 100%) is the drift gate, and it now RUNS on this box instead of skipping.
|
|
112
|
+
|
|
113
|
+
**Why the row is not a shipped patch layer** — measured, because it looks like the obvious fix and it is a
|
|
114
|
+
boot-breaking one: `dsh.bundle.patch` as an ARRAY throws `ERR_INVALID_ARG_TYPE` at
|
|
115
|
+
`dsh-app-boot/lib/index.js:853` on every host before 0.1.7-alpha.1, and a row naming
|
|
116
|
+
`@deepseek-ai/dsh-agent-preset` aborts that host's plugin tree (`Cannot find package`). `disabled: !!js`
|
|
117
|
+
cannot gate it either: the Loader evaluates `disabled` before services mount, so the row is disabled on the
|
|
118
|
+
NEW host too (measured). Full record: `docs/contract.md` § Presets are declaration rows.
|
|
119
|
+
|
|
120
|
+
**Open, in order:**
|
|
121
|
+
|
|
122
|
+
0. **Re-anchor the e2e host, then re-distill the tapes.** `tests/e2e/boot.ts` now refuses by name
|
|
123
|
+
while `dev/dsh-host-lock` pins a pre-0.2.0 host, and the refusal lifts itself when the lock moves —
|
|
124
|
+
that is the upgrade path, not a TODO. `ci.yml`'s `host-capability` job reads that same lock and
|
|
125
|
+
SKIPS the acceptance job with a step-summary reason plus a `::warning::` while the pinned host
|
|
126
|
+
predates declaration rows: visibly skipped, never red-by-accident, never green-by-omission.
|
|
127
|
+
Publishing is unaffected — the release gate is the deterministic suite. The per-boot row pins (`thresholdRatio`, `enrichmentEnabled`,
|
|
128
|
+
`auto`, `elicitedPlot`, `toolResultArtifactTokens`) and the `[e2e] boot pins:` line were deleted with
|
|
129
|
+
the YAML they patched; they come back expressed as CONFIG on the registered row (`compactionThresholdRatio`
|
|
130
|
+
exists now; the rest need the same treatment). Until then the browser acceptance chain does not run, and
|
|
131
|
+
`docs/verify.md`'s P23 is retired rather than passing.
|
|
132
|
+
1. **The roster verdict is a boot log line, and a user has no terminal.** 0.1.18 made the preset a
|
|
133
|
+
visible Loader row (`preset-chapters`) and put the mounted-row count in that line, so the
|
|
134
|
+
"is it actually usable" fact is one grep away — but it is still a grep. Candidate: surface it in
|
|
135
|
+
`/chapters-status`. Nothing else in the plugin reports health to a human today.
|
|
136
|
+
2. **`installChaptersPreset` and `compactionThresholdRatio` are accepted-and-inert** since 0.1.18 (the
|
|
137
|
+
preset is a row; the engine threshold is row config). They must be DELETED in the next minor — a key
|
|
138
|
+
that silently does nothing is its own drift — and the removal needs the same care: check no shipped
|
|
139
|
+
profile patch sets them, then drop both from `Config` and from `cordis.patch.yml`'s header.
|
|
140
|
+
2. **`compositionInventory()` is used by the dev witness only.** Making the boot check itself compare
|
|
141
|
+
mounted rows would catch a row that resolves but never activates — the `broken` flag does not always
|
|
142
|
+
cover that, and the witness proves the shape of the answer already.
|
|
143
|
+
3. **A reset registry can overwrite a chapter — no guard exists today.** `writeArchive`
|
|
144
|
+
(`src/archive.ts:106`) reserves numbers from the registry and writes them; nothing checks whether that
|
|
145
|
+
path already exists on disk. So the exact incident shape — registry gone (host reinstall) while chapter
|
|
146
|
+
files remain, or restored by rehydration — lets a NEW archive reuse number 001 and clobber a surviving
|
|
147
|
+
file, which breaks the append-only rule from the inside. Two rows are missing: at boot, report files the
|
|
148
|
+
registry does not know (`<store>/<root>/chapters/*.md` vs registry rows, counted); and refuse a
|
|
149
|
+
reservation whose target path exists, with the numbers.
|
|
150
|
+
4. **Rehydration restores bytes, not rows.** `rehydrateStoreFromMirror` (`src/sync.ts:476`) fills absent
|
|
151
|
+
chapter and artifact files only. Search works off the files' own frontmatter, but numbering, ancestry and
|
|
152
|
+
collections are registry state, so a rehydrated store can be searched and cannot be continued cleanly.
|
|
153
|
+
Decide: rebuild rows from frontmatter (honest — the bytes are the ground truth), or refuse continuation
|
|
154
|
+
over a partially restored store with numbers. Do not let it look like a fresh chain.
|
|
155
|
+
|
|
156
|
+
## Host bug we cannot fix: the workspace directory-picker modal (2026-10-16)
|
|
157
|
+
|
|
158
|
+
Reported by the user as "the directory selection modal crashes before I can select a workspace", and it
|
|
159
|
+
survived a host upgrade. Root-caused with real boots (`var/picker-probe/FINDINGS.md`, kept out of the
|
|
160
|
+
repo's claims — it is host code): the picker's CLIENT bundle and the shell's client module table must come
|
|
161
|
+
from the same host generation. The 0.1.x picker asks for `IconFolderOpen16`/`IconFolderClose16`;
|
|
162
|
+
0.2.0-rc.2's shell exports only the `…Regular` spellings, with no compat alias in either direction. The
|
|
163
|
+
slot boundary catches it, so the symptom is a rendered-then-dead dialog:
|
|
164
|
+
`Error: Minified React error #130 … got: undefined`, logged as
|
|
165
|
+
`slot entry crashed in 'sidebar.workspaces.directoryFlow'`
|
|
166
|
+
(`@deepseek-ai/dsh-client-ui-renderer/lib/client.js:618`). A stale profile-local copy of
|
|
167
|
+
`@deepseek-ai/dsh-client-ui-directory-picker-*` under `$DSH_HOME/profiles/<p>/node_modules` shadows the
|
|
168
|
+
upgraded global install and reproduces "the new host did not fix it" exactly.
|
|
169
|
+
|
|
170
|
+
Nothing in this plugin contributes to that slot (its client half registers
|
|
171
|
+
`conversation.chat.assistant-actions` only), so the repo can ship no fix — only the recipe, which the
|
|
172
|
+
site documents: purge the stale picker copies, and if the auto-resolved backend is not the one you want,
|
|
173
|
+
pin the pair deliberately in the profile patch (`disabled: true` on `directory-picker`, insert
|
|
174
|
+
`@deepseek-ai/dsh-host-directory-picker-browse` + `@deepseek-ai/dsh-client-ui-directory-picker-browse`).
|
|
175
|
+
Upstream ask: compat aliases for the renamed icon exports, or a boot-time shell↔plugin version handshake
|
|
176
|
+
that rejects a mismatched bundle instead of crashing at render.
|
package/docs/contract.md
CHANGED
|
@@ -244,12 +244,13 @@ The subclass seam, with everything measured by rounds 12-17 (sources: the instal
|
|
|
244
244
|
`resolveConfig` **rejects unknown keys at runtime** (`BasicCompactionConfig:
|
|
245
245
|
unknown key "artifactStoreRoot"`, r18) — peel engine-specific keys in the
|
|
246
246
|
subclass constructor before `super(ctx, baseConfig)`.
|
|
247
|
-
- Preset delivery:
|
|
248
|
-
`
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
247
|
+
- Preset delivery: the plugin registers the `chapters` declaration row at activation
|
|
248
|
+
(`src/preset.ts` -> `ctx.get('agentPresets').register(...)`). The old copy of
|
|
249
|
+
`presets/chapters/` into `$DSH_HOME/.agent-presets/` is GONE with the 0.2.0 support
|
|
250
|
+
floor; the section "Presets are declaration rows" records why the row is not a shipped
|
|
251
|
+
patch layer (`!!js` evaluates `disabled` before services mount, and an array
|
|
252
|
+
`dsh.bundle.patch` throws on a pre-0.1.7 host). Related: profile `!!js` reaches
|
|
253
|
+
`dshHomePath`/`process` but NOT `require` (r20 boot-killer).
|
|
253
254
|
- `ctx.logger` output is invisible to headless `dsh web` (r19); engine failures
|
|
254
255
|
mirror to `$DSH_CHAPTERS_ENGINE_ERRORS` when set. Engine finalization runs on
|
|
255
256
|
BOTH the success and rejection paths of `compactIfNeeded`/`compactNow` — the
|
|
@@ -354,6 +355,92 @@ Entry `apply(ctx, config)`. Build with `tsc -p tsconfig.json` and **build before
|
|
|
354
355
|
UI (later phase) additionally needs `dsh.client` and a `tsdown.client.config.ts` — see
|
|
355
356
|
`dsh-session-fork/package.json` for the shape.
|
|
356
357
|
|
|
358
|
+
### Presets are declaration rows now (host ≥ 0.1.7-alpha.1) — measured, with the incident
|
|
359
|
+
|
|
360
|
+
A user preset used to be a directory `$DSH_HOME/.agent-presets/<id>/` holding `preset.yml` +
|
|
361
|
+
`agent.cordis.yml`. The host's own migration note is blunt: *"Nothing reads that directory any more."*
|
|
362
|
+
(`@deepseek-ai/dsh-agent-preset/skills/editing-cordis-compositions/SKILL.md`). A preset is now an
|
|
363
|
+
`@deepseek-ai/dsh-agent-preset` **declaration row** (`config: {id, name?, description?, order, plugins}`;
|
|
364
|
+
`dsh-agent-preset/lib/index.js:13-20`) carried by a patch layer, and `AgentPresetRegistry.register()` is
|
|
365
|
+
fed only by such rows (`dsh-agent-preset-registry/lib/index.js:470`, installed 0.2.0-rc.2). Nothing
|
|
366
|
+
crashes when a plugin misses this: the bundle boots, its tools register, Settings says "enabled and
|
|
367
|
+
running", the preset is simply absent — and a session's preset id is **durable state** (`resumeObserved()`
|
|
368
|
+
→ `presets.resolve(id)`, `dsh-api-session-controller/lib/index.js:404`, `:364`; `resolve()` throws
|
|
369
|
+
`agent-preset/not-found`, `dsh-agent-preset-registry/lib/index.js:596`), so every chapters session refuses
|
|
370
|
+
to OPEN while its log sits there intact.
|
|
371
|
+
|
|
372
|
+
**This bundle DOES ship the row — in its one existing patch layer, with our own row module.**
|
|
373
|
+
`cordis.patch.yml` ends with `- insert: [{id: preset-chapters, name: '@treeseed/dsh-chapters/preset',
|
|
374
|
+
config: <the preset definition>}]`, so the preset is an ordinary Loader entry: visible in the plugin
|
|
375
|
+
list and `dsh --dump-default-config`, overridable or disable-able from a profile patch, positioned by
|
|
376
|
+
`config.order`. The row module (`src/preset.ts`) mirrors `@deepseek-ai/dsh-agent-preset`'s plugin line
|
|
377
|
+
for line and adds the pruning described below. Why not the host package directly, measured on the
|
|
378
|
+
0.1.5-rc.1 closure and on 0.2.0-rc.2:
|
|
379
|
+
|
|
380
|
+
- `dsh.bundle.patch` as an **array** (what a second layer requires) dies at profile load on a pre-0.1.7
|
|
381
|
+
host: `TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received an
|
|
382
|
+
instance of Array` (`dsh-app-boot/lib/index.js:853`, `join(packageDir, declared)`). Array support and
|
|
383
|
+
declaration rows arrive in the SAME release, 0.1.7-alpha.1 — so one layer, still a string, is both
|
|
384
|
+
enough and the only boot-safe shape.
|
|
385
|
+
- a row naming `@deepseek-ai/dsh-agent-preset` in ANY layer a legacy host reads aborts its plugin tree:
|
|
386
|
+
`plugin tree failed to load: … failed to import loader entry … Cannot find package` (the package is
|
|
387
|
+
first published at 0.1.7-alpha.1). A row naming our own module resolves wherever our package runs.
|
|
388
|
+
- **`disabled: !!js` cannot capability-gate a root row**: the Loader evaluates `disabled` before services
|
|
389
|
+
are mounted (`cordis-plugin-loader/lib/index.js:348` → `with (ctx) { eval(expr) }`), so
|
|
390
|
+
`get('agentPresets')?.register === undefined` is true on the MODERN host too and the row never
|
|
391
|
+
registers — measured (`preset-v2 … failed to import`, roster without it, while the same row with
|
|
392
|
+
`disabled: !!js false` registered normally). Bare `get(...)` also throws there; only `ctx` is bound.
|
|
393
|
+
- and the row must be *ours* anyway for the reason below: it is the one place that can drop a child the
|
|
394
|
+
host cannot load before the mount is attempted.
|
|
395
|
+
|
|
396
|
+
Three consequences of reading the registry rather than the filesystem:
|
|
397
|
+
|
|
398
|
+
- **A row that does not resolve is dropped, never mounted.** `mountPreset` throws if ANY enabled row failed
|
|
399
|
+
(`dsh-agent-preset-registry/lib/index.js:272`), the registry stores it as `record.broken` (`:541`), and
|
|
400
|
+
`retain()` then refuses with `agent-preset/invalid` (`:651`) — so one stale row makes the whole preset
|
|
401
|
+
unusable, which is the same user-visible failure as a missing preset. Measured on 0.2.0-rc.2 with a row
|
|
402
|
+
for `@deepseek-ai/dsh-tool-schedule` (published only from 0.2.1-alpha.1):
|
|
403
|
+
`preset "chapters" is DECLARED BUT BROKEN — tool-schedule …: never started`. `pruneResolvableRows`
|
|
404
|
+
(`src/preset.ts`) drops such rows and NAMES them in the mount line; `resolvableFromThisPackage` is the
|
|
405
|
+
probe, asked with the row's full specifier so a lost subpath export is caught too.
|
|
406
|
+
- **A held disposer, not an effect.** `register()` returns an unregister disposer, and wiring it through
|
|
407
|
+
`ctx.effect` un-registers the preset the moment our activation finishes — measured: `register()`
|
|
408
|
+
succeeded while the roster came back WITHOUT `chapters`. The same apply-fiber trap bit the storage
|
|
409
|
+
domain in r35. The disposer is held in a module array instead.
|
|
410
|
+
- **The roster is the health check, and it must be asked after activation.** The registry re-audits a mount
|
|
411
|
+
by awaiting the settled loader tree (`:562`), and its doc says callers "must not run inside a Host row's
|
|
412
|
+
own activation, which the settlement would wait on". So `reportPresetRoster()` runs deferred, and
|
|
413
|
+
`compositionInventory()` rows say which mounted rows are actually `active` — `broken` alone hides a row
|
|
414
|
+
that resolved but never started.
|
|
415
|
+
|
|
416
|
+
Rows are renamed and dropped between host versions, so re-anchor `src/preset.ts` against the installed
|
|
417
|
+
host's own `@deepseek-ai/dsh-web-app/presets/standard.patch.yml` after an upgrade. Measured drift across
|
|
418
|
+
the five published versions (rc.1, rc.2, alpha.1, alpha.2, alpha.4): alpha.1 ADDS `dsh-time-context` +
|
|
419
|
+
`dsh-tool-schedule` + `toolFilter.deny[schedule_*]`; alpha.2 DROPS the persona `suffix` (with its
|
|
420
|
+
`{{cwd}}`), `backgroundMode`, and the codex/claude-code/ralph rows; alpha.4 rewrites the plan-mode
|
|
421
|
+
sentence, drops `toolFilter`, adds `dsh-plugin-market`. rc.1 and rc.2 are byte-identical.
|
|
422
|
+
`tests/unit/preset-declaration.test.ts` is what turns the next rename into a red row instead of a broken
|
|
423
|
+
user session — and it pins that `dsh.bundle.patch` is still a string.
|
|
424
|
+
|
|
425
|
+
- **Prompt variables are a closed set, and violating them throws per turn, not at boot.** `dsh-agent-loop`
|
|
426
|
+
registers `provider`/`model`/`cwd` through 0.2.0-rc.2 (`lib/index.js:1564-1566`) and only `provider` and
|
|
427
|
+
`model` from 0.2.1-alpha.2 (`:1591-1592`); `interpolate()` rejects any other reference with `prompt
|
|
428
|
+
variable "{{x}}" has no value for this assembly` (`dsh-system-prompt/lib/index.js:171`). There is no
|
|
429
|
+
`{{cwd}}` worth writing: `dsh-working-directory` (host plane) already prints `Current working directory:
|
|
430
|
+
"…"` into the runtime context. This is what broke every new chapters session immediately AFTER the
|
|
431
|
+
delivery fix above — the preset composed fine, the legacy `persona.suffix` interpolated `{{cwd}}`, and
|
|
432
|
+
assembly threw on every turn. Pinned by the `{{variable}}` row of the test.
|
|
433
|
+
- **Some host plugins reject unknown config keys**, so a preset row's config is not free-form:
|
|
434
|
+
`dsh-compaction-tool-result-pruner`, `dsh-compaction-basic` and `dsh-plan-mode` throw on any key they do
|
|
435
|
+
not know (`PlanModeConfig has unknown key(s) …`); `dsh-tool-subagent` and `dsh-tool-ralph` are plain zod
|
|
436
|
+
objects that strip them. Keep exactly `{thresholdChars, headChars, tailChars}` on the pruner and exactly
|
|
437
|
+
`{section}` on plan mode.
|
|
438
|
+
- **Persona wording is load-bearing for the engine.** `src/engine-core.ts`'s `PLOT_TEMPLATE_ECHO` suppresses
|
|
439
|
+
a "plot" that is really our own instruction quoted back (the 2026-09-25 audit: eight checkpoints of a
|
|
440
|
+
real run inherited boilerplate, and because the result was non-null the bounded elicited fallback never
|
|
441
|
+
ran). It matches phrases inside the suffix, so reword the suffix and the guard in the same change — the
|
|
442
|
+
gate asserts that coupling by behaviour, not by copying the regex.
|
|
443
|
+
|
|
357
444
|
## Trigger Constraints
|
|
358
445
|
|
|
359
446
|
Two different actions, two different timings — Phase 0b measured the seam so this split is now factual,
|
package/docs/development.md
CHANGED
|
@@ -355,7 +355,7 @@ happened in these notes before it was caught.
|
|
|
355
355
|
| `src/notice.ts` + `test/notice.test.ts` — TOC-notice builder, kernel-rejection reproduction, vocabulary pins + drift guard (Phase 0b check 5) | **done**, 10 tests |
|
|
356
356
|
| `src/registry.ts` + `src/store.ts` — pure numbering/ancestry/plan state; zod domain spec, storage-domain + node-fs adapters | **done**, 13 + 7 tests; durability witness passed |
|
|
357
357
|
| `src/engine-core.ts` + `src/engine.ts` — ChaptersCompactionEngine | **done & boot-proven** (r18/r19: realm subpath row mounts; real cascade finalizes; catch-path fix — FINDINGS § Phase 1) |
|
|
358
|
-
| `presets/chapters/` + copy-on-boot
|
|
358
|
+
| preset delivery | **0.1.18: the preset IS a declaration row** — `preset-chapters` in `cordis.patch.yml`, mounted by `@treeseed/dsh-chapters/preset` (`src/preset.ts`), which registers what the host can load and drops what it cannot. The `presets/chapters/` + copy-on-boot mechanism is deleted with the 0.2.0 floor. Verify.md P24 (rows + three-host boots) |
|
|
359
359
|
| `npm test` (unit + integration) | **188 passing, <2 s** — unit imports no harness services; integration runs the sync loop against a fake git driver AND a real git smart-HTTP server (git-http-backend on loopback; the git binary is test-only scaffolding, the product stays pure-JS) |
|
|
360
360
|
| 20-check verify.md pass | **r26/26b**: 18 mechanical on `.dshdev2` (refusals with numbers, tamper marks, idempotent retry, cross-boot registry + resume); human rows per verify.md evidence map |
|
|
361
361
|
| dsh-session-fork coexistence | **moot at this version** (r26a pair-boot): the example fails to boot the installed host ALONE (unguarded `webServer` read in the installed dsh-client-connection) — assumed-neutral until a version-matched pair-boot |
|
|
@@ -465,3 +465,66 @@ commands were *verified*.
|
|
|
465
465
|
|
|
466
466
|
`docs/contract.md`'s file:line citations are from the `examples/` snapshot (rc.1-era), not the rc.2 fork —
|
|
467
467
|
re-anchor them against whichever version a test actually boots.
|
|
468
|
+
|
|
469
|
+
### Running the TS tests on this box (node 22, no native type stripping)
|
|
470
|
+
|
|
471
|
+
Measured 2026-10-16: `npm test` (`node --test tests/unit/*.test.ts`) needs node ≥ 23 for type
|
|
472
|
+
stripping, and this machine's `/usr/bin/node` is **v22.22.1 compiled WITHOUT TypeScript support**
|
|
473
|
+
(`ERR_NO_TYPESCRIPT`, not merely a missing flag). The package's `engines` floor is node 24, and CI
|
|
474
|
+
installs it; locally, run the same files through `tsx`:
|
|
475
|
+
|
|
476
|
+
```bash
|
|
477
|
+
npm install --no-save --no-audit --no-fund tsx unrun # both, in ONE command
|
|
478
|
+
node --import tsx --test tests/unit/*.test.ts tests/integration/*.test.ts
|
|
479
|
+
npx c8 --check-coverage --statements 85 --branches 85 --functions 85 --lines 85 \
|
|
480
|
+
node --import tsx --test tests/unit/*.test.ts tests/integration/*.test.ts
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
Two traps measured while finding that:
|
|
484
|
+
|
|
485
|
+
- **`npm install --no-save A` then `--no-save B` removes A.** npm prunes what is not in the manifest,
|
|
486
|
+
so unsaved helpers must be installed in a single command (`unrun` disappeared the moment `tsx` was
|
|
487
|
+
added, and `npm run build` — which bundles the client through `tsdown` — failed with
|
|
488
|
+
`Cannot find module "unrun"`).
|
|
489
|
+
- `tsconfig.test.json` (committed) transpiles `src` + `tests` to `var/tsbuild` with `--noCheck` for
|
|
490
|
+
the rare case you want emitted JS instead of a loader. It is NOT how the suite is meant to run;
|
|
491
|
+
tests import `.ts` sources directly and are not typechecked by design (`npm run typecheck` covers
|
|
492
|
+
`src/` only).
|
|
493
|
+
|
|
494
|
+
Never fix a missing test runner by editing the tests. The suite is the contract; the runner is local.
|
|
495
|
+
|
|
496
|
+
### Verifying against a host version that is not the installed one
|
|
497
|
+
|
|
498
|
+
A preset/delivery change must be proven on EVERY host release the package claims to serve, and the
|
|
499
|
+
installed host is one data point. Recipe measured 2026-10-16 (this is how 0.1.17 was verified on
|
|
500
|
+
0.2.0-rc.2, 0.2.1-alpha.4 and the pinned 0.1.5-rc.1 in one sitting):
|
|
501
|
+
|
|
502
|
+
```bash
|
|
503
|
+
# 1. a second host, installed as its own tree — never over the running one
|
|
504
|
+
mkdir -p var/host-versions/a4 && cd var/host-versions/a4
|
|
505
|
+
echo '{"name":"a4-host","private":true,"dependencies":{"@deepseek-ai/dsh":"0.2.1-alpha.4"}}' > package.json
|
|
506
|
+
npm install --no-audit --no-fund # ~1 min, 616 packages
|
|
507
|
+
# 2. a THROWAWAY home for it (profiles/web/package.json listing the bundles + this plugin),
|
|
508
|
+
# then boot THAT bin with DSH_HOME pointed at the new home:
|
|
509
|
+
DSH_HOME=$PWD/var/a4-home DSH_CHAPTERS_WITNESS=$PWD/var/a4-home/w.json \
|
|
510
|
+
node var/host-versions/a4/node_modules/@deepseek-ai/dsh/lib/bin.js web --port 0 --no-open
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
The witness JSON is the answer — `presetRegister`, `presetDroppedRows`, `presetRoster` and
|
|
514
|
+
`presetInventoryRows`/`presetUnusableRows` say what a log line only hints at, and `presetUnusableRows: []`
|
|
515
|
+
is the assertion that the composition actually ACTIVATES rather than merely being declared.
|
|
516
|
+
|
|
517
|
+
Three traps, all paid for:
|
|
518
|
+
|
|
519
|
+
- **Never symlink a host tree into an npm-managed profile's `node_modules`.** `npm install` treats what it
|
|
520
|
+
does not recognize as extraneous and **deletes it through the link** — it ate a 500-package host closure
|
|
521
|
+
this way ("the closure is gone, `ls` returns nothing"). Copy instead (`cp -r`), or point the boot's own
|
|
522
|
+
installation anchor at the other tree and install ONLY the plugin into the profile.
|
|
523
|
+
- **`npm pack` + install-from-tarball, not `file:`-linked sources.** The artifact users get is the packed
|
|
524
|
+
`files` list; a linked working tree hides a missing `files` entry (e.g. an asset the preset needs).
|
|
525
|
+
- Boot with `timeout 45 …` and read the exit code: **124 means it stayed up** (healthy), `1` means the
|
|
526
|
+
plugin tree refused to load. A crashed boot and a slow boot both print to the same log; the code is the
|
|
527
|
+
difference between "no preset" and "no harness".
|
|
528
|
+
|
|
529
|
+
Prune after: `var/host-versions/`, the throwaway homes and `var/coverage` are all regenerable in minutes and
|
|
530
|
+
together cost ~2 GB — this repository's contract is the *measurements written down*, not their scratch.
|
package/docs/plans-carriage.md
CHANGED
|
@@ -4,7 +4,7 @@ Written 2026-10-09. **Read before writing code.** Human decisions for this featu
|
|
|
4
4
|
§ Decisions Taken and are binding; anything marked *deferred* is a human call, not ours.
|
|
5
5
|
|
|
6
6
|
DSh ships plan mode (`@deepseek-ai/dsh-plan-mode`) and our own preset mounts it
|
|
7
|
-
(`presets/chapters/agent.cordis.yml
|
|
7
|
+
(the `plan-mode` row of the registered preset: `PLAN_MODE_SECTION` in `src/preset.ts`; before the 0.2.0 floor it lived in `presets/chapters/agent.cordis.yml`). Today a session plan survives an archive only as the
|
|
8
8
|
first 400 characters of a tool-call line. That is the worst possible outcome for the one artifact in a
|
|
9
9
|
conversation that is *deliberately* decision-complete — and the reason the plugin exists at all: a
|
|
10
10
|
continuation that cannot see the approved plan either re-plans from recall (lossy) or implements
|
package/docs/verify.md
CHANGED
|
@@ -152,7 +152,8 @@ renderer, the segmentation tool, and the child's notice.
|
|
|
152
152
|
| P10 | Artifact citations resolve wherever they were written | a chapter's `artifacts/aa/<sha>.txt` reference resolves whether the blob sits at the STORE ROOT (arrival stubs) or inside the ANCESTRY TREE (`<store>/<root>/artifacts/…`, where `writeArchive` puts deferred results and every plan) — before this fix the second form was unreadable through `chapters_artifact`, which is what "every byte remains retrievable" actually promised | `artifact-query.test.ts` rows + integration `plans-carriage.test.ts` (`resolveArtifactWithMirror` over the real tree) |
|
|
153
153
|
| P11 | The browser plane runs in the chain | `/plan <message>` → a real `exit_plan_mode` call → the host's review card opened and answered by a real CLICK → fork; asserts the submission's bytes are the artifact's bytes (or provably only-redacted), `planMode: inactive` from the fold, a `kind: plan` document on disk, and that the BRANCH child carries **no** `## Active plan` and no call-out while still citing the archive; plus a guard that no `compaction/*` entered the journey | `tests/e2e/plans.spec.ts` = chain project `plans` (13 s replay, no LLM; 3-entry tape). **Scope, learned at cost:** the continuation leg was REMOVED from this journey because a tape freezes the model's tool ARGUMENTS — my scripted upstream derived `chapters_continue` ranges from the ceiling the live segment tool reported while distilling (30 here), and the runner's session lands at 29, so `validateRanges` refused the stale input (correctly). A journey whose recorded request embeds a machine-specific seq cannot be hardware-neutral. Continuation semantics — `## Active plan`, `## Previous plans`, the seeded `plan/mode`, the checkpoint carry — are covered by the deterministic plane (`continue-plans`, `plans-engine`, `plan-documents`, the § 10 kernel probe) and the in-place path by the heavy/arrival/enrich/fanout journeys. Tape provenance: scripted upstream, see `tests/fixtures/model-tape/plans/PROVENANCE.md` |
|
|
154
154
|
|
|
155
|
-
| P23 |
|
|
155
|
+
| P23 | **Retired in 0.1.17 with the legacy copy** (support floor 0.2.0): `installChaptersPreset` and `presets/chapters/` are deleted, so there is no installed file set to half-delete. The key is ACCEPTED-and-inert since 0.1.18 — removing it would make an existing profile patch fail to parse, and a bundle that will not mount is worse than a key that does nothing | see P24 |
|
|
156
|
+
| P24 | The preset is a **visible declaration row**, and the row composes on every host in range | `preset-chapters` is inserted by our own `cordis.patch.yml` with the preset definition in its `config`, mounted by `@treeseed/dsh-chapters/preset` (`src/preset.ts`) — the visible Loader entry the host's own presets are, overridable and `disabled`-able from a profile patch, with the registration's lifetime yielded to Cordis (an `apply()`-scoped disposer un-registered it: measured as `register()` succeeding with the roster WITHOUT `chapters`). Rows the host cannot import are dropped and NAMED, because one unresolvable child makes the whole preset `broken` | 15 rows in `preset-declaration.test.ts`, **each verified to fail on the mutation it guards** (ralph re-disabled, an unknown pruner key, `toolFilter` restored, `{{cwd}}` restored, the durable id renamed) — plus real boots of the packed tarball on 0.2.0-rc.2 (`roster: present`, `inventoryRows: 28`, `unusableRows: []`, tool-schedule pruned), 0.2.1-alpha.4 (`registered`, `inventoryRows: 29`, nothing pruned) and 0.1.5-rc.1 (boots; the row says the host cannot register rows) |
|
|
156
157
|
| P21 | The continuation has a **human-pulled door**, and the two doors cannot drift | `/chapters-continue` = `deterministicCommand({intent:'continue'})`, the same builder as `/chapters-fork`; only name/verbs/intent/no-directive prose differ; archive semantics identical (everything since the watermark). CLIENT SIDE, same discipline: the assistant row registers both doors from ONE runner (`src/client/doors.ts` `makeDoorRunner` + a two-row `DOOR_ACTIONS` table) — a composer-only continue left the user with a created child and no affordance | unit `continue-command.test.ts` (7 rows) incl. the **no-drift row**: same events in ⇒ same ranges, titles and plan facts out of both doors; the no-plan case, the refusal naming its own door, and approved ⇒ NOT seeded vs still-planning ⇒ seeded `['user/message','plan/mode']`. Unit `client-doors.test.ts` (7 rows) pins the two client rows (distinct id/order/label/command, the fork label frozen for the specs), that ONE child-id regex parses BOTH door wordings, that both rows fire their own command through the same body, and — by reading the server's own templates — that the reply wording the client parses is still what `deterministicCommand` emits. Browser: project `fork` asserts BOTH buttons render |
|
|
157
158
|
| P22 | The continuation shape is covered **in the browser**, on every push | the leg that a tape could not carry (§ 15) is back, driven by the command — no model request, no frozen `endSeq`; asserted from the child's durable log without switching the UI: `## Active plan`, the operative plan by title with its real verdict, `plan document:` and `chapters_artifact` citations, `## Finding things`, no seeded gate after approval, exactly one synthetic notice (invariant 2) | `tests/e2e/plans.spec.ts` project `plans` — replays in ~14 s with no LLM/GPU, eight-project chain green |
|
|
158
159
|
| P18 | Search answers **without a pool linked** — the findability audit | `buildLocalCorpus` walks the workspace store (chapters, `kind: plan` documents, rules) and answers through the SAME scorer as the mirror door (`rankEntries`), so ranking cannot drift between doors; artifacts are deliberately not indexed (they answer to `chapters_artifact`); the answer names its door ("searched this machine's store, N docs, no pool mirror") so a session never reads *this machine only* as *the whole project*; "nothing matched" (`documents>0`) is distinguished from "nothing here" (`empty`); a pool miss falls back to the local store before answering empty | unit `local-corpus.test.ts` (5) + integration `find-without-a-pool.test.ts` through the real tool wrapper — the empty answer used to be born in the wrapper, not the pure function |
|