@treeseed/dsh-chapters 0.1.0
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/LICENSE +14 -0
- package/README.md +644 -0
- package/cordis.patch.yml +88 -0
- package/docs/architecture.md +460 -0
- package/docs/contract.md +375 -0
- package/docs/development.md +437 -0
- package/docs/host-compaction-seam.md +117 -0
- package/docs/knowledge-repo.md +730 -0
- package/docs/provider.md +98 -0
- package/docs/verify.md +200 -0
- package/lib/archive.d.ts +88 -0
- package/lib/archive.js +114 -0
- package/lib/archive.js.map +1 -0
- package/lib/arrival.d.ts +23 -0
- package/lib/arrival.js +86 -0
- package/lib/arrival.js.map +1 -0
- package/lib/artifact-query.d.ts +53 -0
- package/lib/artifact-query.js +130 -0
- package/lib/artifact-query.js.map +1 -0
- package/lib/client.js +234 -0
- package/lib/client.js.map +1 -0
- package/lib/commands.d.ts +28 -0
- package/lib/commands.js +305 -0
- package/lib/commands.js.map +1 -0
- package/lib/compose.d.ts +30 -0
- package/lib/compose.js +131 -0
- package/lib/compose.js.map +1 -0
- package/lib/continue-core.d.ts +153 -0
- package/lib/continue-core.js +252 -0
- package/lib/continue-core.js.map +1 -0
- package/lib/engine-core.d.ts +219 -0
- package/lib/engine-core.js +405 -0
- package/lib/engine-core.js.map +1 -0
- package/lib/engine.d.ts +211 -0
- package/lib/engine.js +571 -0
- package/lib/engine.js.map +1 -0
- package/lib/enrich-queue.d.ts +66 -0
- package/lib/enrich-queue.js +100 -0
- package/lib/enrich-queue.js.map +1 -0
- package/lib/enrich-store.d.ts +31 -0
- package/lib/enrich-store.js +103 -0
- package/lib/enrich-store.js.map +1 -0
- package/lib/enrich-wire.d.ts +73 -0
- package/lib/enrich-wire.js +209 -0
- package/lib/enrich-wire.js.map +1 -0
- package/lib/enrich.d.ts +73 -0
- package/lib/enrich.js +150 -0
- package/lib/enrich.js.map +1 -0
- package/lib/gitops.d.ts +107 -0
- package/lib/gitops.js +539 -0
- package/lib/gitops.js.map +1 -0
- package/lib/index.d.ts +81 -0
- package/lib/index.js +305 -0
- package/lib/index.js.map +1 -0
- package/lib/indexing.d.ts +85 -0
- package/lib/indexing.js +212 -0
- package/lib/indexing.js.map +1 -0
- package/lib/injections.d.ts +66 -0
- package/lib/injections.js +83 -0
- package/lib/injections.js.map +1 -0
- package/lib/notice.d.ts +60 -0
- package/lib/notice.js +84 -0
- package/lib/notice.js.map +1 -0
- package/lib/provider.d.ts +44 -0
- package/lib/provider.js +66 -0
- package/lib/provider.js.map +1 -0
- package/lib/redact.d.ts +24 -0
- package/lib/redact.js +85 -0
- package/lib/redact.js.map +1 -0
- package/lib/registry.d.ts +120 -0
- package/lib/registry.js +130 -0
- package/lib/registry.js.map +1 -0
- package/lib/render.d.ts +40 -0
- package/lib/render.js +280 -0
- package/lib/render.js.map +1 -0
- package/lib/repo.d.ts +78 -0
- package/lib/repo.js +184 -0
- package/lib/repo.js.map +1 -0
- package/lib/rules-commands.d.ts +24 -0
- package/lib/rules-commands.js +138 -0
- package/lib/rules-commands.js.map +1 -0
- package/lib/rules.d.ts +90 -0
- package/lib/rules.js +185 -0
- package/lib/rules.js.map +1 -0
- package/lib/search.d.ts +48 -0
- package/lib/search.js +152 -0
- package/lib/search.js.map +1 -0
- package/lib/signature.d.ts +28 -0
- package/lib/signature.js +149 -0
- package/lib/signature.js.map +1 -0
- package/lib/store.d.ts +130 -0
- package/lib/store.js +235 -0
- package/lib/store.js.map +1 -0
- package/lib/sync.d.ts +183 -0
- package/lib/sync.js +719 -0
- package/lib/sync.js.map +1 -0
- package/lib/tools.d.ts +84 -0
- package/lib/tools.js +581 -0
- package/lib/tools.js.map +1 -0
- package/lib/treedx/client.d.ts +57 -0
- package/lib/treedx/client.js +82 -0
- package/lib/treedx/client.js.map +1 -0
- package/lib/treedx/provider.d.ts +39 -0
- package/lib/treedx/provider.js +479 -0
- package/lib/treedx/provider.js.map +1 -0
- package/lib/treedx/state.d.ts +27 -0
- package/lib/treedx/state.js +65 -0
- package/lib/treedx/state.js.map +1 -0
- package/lib/types.d.ts +100 -0
- package/lib/types.js +19 -0
- package/lib/types.js.map +1 -0
- package/lib/vocabulary.d.ts +41 -0
- package/lib/vocabulary.js +118 -0
- package/lib/vocabulary.js.map +1 -0
- package/package.json +132 -0
- package/presets/chapters/agent.cordis.yml +272 -0
- package/presets/chapters/preset.yml +3 -0
- package/presets/probe-roots/agent.cordis.yml +11 -0
- package/presets/probe-roots/preset.yml +3 -0
|
@@ -0,0 +1,730 @@
|
|
|
1
|
+
# Knowledge Repository Design Record
|
|
2
|
+
|
|
3
|
+
The governing design for the **git-based shared knowledge pool**: multiple DSH harness instances,
|
|
4
|
+
on multiple machines, feeding and searching one private per-project repository of past
|
|
5
|
+
conversations — chapters, artifacts, a topic index, and rules — while keeping every individual
|
|
6
|
+
session's context small. Written to *govern*: implementation plans derive from this file, and
|
|
7
|
+
amendments are human decisions recorded in git history (see **Amendments** below).
|
|
8
|
+
|
|
9
|
+
Status: **agreed 2026-09-17, pre-implementation.** Phased build in §13.
|
|
10
|
+
|
|
11
|
+
## 0. What this is, and what it is not
|
|
12
|
+
|
|
13
|
+
**What it is:** the plugin's existing artifacts (chapters, deferred tool results, the TOC
|
|
14
|
+
continuation mechanic) become the contents of a private repository per project, synced across
|
|
15
|
+
every harness the user connects to it — over **pluggable transport**: a git repo (§3, the
|
|
16
|
+
default) or a TreeDX service (`treedx+…`, Amendment 2026-09-21, transport). Compaction and forking remain the
|
|
17
|
+
*knowledge generators*; the repo is the *transport and the shared corpus*. Agents gain one
|
|
18
|
+
new capability: search and load of past work across their own and other machines' sessions,
|
|
19
|
+
plus a bounded set of
|
|
20
|
+
human-approved rules to live by.
|
|
21
|
+
|
|
22
|
+
**What it is not:**
|
|
23
|
+
|
|
24
|
+
- Not a general vector store or embedding search. Scoring is deterministic and inspectable (§8).
|
|
25
|
+
- Not a system-prompt injection channel. Rules enter context only through the continuation
|
|
26
|
+
notice (§7) — per-continuation, budgeted, append-only — never as a contributed header section
|
|
27
|
+
(invariant 3, `AGENTS.md`).
|
|
28
|
+
- Not a rewrite of what shipped. The MVP loop (engine, registry, TOC continuation, fork button)
|
|
29
|
+
is the foundation this record extends; §1 lists the invariants that must survive the extension.
|
|
30
|
+
- Not real-time collaboration. Sync points are deliberate (§5); the repo is a pool, not a shared
|
|
31
|
+
live document.
|
|
32
|
+
|
|
33
|
+
## 1. Invariants the extension must not erode
|
|
34
|
+
|
|
35
|
+
Carried from `AGENTS.md` and this project's measured history, restated because the knowledge
|
|
36
|
+
layer tempts each one:
|
|
37
|
+
|
|
38
|
+
1. **The compaction/fork critical path stays zero-model-token.** Model enrichment is strictly
|
|
39
|
+
post-archive, async, optional (§6). A fork must remain instant even when the model is down,
|
|
40
|
+
busy, or slow — the deterministic path is the product; enrichment is garnish.
|
|
41
|
+
2. **Refuse with numbers, never truncate.** Applies to the rules core set rendered into the
|
|
42
|
+
notice (§7) and to search output packing (§8): over budget → refuse/explain with the
|
|
43
|
+
measured numbers, or the user curates; silent clipping is banned.
|
|
44
|
+
3. **The local store is append-only.** Chapters are never renumbered, rewritten, or
|
|
45
|
+
re-chaptered after archive. Composition must be right at archive time (§4); enrichment can
|
|
46
|
+
relabel, never re-chapter.
|
|
47
|
+
4. **Model proposes, plugin commits — extended.** The model proposes topics, summaries, and
|
|
48
|
+
rule text. The plugin owns the canonical vocabulary, the index, and rule status (§6, §7).
|
|
49
|
+
The model never writes to the repo directly; the plugin is the only writer.
|
|
50
|
+
5. **Per-machine write namespaces.** Two machines never write the same file. Zero merge
|
|
51
|
+
conflicts by construction, not by hope (§3.3).
|
|
52
|
+
6. **Budget the added context, never the corpus.** Continuation notices stay O(1) in corpus
|
|
53
|
+
size (§7.3). The corpus grows without bound; the head it pays does not.
|
|
54
|
+
7. **Verbatim stays verbatim** except the redaction carve-out (§9) — and the carve-out is
|
|
55
|
+
credentials-only, marked, and deterministic.
|
|
56
|
+
|
|
57
|
+
## 2. The knowledge repository
|
|
58
|
+
|
|
59
|
+
### 2.1 One private repo per project, user-created
|
|
60
|
+
|
|
61
|
+
The user creates a private repository per project and registers it with
|
|
62
|
+
`/chapters-link` (four modes, live): **no args** shows the upstream + mirror state,
|
|
63
|
+
**a local path** binds a directory upstream, **a URL + token** binds a network git
|
|
64
|
+
upstream, **`treedx+<url>/<repo>` + token** binds a TreeDX service (managed repository
|
|
65
|
+
resolved-or-created by the link itself — the explicit human act; §15.1).
|
|
66
|
+
Profile config (`knowledgeRemote`) supplies the same URL as a lazy default; it never
|
|
67
|
+
creates anything by itself. A personal cross-project repo is the *user's* choice of an
|
|
68
|
+
additional project entry, not a built-in mode.
|
|
69
|
+
|
|
70
|
+
Per-harness credentials are **scoped, revocable tokens limited to that repository** —
|
|
71
|
+
deliberately better than SSH deploy keys: revocable, per-repo, no shell access.
|
|
72
|
+
**Credentials are mandatory for network upstreams** (loopback exempt, so local test
|
|
73
|
+
forges work) and **never needed for local-path upstreams**. Storage is under the DSH
|
|
74
|
+
home — `<DSH_HOME>/dsh-chapters/credentials/<projectKey>`, 0600 — never the project
|
|
75
|
+
tree: the workspace is the agent's reading room and only the plugin touches the token
|
|
76
|
+
(a same-uid hygiene boundary, not a cryptographic one; the hard guarantee is that no
|
|
77
|
+
code path ever echoes it into a context).
|
|
78
|
+
|
|
79
|
+
Why per-project: natural secret boundary (§9), natural permission boundary, and the
|
|
80
|
+
`projectKey` (§2.3) is what keeps a small model's session from wading into a foreign corpus by
|
|
81
|
+
accident.
|
|
82
|
+
|
|
83
|
+
### 2.2 Repository layout
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
<knowledge-repo>/
|
|
87
|
+
project.yml # projectKey, slug, created-by, created-at (plugin-maintained)
|
|
88
|
+
chapters/
|
|
89
|
+
<projectKey>/
|
|
90
|
+
<sessionId>/
|
|
91
|
+
<chapter-file>.md # frontmatter + verbatim body (today's format, §2.4)
|
|
92
|
+
artifacts/
|
|
93
|
+
<projectKey>/
|
|
94
|
+
<sha256-prefix>/<sha256> # deferred tool results, content-addressed (today's format)
|
|
95
|
+
collections/
|
|
96
|
+
<projectKey>/
|
|
97
|
+
<sessionId>.jsonl # per-turn signatures, one line per completed turn (§4.1)
|
|
98
|
+
index/ # LAYER 2 — derived, plugin-built, do not hand-edit (§10.2)
|
|
99
|
+
<topic-shard>.md # one shard per canonical topic, alphabetical
|
|
100
|
+
manifest.json # build manifest: file hash → last processed state (§10.2)
|
|
101
|
+
edits/
|
|
102
|
+
<harness-id>/
|
|
103
|
+
curation.jsonl # LAYER 1 — per-machine editable index facts (§10.1)
|
|
104
|
+
rules/
|
|
105
|
+
<projectKey>/
|
|
106
|
+
<rule-file>.md # rules-as-chapters: kind: rule (§7)
|
|
107
|
+
topics/
|
|
108
|
+
vocabulary.json # plugin-owned canonical vocabulary + aliases (§6.3)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Machine-specific and derived state lives in machine-namespaced or manifest-tracked files (§10);
|
|
112
|
+
human-meaningful content (chapters, rules, vocabulary) is globally readable and git-diffable so
|
|
113
|
+
the user can see what any harness decided and revert it.
|
|
114
|
+
|
|
115
|
+
### 2.3 `projectKey`: UUIDv5 over the normalized remote
|
|
116
|
+
|
|
117
|
+
The key must be **identical on every machine** for the same project — that is the whole point,
|
|
118
|
+
since local paths differ (`/Users/adrian/repo` vs `/home/adrian/Projects/repo`).
|
|
119
|
+
|
|
120
|
+
Canonicalization, exact:
|
|
121
|
+
|
|
122
|
+
1. Take the project's git remote URL (`origin`, first if several).
|
|
123
|
+
2. Lowercase.
|
|
124
|
+
3. Normalize form: strip `https://` and `ssh://` schemes; convert `git@host:org/repo` →
|
|
125
|
+
`host/org/repo`; strip trailing `/` and `.git`.
|
|
126
|
+
4. **Keep the host.** Two hosts, same path = different projects.
|
|
127
|
+
5. `projectKey = UUIDv5(NAMESPACE_OID, canonical-string)` — standards-based, deterministic,
|
|
128
|
+
name-derived; the same canonical string always yields the same key, anywhere.
|
|
129
|
+
|
|
130
|
+
Fallbacks, all recorded in `project.yml` and the notice so they are visible:
|
|
131
|
+
|
|
132
|
+
- **Non-git workspace:** UUIDv5 over the absolute path, flagged `path-derived` (unstable across
|
|
133
|
+
machines — the notice says so).
|
|
134
|
+
- **User override:** a `projectKey:` line in config wins, for monorepo subdirectories and
|
|
135
|
+
multi-remote situations.
|
|
136
|
+
|
|
137
|
+
### 2.4 Chapter frontmatter (extended)
|
|
138
|
+
|
|
139
|
+
Today's frontmatter (title, summary, ranges, sha, …) gains:
|
|
140
|
+
|
|
141
|
+
```yaml
|
|
142
|
+
projectKey: 1c3f… # §2.3 — every chapter carries its project
|
|
143
|
+
kind: chapter # chapter | rule (§7)
|
|
144
|
+
harnessId: adrian-mbp # writing machine, human-readable
|
|
145
|
+
topics: [auth, middleware] # deterministic signature topics at archive time (§4);
|
|
146
|
+
# model-enrichment may later add semantic topics
|
|
147
|
+
generated: # provenance per field class (user-confirmed, §6)
|
|
148
|
+
title: {by: deterministic}
|
|
149
|
+
summary: {by: model, model: qwen3.8-flash-next, at: 2026-09-17T…} # or {by: deterministic}
|
|
150
|
+
topics: {by: deterministic}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Provenance is load-bearing: it is what makes the emergent vocabulary auditable and enrichment
|
|
154
|
+
re-runnable (§6).
|
|
155
|
+
|
|
156
|
+
## 3. Storage vs transport
|
|
157
|
+
|
|
158
|
+
### 3.1 The store stays in the workspace; the clone is a mirror
|
|
159
|
+
|
|
160
|
+
Chapters must remain reachable by the existing `read` tool — a property proven load-bearing in
|
|
161
|
+
the r22 read-back test, and one the agent sandbox may gate for outside-workspace paths.
|
|
162
|
+
Therefore:
|
|
163
|
+
|
|
164
|
+
- **Storage** stays exactly where it is: `.dsh-chapters/<rootSessionId>/…` in the workspace.
|
|
165
|
+
- **Transport** is a git clone in the same workspace: `.dsh-knowledge/` (hidden; the plugin
|
|
166
|
+
writes/updates a gitignore hint in the project on first sync so the project's own repo
|
|
167
|
+
ignores it).
|
|
168
|
+
- Sync = copy-out (store → clone, commit, push) and copy-in (clone → store, new files only).
|
|
169
|
+
The clone is *transport*, never the read path's source of truth for the live session.
|
|
170
|
+
|
|
171
|
+
Failure domains stay separate: a broken remote never breaks compaction/fork (sync is
|
|
172
|
+
best-effort, §5.3), and a broken clone never corrupts the store.
|
|
173
|
+
|
|
174
|
+
### 3.2 Git client: pure-JS SDK, not the executable
|
|
175
|
+
|
|
176
|
+
**Decision (user-directed):** no dependency on a `git` binary. A pure-JS client
|
|
177
|
+
(isomorphic-git-class) over **HTTPS** — which is what makes the scoped-token credential model
|
|
178
|
+
of §2.1 possible. Logged risks: pure-JS fetch/push is slower than native git (mitigation: the
|
|
179
|
+
knowledge repo is small relative to source repos; lazy-clone on first use); the repo grows a
|
|
180
|
+
dependency to own. Fallback, config-gated and documented: the CLI, for hosts where the SDK
|
|
181
|
+
chokes. SSH-only remotes are a known gap in this decision (HTTPS tokens are the supported path).
|
|
182
|
+
|
|
183
|
+
**Local-path upstreams bypass HTTP entirely** (`/chapters-link <path>`, added with the
|
|
184
|
+
P1 close-out): the pool is a directory (bare repo created on first sync if absent) and
|
|
185
|
+
transfer is isomorphic-git's own pack layer — `packObjects` fed an **explicitly computed
|
|
186
|
+
reachability closure** (commits → trees → blobs; packObjects packs exactly what it is
|
|
187
|
+
given, measured) written straight into the target's `objects/pack/` and `indexPack`ed,
|
|
188
|
+
then refs moved under the same ff-only rule. Zero network, zero credentials, still zero
|
|
189
|
+
git-binary. Two machines converging through one shared path — including a real fork
|
|
190
|
+
resolved by mirror rebuild — is covered by `tests/integration/sync-local-upstream.test.ts`.
|
|
191
|
+
|
|
192
|
+
**§3.1/§3.2 are the git provider's rules.** Since the 2026-09-21 transport amendment the transport behind them
|
|
193
|
+
is one implementation of a six-verb seam (`src/provider.ts`); the TreeDX service provider
|
|
194
|
+
keeps §3.1's store/mirror split verbatim (the mirror is provider-materialized content, not a
|
|
195
|
+
clone) and §5.2's rebuild-on-divergence verbatim. The per-verb mapping, failure classes, and
|
|
196
|
+
mirror state file are specified in [docs/provider.md](provider.md); API facts and the
|
|
197
|
+
[LIVE-GATED] list live in [spikes/treedx/FINDINGS.md](../spikes/treedx/FINDINGS.md).
|
|
198
|
+
|
|
199
|
+
### 3.3 The no-conflict rule
|
|
200
|
+
|
|
201
|
+
Two machines never write the same file:
|
|
202
|
+
|
|
203
|
+
- `chapters/`, `artifacts/`, `collections/` are partitioned by **sessionId** (UUID — collision-free)
|
|
204
|
+
under the project key; a session belongs to exactly one machine.
|
|
205
|
+
- `edits/<harness-id>/` is partitioned by **harness id** — one writer per file, ever.
|
|
206
|
+
- `index/`, `topics/vocabulary.json`, `project.yml` are **plugin-derived, single-consumer
|
|
207
|
+
writes**: built by the syncing machine from append-only inputs. If a pull ever finds a
|
|
208
|
+
divergent derived file, the plugin **rebuilds it locally and commits the rebuild** — never
|
|
209
|
+
merge-resolves it (§10.2). Merge conflicts are structurally impossible for content and
|
|
210
|
+
structurally repaired for derived state.
|
|
211
|
+
|
|
212
|
+
## 4. Chapter composition: topic-sequential, deterministic
|
|
213
|
+
|
|
214
|
+
The user requirement: **consecutive message collections that reference the same task merge into
|
|
215
|
+
one chapter; a chapter splits only when the task changes or the chapter outgrows a configured
|
|
216
|
+
limit.** This replaces size-only splitting as the primary axis and is the load-bearing new
|
|
217
|
+
algorithm of the whole record.
|
|
218
|
+
|
|
219
|
+
### 4.1 Collection and signature
|
|
220
|
+
|
|
221
|
+
- **Collection = one completed turn**: the user message(s) of the turn, its assistant
|
|
222
|
+
messages, and its `turn/end`. (The "user + first + last assistant" slice is the *enrichment
|
|
223
|
+
input*, §6.1; composition works on the whole collection.)
|
|
224
|
+
- **At `turn/end`, synchronously** (millisecond-scale, no queue), the plugin extracts the
|
|
225
|
+
collection's **deterministic signature** and appends one line to
|
|
226
|
+
`collections/<projectKey>/<sessionId>.jsonl`:
|
|
227
|
+
|
|
228
|
+
```json
|
|
229
|
+
{"seqs":[41,42,43,44,45],"paths":["src/auth/middleware.ts","src/auth/token.ts"],
|
|
230
|
+
"commands":["npm test","git diff"],"terms":["auth","refresh","jwt","middleware"],
|
|
231
|
+
"size":1830,"by":"deterministic","at":"2026-09-17T…"}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
- `paths`: file paths mentioned in tool calls/results (highest-signal source in coding
|
|
235
|
+
sessions).
|
|
236
|
+
- `commands`: command/argv tokens, normalized.
|
|
237
|
+
- `terms`: stopword-filtered keyword frequency, top-N.
|
|
238
|
+
- `size`: estimated tokens (cross-machine heuristic — fine for composition, not for
|
|
239
|
+
display; the TOC shows message counts, not tokens, §7.3, because tokenization is not
|
|
240
|
+
comparable across models).
|
|
241
|
+
|
|
242
|
+
Because the signature is computed *when the turn completes*, **no compaction or fork ever
|
|
243
|
+
needs unprocessed material**: every completed collection is already signed, and an
|
|
244
|
+
unfinished collection (the in-flight turn) is never in scope — the host compacts at
|
|
245
|
+
`agent/pre-step` only, never mid-step (measured r13/r17), and its retained tail always
|
|
246
|
+
contains the current turn. This is the answer to "compaction can happen at any step."
|
|
247
|
+
|
|
248
|
+
### 4.2 Composition (at archive time)
|
|
249
|
+
|
|
250
|
+
When a compaction or fork archives a span of completed collections, chapters are composed by a
|
|
251
|
+
**greedy sequential merge**, deterministic and O(collections in span):
|
|
252
|
+
|
|
253
|
+
1. Open chapter 1 at collection 1; accumulate its running signature (union, with
|
|
254
|
+
per-collection weighting).
|
|
255
|
+
2. For each next collection: **merge** if
|
|
256
|
+
`overlap(collection, running) ≥ τ` **or** `primaryMatch(collection, running)` — **and**
|
|
257
|
+
`chapterSize < chapterLimit` — otherwise **close the chapter and open a new one**
|
|
258
|
+
at this collection.
|
|
259
|
+
3. `overlap` is a normalized term overlap: shared paths (heavy weight — same file is
|
|
260
|
+
near-probably same task), shared command families, shared terms, over the union.
|
|
261
|
+
`τ` (merge threshold) and `chapterLimit` are config (§12), both in the same
|
|
262
|
+
"no hardcoded tunables" register as `chapterTokenTarget`.
|
|
263
|
+
4. `primaryMatch`: the first path of either running member (`paths[0]` — the file a
|
|
264
|
+
turn is *about*, by causal order of touch) names the same file as the incoming
|
|
265
|
+
collection's primary, with a spelling tolerance (`src/render` = `src/render.ts`).
|
|
266
|
+
**Amendment r28 (2026-09-18, measured live):** the first real topic test — five
|
|
267
|
+
research turns on this repo — showed overlap ALONE never reaches τ for legitimate
|
|
268
|
+
same-topic pairs: real turns touch 5–6 files, one shared path dilutes in a union
|
|
269
|
+
of ~10 (measured 0.077 vs τ 0.3), while cross-topic pairs score 0.0–0.02. The
|
|
270
|
+
intent of rule 3's "same file is near-probably same task" is thus carried
|
|
271
|
+
explicitly: a shared primary is a first-class merge signal, not a diluted weight.
|
|
272
|
+
τ and the size cap are unchanged; every decision still logs the rule that fired.
|
|
273
|
+
|
|
274
|
+
Boundary, measured live (r29/r36): the host's pressure selection ends at
|
|
275
|
+
tool-pair balance points, blind to turn boundaries — shallow repairs slice
|
|
276
|
+
collections, and the completeness guard then correctly falls back to legacy
|
|
277
|
+
single-chapter fragments. Topic-sequential composition therefore governs the
|
|
278
|
+
turn-aligned archive moments (fork/continue, where the span is anchored at a
|
|
279
|
+
`turn/end`); the engine wiring (r29) is the opportunistic upgrade for deep
|
|
280
|
+
selections (first breach of a heavily-over window), never a fragment
|
|
281
|
+
regression. If engine-path merges ever need to be reliable under steady
|
|
282
|
+
pressure, the fix is cross-chapter collection stitching in the index (P2),
|
|
283
|
+
not selection tuning.
|
|
284
|
+
|
|
285
|
+
Honest quality bar: strong for coding sessions (files and commands leave loud signatures),
|
|
286
|
+
degrading to time/size splitting for pure-prose conversations — never worse than today's
|
|
287
|
+
behavior, never blocking.
|
|
288
|
+
|
|
289
|
+
### 4.3 The completeness guard
|
|
290
|
+
|
|
291
|
+
Chapters include **only collections with a `turn/end`** inside the span. If a compaction
|
|
292
|
+
boundary ever lands mid-collection (the manual-`/compact` edge case — the automatic path
|
|
293
|
+
cannot produce one, §4.1), the composition clamps to the last completed collection and the
|
|
294
|
+
TOC notes the clamp. The unfinished collection stays in the live context, per the user
|
|
295
|
+
requirement, and composes at the next archive.
|
|
296
|
+
|
|
297
|
+
### 4.4 Consequence: composition is archive-time-only
|
|
298
|
+
|
|
299
|
+
Because chapters are append-only (invariant 3), the merge threshold must be right *when the
|
|
300
|
+
archive happens*. The per-collection signature is stored with provenance (§4.1) precisely so
|
|
301
|
+
any merge or split is **inspectable after the fact** — and so `τ` can be tuned for future
|
|
302
|
+
archives without pretending to rewrite old ones. Enrichment (§6) may later *relabel* a
|
|
303
|
+
chapter's topics but may never re-chapter it.
|
|
304
|
+
|
|
305
|
+
## 5. The sync loop
|
|
306
|
+
|
|
307
|
+
### 5.1 Points
|
|
308
|
+
|
|
309
|
+
| Event | Action |
|
|
310
|
+
|---|---|
|
|
311
|
+
| New chapters-preset session created | **pull** (fresh corpus before the first turn; §5.2 ordering) |
|
|
312
|
+
| Fork (button or command) | **pull → compose/archive → push** |
|
|
313
|
+
| Compaction finalization | **push** (debounced — see 5.2) |
|
|
314
|
+
| Idle (debounce expired after the last archive) | **push** |
|
|
315
|
+
| Pull failure / push failure | log, surface via `/chapters-status`, **never fail the archive** |
|
|
316
|
+
| `/chapters-link <path>` | binds a directory upstream (bare repo materialized on first sync); no credentials |
|
|
317
|
+
|
|
318
|
+
Compaction and fork are simultaneously push points *and* the right refresh points — the loop
|
|
319
|
+
is pull → archive new span → push, so a fork point always sees a current index.
|
|
320
|
+
|
|
321
|
+
### 5.2 Ordering and serialization
|
|
322
|
+
|
|
323
|
+
- A **per-repo writer lock** serializes (a) store transactions (reserve → write → commit) and
|
|
324
|
+
(b) clone operations. A pull never lands mid-archive-write and a push never ships a
|
|
325
|
+
half-committed store.
|
|
326
|
+
- Push = `copy-out → commit → fetch → if diverged: rebase-apply (append-only ⇒ fast-forward
|
|
327
|
+
safe) → push`, with bounded retry. All git calls run with no TTY prompt (
|
|
328
|
+
`GIT_TERMINAL_PROMPT=0` equivalent in the SDK), hard timeouts, and network treated as
|
|
329
|
+
best-effort.
|
|
330
|
+
**Implementation note (r35, via §15):** divergence is resolved by REBUILDING the mirror,
|
|
331
|
+
not rebasing it — `remove → fresh clone → re-publish from the store → push`. Same
|
|
332
|
+
semantics (the local backlog reaches the remote without force; append-only content merges
|
|
333
|
+
trivially), fewer moving parts, and no rebase primitive needed (isomorphic-git 1.42 has
|
|
334
|
+
none). The rebuild is safe only because the mirror is transport (§3.1) — the workspace
|
|
335
|
+
store is the truth it re-publishes from. Diverged-vs-network is a machine-readable
|
|
336
|
+
failure CODE, not prose matching: a network error's message once contained the word
|
|
337
|
+
"diverge" and falsely triggered this path (caught by the real-HTTP test).
|
|
338
|
+
- Debounce: consecutive archive events within the configured window coalesce into one push.
|
|
339
|
+
The triggers live in both planes: the host plugin pulls before, and schedules after,
|
|
340
|
+
fork/continue archives; the realm engine schedules after compaction finalization and
|
|
341
|
+
pulls at each session's first `turn/start` (the "new session" refresh point, read
|
|
342
|
+
process-scoped). The file lock is what makes the two schedulers safe side by side.
|
|
343
|
+
|
|
344
|
+
### 5.3 Failure semantics
|
|
345
|
+
|
|
346
|
+
Remote down, token revoked, clone corrupted — each degrades to *local-only mode*: the store
|
|
347
|
+
keeps working, pending sync accumulates, `/chapters-status` says so with numbers. Nothing in
|
|
348
|
+
the conversation path waits on the network.
|
|
349
|
+
|
|
350
|
+
## 6. Model enrichment: async, provenance-tagged, re-runnable
|
|
351
|
+
|
|
352
|
+
### 6.1 The pass
|
|
353
|
+
|
|
354
|
+
For each archived chapter, one structured call whose input is the cheap slice the user
|
|
355
|
+
specified — **the user request(s) plus first and last assistant message**, each capped, plus
|
|
356
|
+
the first ~200 tokens of the chapter body for grounding — and whose output is validated JSON:
|
|
357
|
+
|
|
358
|
+
```json
|
|
359
|
+
{"title": "…one sentence…", "summary": "…short…", "topics": ["…up to 10…"]}
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
- **Validation before commit:** schema-check; on failure, retry once; on second failure, keep
|
|
363
|
+
the deterministic values (title from the existing derivation, excerpt-labeled summary,
|
|
364
|
+
signature topics). A slow or failing model never blocks or blanks a chapter.
|
|
365
|
+
- **Thinking models:** the pass runs at low reasoning effort and tight `maxTokens`; on the
|
|
366
|
+
local server this is the only model work this feature ever forces, and it is batched,
|
|
367
|
+
below.
|
|
368
|
+
- **Topics are proposals:** they enter the chapter's frontmatter only through the
|
|
369
|
+
canonicalizer (§6.3) — unknown labels pass as `unvetted`.
|
|
370
|
+
|
|
371
|
+
### 6.2 When it runs: idle-batched, never per-turn
|
|
372
|
+
|
|
373
|
+
**Measured constraint (r29-era):** the local server is **single-slot**
|
|
374
|
+
(`maxConcurrent: 1`). Per-turn model work would queue against the user's own conversation
|
|
375
|
+
turns. Therefore:
|
|
376
|
+
|
|
377
|
+
- **Per turn:** deterministic signature only (§4.1) — free, synchronous, no model.
|
|
378
|
+
- **Model enrichment:** batched at natural idle points — after a compaction/fork push
|
|
379
|
+
completes, or on an idle debounce — processing *archived* chapters only. It can be
|
|
380
|
+
disabled per-harness in config; a harness on a contended machine can run
|
|
381
|
+
signatures-only and still contribute a fully functional corpus.
|
|
382
|
+
- **Re-runnable:** a command re-annotates a chapter (or the whole project) with a newer model
|
|
383
|
+
or corrected vocabulary, overwriting only the model-owned fields (frontmatter provenance
|
|
384
|
+
records each overwrite). The verbatim body is never touched.
|
|
385
|
+
|
|
386
|
+
### 6.3 Self-bootstrapping vocabulary (no seed list, no hand-editing)
|
|
387
|
+
|
|
388
|
+
Per the user requirement, the vocabulary is **plugin-owned and emergent** — the user neither
|
|
389
|
+
authors a seed list nor edits files:
|
|
390
|
+
|
|
391
|
+
1. **First runs:** no canonical vocabulary. Topic labels are raw deterministic candidates;
|
|
392
|
+
enrichment topics enter flagged `unvetted`.
|
|
393
|
+
2. **Merge signals**, watched by the plugin against its own corpus:
|
|
394
|
+
- two labels **co-occur** in the same chapter repeatedly;
|
|
395
|
+
- two labels share **high normalized token overlap** across chapters;
|
|
396
|
+
- **alias patterns** (substring/singular-plural: `auth` ⊂ `authentication`).
|
|
397
|
+
Past threshold, the plugin writes a **curation entry** into `edits/<harness-id>/`
|
|
398
|
+
(Layer 1, §10.1): `author: plugin`, `reason: <signal + counts>`, `merge: {from, to}`.
|
|
399
|
+
3. The derived index and `topics/vocabulary.json` reflect curation entries on the next build;
|
|
400
|
+
the vocabulary file stays **diffable and git-revertable** — the user can see every
|
|
401
|
+
decision the plugin made, and undo one, without ever having to make one.
|
|
402
|
+
4. Escape hatch (P3, optional): `/chapters-topic merge <a> <b>` — the same curation entry,
|
|
403
|
+
human-authored.
|
|
404
|
+
|
|
405
|
+
Provenance in frontmatter (§2.4) is what makes all of this auditable: any label on any
|
|
406
|
+
chapter traces to a deterministic pass, a specific model run, or a specific curation entry.
|
|
407
|
+
|
|
408
|
+
## 7. Rules: chapters of a different kind
|
|
409
|
+
|
|
410
|
+
### 7.1 Model
|
|
411
|
+
|
|
412
|
+
Rules reuse the chapter machinery end-to-end — same file format, registry, budget preflight,
|
|
413
|
+
search, and sync. Frontmatter: `kind: rule`, plus `category`, `status`, and provenance.
|
|
414
|
+
**Rules are another form of chapter to search when needed** (user's framing, adopted); the
|
|
415
|
+
plugin's modularity is what makes this nearly free — the chapter code paths are parameterized
|
|
416
|
+
by `kind`, not duplicated.
|
|
417
|
+
|
|
418
|
+
### 7.2 Lifecycle: proposed → core, approved in-session
|
|
419
|
+
|
|
420
|
+
- **Proposal:** a user command (`/chapters-rule add <category> <text>`) or an agent's async
|
|
421
|
+
derivation from a completed session (P3, always `status: proposed`, provenance-tagged with
|
|
422
|
+
the source session).
|
|
423
|
+
- **Review/approve:** entirely in-session DSH commands — the user never leaves the session,
|
|
424
|
+
and **command output never enters the context window** (verified 2026-09-17: command results
|
|
425
|
+
persist as `command/run` / `command/done` event types — a distinct type, not
|
|
426
|
+
user/assistant/tool — which are neither prefilled to the model nor priced by the compaction
|
|
427
|
+
engine; 24 such pairs observed in the r29 session's durable log from the e2e suite):
|
|
428
|
+
- `/chapters-rule list` (`--proposed`, `--category <name>`) — table: id, status, category,
|
|
429
|
+
one-line text, provenance.
|
|
430
|
+
- `/chapters-rule approve <id>` — `proposed → core`.
|
|
431
|
+
- `/chapters-rule revoke <id>` — core back to the file, out of the notice.
|
|
432
|
+
- **`core` is the only tier that renders into the continuation notice** (§7.3).
|
|
433
|
+
**Amended 2026-09-20 (§15):** `approve` does not rewrite the rule file — it appends a
|
|
434
|
+
`rule-status` curation fact to the APPROVING machine's own `edits/<harness>/curation.jsonl`.
|
|
435
|
+
Effective status is per-machine (file otherwise `proposed`); a rule affects no other
|
|
436
|
+
machine's notice until that machine's human approves. Reason: authorship partition — a
|
|
437
|
+
shared-file status would let any machine rewrite another's authored bytes, which r37
|
|
438
|
+
showed is exactly where transport bugs live. The human
|
|
439
|
+
in the loop is the one who types `approve`; the git diff of the rule file is the audit
|
|
440
|
+
trail. This is the trust boundary for shared state (§11.3).
|
|
441
|
+
|
|
442
|
+
### 7.3 The rules section of the continuation notice
|
|
443
|
+
|
|
444
|
+
The notice carries (all O(1), §1 invariant 6):
|
|
445
|
+
|
|
446
|
+
- **Core rules:** the `status: core` set, rendered verbatim, **size-capped** by the same
|
|
447
|
+
refuse-don't-truncate rule — if core rules exceed their budget, the notice names the
|
|
448
|
+
overflow with numbers and the user curates (`revoke`, or split a category). Never silent
|
|
449
|
+
clipping.
|
|
450
|
+
- **Rules category index:** category names + one-line essence + paths, with the instruction
|
|
451
|
+
*load a rule category when this session's topics overlap a category* — i.e., the agent
|
|
452
|
+
reads the full category from the repo when (and only when) relevant.
|
|
453
|
+
- **Search instructions** for the chapters index (one paragraph, pointing at
|
|
454
|
+
`chapters_search` and the shard paths).
|
|
455
|
+
- **`Project: <slug> · <projectKey>` — prominently near the top** (user requirement): the
|
|
456
|
+
agent always knows which corpus it is in and where searches point.
|
|
457
|
+
- The existing sections (chapters list with single-sentence title, short summary, file link,
|
|
458
|
+
**message count** — not token count, since tokenization is not comparable across models —
|
|
459
|
+
and parent/fork links) unchanged.
|
|
460
|
+
|
|
461
|
+
### 7.4 Later: topic-matched auto-inclusion
|
|
462
|
+
|
|
463
|
+
When a continuation is created, the plugin can compute topic overlap between the archived
|
|
464
|
+
span's topics and rule categories and inline matching *category-level* rules within the same
|
|
465
|
+
budget. P3b; gated by the same cap-and-refuse rule.
|
|
466
|
+
|
|
467
|
+
## 8. Search: `chapters_search`
|
|
468
|
+
|
|
469
|
+
### 8.1 Contract
|
|
470
|
+
|
|
471
|
+
```
|
|
472
|
+
chapters_search { query: string, maxTokens?: number, topic?: string, crossProject?: boolean }
|
|
473
|
+
→ { matches: [...], shown: n, total: m, budget: {requested, used, remaining} }
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
- `maxTokens` is **the agent's request** (user requirement: the model knows its own window;
|
|
477
|
+
the plugin measures and packs to it, the same way the continuation budget preflight does).
|
|
478
|
+
Default: small (config), overridable upward by the agent.
|
|
479
|
+
- **Deterministic scorer** (no model, reproducible, transparent):
|
|
480
|
+
- term hits on **topic (×2 weight), title, summary, category** (rules: category + text);
|
|
481
|
+
- **recency** boost: exponential decay on chapter date;
|
|
482
|
+
- small bonus for `kind: rule, status: core`.
|
|
483
|
+
- **Output lines:** `score | date | kind | title | path (topics: …)` — packed until
|
|
484
|
+
`maxTokens` is spent, most-relevant first; the response reports `total` so the agent can
|
|
485
|
+
re-request with a larger `maxTokens` to page (transparent, no hidden truncation).
|
|
486
|
+
- **`topic: <name>`** reads an entire index shard (a bounded, alphabetical file).
|
|
487
|
+
- **Default filter: the caller's `projectKey`** — cross-project search is an explicit
|
|
488
|
+
`crossProject: true`, so a small-model session never wanders into a foreign corpus by
|
|
489
|
+
accident.
|
|
490
|
+
- **Zero new trust surface:** every `path` returned is a plain file the existing `read` tool
|
|
491
|
+
opens (chapters in `.dsh-chapters/`, shards in `.dsh-knowledge/index/`). The search tool
|
|
492
|
+
adds a scorer and a formatter; it adds no new way to read.
|
|
493
|
+
|
|
494
|
+
## 9. Redaction: minimal, deterministic, marked
|
|
495
|
+
|
|
496
|
+
User position, adopted: **credentials only, deterministic patterns only, everything else
|
|
497
|
+
byte-identical** — the character of the conversation is historical value and stays intact.
|
|
498
|
+
|
|
499
|
+
- **Patterns (deterministic):** `AKIA…` (AWS), `sk-…` (OpenAI-shape), `ghp_/gho_/
|
|
500
|
+
github_pat_`, JWT `eyJ…` shape, `Authorization: Bearer …`, `token=…` in URLs, PEM private
|
|
501
|
+
key blocks. Deliberately conservative; new patterns are config additions, not magic.
|
|
502
|
+
- **Marker format:** `⟦redacted:credential sha256=8f3a1c2b⟧` — (a) obviously not a real value
|
|
503
|
+
to any model reading the chapter, (b) **deterministic per secret** (same secret → same
|
|
504
|
+
marker, so diffs and cross-chapter greps stay stable), (c) leaks nothing but a prefix hash,
|
|
505
|
+
which is also what lets a user ask "which secrets touched my sessions" without exposing
|
|
506
|
+
them.
|
|
507
|
+
- **Applied once, at the archive chokepoint** — render → redact → write — covering chapters
|
|
508
|
+
*and* artifacts, so a value is redacted at the moment it leaves the session log, wherever it
|
|
509
|
+
appears.
|
|
510
|
+
- **Honest limit:** pattern-based redaction is a speed bump, not a guarantee. The doc that
|
|
511
|
+
ships with the repo (and the notice) says the corpus is **plaintext memory**: per-project,
|
|
512
|
+
private, user-created, with a known-small redaction carve-out.
|
|
513
|
+
|
|
514
|
+
## 10. The two-layer index
|
|
515
|
+
|
|
516
|
+
### 10.1 Layer 1 — editable, per-machine, append-only
|
|
517
|
+
|
|
518
|
+
`edits/<harness-id>/curation.jsonl` — one JSON line per index fact, **one writer per file,
|
|
519
|
+
ever** (§3.3), zero conflicts by construction:
|
|
520
|
+
|
|
521
|
+
```json
|
|
522
|
+
{"type":"topic-alias","from":"auth","to":"authentication","author":"plugin",
|
|
523
|
+
"reason":"co-occurrence in 14 chapters","session":"…","at":"…"}
|
|
524
|
+
{"type":"group","chapters":["…a","…b"],"label":"jwt-refresh-work","author":"user","at":"…"}
|
|
525
|
+
{"type":"weight","topic":"auth","weight":2,"author":"user","at":"…"}
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
Fact types: `topic-alias` (merge/rename), `topic-pin`, `group` (sibling chapters that
|
|
529
|
+
belong together — the semantic grouping that composition cannot do alone), `weight`,
|
|
530
|
+
`note`. Authors are `plugin` or `user` (user entries via commands, P3). This is the
|
|
531
|
+
"improve over time" layer: curation accumulates as facts, and the derived index reflects
|
|
532
|
+
them — performance improves without anyone hand-editing, and every fact is inspectable and
|
|
533
|
+
revertable in git.
|
|
534
|
+
|
|
535
|
+
### 10.2 Layer 2 — compressed, derived, incremental
|
|
536
|
+
|
|
537
|
+
`index/<topic-shard>.md` — one shard per canonical topic (alphabetical segmentation:
|
|
538
|
+
bounded files, and writes from different chapters usually touch different shards). Shard
|
|
539
|
+
content: a header line (topic, count, last-updated) then one line per chapter:
|
|
540
|
+
`date | title | path | (kind) | recency-hint`.
|
|
541
|
+
|
|
542
|
+
**Build is incremental** (user requirement: generation time must not scale with the corpus):
|
|
543
|
+
`manifest.json` records, per input file, the content hash last processed. A build touches
|
|
544
|
+
only (a) new chapters/artifacts/collections and (b) new curation entries since the last
|
|
545
|
+
build — O(delta). Because inputs are append-only, "changed" ≈ "new" in practice.
|
|
546
|
+
|
|
547
|
+
**Full rebuild stays possible** (deterministic from the tree) as the *recovery* path — a
|
|
548
|
+
machine with a lost manifest regenerates, and any machine can repair a divergent derived
|
|
549
|
+
file by rebuild-and-commit rather than merge-resolving (§3.3). Incremental is the fast path;
|
|
550
|
+
rebuild is the repair path.
|
|
551
|
+
|
|
552
|
+
## 11. Trust and security
|
|
553
|
+
|
|
554
|
+
### 11.1 Credentials
|
|
555
|
+
|
|
556
|
+
Scoped, revocable, per-repo, per-harness (§2.1). No SSH. A compromised harness exposes at
|
|
557
|
+
most one repo token, revocable in one click.
|
|
558
|
+
|
|
559
|
+
### 11.2 Data exposure
|
|
560
|
+
|
|
561
|
+
The corpus is plaintext memory (chapters are verbatim by design). Mitigations are the
|
|
562
|
+
boundaries, not illusion: per-project repos, private, user-created; redaction carve-out
|
|
563
|
+
(§9); per-harness namespacing so one machine's sessions are always attributable.
|
|
564
|
+
|
|
565
|
+
### 11.3 The blast-radius rule
|
|
566
|
+
|
|
567
|
+
Everything in the corpus that a *model* can influence and that other machines will *follow*
|
|
568
|
+
needs a human gate:
|
|
569
|
+
|
|
570
|
+
- **Chapters:** low blast radius — evidence, provenance-tagged, attributable. No gate.
|
|
571
|
+
- **Topics/vocabulary:** medium — affects search, not behavior. Plugin-autonomous with
|
|
572
|
+
git-visible curation entries (§6.3).
|
|
573
|
+
- **Rules:** high — they are prompts by another name, shared across machines. **Human
|
|
574
|
+
`approve` is mandatory** (§7.2). A proposed rule that is never approved affects nothing.
|
|
575
|
+
|
|
576
|
+
Git history + provenance makes every tier auditable and every mistake revertible.
|
|
577
|
+
|
|
578
|
+
## 12. Configuration surface
|
|
579
|
+
|
|
580
|
+
No hardcoded tunables (existing hard rule). New config, all with defaults documented in
|
|
581
|
+
`dev/` templates:
|
|
582
|
+
|
|
583
|
+
| Key | Meaning | Default |
|
|
584
|
+
|---|---|---|
|
|
585
|
+
| `knowledgeRemote` | the project's repo URL (or `/chapters-link`) | none (local-only) |
|
|
586
|
+
| `projectKeyOverride` | wins over §2.3 derivation | none |
|
|
587
|
+
| `mergeThreshold` (τ) | collection-merge overlap, §4.2 | tuned in P1 |
|
|
588
|
+
| `chapterLimit` | max chapter size (tokens, heuristic) before forced split | 8000 (today's `chapterTokenTarget`) |
|
|
589
|
+
| `coreRulesBudgetTokens` | cap on the notice's core-rules section | refused-with-numbers on overflow |
|
|
590
|
+
| `syncDebounceMs` | coalesce window for pushes | 30000 |
|
|
591
|
+
| `toolResultArtifactTokens` | arrival-time artifacting floor (architecture.md amendment 2026-09-19): tool results at/above this many estimated tokens are stored as artifacts and reference-stubbed BEFORE the next request composes — the blob never enters any prompt | 8000 |
|
|
592
|
+
| `elicitedPlot` | when a compaction region holds no model-authored `PLOT:` note, spend ONE bounded stream call (≤8K chars in, ≤220 tokens out) to mint the working plot carried into the checkpoint; the approved, config-killed exception to zero-inference | true |
|
|
593
|
+
| `enrichment.enabled` / `.model` | per-harness enrichment toggle + model pin (built flat: `enrichmentEnabled`, `enrichmentModel`) | enabled, conversation model |
|
|
594
|
+
| `enrichment.trigger` / `.idleMs` / `.batchCap` | P2 queue policy seam (built: `enrichmentTrigger/enrichmentIdleMs/enrichmentBatchCap`); realm owns auto, host plane is manual-only; `/chapters-enrich model` overrides durably | both / 60000 / 5 |
|
|
595
|
+
| `vocabulary.*` | built flat: `vocabApply` (false = **shadow**: candidates reported in the sync status line, nothing written), `vocabCoMin` (3), `vocabOverlapMin` (0.5); applied merges land as §10.1 `topic-alias` facts under `edits/<harness>/`; `topics/vocabulary.json` derived | shadow / 3 / 0.5 |
|
|
596
|
+
| `coreRulesBudgetTokens` | P3 §7.3 cap on the notice's core-rules block; overflow refuses with per-rule numbers | 1200 |
|
|
597
|
+
| `rulesCoreBonus` | P3 §8 query-time rank bonus for rules effective-core on this machine (never baked into shared shards) | 0.15 |
|
|
598
|
+
| `redaction.patterns` | additive to the built-in list (§9) | built-ins |
|
|
599
|
+
| `search.defaultMaxTokens` | default pack size for `chapters_search` | small, config |
|
|
600
|
+
| `knowledgeProvider` | transport policy (Amendment 2026-09-21, transport): `auto` dispatches on the remote scheme; an explicit `git`/`treedx` must match it — contradictions refuse, nothing falls back into a wrong pool | auto |
|
|
601
|
+
| `treedxFetchTimeoutMs` / `treedxWorkspaceTtlSeconds` / `treedxLeaseRetries` / `treedxLeaseRetryDelayMs` | TreeDX transport tunables (provider.md); lease retries bound the one-writable-lease-per-branch contention window | 15000 / 900 / 3 / 1000 |
|
|
602
|
+
|
|
603
|
+
## 13. Phasing (each phase independently useful)
|
|
604
|
+
|
|
605
|
+
**P1 — Transport + deterministic corpus (zero new inference).**
|
|
606
|
+
Clone/sync loop (§3, §5); `projectKey` (§2.3); per-turn signatures at `turn/end` (§4.1);
|
|
607
|
+
topic-sequential composition replacing size-only splitting (§4.2–4.4) for the fork path
|
|
608
|
+
first (compaction spans use the same composer); derived sharded index + incremental manifest
|
|
609
|
+
(§10.2); `chapters_search` (§8); redaction at the archive chokepoint (§9); notice v2 —
|
|
610
|
+
`Project:` line, message counts, search instructions (§7.3).
|
|
611
|
+
*Exit criterion: two machines, one repo; a fork on machine A sees machine B's chapters via
|
|
612
|
+
search; sync failure degrades to local-only without breaking the fork.*
|
|
613
|
+
|
|
614
|
+
**P2 — Model enrichment + emergent vocabulary.**
|
|
615
|
+
Idle-batched enrichment pass (§6.1–6.2) with validation/fallback; provenance frontmatter
|
|
616
|
+
final; self-bootstrapping vocabulary + curation entries (§6.3); Layer 1 edit types finalized.
|
|
617
|
+
*Exit criterion: enriching a corpus is idempotent and re-runnable; vocabulary merges are
|
|
618
|
+
git-visible and revertible; disabling enrichment leaves a fully working corpus.*
|
|
619
|
+
|
|
620
|
+
**P2 STATUS (2026-09-20): BUILT.** The ladder (slice → validate → retry-once → keep
|
|
621
|
+
deterministic; `src/enrich.ts`), queue with the pluggable trigger seam (`both`/60s defaults,
|
|
622
|
+
realm owns auto, host is manual + `/chapters-enrich run|model|report`), provenance chains +
|
|
623
|
+
the body-hash-guarded frontmatter write as the ONLY sanctioned chapter mutation
|
|
624
|
+
(`enrich-store.ts`), the vocabulary canonicalizer shadow-by-default with applied merges as
|
|
625
|
+
git-visible `topic-alias` entries + derived `topics/vocabulary.json` (`vocabulary.ts` in the
|
|
626
|
+
sync pass), and index-level fragment stitching (pure read-view `stitchFragments` — adjacent
|
|
627
|
+
legacy fragments cohere into one search entry citing all members, nothing merged on disk).
|
|
628
|
+
Idempotent re-run / kill-switch parity / never-blank failure: unit + integration; the `enrich`
|
|
629
|
+
e2e project replays the whole ladder through the model tape. CLOSED 2026-09-20: two-machine grade proven (two-machine-enrich test — which caught and fixed r37, a commit-invisible-modification bug in the transport), and the four-project replay chain is green with zero misses.
|
|
630
|
+
|
|
631
|
+
**P3 — Rules.**
|
|
632
|
+
Rules-as-chapters (`kind: rule`); the in-session command family `/chapters-rule
|
|
633
|
+
list|add|approve|revoke` (§7.2 — command output verified context-free); core-rules section +
|
|
634
|
+
category index in the notice (§7.3); agent-side rule proposals (async, always `proposed`);
|
|
635
|
+
`/chapters-topic` escape hatch; P3b topic-matched auto-inclusion (§7.4).
|
|
636
|
+
*Exit criterion: a rule proposed by a machine affects no other machine until a human types
|
|
637
|
+
`approve`; the core set overflows by refusing with numbers, never clipping.*
|
|
638
|
+
|
|
639
|
+
**P3 STATUS (2026-09-20): BUILT.** `src/rules.ts` (write-once proposed files, per-machine
|
|
640
|
+
status resolution, budgeted section with refuse-with-numbers) + `rules-commands.ts`
|
|
641
|
+
(`/chapters-rule add|list|approve|revoke`, host plane) + `chapters_rule_propose` tool +
|
|
642
|
+
notice seam (`assembleNotice` rulesSection — byte-identical ABSENT, golden-tested so the
|
|
643
|
+
tape corpus is untouched by construction) + transport (rules tree publishes; commit is
|
|
644
|
+
attempted even at copied===0 so fact-only passes travel — the change that made approvals
|
|
645
|
+
shareable) + scorer (category 1.5, rule bodies, per-machine core bonus at query time).
|
|
646
|
+
Exit-criterion tests: unit ('machine B sees nothing core'), transport-grade (three machines
|
|
647
|
+
over a real pool), e2e (`rules` project: UI add → approve → fork → the child's own log
|
|
648
|
+
carries the rule verbatim). §7.4 auto-inclusion remains P3b, unbuilt by design.
|
|
649
|
+
|
|
650
|
+
## 14. Open risks (honest register)
|
|
651
|
+
|
|
652
|
+
| Risk | State |
|
|
653
|
+
|---|---|
|
|
654
|
+
| Pure-JS git SDK performance at repo scale; SSH-only remotes | accepted (user-directed); CLI fallback config-gated |
|
|
655
|
+
| Deterministic topic quality on pure-prose (non-code) sessions | degrades to today's time/size behavior; enrichment improves recall later |
|
|
656
|
+
| Pattern redaction is a speed bump, not a guarantee | documented in-repo; patterns are config-extensible |
|
|
657
|
+
| Single-slot model server contention | enrichment is idle-batched and per-harness disableable; signatures are model-free |
|
|
658
|
+
| Cross-model topic vocabulary drift | canonicalizer + provenance + re-run (§6.3) — mitigated by design, observed in practice at P2 |
|
|
659
|
+
| `mergeThreshold` mis-tune at archive time (composition is archive-time-only, §4.4) | signatures are stored with provenance; tune for future archives, inspect past ones |
|
|
660
|
+
| TreeDX service down or token expired (Amendment 2026-09-21, transport) | same §5.3 degradation (offline mirror, deferred push, auth re-link surfaced with numbers); no corpus loss |
|
|
661
|
+
| TreeDX corpus pull is per-file (`paths/list` + `files/read`): N+1 requests at large pools | acceptable at per-project corpus sizes (the record's own scaling premise); snapshot/`context` APIs are the Phase-F optimization |
|
|
662
|
+
|
|
663
|
+
## 15. Amendments
|
|
664
|
+
|
|
665
|
+
This record governs implementation. An amendment is a **human decision** — discussed,
|
|
666
|
+
accepted, and edited in with a commit message stating the reason — because the record's value
|
|
667
|
+
is that every architectural fact in it was chosen, not generated. Machine-derived knowledge
|
|
668
|
+
lives in the *corpus* (chapters, curation entries, vocabulary with provenance); machine
|
|
669
|
+
opinions about *this document* do not.
|
|
670
|
+
|
|
671
|
+
### Amendment 2026-09-21 — transport is pluggable; git is one provider
|
|
672
|
+
|
|
673
|
+
Accepted with the human-approved provider-plugin plan. §3.1/§3.2 stand as the git
|
|
674
|
+
provider's rules; they are now one implementation behind a seam
|
|
675
|
+
(`src/provider.ts`, contract in [provider.md](provider.md)). The six transport verbs
|
|
676
|
+
`runSync` already injected (`GitDriver`) define the provider interface; a second
|
|
677
|
+
implementation, `kind: 'treedx'`, transports the same mirror layout over TreeDX's
|
|
678
|
+
no-clone HTTP API (`src/treedx/`), selected by the `treedx+<url>/<repo>` remote
|
|
679
|
+
scheme (`/chapters-link mode 4`; §12 keys `knowledgeProvider`, `treedx*`). What the
|
|
680
|
+
amendment changes in the record, precisely:
|
|
681
|
+
|
|
682
|
+
- §3.1 holds unchanged: the store is truth, the mirror is transport — in TreeDX
|
|
683
|
+
mode the mirror is a provider-materialized plain directory instead of a clone.
|
|
684
|
+
Search, rules, index build, notice assembly, and the `read` path see no
|
|
685
|
+
difference.
|
|
686
|
+
- §5.2's divergence rule generalizes: any head movement detected at pull or commit
|
|
687
|
+
routes through the same rebuild-the-mirror-from-the-store path. Never a merge.
|
|
688
|
+
- §2.3's identity rule extends: TreeDX keys derive from the canonical
|
|
689
|
+
`host[:port]/repo` string under a `treedx/` namespace prefix, so a service at a
|
|
690
|
+
git-colliding URL is never the same pool.
|
|
691
|
+
- §11.3's blast-radius gate is provider-independent: per-machine rule approval is
|
|
692
|
+
a local curation fact — no server, federated or otherwise, can pre-approve.
|
|
693
|
+
- §5.3's degradation semantics are identical, plus one auth class (a rejected
|
|
694
|
+
token surfaces a re-link instruction; it never enters the conversation path).
|
|
695
|
+
|
|
696
|
+
The TreeDX backend's API-level facts (and what remains [LIVE-GATED] pending a real
|
|
697
|
+
container) are pinned in [spikes/treedx/FINDINGS.md](../spikes/treedx/FINDINGS.md);
|
|
698
|
+
transport-grade proof runs over the stub service (`treedx-{provider,sync,link}.test.ts`)
|
|
699
|
+
and the live exit criterion over `treedx-live.test.ts` when `scripts/treedx-local.sh`
|
|
700
|
+
brings the service up.
|
|
701
|
+
|
|
702
|
+
### Amendment 2026-09-21 — host-injected context is not conversation
|
|
703
|
+
Accepted with the same plan (the human directive: the knowledge layer must not
|
|
704
|
+
depend on any project's instruction files — every target project ships its own,
|
|
705
|
+
unpredictably). The archive guarantee restates: **every byte of the CONVERSATION
|
|
706
|
+
remains retrievable** in chapters or artifacts; host-injected context — project
|
|
707
|
+
instruction files (`agent-instructions` events and the `instructions from:`
|
|
708
|
+
reminder form), the runtime-context snapshot, the skill catalog, and this plugin's
|
|
709
|
+
own TOC notice — is re-derivable project state and is EXCLUDED at the render
|
|
710
|
+
chokepoint (`src/injections.ts`), each omission leaving a countable marker.
|
|
711
|
+
`stats.messages` counts conversation only, so TOC `(N msgs)` stays honest. The
|
|
712
|
+
durable session log is untouched by this (invariant: append-only) — originals live
|
|
713
|
+
in the parent session; the screen is archive-time bytes, applied before redaction
|
|
714
|
+
(§9) at the same chokepoint. The signature layer has applied the same predicate
|
|
715
|
+
since r28; the two now share one implementation so they can never drift. The
|
|
716
|
+
model-tape masking rule (AGENTS-BLIND, 2026-09-20) is the test-side half of the
|
|
717
|
+
same position: development artifacts, never product facts.
|
|
718
|
+
|
|
719
|
+
### Amendment 2026-09-20 — rule approval is a per-machine curation fact
|
|
720
|
+
|
|
721
|
+
Accepted as the P3 plan's single decision point (human-approved with the plan). §7.2's
|
|
722
|
+
file-status rewrite replaced by `rule-status` facts in each machine's own edits file
|
|
723
|
+
(see §7.2 note for the reason). Everything else in §7 stands.
|
|
724
|
+
|
|
725
|
+
---
|
|
726
|
+
|
|
727
|
+
*Cross-references: invariants and the four invariants — `AGENTS.md`; budget math —
|
|
728
|
+
`docs/contract.md` (continuation budget) and `docs/architecture.md` (storage); the engine
|
|
729
|
+
seam the composer hooks into — `docs/host-compaction-seam.md`; measured claims cited as
|
|
730
|
+
`rNN` — `spikes/probe/FINDINGS.md`.*
|