unknown-knowledge 2.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.
Files changed (147) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +5 -0
  3. package/README.md +167 -0
  4. package/cli/.gitkeep +0 -0
  5. package/cli/commands/init-copy.js +90 -0
  6. package/cli/commands/init.js +386 -0
  7. package/cli/init-copy.js +24 -0
  8. package/cli/init.js +24 -0
  9. package/cli/kit.manifest.yaml +256 -0
  10. package/cli/lib/copy-payload.js +334 -0
  11. package/cli/lib/generate-wrappers.js +162 -0
  12. package/package.json +36 -0
  13. package/payload/adapter-fixtures/README.md +53 -0
  14. package/payload/adapter-fixtures/html/EXPECTED.yaml +50 -0
  15. package/payload/adapter-fixtures/html/sample.html +38 -0
  16. package/payload/adapter-fixtures/md/EXPECTED.yaml +65 -0
  17. package/payload/adapter-fixtures/md/sample.md +32 -0
  18. package/payload/adapter-fixtures/pdf/EXPECTED.yaml +45 -0
  19. package/payload/adapter-fixtures/pdf/sample.pdf +0 -0
  20. package/payload/adapter-fixtures/txt/EXPECTED.yaml +31 -0
  21. package/payload/adapter-fixtures/txt/sample.txt +18 -0
  22. package/payload/docs/README.md +102 -0
  23. package/payload/docs/boundaries.md +60 -0
  24. package/payload/docs/ci-wiring.md +109 -0
  25. package/payload/docs/steward-guide.md +238 -0
  26. package/payload/engine/audit.js +34 -0
  27. package/payload/engine/commands/audit.js +289 -0
  28. package/payload/engine/commands/derive.js +334 -0
  29. package/payload/engine/commands/ingest.js +124 -0
  30. package/payload/engine/commands/log-entry.js +85 -0
  31. package/payload/engine/commands/phoenix.js +206 -0
  32. package/payload/engine/commands/preflight.js +530 -0
  33. package/payload/engine/commands/resolve.js +1678 -0
  34. package/payload/engine/commands/survey-map.js +351 -0
  35. package/payload/engine/commands/validate-values.js +315 -0
  36. package/payload/engine/commands/validate.js +1426 -0
  37. package/payload/engine/derive.js +34 -0
  38. package/payload/engine/ingest.js +34 -0
  39. package/payload/engine/lib/anchor-signatures.js +126 -0
  40. package/payload/engine/lib/boot.js +39 -0
  41. package/payload/engine/lib/call-numbers.js +133 -0
  42. package/payload/engine/lib/cli.js +147 -0
  43. package/payload/engine/lib/coverage.js +849 -0
  44. package/payload/engine/lib/decomposition.js +225 -0
  45. package/payload/engine/lib/derived.js +494 -0
  46. package/payload/engine/lib/engine-refusal.js +40 -0
  47. package/payload/engine/lib/exit-codes.js +14 -0
  48. package/payload/engine/lib/extractor-kinds.js +955 -0
  49. package/payload/engine/lib/format-adapters.js +802 -0
  50. package/payload/engine/lib/id-grammars.js +178 -0
  51. package/payload/engine/lib/iso-date.js +55 -0
  52. package/payload/engine/lib/kit-root.js +101 -0
  53. package/payload/engine/lib/load-stores.js +1624 -0
  54. package/payload/engine/lib/log-entry.js +196 -0
  55. package/payload/engine/lib/phoenix.js +628 -0
  56. package/payload/engine/lib/scoring.js +150 -0
  57. package/payload/engine/lib/suppressions.js +172 -0
  58. package/payload/engine/lib/time-verdicts.js +282 -0
  59. package/payload/engine/lib/usage-error.js +14 -0
  60. package/payload/engine/lib/validate-record.js +504 -0
  61. package/payload/engine/log-entry.js +34 -0
  62. package/payload/engine/phoenix.js +39 -0
  63. package/payload/engine/preflight.js +34 -0
  64. package/payload/engine/resolve.js +34 -0
  65. package/payload/engine/survey-map.js +34 -0
  66. package/payload/engine/validate-values.js +34 -0
  67. package/payload/engine/validate.js +34 -0
  68. package/payload/extractor-fixtures/.gitkeep +0 -0
  69. package/payload/extractor-fixtures/README.md +29 -0
  70. package/payload/extractor-fixtures/swift/strings-keys/EXPECTED.yaml +8 -0
  71. package/payload/extractor-fixtures/swift/strings-keys/sample.strings +15 -0
  72. package/payload/extractor-fixtures/swift/swift-const-array/EXPECTED.yaml +7 -0
  73. package/payload/extractor-fixtures/swift/swift-const-array/sample.swift +21 -0
  74. package/payload/extractor-fixtures/swift/swift-enum/EXPECTED.yaml +8 -0
  75. package/payload/extractor-fixtures/swift/swift-enum/sample.swift +30 -0
  76. package/payload/extractor-fixtures/swift/yaml-keys/EXPECTED.yaml +6 -0
  77. package/payload/extractor-fixtures/swift/yaml-keys/sample.yaml +23 -0
  78. package/payload/extractor-fixtures/swift/yaml-map-keys/EXPECTED.yaml +7 -0
  79. package/payload/extractor-fixtures/swift/yaml-map-keys/sample.yaml +15 -0
  80. package/payload/extractor-fixtures/ts/dir-modules/EXPECTED.yaml +12 -0
  81. package/payload/extractor-fixtures/ts/dir-modules/sample-modules/alpha.widget.ts +1 -0
  82. package/payload/extractor-fixtures/ts/dir-modules/sample-modules/beta.widget.ts +1 -0
  83. package/payload/extractor-fixtures/ts/dir-modules/sample-modules/gamma.widget.ts +1 -0
  84. package/payload/extractor-fixtures/ts/dir-modules/sample-modules/helpers/format.ts +3 -0
  85. package/payload/extractor-fixtures/ts/dir-modules/sample-modules/widgets.test.ts +2 -0
  86. package/payload/extractor-fixtures/ts/json-keys/EXPECTED.yaml +7 -0
  87. package/payload/extractor-fixtures/ts/json-keys/sample.json +5 -0
  88. package/payload/extractor-fixtures/ts/json-map-keys/EXPECTED.yaml +7 -0
  89. package/payload/extractor-fixtures/ts/json-map-keys/sample.json +13 -0
  90. package/payload/extractor-fixtures/ts/ts-const-array/EXPECTED.yaml +6 -0
  91. package/payload/extractor-fixtures/ts/ts-const-array/sample.ts +13 -0
  92. package/payload/extractor-fixtures/ts/ts-enum/EXPECTED.yaml +7 -0
  93. package/payload/extractor-fixtures/ts/ts-enum/sample.ts +11 -0
  94. package/payload/extractor-fixtures/ts/ts-object-keys/EXPECTED.yaml +6 -0
  95. package/payload/extractor-fixtures/ts/ts-object-keys/sample.tsx +23 -0
  96. package/payload/extractor-fixtures/ts/ts-union/EXPECTED.yaml +5 -0
  97. package/payload/extractor-fixtures/ts/ts-union/sample.ts +9 -0
  98. package/payload/hooks/pre-commit +37 -0
  99. package/payload/hooks/reverse-lookup +66 -0
  100. package/payload/package.json +3 -0
  101. package/payload/protocol/.gitkeep +0 -0
  102. package/payload/protocol/AGENTS.md +239 -0
  103. package/payload/protocol/derived-layer.md +174 -0
  104. package/payload/protocol/new-kind-pipeline.md +179 -0
  105. package/payload/protocol/registry-warrant.md +162 -0
  106. package/payload/protocol/skills/kb-build.md +303 -0
  107. package/payload/protocol/skills/knowledge-audit.md +183 -0
  108. package/payload/protocol/skills/knowledge-bootstrap.md +229 -0
  109. package/payload/protocol/skills/knowledge-reflect.md +397 -0
  110. package/payload/schemas/catalog.schema.json +32 -0
  111. package/payload/schemas/decision-entry.schema.json +122 -0
  112. package/payload/schemas/finding.schema.json +77 -0
  113. package/payload/schemas/gap.schema.json +52 -0
  114. package/payload/schemas/graduation-categories.schema.json +64 -0
  115. package/payload/schemas/knowledge-leaf.schema.json +194 -0
  116. package/payload/schemas/miss.schema.json +45 -0
  117. package/payload/schemas/ontology-concept.schema.json +115 -0
  118. package/payload/schemas/phoenix-event.schema.json +76 -0
  119. package/payload/schemas/registry.schema.json +57 -0
  120. package/payload/schemas/rules.schema.json +14 -0
  121. package/payload/schemas/survey-scope.schema.json +23 -0
  122. package/payload/templates/decisions/_catalog.yaml +7 -0
  123. package/payload/templates/decisions/_registries/graduation-categories.yaml +42 -0
  124. package/payload/templates/decisions/phoenix-event.yaml +74 -0
  125. package/payload/templates/decisions/reflect-mint-proposal.yaml +100 -0
  126. package/payload/templates/decisions/registry-minting.yaml +58 -0
  127. package/payload/templates/decisions/trust-graduation.yaml +120 -0
  128. package/payload/templates/decisions/trust-revocation.yaml +106 -0
  129. package/payload/templates/knowledge/_catalog.yaml +9 -0
  130. package/payload/templates/knowledge/_registries/anchor.yaml +42 -0
  131. package/payload/templates/knowledge/_registries/authority-tiers.yaml +32 -0
  132. package/payload/templates/knowledge/_registries/domains.yaml +43 -0
  133. package/payload/templates/knowledge/_registries/form.yaml +38 -0
  134. package/payload/templates/knowledge/_registries/jurisdictions.yaml +20 -0
  135. package/payload/templates/knowledge/_registries/operations.yaml +18 -0
  136. package/payload/templates/knowledge/_registries/stage.yaml +53 -0
  137. package/payload/templates/knowledge/_rules.yaml +6 -0
  138. package/payload/templates/new-kind/README.md +107 -0
  139. package/payload/templates/new-kind/descriptor.example.yaml +18 -0
  140. package/payload/templates/new-kind/fixture/EXPECTED.yaml +6 -0
  141. package/payload/templates/new-kind/fixture/demo-anchor.list +2 -0
  142. package/payload/templates/new-kind/fixture/sample.list +7 -0
  143. package/payload/templates/new-kind/parser.example.js +98 -0
  144. package/payload/templates/ontology/_catalog.yaml +6 -0
  145. package/payload/templates/ontology/_rules.yaml +6 -0
  146. package/payload/wrappers/cursor.mdc +15 -0
  147. package/payload/wrappers/pointer.md +10 -0
@@ -0,0 +1,239 @@
1
+ # AGENTS.md — navigation contract + runtime loop (PRD §7)
2
+
3
+ > Paths in this document are client-relative — relative to the vendored kit
4
+ > root after init (`ontology/…`, `engine/…`, `protocol/…`). In the kit repo
5
+ > itself these live under `payload/`. Commands are written to run from the
6
+ > **repo root** with the kit dir at its default name `unknown-knowledge/`;
7
+ > substitute your chosen kit dir name if it differs.
8
+
9
+ You are a coding agent in a repo seeded with the unknown-knowledge kit: three
10
+ YAML stores that map the system, a deterministic engine that checks the map,
11
+ and this protocol. This file is the platform-agnostic contract — per-platform
12
+ wrapper files are thin pointers here. Follow it on every request.
13
+
14
+ Two `--root` conventions, stated once:
15
+
16
+ - Every store-reading CLI (`resolve.js`, `preflight.js`, `validate.js`,
17
+ `validate-values.js`, `audit.js`, `survey-map.js`) takes `--root` as the
18
+ **repo root** (default: cwd). The stores are auto-located at
19
+ `<root>/unknown-knowledge/`; source-of-truth pointers resolve against the
20
+ repo root (§9.1).
21
+ - `log-entry.js` takes `--root` as the **kit dir** (the directory containing
22
+ `logs/`), e.g. `--root unknown-knowledge`.
23
+
24
+ ## The SSOT contract — the map is never the fact
25
+
26
+ Stores hold **claims and pointers**; source files hold **facts**. Prose in
27
+ any store — summaries, definitions, even `enumerates` values — is navigation,
28
+ never truth. The rules that follow from this:
29
+
30
+ - **Always follow the pointer and read the source.** Never answer from a
31
+ concept's prose or its `enumerates` values; they exist so the engine can
32
+ diff them against the artifact, not so you can skip the read.
33
+ - **Reference by concept ID and path, never by copied value.** Anything you
34
+ write into a store or a log carries `K-NNN` / `D-NNN` / leaf accession
35
+ (`L-NNNNNN`) and
36
+ file paths — copying a value out of source into prose mints a second,
37
+ uncheckable claim.
38
+ - **Trust is per-run.** A verdict is valid for the run that computed it;
39
+ never cache or carry one across sessions (D-011 — a stale "trusted" is a
40
+ false all-clear).
41
+
42
+ ## Store navigation contract
43
+
44
+ Three stores, one navigational grammar: **`_catalog.yaml` → `_rules.yaml` →
45
+ entries** (`classes/*.yaml`, tree leaves, `entries/*.yaml`). Enter through
46
+ the catalog, honor the rules file, then read the entry the catalog names —
47
+ never grep the store tree cold, and never raw-traverse the repo (triage
48
+ `engine/survey-map.js` output instead).
49
+
50
+ | Store | Truth anchor | Points | Write gate |
51
+ |---|---|---|---|
52
+ | `ontology/` | the artifact (code) | inward — `source-of-truth` into the repo | proposal-first; updates travel with the code change |
53
+ | `knowledge/` | the world | outward — `citations` to external evidence | human-only; the kb-build skill is the sole write path |
54
+ | `decisions/` | the team | sideways — typed refs to concepts & leaves | gated append-mostly; see the authoring path below |
55
+
56
+ Cross-reference semantics (knowledge leaves, §3.2):
57
+
58
+ - **`class-elsewhere` is a redirect — follow it.** The content lives at the
59
+ target accession; the leaf you found is a signpost, not an answer.
60
+ - **`see-also` is context.** Related material; consult when useful, never a
61
+ substitute for the leaf you resolved.
62
+ - **`including` is standing room.** Candidate topics parked under a heading —
63
+ not authoritative, never citable as fact.
64
+
65
+ All cross-store references travel by ID (`used-by`, `confusable-with`,
66
+ `rationale`, `relates-to`, cross-refs) — content is never embedded across
67
+ store lines. A `confusable-with` entry on a resolved concept is an explicit
68
+ disambiguation: confirm you have the right concept before acting on it.
69
+
70
+ **`knowledge/derived/` is engine output, not a store.** Browse trees and the
71
+ resolution index are regenerated by `engine/derive.js` and deleting them loses
72
+ nothing. Two rules when you read one: never hand-edit a derived file, and
73
+ **never cite a synthesized call number** (`SPO/ODD/REF·L-000117`) — it is a
74
+ display string naming a position in one projection, it differs between trees,
75
+ and the citable half is the accession inside it. See
76
+ `protocol/derived-layer.md`.
77
+
78
+ ## The runtime loop (every request)
79
+
80
+ `RESOLVE → PREFLIGHT → GATHER → ACT → RECORD`
81
+
82
+ ### 1. RESOLVE — request terms → concepts
83
+
84
+ ```
85
+ node unknown-knowledge/engine/resolve.js "export format" --json --root .
86
+ ```
87
+
88
+ Query terms are positional (joined into one query); results come scored with
89
+ `source-of-truth` pointers, `confusable-with` disambiguation, and knowledge
90
+ entry points. Exit 0 = the lookup ran (hits or none); exit 2 = it never ran —
91
+ stop, that is an engine failure, not an empty result.
92
+
93
+ **Zero resolution is a normal outcome** (common in month one — bootstrap is
94
+ deliberately non-exhaustive). Proceed **without store claims**: fall back to
95
+ search within the paths `survey-scope.yaml` includes, and append a
96
+ `retrieval-miss` finding (RECORD, below) only when the topic plausibly should
97
+ be mapped — an unmapped area the scope excludes is expected, not a miss.
98
+
99
+ ### 2. PREFLIGHT — deterministic verdicts on what you resolved
100
+
101
+ ```
102
+ node unknown-knowledge/engine/preflight.js --concepts K-101,K-102 --json --root .
103
+ ```
104
+
105
+ One verdict per concept — `trusted` / `quarantined` / `unknown` — computed by
106
+ engine code, never by your judgment. Store-wide failures degrade every
107
+ requested verdict to `unknown`. Exit codes: 0 all trusted, 1 quarantines
108
+ present, 2 engine failure / check-never-ran. What you DO with a verdict is
109
+ the conduct policy below. Add `--log --today <YYYY-MM-DD>` so quarantine
110
+ findings auto-append (one fragment per quarantined concept).
111
+
112
+ With an empty/omitted `--concepts`, preflight validates store health only and
113
+ exits on the store verdict — the zero-resolution branch still preflights.
114
+
115
+ ### 3. GATHER — read the fact, not the map
116
+
117
+ Follow each resolved concept's `source-of-truth` pointer and **read the
118
+ file**. One targeted read per fact. Knowledge leaves: read the leaf body and
119
+ follow `class-elsewhere` redirects. Never substitute a summary, an
120
+ `enumerates` list, or a leaf heading for the artifact.
121
+
122
+ ### 4. ACT — execute, then attribute before committing
123
+
124
+ Do the work. Before committing, reverse-look-up every file you changed:
125
+
126
+ ```
127
+ node unknown-knowledge/engine/resolve.js --paths src/registry/export-formats.ts,src/types/color-space.ts --json --root .
128
+ ```
129
+
130
+ This lists every concept whose pointer covers a changed path (folder pointers
131
+ match nested files). **Update those concepts in the same commit** — the store
132
+ change rides your normal PR — and re-run both validators filtered to them:
133
+
134
+ ```
135
+ node unknown-knowledge/engine/validate.js --concepts K-101,K-108 --root .
136
+ node unknown-knowledge/engine/validate-values.js --concepts K-101,K-108 --root .
137
+ ```
138
+
139
+ Exit 0 = clean; exit 1 = findings — fix the store or the code before
140
+ committing; exit 2 = the check never ran — stop. These validators are
141
+ blocking-grade; `engine/audit.js` is advisory (proposals for review) and is
142
+ never a gate.
143
+
144
+ Where the client has wired the seeded hooks (`hooks/pre-commit`,
145
+ `hooks/reverse-lookup`), both of these run automatically: the pre-commit hook
146
+ runs the blocking validation before the commit exists, and the reverse-lookup
147
+ hook runs the `--paths` lookup over the staged diff. Each is a thin wrapper
148
+ around the command above and exits with its code, unchanged — so a wired repo
149
+ enforces this step mechanically rather than depending on you to remember it.
150
+ Run the commands yourself anyway: seeing the findings before the commit is
151
+ cheaper than being refused by it, and a hook the client never wired enforces
152
+ nothing.
153
+
154
+ ### 5. RECORD — append findings when a trigger fires
155
+
156
+ See capture obligations below. Findings, misses, and gaps are appended via
157
+ `engine/log-entry.js` — never by hand-editing YAML.
158
+
159
+ ## Conduct-on-verdict policy (D-011)
160
+
161
+ > **CLIENT-EDITABLE.** Verdicts are deterministic engine facts; what a
162
+ > session does about one is policy in this markdown, which you (the client)
163
+ > own. The table below is the recommended default — quarantine-and-continue.
164
+ > A stricter shop may edit `quarantined` to fail-stop. What is NOT editable:
165
+ > the engine's verdicts, exit codes, and evidence — and a policy edit must
166
+ > never tell an agent to bypass a gate or trust quarantined claims.
167
+
168
+ | Verdict / outcome | Conduct (recommended default) |
169
+ |---|---|
170
+ | `trusted` | Proceed. The verdict is fresh this run; never cache it. |
171
+ | `quarantined` | **Quarantine-and-continue**: continue the task, but do NOT trust the concept's claims — gather from the source artifact directly (falling back to survey-scoped search if the pointer itself is the problem), and make sure the quarantine finding was appended (run preflight with `--log --today <date>`; it does this for you). |
172
+ | `unknown` | Treat as unverified — draft/proposed concepts and store-wide degradations land here. Work from the source artifact; do not present store claims as checked. |
173
+ | exit 2 — engine failure / check-never-ran | **STOP.** A check that never ran is a blocking defect, never a silent pass. Report the engine error; do not proceed as if preflight passed. |
174
+
175
+ ## Gate rules
176
+
177
+ - **All store mutations beyond logging are human-gated.** Agents draft;
178
+ humans approve through the normal PR gate. Logging (`logs/`) is the one
179
+ agent-writable surface, and only via `engine/log-entry.js`.
180
+ - **Ontology** edits are proposal-first and travel with the code change they
181
+ describe (ACT step). **Knowledge** is written only through the kb-build
182
+ skill — cited, human-gated. **Decisions** follow the authoring path below.
183
+ - **Never wire a parser you authored into the validator in the same session**
184
+ (D-005). Unextractable anchors go to the miss-log; the governed path is
185
+ `protocol/new-kind-pipeline.md`.
186
+ - **Never bypass a gate to go green**: do not delete or edit findings to
187
+ unblock a merge, do not hand-edit log YAML, do not skip the ACT re-run,
188
+ do not pass `--no-verify` or unwire a hook to get a commit through,
189
+ treat `audit.js` output as advisory (never blocking), and do not carry a
190
+ cached verdict. Autonomy is graduated per change-category by recorded decision
191
+ (category `trust`), never assumed.
192
+
193
+ ## Capture obligations — the five triggers
194
+
195
+ Append a finding whenever one fires; capture is cheap and judgment-free
196
+ (consolidation happens later, in reflect). The `trigger` vocabulary is closed
197
+ (`schemas/finding.schema.json`):
198
+
199
+ | Trigger | Signal |
200
+ |---|---|
201
+ | `correction` | the user states the agent / a store is wrong |
202
+ | `recurrence` | the same thing is asked for repeatedly |
203
+ | `retrieval-struggle` | found, but slowly or indirectly (resolver missed; aliases lacking) |
204
+ | `retrieval-miss` | could not find it at all (and the topic plausibly belongs in scope) |
205
+ | `quarantine` | engine-attributed: preflight flagged a concept and the session proceeded degraded (auto-appended by `preflight.js --log`) |
206
+
207
+ Append with the helper — `--date` is mandatory and injected, never wall-clock:
208
+
209
+ ```
210
+ node unknown-knowledge/engine/log-entry.js create --log findings --date 2026-07-08 \
211
+ --root unknown-knowledge \
212
+ --entry '{"trigger":"correction","summary":"correction: K-101 src/registry/export-formats.ts","consulted":{"concepts":["K-101"]}}'
213
+ ```
214
+
215
+ Each finding's `consulted:` refs ARE the consultation trail. Sibling logs,
216
+ same helper: `--log misses` (anchors no extractor kind can read) and
217
+ `--log gaps` (requests no protocol/skill could route).
218
+
219
+ **Capture content policy (§3.4 — not optional):** summaries carry concept IDs
220
+ and file paths only — **never verbatim user text, quoted session content, or
221
+ secrets**. The `session` field is an opaque ID. Committed fragments are
222
+ permanent git history in this repo and are reviewable content like any other
223
+ PR change.
224
+
225
+ ## Decisions-authoring path (§3.5)
226
+
227
+ When your work surfaces a decision — a trade-off taken, a graduation of
228
+ trust, a scope call — it gets recorded, not lost in a PR description:
229
+
230
+ 1. **Draft** a decision entry in `decisions/entries/` with a provisional
231
+ date-suffixed id (`D-2026-07-08-<slug>`), `status: proposed`, and
232
+ `relates-to` refs to the concepts/leaves/decisions it touches.
233
+ 2. **Propose** it through the normal PR gate — anyone (agent or human) may
234
+ draft a proposed entry; that IS the decisions store's write path.
235
+ 3. **Human gate**: the steward assigns the final `D-NNN` at acceptance
236
+ (minted within range, never renumbered once published). Status moves
237
+ `proposed → accepted → addressed → archived` (plus `rejected` /
238
+ `superseded`); transitions never rewrite `context`/`decision` — the store
239
+ is append-mostly, and supersession chains must resolve and stay acyclic.
@@ -0,0 +1,174 @@
1
+ # The derived layer: browse trees, call numbers, and the recall gate (UCS-1158)
2
+
3
+ > Paths in this document are client-relative — relative to the vendored kit
4
+ > root after init (`knowledge/…`, `engine/…`, `protocol/…`). In the kit repo
5
+ > itself these live under `payload/`.
6
+
7
+ Everything discovery-shaped in this store is **derived**: computed from the flat
8
+ set of leaves, written into one directory, and thrown away without loss.
9
+
10
+ ```
11
+ knowledge/
12
+ ├── _registries/ # governed vocabularies — AUTHORED
13
+ ├── design-system/… # leaves — AUTHORED
14
+ └── derived/ # engine output — DISPOSABLE
15
+ ├── index.json # the resolution index
16
+ ├── tree.domain-form.md # browse tree, steward ordering
17
+ └── tree.form-domain.md # browse tree, agent ordering
18
+ ```
19
+
20
+ Regenerate it with the `derive` surface:
21
+
22
+ ```sh
23
+ node unknown-knowledge/engine/derive.js --root . --today 2026-08-16 --write
24
+ ```
25
+
26
+ Two verbs, and the read-only one is the default — the same shape `phoenix` uses:
27
+
28
+ | Verb | Effect | Exit |
29
+ |---|---|---|
30
+ | `--check` (default) | compare the layer on disk against what the store projects | 0 up to date / 1 findings |
31
+ | `--write` | replace the directory with what the store projects | 0 written |
32
+
33
+ ## Deleting it loses nothing
34
+
35
+ That is the design claim, and it is enforced rather than promised:
36
+
37
+ - **Nothing authored lives here.** No field, no id, no decision. Every byte is a
38
+ function of the leaves.
39
+ - **Nothing reads it back.** The loader skips `derived/` by name. Browse trees
40
+ are markdown under `knowledge/`, exactly like leaves, so the name is the only
41
+ thing distinguishing engine output from a record — and if the walk descended,
42
+ a generated tree would fail the leaf schema and the store's health would
43
+ depend on a directory whose whole contract is that it is disposable.
44
+ - **Regeneration is byte-identical.** Delete the directory, re-run `--write`,
45
+ and the bytes match. A round-trip test asserts exactly that; it fails the
46
+ moment something unreproducible starts living here.
47
+ - **A file no axis generates is a finding.** `derived-unexpected` fires on
48
+ anything in the directory the engine did not produce, because a regeneration
49
+ would delete it.
50
+
51
+ Never hand-edit a derived file. `--check` reports it as `derived-stale`, and the
52
+ next `--write` overwrites it.
53
+
54
+ ### Commit it, or ignore it — both work
55
+
56
+ The kit does not decide this for you, because the two choices trade off real
57
+ things and the answer is local:
58
+
59
+ - **Commit it** and browse trees are readable on the forge, and a reviewer sees
60
+ a reclassification's effect in the diff. The cost is that every PR touching a
61
+ leaf also touches the trees, and a contributor who forgets to regenerate lands
62
+ a stale artifact — which `derive --check` catches in CI.
63
+ - **Ignore it** (`knowledge/derived/` in `.gitignore`) and the store's history
64
+ stays free of generated churn. The cost is that a reader has to run the
65
+ command to browse.
66
+
67
+ Either way nothing is lost, because nothing here is authored. If you commit it,
68
+ run `derive --check --today <date>` in CI so a stale tree is a failed build
69
+ rather than a misleading document.
70
+
71
+ ## Plural browse trees
72
+
73
+ The store projects along **two axes**, from one flat set of leaves:
74
+
75
+ | Axis | Ordering | For |
76
+ |---|---|---|
77
+ | `domain-form` | domain → form | stewards — subject matter first, the way a person holds a library in their head |
78
+ | `form-domain` | form → domain | agents — kind of knowledge first, narrowed by subject afterwards |
79
+
80
+ Plural is the point. The store's first life filed each leaf at one position in
81
+ one tree, and that position was also its identity — so a leaf in the wrong place
82
+ stayed there, because moving it broke every citation to it. Accession identity
83
+ removed that cost, and the derived layer spends the refund: a projection commits
84
+ to nothing, so there can be as many as there are useful reading orders.
85
+
86
+ A third axis is one row in `AXES` (`engine/lib/derived.js`). Nothing else names
87
+ an axis.
88
+
89
+ ### Stale and draft leaves are annotated, never hidden
90
+
91
+ A demoted leaf appears in every tree it belongs in, wearing its reason:
92
+
93
+ ```
94
+ - **frontend/** (2)
95
+ - **runbook/** (1)
96
+ - `ENG/FRO/RUN·L-000171` Visual regression triage playbook — **demoted** (stage, time)
97
+ - stage: stage "draft" is pre-promotion — no moderator has certified this leaf's citations
98
+ - time: verified 154 day(s) ago, past the 90-day limit for volatile knowledge
99
+ ```
100
+
101
+ Hiding it would make the tree lie by omission: a steward would read an empty
102
+ shelf as "we know nothing about this", when the truth is "what we know has
103
+ rotted" — opposite calls to action. Demotion sorts a leaf last within its node
104
+ and does nothing else.
105
+
106
+ Both demotions come from the surfaces that already own them — `timeVerdict` and
107
+ `isPrePromotionStatus` — so a leaf cannot read stale in a tree and trusted in
108
+ the resolver.
109
+
110
+ **Time verdicts need `--today`.** Staleness is measured from an injected date,
111
+ never the wall clock. Without `--today` the time verdicts are `skipped` and every
112
+ artifact says so in its header; it is never a silent pass. Stage demotion needs
113
+ no date and still applies.
114
+
115
+ ## Call numbers are display strings, never identities
116
+
117
+ A call number names a leaf's position in one projection:
118
+
119
+ ```
120
+ SPO/ODD/REF·L-000117 in the domain-first tree
121
+ REF/SPO/ODD·L-000117 the SAME leaf, in the form-first tree
122
+ ```
123
+
124
+ **Cite the accession id. Never a call number.** A call number is the most
125
+ tempting thing in this layer to paste into a `see-also` — compact, readable,
126
+ positional, everything the retired dotted notation was. If that paste ever
127
+ resolved, the accession inversion would reverse itself one convenient citation at
128
+ a time, and references would break on reclassification again.
129
+
130
+ It cannot resolve, and the defense is structural:
131
+
132
+ 1. **The grammar refuses it.** The middle dot (`·`) appears in no id space, and
133
+ a test checks every synthesized call number against every grammar in
134
+ `ID_GRAMMARS` — so a new id space that accepted them would fail the day it
135
+ was added.
136
+ 2. **Nothing accepts one.** Citations are `leaf-ref`, accession-only. A call
137
+ number in a citation field is both a `pattern-mismatch` and an
138
+ `unresolved-ref`.
139
+ 3. **The accession travels inside it.** The tempting thing to paste contains the
140
+ correct thing to paste.
141
+
142
+ The same leaf reads differently on each axis, and that is deliberate: a call
143
+ number describes a position, and a leaf holds one per projection. A string that
144
+ looked stable while changing meaning between trees would be worse than one that
145
+ visibly reorders.
146
+
147
+ ## Embedding recall: the slot and the gate
148
+
149
+ Probabilistic recall has **exactly one architectural position**, declared in
150
+ `engine/lib/derived.js` as `RECALL_SLOT`:
151
+
152
+ | Property | Value | Why |
153
+ |---|---|---|
154
+ | `location` | `derived` | recall lives in the disposable layer and nowhere else — deleting `derived/` deletes every probabilistic artifact the system has ever computed |
155
+ | `consulted` | `after-deterministic-exhausted` | recall runs only when the deterministic layer returns nothing; consulting it earlier would let a similarity score outrank a declared edge |
156
+ | `output` | `proposals` | it may propose |
157
+ | `citable` | `false` | a proposal is never a citation |
158
+ | `persistable` | `false` | a proposal never enters the store |
159
+
160
+ **The implementation is out of scope, deliberately.** This ticket ships the slot
161
+ and the gate; model selection and semantic search are not built here. What the
162
+ slot does is fix the position so it cannot be occupied somewhere worse. An
163
+ embedding cached in frontmatter would be a guess sitting in the same file as
164
+ cited facts, indistinguishable to every later reader — which is precisely the
165
+ failure the three constraints above prevent.
166
+
167
+ `gateProposal()` refuses **every** proposal, by construction. There is no
168
+ argument that admits one. A future recall implementation calls it, gets a
169
+ refusal, and the only route into the store remains the ordinary one: a human
170
+ reads the proposal and authors an ordinary leaf with citations, which a moderator
171
+ promotes. At that point the record is knowledge, and the probabilistic step is
172
+ history rather than provenance.
173
+
174
+ Nothing probabilistic persists or is cited without that gate.
@@ -0,0 +1,179 @@
1
+ # The new-kind pipeline (PRD §5.2) — the trust boundary
2
+
3
+ > Paths in this document are client-relative — relative to the vendored kit
4
+ > root after init (`schemas/…`, `templates/…`, `engine/…`, `protocol/…`). In
5
+ > the kit repo itself these live under `payload/`.
6
+
7
+ An extractor **kind** is a small deterministic recipe (~30-line parser) that
8
+ reads a value set out of a reified anchor. The shipped kind library covers the
9
+ common shapes (PRD §5.1); everything else surfaces as a **miss** — an anchor no
10
+ shipped kind can read. This document is the governed path from a miss to a new
11
+ kind. It is a trust boundary, not a convenience path: a subtly wrong parser
12
+ produces a false all-clear, which is worse than no check.
13
+
14
+ > **Hard rule (D-005): validators execute only vendored, versioned,
15
+ > test-covered code — never code authored in the session that runs it.**
16
+ > A kind drafted in a session is NEVER wired into the validator in that same
17
+ > session. It enters via the GATE step: a PR carrying the parser, its fixture
18
+ > tests, and a demo run, merged after human review. Agents draft; humans gate;
19
+ > code validates.
20
+
21
+ Before drafting any parser, weigh the alternative of **reifying the anchor
22
+ into a standard shape** (product principle 6): when a fact can't be extracted
23
+ because the code never reified it, the right proposal is often a registry/enum
24
+ in the code — pushing the system toward being describable — not a bespoke
25
+ parser.
26
+
27
+ ## Entry point: the miss-log
28
+
29
+ The pipeline consumes `logs/misses/` — the demand-driven extractor backlog.
30
+ A miss entry is **one file per entry** (`logs/misses/<date>-<hex8>.yaml`,
31
+ D-010) so concurrent sessions never merge-conflict, written via the
32
+ append/transition helper (`engine/log-entry.js`), never by hand-editing YAML.
33
+
34
+ Fields, per `schemas/miss.schema.json`:
35
+
36
+ | Field | Req. | Meaning |
37
+ |---|---|---|
38
+ | `schema-version` | yes | integer ≥ 1; stamped by the helper (§3.5, additive-only evolution) |
39
+ | `date` | yes | ISO date the miss was recorded (injected — never wall-clock) |
40
+ | `path` | yes | root-relative path of the anchor no kind can read |
41
+ | `shape` | yes | shape description — enough for this pipeline to draft a parser; paths and structure only, never file contents verbatim |
42
+ | `session` | no | opaque platform/session hint — an ID, never quoted session content |
43
+ | `status` | yes | `open → proposed → resolved / rejected`; re-open-not-duplicate |
44
+ | `verified` | — | stamped by the transition helper when the entry resolves (validator re-run passed); removed on re-open |
45
+ | `reason` | — | required on rejection; travels only with `rejected` |
46
+ | `occurrences` | — | re-open dates: recurrences re-open the same entry, never duplicate |
47
+
48
+ Lifecycle invariants. The schema gate (`engine/lib/validate-record.js`)
49
+ enforces exactly two of them on any fragment, hand-edited or not, matching
50
+ what the transition helper enforces on its write path — on these two the
51
+ gates cannot disagree:
52
+ `verified` ⇔ status `resolved`, both directions; `rejected` ⇒ non-empty
53
+ `reason`, and `reason` travels only with `rejected`.
54
+ The rest is **helper-only**: only the transition helper's write path drops
55
+ `verified`/`reason` on re-open and appends the re-open date to `occurrences`
56
+ — a hand-edited fragment missing an occurrence entry still validates clean,
57
+ which is one more reason fragments are written via the helper, never by hand.
58
+
59
+ **Capture content policy (§3.4):** miss entries carry concept IDs and file
60
+ paths only — never verbatim user text, quoted session content, or secrets.
61
+ Committed fragments are permanent git history.
62
+
63
+ ## The six steps
64
+
65
+ ```
66
+ 1. SURVEY read-only: the bootstrap/reflect agent inventories anchor shapes
67
+ 2. MATCH configure concepts from the shipped kind library
68
+ 3. DRAFT for each miss: agent writes the parser
69
+ + a test fixture (sample file → expected values, following
70
+ the shipped extractor-fixture template, D-009)
71
+ + a demo run against the live anchor it was drafted for
72
+ 4. GATE human review — fast, because the claim is mechanical
73
+ 5. INTEGRATE parser lands as versioned, tested code (client zone of their repo;
74
+ kit library if the vendor harvests it for future inits)
75
+ 6. RE-RUN concepts depending on the new kind emit last
76
+ ```
77
+
78
+ ### 1. SURVEY — inventory anchor shapes (read-only)
79
+
80
+ The bootstrap/reflect agent triages the survey-map artifact (never
81
+ raw-traverses the repo) and inventories the anchor shapes concepts point at.
82
+ Anchors with a shipped kind proceed to MATCH. Anchors with **kind-shaped
83
+ evidence but no kind** — a real registry the library can't read — become miss
84
+ entries, written via the helper (verified working invocation; `--date` is
85
+ mandatory and injected, never wall-clock):
86
+
87
+ ```
88
+ node engine/log-entry.js create --log misses --date 2026-07-08 \
89
+ --entry '{"path":"config/regions.txt","shape":"plain-text list, one region code per line, # comments"}'
90
+ ```
91
+
92
+ SURVEY consumes the misses already in the backlog too — never minting a
93
+ sibling for a known anchor:
94
+
95
+ - a recurrence of a **still-open** miss needs no transition and gets none:
96
+ `open → open` is illegal (the helper hard-errors), because the open entry
97
+ *is* the standing demand signal. The helper offers no occurrences-append
98
+ for open entries; the agent simply leaves the entry as-is and moves on.
99
+ - a recurrence of a **resolved or rejected** miss **re-opens the same entry**
100
+ (`transition --to open`), which appends the date to `occurrences` and drops
101
+ the stale `verified`/`reason`.
102
+
103
+ ### 2. MATCH — configure from the shipped library first
104
+
105
+ Every anchor that a shipped kind *can* read gets an `enumerates` descriptor
106
+ (`kind` / `source` / `values`, plus `symbol`/`emit` where the kind needs them)
107
+ against the shipped library. Only what remains unmatched justifies DRAFT —
108
+ and even then, weigh reification (principle 6) before a bespoke parser.
109
+
110
+ ### 3. DRAFT — parser + fixture + demo run
111
+
112
+ For each miss that survives MATCH, the agent drafts, from the shipped template
113
+ (`templates/new-kind/`, see its README):
114
+
115
+ 1. **the parser** — a pure, deterministic function: source text in, value set
116
+ out; hard-errors loudly on anything it can't parse, and declares a
117
+ **syntactic envelope**, hard-erroring when out-of-envelope sentinels appear
118
+ in the matched span — a confident wrong parse is a false all-clear, the
119
+ D-005/D-012 failure class;
120
+ 2. **a test fixture** — sample file → expected values, following the shipped
121
+ extractor-fixture template (D-009);
122
+ 3. **a demo run** against the live anchor the miss recorded (`path`), with the
123
+ output attached to the PR.
124
+
125
+ The draft NEVER runs inside the validator in this session (D-005). The miss
126
+ entry transitions `open → proposed` when the draft PR exists.
127
+
128
+ ### 4. GATE — human review
129
+
130
+ A human reviews the PR. The review is fast because the claim is mechanical:
131
+ parser + fixture + demo-run output — does the fixture cover the envelope, does
132
+ the demo run reproduce the live anchor's values, is the envelope honest about
133
+ what it rejects? Rejection transitions the miss `proposed → rejected` with a
134
+ `reason` (rejections record the reason, §8). A rejected miss that recurs
135
+ re-opens (`rejected → open`) — the backlog keeps the demand signal.
136
+
137
+ ### 5. INTEGRATE — versioned, tested code
138
+
139
+ The merged parser lands as versioned, test-covered code in the client zone of
140
+ their repo (the vendor harvests it into the kit library only for *future*
141
+ inits — seeded-once, D-001). It is now vendored code the validator may
142
+ execute: this preserves D-014 (the engine never imports/evals/spawns repo
143
+ *content* at validate time — a new kind enters the engine's own reviewed code,
144
+ it is never dynamically loaded from the store or the scanned tree).
145
+
146
+ ### 6. RE-RUN — dependent concepts emit last
147
+
148
+ Concepts whose `enumerates` descriptors depend on the new kind are (re)emitted
149
+ and validated last, once the kind is merged. A green validator re-run
150
+ transitions the miss `proposed → resolved`; the helper stamps `verified` with
151
+ the run date. A later regression or recurrence re-opens the same entry
152
+ (`resolved → open`, dropping `verified`, appending to `occurrences`).
153
+
154
+ ## Miss lifecycle ↔ pipeline steps
155
+
156
+ | Transition | When |
157
+ |---|---|
158
+ | *(created `open`)* | SURVEY records an unextractable anchor |
159
+ | `open → proposed` | DRAFT produced parser + fixture + demo run; PR open |
160
+ | `proposed → resolved` (+ `verified`) | GATE approved, INTEGRATE merged, RE-RUN passed |
161
+ | `proposed → rejected` (+ `reason`) | GATE rejected the draft |
162
+ | `resolved → open` / `rejected → open` | recurrence — same entry re-opens, `occurrences` appended; `verified`/`reason` dropped |
163
+
164
+ These are exactly the legal transitions the helper enforces; any other move is
165
+ a hard error that leaves the fragment untouched.
166
+
167
+ ## Drafting template
168
+
169
+ `templates/new-kind/` ships the drafting skeleton — six files: a
170
+ framework-agnostic parser module (whose CLI is the demo-run tool), a
171
+ descriptor example, a fixture pair (`fixture/sample.list` →
172
+ `fixture/EXPECTED.yaml`), a stand-in demo anchor
173
+ (`fixture/demo-anchor.list`), and a README that walks the demo run. Together
174
+ these cover the three DRAFT artifacts above (parser, fixture, demo run).
175
+ The template README also records one manual walkthrough of the
176
+ template against its own fixture (an honest seam: a documented manual
177
+ exercise, not CI — the kit's CI pins the template artifacts against each
178
+ other and replays the walkthrough's CLI runs so the transcripts can't rot
179
+ apart, but exercising a draft against a *live* anchor is always manual).