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,229 @@
1
+ # /knowledge-bootstrap — phase-2 store population (PRD §6, D-019)
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
+ Init's phase 1 (the CLI) scaffolded empty stores; this skill is phase 2 — the
10
+ judgment half, run by whatever agent the client already uses. It populates
11
+ the stores: a confirmed traversal scope, a per-project class taxonomy, the
12
+ first ontology concepts (each born with its checkability rung), the miss-log
13
+ backlog, and the knowledge-store skeleton. Per-platform wrapper files are
14
+ thin pointers here; this is the platform-agnostic skill body. You operate
15
+ under `protocol/AGENTS.md` throughout — every gate rule there binds here.
16
+
17
+ Two `--root` conventions, stated once (same as AGENTS.md):
18
+
19
+ - Every store-reading CLI (`survey-map.js`, `resolve.js`, `validate.js`,
20
+ `validate-values.js`) takes `--root` as the **repo root** (default: cwd);
21
+ the stores are auto-located at `<root>/unknown-knowledge/`.
22
+ - `log-entry.js` takes `--root` as the **kit dir** (the directory containing
23
+ `logs/`), e.g. `--root unknown-knowledge`.
24
+
25
+ ## Resumable by construction
26
+
27
+ A dead session costs the remaining steps, never a restart. **On entry,
28
+ detect existing artifacts and resume from the first incomplete step** —
29
+ each step below also carries its own "On resume" rule:
30
+
31
+ | Artifact found on entry | What it means |
32
+ |---|---|
33
+ | `survey-scope.yaml` at the repo root AND class rules in `ontology/_rules.yaml` | the scope+taxonomy gate was confirmed — it is **never re-litigated on resume**; skip to EMIT (survey-map now honors the scope automatically). Scope file WITHOUT class rules means the session stopped mid-gate: complete the taxonomy half (the confirmed scope itself is not re-asked) before moving on |
34
+ | `ontology/_rules.yaml` with class rules | the taxonomy half of the gate is done; do not re-propose it |
35
+ | Concepts in `ontology/classes/` | emission already started — it is **idempotent by anchor identity**: probe each anchor with the reverse lookup before emitting; never a duplicate concept for the same anchor |
36
+ | An **open** `logs/misses/` entry for a path | the standing demand signal already exists — never mint a sibling for a known anchor (`protocol/new-kind-pipeline.md`) |
37
+ | `knowledge/_rules.yaml` with rule items | the KB interview happened; skip to FINISH |
38
+ | A proposed bootstrap decision in `decisions/entries/` | the wrap-up draft exists; go straight to the validator run |
39
+
40
+ ## The six steps
41
+
42
+ ```
43
+ 1. SURVEY read-only: engine/survey-map.js → triage, never traverse
44
+ 2. GATE ONE human gate: scope + taxonomy together → survey-scope.yaml
45
+ 3. EMIT concepts at their highest checkable rung, not exhaustively
46
+ 4. MISS unreadable anchors → logs/misses/ via engine/log-entry.js
47
+ 5. INTERVIEW knowledge-store skeleton from the human
48
+ 6. FINISH both validators green, bootstrap decisions drafted
49
+ ```
50
+
51
+ ### 1. SURVEY — run the map, triage the artifact
52
+
53
+ ```
54
+ node unknown-knowledge/engine/survey-map.js --root .
55
+ ```
56
+
57
+ **Raw repo traversal is a protocol violation.** The survey map is the
58
+ traversal surface: git-tracked files only, denylist applied, per-directory
59
+ extension histograms, and an anchor-candidate pre-scan sharing one regex
60
+ table with the extractor kinds. You *triage* this artifact — grep is free,
61
+ reading is expensive, judgment is what you are for. Add `--json` when you
62
+ need the histograms structured.
63
+
64
+ Exit codes: 0 clean; 1 = blind spots disclosed under `unsurveyed:`
65
+ (submodule gitlinks, out-of-root symlinks) — carry them to the gate, the
66
+ human decides; 2 = engine failure — **stop**, a check that never ran is a
67
+ blocking defect, never a silent pass.
68
+
69
+ From the map, prepare the gate material: the per-directory histograms (what
70
+ lives where), the ranked anchor-candidate list (what looks extractable, by
71
+ which kind), and the proposed include/exclude scope over top-level
72
+ directories.
73
+
74
+ **On resume:** always safe to re-run — the map is deterministic and honors
75
+ `survey-scope.yaml` once it exists (`scope (survey-scope.yaml): …` in the
76
+ output confirms the binding).
77
+
78
+ ### 2. GATE — one combined scope + taxonomy review (human)
79
+
80
+ **ONE human gate, not two.** Present together, in the conversation — "here
81
+ is how I read your codebase":
82
+
83
+ 1. **Scope** — which top-level areas are product surface (include) vs.
84
+ vendored/generated/infra (exclude), plus any `unsurveyed:` blind spots
85
+ the map disclosed. The kit dir itself belongs in the excludes: the map is
86
+ never the territory.
87
+ 2. **Taxonomy** — the proposed top-level ontology class spine
88
+ (per-project, derived from the surveyed structure), with an id range per
89
+ class.
90
+
91
+ On confirmation, write both artifacts:
92
+
93
+ - `survey-scope.yaml` at the **repo root** (shape:
94
+ `schemas/survey-scope.schema.json` — `schema-version`, `include` with at
95
+ least one prefix, optional `exclude`; exclude wins; `.` covers root-level
96
+ files only; trailing slashes are normalized away).
97
+ - `ontology/_rules.yaml` — one rule per class with its `id-range`
98
+ (`{ class: 100-…, id-range: [K-100, K-199] }`).
99
+
100
+ Then re-run survey-map and confirm the output header reads
101
+ `scope (survey-scope.yaml): …` — the map is now bounded.
102
+
103
+ This is **the honor-it contract**: every future audit and reflect sweep is
104
+ bounded to this scope and never rescans excluded areas. Widening happens
105
+ via `retrieval-miss` findings through the normal loop — **never by
106
+ re-litigating the gate**. Once `survey-scope.yaml` exists the gate is
107
+ confirmed; on resume it is never re-asked.
108
+
109
+ **On resume:** `survey-scope.yaml` present → skip this step entirely.
110
+
111
+ ### 3. EMIT — concepts at their highest checkable rung
112
+
113
+ Walk the in-scope anchor candidates and emit §3.1 concept records into
114
+ `ontology/classes/`, catalog rows into `ontology/_catalog.yaml`. Every
115
+ concept is born knowing its rung — emit at the **highest checkable** one:
116
+
117
+ - **Rung 2 (value agreement)** — a shipped extractor kind fits the anchor:
118
+ emit an `enumerates` descriptor (`kind` / `source` / `values`, plus
119
+ `symbol`/`emit` where the kind needs them). **Read the source file and
120
+ derive the values from it** — never guess from prose; equality is
121
+ byte-exact, case-sensitive, set-based (§3.5), and YAML 1.1 coercion is a
122
+ real trap (quote `on`, `no`, digit-leading keys).
123
+ - **Rung 1 (existence-only)** — no kind fits, or the concept is a grouping:
124
+ a `source-of-truth` pointer and prose, no descriptor. The pointer rule:
125
+ **point at a folder for identity, at a file for facts** — a vertical gets
126
+ its directory (and pair load-bearing folder pointers with the grouping's
127
+ entry file so the existence check has teeth); a value set gets the file
128
+ that declares it.
129
+ - Prose-only concepts carry `last-verified` (rung 4); the reverification
130
+ sweep maintains them.
131
+
132
+ **Deliberately not exhaustive: an ontology born complete is born wrong.**
133
+ Seed the load-bearing concepts — rung-1/2 coverage plus the high-value
134
+ prose concepts; the reverse audit (`engine/audit.js`, advisory) grows the
135
+ map proposal-first from there. Skipping a candidate is a normal triage
136
+ outcome, not a gap.
137
+
138
+ Verify each emitted batch immediately (repo-root `--root`; the
139
+ `--concepts` list is EXACTLY the ids the batch emitted — an id left off the
140
+ list is a check that never ran):
141
+
142
+ ```
143
+ node unknown-knowledge/engine/validate.js --concepts K-100,K-110,K-120 --root .
144
+ node unknown-knowledge/engine/validate-values.js --concepts K-100,K-110,K-120 --root .
145
+ ```
146
+
147
+ Exit 1 = the draft disagrees with the source — fix the draft (re-read the
148
+ file); exit 2 = **stop**. A descriptor that hard-errors as out-of-envelope
149
+ does not get "simplified" until it passes — the anchor goes to MISS.
150
+
151
+ **On resume (idempotent by anchor identity):** before emitting for an
152
+ anchor, reverse-look it up —
153
+
154
+ ```
155
+ node unknown-knowledge/engine/resolve.js --paths Sources/Canvas/CanvasTool.swift --root .
156
+ ```
157
+
158
+ If a concept's pointer already covers the path (folder pointers match
159
+ nested files), the anchor is emitted — skip it, never mint a duplicate
160
+ concept for the same anchor. `no concepts point at this path` means it
161
+ still needs one.
162
+
163
+ ### 4. MISS — the anchors no shipped kind can read
164
+
165
+ Anchors with kind-shaped evidence but no shipped kind — a real registry the
166
+ library cannot read (computed arrays, conditional compilation, dynamic
167
+ derivation) — become miss entries, **one fragment each**, via the helper
168
+ (`--date` is injected, never wall-clock; note `--root` is the **kit dir**):
169
+
170
+ ```
171
+ node unknown-knowledge/engine/log-entry.js create --log misses --date 2026-07-08 \
172
+ --root unknown-knowledge \
173
+ --entry '{"path":"Sources/Integrations/Providers.swift","shape":"computed Swift array: static let all = core + regional (concatenation, no literal value set); swift-const-array is out of envelope"}'
174
+ ```
175
+
176
+ `shape` carries paths and structure only — never file contents verbatim
177
+ (§3.4). This backlog feeds the governed §5.2 pipeline
178
+ (`protocol/new-kind-pipeline.md`): agents draft parsers there, humans gate
179
+ them, and a kind you author is **never wired into the validator in the same
180
+ session** (D-005). Do not write a bespoke parser during bootstrap.
181
+
182
+ **On resume:** an open miss for the path already IS the demand signal —
183
+ leave it; never mint a sibling (`open → open` is illegal, and the helper
184
+ hard-errors on it).
185
+
186
+ ### 5. INTERVIEW — the knowledge-store skeleton (human)
187
+
188
+ The knowledge store's truth anchor is the world, not the code — so its
189
+ spine comes from the **human**, in a structured interview seeded by the
190
+ vocabulary the survey surfaced: which external domains does this product
191
+ answer to (regulation, industry rules, user research…), and what divisions
192
+ matter inside each?
193
+
194
+ Write the skeleton: the domain/division spine plus governance notes into
195
+ `knowledge/_rules.yaml` — **writes are human-only through the kb-build
196
+ skill (the sole write path), and every leaf requires citations; an
197
+ unsourced claim is not promotable** (§3.2). Bootstrap emits the skeleton
198
+ and standing-room structure only; cited leaves land afterwards through
199
+ kb-build's human gate, never in this session.
200
+
201
+ **On resume:** `knowledge/_rules.yaml` already carrying rule items → the
202
+ interview happened; do not re-ask it.
203
+
204
+ ### 6. FINISH — green validators, recorded decisions
205
+
206
+ Bootstrap surfaced decisions — the taxonomy acceptance, any scope
207
+ trade-offs. Record them through the §3.5 decisions path: draft entries in
208
+ `decisions/entries/` with provisional date-suffixed ids
209
+ (`D-2026-07-08-<slug>`), `status: proposed`, `relates-to` refs to the
210
+ concepts they touch, plus catalog rows. The steward mints final `D-NNN`s at
211
+ acceptance — never the agent.
212
+
213
+ Then the whole-store gate — the skill declares done **only** when both
214
+ validators run clean, unfiltered:
215
+
216
+ ```
217
+ node unknown-knowledge/engine/validate.js --root .
218
+ node unknown-knowledge/engine/validate-values.js --root .
219
+ ```
220
+
221
+ Exit 0 + 0 = done: report the confirmed scope, the class spine, the
222
+ concepts emitted per rung, the misses logged, and the KB skeleton — all of
223
+ it lands through the normal PR gate (agents draft; humans approve). Exit 1
224
+ = fix the store, re-run. Exit 2 = **stop and report**; never declare
225
+ bootstrap done over a check that never ran.
226
+
227
+ **On resume:** an existing proposed bootstrap decision is not re-drafted —
228
+ but the validator run is never skipped: verdicts are per-run (D-011), so a
229
+ resumed session always re-runs both validators before declaring done.
@@ -0,0 +1,397 @@
1
+ # /knowledge-reflect — findings consolidation (PRD §8, D-019)
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
+ Sessions append findings; nobody judges them at capture time. This skill is
10
+ the judgment half — human-run, on cadence (weekly; daily at high finding
11
+ volume): read the fragment logs, cluster recurring signals, put a per-item
12
+ recommendation list in front of the human, apply what they approve, close
13
+ the loop with a validator re-run, and prune what never corroborated. You
14
+ operate under `protocol/AGENTS.md` throughout — every gate rule there binds
15
+ here, and its two `--root` conventions apply verbatim (store-reading CLIs
16
+ take the **repo root**; `log-entry.js` takes the **kit dir**).
17
+
18
+ Everything this run produces — applied diffs, transitioned fragments,
19
+ pruned fragments, the stamp — travels together as **one reflect PR**
20
+ (D-010): the improvement loop's own changes pass normal review and CI.
21
+
22
+ ## The evidence standard
23
+
24
+ Any gated change requires multiple corroborating findings — **one
25
+ correction is a data point, three are a pattern**. The threshold is three
26
+ distinct fragments — distinct meaning **independent resolution events, not
27
+ files** (defined in full under Minting conduct below; a re-opened entry's
28
+ `occurrences` dates each count, three logs from one session do not).
29
+ Single-occurrence noise never reaches the
30
+ review queue, or the humans stop trusting it and the graduation path dies
31
+ before it starts. Two hard qualifiers:
32
+
33
+ - **A dispute never counts as corroboration** — mutually contradictory
34
+ corrections cancel, they do not add (see Disputed clusters below).
35
+ - Corroboration is per-cluster, never per-log: three findings about three
36
+ different concepts are three data points, not one pattern.
37
+
38
+ The corroboration rule is counted **by hand, here** — the engine never
39
+ counts it. `log-entry.js` appends fragments and transitions them; it has no
40
+ opinion about how many make a pattern, and no CLI reports a corroboration
41
+ score. Reflect is the judgment half, and this threshold is judgment.
42
+
43
+ ### Residue and candidate findings — the misses that become edges
44
+
45
+ Sessions append two kinds of finding that exist to be minted from
46
+ (UCS-1160), both through `log-entry.js` like every other fragment:
47
+
48
+ - **Residue** — from `resolve`'s `decomposition.residue`: the non-stopword
49
+ tokens no join consumed. Each fragment carries `resolved-context`, the
50
+ operations, concept ids, and jurisdictions that *did* resolve in the same
51
+ ask. A bare unresolved token is a finding nobody can act on; `stencil`
52
+ unresolved in an ask that resolved `add-tool` and `eu-eaa` localizes
53
+ the gap precisely enough that the minting decision writes itself.
54
+ - **Document candidates** — from `resolve --doc`'s ranked
55
+ `candidates-ranked`: the document's own residue. Each fragment adds a
56
+ `section` locator (document path, heading address, line or page), so
57
+ clustering opens the section just-in-time instead of re-reading the
58
+ document.
59
+
60
+ Cluster these by their `residue` terms, and read `resolved-context` (and,
61
+ for candidates, the located section) as the cluster's evidence. Zero
62
+ resolution is a normal outcome, not a miss — `resolve` says so in its own
63
+ payload; append a residue finding only when the topic plausibly should be
64
+ mapped.
65
+
66
+ ## Minting conduct — how a miss becomes a deterministic edge
67
+
68
+ A corroborated residue cluster is evidence the store has no word for
69
+ something the material keeps naming. Minting that word is the loop's whole
70
+ point, and it is a governed act. Four vocabularies can be minted from
71
+ reflect: **terms**, **aliases**, **operations**, and **domain classes**.
72
+
73
+ ### What "three distinct fragments" means
74
+
75
+ The evidence standard counts **independent resolution events, not files.**
76
+ Three fragments are distinct when each records a *separate occasion on which
77
+ the store failed to resolve the thing* — which in practice means a different
78
+ session, or the same session on a different date.
79
+
80
+ The case this rules out: one session that hit `stencil` three times in a
81
+ row and logged three fragments. That is **one** data point wearing three
82
+ filenames. Nothing was independently corroborated — the same agent, the same
83
+ ask, the same unresolved token — and minting from it would let a single
84
+ session vote three times. Cluster them as one.
85
+
86
+ The case it admits: a re-opened entry. Each date in `occurrences` is a
87
+ genuinely separate occasion (the issue fired again, later, after someone
88
+ thought it was closed), so each counts. Same for the same term surfacing as
89
+ query residue in one session and as a document candidate in another — two
90
+ occasions, two data points.
91
+
92
+ When in doubt, ask what the fragment is evidence *of*. A file is evidence
93
+ that someone pressed enter; an occasion is evidence that the store has a
94
+ hole. The threshold counts holes.
95
+
96
+ Three rules bind every one of them:
97
+
98
+ 1. **Literary warrant, always.** A value is minted only when material
99
+ exists to fill it. Corroboration says the word keeps coming up; warrant
100
+ says there is something for it to hold. Both, or neither — a term minted
101
+ on three findings with no material behind it is speculative shelving
102
+ wearing evidence. Read the source before proposing: a candidate is a
103
+ claim about the map until someone opens the document its `section`
104
+ locator addresses.
105
+ 2. **Evidence attached.** The proposal names the corroborating fragment
106
+ paths verbatim, exactly as every other recommendation item does. A mint
107
+ proposal without its findings attached is not reviewable and does not go
108
+ on the list.
109
+ 3. **One Decisions entry per minting.** Every minted value — each domain
110
+ segment, each alias, each operation — carries its own entry recording
111
+ the warrant and the evidence. Draft it from
112
+ `templates/decisions/reflect-mint-proposal.yaml`; its `id` and `date`
113
+ are deliberately invalid placeholders, so a proposal pasted unedited
114
+ fails validation rather than reaching the Decisions store with a
115
+ rationale nobody wrote. A suppression uses the same shape and the same
116
+ entry: a refused term stays listed with `status: suppressed` so the next
117
+ cycle that clusters the same residue can see it was already considered.
118
+
119
+ Minting is a `mint-proposal` item at the GATE like any other item —
120
+ proposal-first, agents draft and humans approve. Reflect never edits a
121
+ registry ahead of its approval, and never mints a child path segment whose
122
+ parent is unminted.
123
+
124
+ ## Resumable by construction
125
+
126
+ A dead session costs the remaining steps, never a restart. **On entry,
127
+ detect existing artifacts and resume from the first incomplete step** —
128
+ each step below also carries its own "On resume" rule:
129
+
130
+ | Artifact found on entry | What it means |
131
+ |---|---|
132
+ | `logs/last-reflect.yaml` | prior cycles ran — read `cycles:` for archival counting (STAMP); this run appends its date, never rewrites history |
133
+ | Fragments at `status: proposed` | a prior reflect died between RECOMMEND and APPLY: those items are already on a recommendation list — re-present them at the gate as-is, never re-cluster them into new items |
134
+ | A store diff already applied but its justifying findings still `proposed` | the close-the-loop re-run never happened — run the filtered validators now; a check that never ran is a blocking defect |
135
+ | A `resolved`/`rejected` fragment whose issue fired again | **re-open, not duplicate**: transition the original back to `open`, never mint a sibling |
136
+
137
+ ## The six steps
138
+
139
+ ```
140
+ 1. SWEEP read-only: inventory open/proposed fragments in logs/
141
+ 2. CLUSTER group by concept/path/trigger; flag disputed clusters
142
+ 3. RECOMMEND evidence-gated per-item list, justifying findings attached
143
+ 4. GATE human approves/rejects PER ITEM — never in bulk
144
+ 5. APPLY approved diffs + filtered validator re-run close the loop
145
+ 6. STAMP prune uncorroborated-after-N; write logs/last-reflect.yaml
146
+ ```
147
+
148
+ ### 1. SWEEP — inventory the queue, read-only
149
+
150
+ Read every `open` and `proposed` fragment under `logs/findings/` (and the
151
+ sibling logs — `logs/misses/`, `logs/gaps/` — same lifecycle, same
152
+ machinery). Fragments are one-file-per-entry and small: grep is free,
153
+ reading a fragment is cheap, and the fragment IS the entry — there is no
154
+ index to consult. `resolved`/`rejected` fragments are swept only to catch
155
+ recurrences (re-open, not duplicate).
156
+
157
+ The sweep is bounded by `survey-scope.yaml` — the honor-it contract from
158
+ bootstrap. Findings pointing outside the confirmed scope are widening
159
+ signals (`scope-widen` items, below), **never** cause to re-litigate the
160
+ gate or rescan excluded areas.
161
+
162
+ **On resume:** always safe to re-run — the sweep writes nothing.
163
+
164
+ ### 2. CLUSTER — group signals, flag disputes
165
+
166
+ Group the swept fragments into clusters by, in order of preference: the
167
+ `consulted:` concept refs, the file paths named in `summary`, then the
168
+ trigger kind. A path-only fragment joins a concept cluster via the reverse
169
+ lookup:
170
+
171
+ ```
172
+ node unknown-knowledge/engine/resolve.js --paths Sources/Canvas/CanvasTool.swift --root .
173
+ ```
174
+
175
+ Completion criterion: **every swept open fragment is in exactly one
176
+ cluster** — a fragment that fits nowhere is its own cluster of one (that is
177
+ what uncorroborated means), never silently dropped.
178
+
179
+ While clustering, flag every cluster whose corrections **mutually
180
+ contradict** (two findings asserting opposite facts about the same claim)
181
+ as `disputed` — a cluster flag for this run, never a fragment status. A
182
+ disputed cluster does not proceed to RECOMMEND on its correction count;
183
+ it takes the Disputed-clusters procedure (below) first.
184
+
185
+ **On resume:** re-cluster from the current fragments — clustering is
186
+ derived, never stored; `proposed` fragments keep their prior item
187
+ membership (see the resume table).
188
+
189
+ ### 3. RECOMMEND — the evidence-gated list
190
+
191
+ Build the recommendation list. One item per cluster that **meets the
192
+ evidence standard**; each item carries:
193
+
194
+ - **category** — one of the closed change-category vocabulary (additive,
195
+ §3.5): `concept-fix`, `alias-addition`, `ssot-repoint`, `scope-widen`,
196
+ `knowledge-promotion`, `extractor-draft`, `mint-proposal`. Categories are what approval
197
+ outcomes are recorded against (STAMP) and what trust graduation is
198
+ measured per — a miscategorized item corrupts the graduation signal.
199
+ - **the concrete diff** — the exact store change proposed (fix a concept's
200
+ descriptor, add an alias, repoint an SSOT, widen `survey-scope.yaml`,
201
+ promote a finding to knowledge, draft an extractor). Draft it from the
202
+ **source artifact, never from the findings' prose** — the map is never
203
+ the fact, and a finding is a claim about the map. Follow the concept's
204
+ pointer and read the file before writing the diff.
205
+ - **the justifying findings** — the cluster's fragment paths, verbatim.
206
+ An item without its evidence attached is not reviewable and does not go
207
+ on the list.
208
+
209
+ Two categories recommend a *handoff*, never a direct diff:
210
+ `knowledge-promotion` items point into the kb-build skill (the sole
211
+ knowledge write path — reflect never writes a leaf); `extractor-draft`
212
+ items point into `protocol/new-kind-pipeline.md` (D-005 — a parser is
213
+ never drafted and wired in the same session, and never by reflect).
214
+
215
+ Clusters below the threshold get **no item**: they stay `open` and age
216
+ (STAMP counts their cycles). Completion criterion: every cluster is either
217
+ on the list, explicitly held as under-corroborated, or flagged disputed —
218
+ none unaccounted for.
219
+
220
+ **On resume:** items whose fragments are already `proposed` are re-presented
221
+ as-is; only clusters never yet listed get new items.
222
+
223
+ ### 4. GATE — per-item human approval
224
+
225
+ **The moderator's interface is the reflect queue, not the store.** A human
226
+ governing this system reviews what reflect puts in front of them; they do
227
+ not browse `knowledge/` or `ontology/` looking for things to fix. Browsing
228
+ a store is unbounded and finds whatever the eye lands on, while the queue
229
+ is bounded, evidenced, and complete — every item carries its justifying
230
+ findings, and every swept fragment is accounted for. The queue has exactly
231
+ four sections, presented in this order:
232
+
233
+ | Section | What it holds |
234
+ |---|---|
235
+ | **Mint proposals** | `mint-proposal` items — the proposed value, its literary warrant, and the corroborating fragments, one draft Decisions entry each |
236
+ | **Corroborated findings** | every other item that met the evidence standard, with its concrete diff and its justifying fragment paths |
237
+ | **Drafts awaiting promotion** | handoff items (`knowledge-promotion`, `extractor-draft`) — drafted here, written elsewhere; reflect reports them as handed off, never writes them |
238
+ | **Sampled spot-checks** | a sample of what reflect did NOT raise — under-corroborated clusters and fragments due to age out — so the moderator can audit the threshold itself rather than only the items that cleared it |
239
+
240
+ The spot-check sample is what keeps the queue honest: without it the human
241
+ only ever sees what the evidence standard admitted, and a threshold nobody
242
+ audits is a threshold nobody can tune. Present the sample as
243
+ under-corroborated, explicitly — it is context for judging the queue, never
244
+ a recommendation, and approving one does not bypass the standard.
245
+
246
+ Present the list in the conversation and transition each listed item's
247
+ justifying findings `open → proposed` — entering the review queue IS the
248
+ proposal:
249
+
250
+ ```
251
+ node unknown-knowledge/engine/log-entry.js transition --file logs/findings/2026-07-01-00000001.yaml \
252
+ --to proposed --date 2026-07-09 --root unknown-knowledge
253
+ ```
254
+
255
+ The human decides **per item — approve / approve-with-modification /
256
+ reject — never as a bulk yes**. Record each outcome (it feeds the STAMP).
257
+ For every rejection, capture the human's reason and transition the item's
258
+ findings immediately — the helper refuses a reasonless rejection:
259
+
260
+ ```
261
+ node unknown-knowledge/engine/log-entry.js transition --file logs/findings/2026-07-04-00000005.yaml \
262
+ --to rejected --date 2026-07-09 --reason "steward: intended behavior, store is right" --root unknown-knowledge
263
+ ```
264
+
265
+ Nothing is applied before its item is approved — proposal-first, agents
266
+ draft and humans approve. Completion criterion: every listed item has a
267
+ recorded outcome and every rejected item's findings are `rejected` with
268
+ the reason.
269
+
270
+ **On resume:** an item already approved but not applied goes straight to
271
+ APPLY; never re-ask a recorded outcome.
272
+
273
+ ### 5. APPLY — approved diffs, then close the loop
274
+
275
+ Apply each approved diff (as modified, if approve-with-modification), then
276
+ re-run **both validators filtered to exactly the concepts the diff
277
+ touched** — an id left off the list is a check that never ran:
278
+
279
+ ```
280
+ node unknown-knowledge/engine/validate.js --concepts K-110 --root .
281
+ node unknown-knowledge/engine/validate-values.js --concepts K-110 --root .
282
+ ```
283
+
284
+ - **Exit 0 + 0** — the loop is closed: transition the item's findings
285
+ `proposed → resolved` (the helper stamps `verified` with the run date).
286
+ A finding is never resolved ahead of the green re-run — green first,
287
+ then the transition.
288
+ - **Exit 1** — the applied diff disagrees with the source: re-read the
289
+ source and fix the diff, then re-run. If it cannot be made green this
290
+ session, revert the diff and return the item to the queue with the
291
+ failure on record: `--to rejected --reason "re-validation failed: …"`,
292
+ then `--to open` (re-open, not duplicate — the signal is not lost, the
293
+ next cycle sees it with its history).
294
+ - **Exit 2** — **stop.** A check that never ran is a blocking defect,
295
+ never a silent pass; no finding transitions to `resolved` over it.
296
+
297
+ Handoff categories (`knowledge-promotion`, `extractor-draft`) have no diff
298
+ to apply here: their findings stay `proposed` and resolve when the
299
+ downstream gate (kb-build / new-kind pipeline) lands — reflect reports
300
+ them as handed off.
301
+
302
+ Completion criterion: every approved item is either resolved over a green
303
+ filtered re-run, reverted-and-re-opened with the failure recorded, or
304
+ handed off — and no fragment was ever edited by hand (`log-entry.js` is
305
+ the only write path into `logs/`).
306
+
307
+ **On resume:** per the resume table — an applied diff whose findings are
308
+ still `proposed` gets its filtered re-run now, before anything else.
309
+
310
+ ### 6. STAMP — prune, then make the cycle observable
311
+
312
+ First the hygiene prune (Archival mechanics below): every `open` fragment
313
+ still uncorroborated after **N reflect cycles** (default **N = 3** — this
314
+ file is client-owned protocol markdown; tune N by editing it here) is
315
+ archived — the fragment file is deleted in the reflect PR and a rollup
316
+ line records it in the stamp. Completion criterion: every open fragment
317
+ was either kept (with its cycle count still below N) or archived with a
318
+ rollup line — none skipped.
319
+
320
+ Then write the stamp — `logs/last-reflect.yaml` in the kit dir, plain
321
+ engine-readable YAML (the knowledge-audit heartbeat reads it for
322
+ days-since-last-reflect; the trust-graduation trigger reads the per-category
323
+ outcomes — this stamp is the only place either is observable):
324
+
325
+ ```yaml
326
+ schema-version: 1
327
+ date: 2026-07-09 # this run — heartbeat: days-since-last-reflect
328
+ cycles: [2026-06-25, 2026-07-02, 2026-07-09] # every run, appended
329
+ outcomes: # per-item approval outcome BY CATEGORY, this run
330
+ concept-fix: { approved: 1, approved-with-modification: 0, rejected: 0 }
331
+ ssot-repoint: { approved: 0, approved-with-modification: 0, rejected: 1 }
332
+ archived: # the rollup note for this run's prune
333
+ - file: logs/findings/2026-04-02-9c11d0aa.yaml
334
+ summary: "retrieval-struggle: K-130 alias missing"
335
+ reason: uncorroborated after 3 reflect cycles
336
+ ```
337
+
338
+ The stamp is reflect output, not a log fragment — it is the one file this
339
+ skill writes directly (never via `log-entry.js`, and never by another
340
+ skill). Append to `cycles:` and replace `date:`/`outcomes:`/`archived:`
341
+ with this run's values; prior cycles' dates are history, never rewritten.
342
+
343
+ Reflect declares done only when: every gate outcome is recorded in
344
+ `outcomes:`, every close-the-loop re-run was green (or its item
345
+ reverted/handed off), the stamp is written, and the whole bundle is on the
346
+ reflect PR. Then report: clusters formed, items recommended, per-category
347
+ outcomes, fragments resolved/rejected/re-opened/archived.
348
+
349
+ **On resume:** if fragments transitioned this cycle but no stamp carries
350
+ today's date, the cycle is unfinished — the prune and the stamp still
351
+ count as steps; a reflect that mutated the queue but never stamped is
352
+ invisible to the heartbeat.
353
+
354
+ ## Disputed clusters — resolve by reading the SSOT
355
+
356
+ When a cluster's corrections mutually contradict (flagged in CLUSTER):
357
+
358
+ 1. **Read the SSOT** — follow the concept's `source-of-truth` pointer and
359
+ read the file; the map is never the fact, and neither is either finding.
360
+ Cite what you read (path and line) — the citation is the resolution's
361
+ evidence.
362
+ 2. Where the descriptor is machine-checkable, let the engine confirm the
363
+ read: `validate.js` / `validate-values.js` filtered to the concept.
364
+ 3. Transition each finding on the side the SSOT **contradicts**:
365
+ `open → proposed`, then `--to rejected --reason` citing the SSOT read
366
+ (path, line, what it declares).
367
+ 4. The side the SSOT **supports**: if the store already agrees with the
368
+ source, the finding resolves over the green filtered re-run (step 2);
369
+ if the store is wrong, the surviving findings re-enter CLUSTER as an
370
+ ordinary cluster — which must meet the evidence standard **on its own
371
+ count**: a dispute never counts as corroboration, so the contradicted
372
+ exchange adds nothing.
373
+ 5. If the SSOT itself is ambiguous (the pointer is stale, the file does
374
+ not decide it), the cluster goes to the GATE as a flagged question for
375
+ the human — never as a recommendation, and never silently dropped.
376
+
377
+ ## Archival mechanics — uncorroborated `open` findings
378
+
379
+ Unbounded open fragments drown reflect by month ten; the prune keeps the
380
+ queue trustworthy. An `open` fragment is **archived** when at least N
381
+ stamped cycle dates in `logs/last-reflect.yaml` postdate its `date` (or
382
+ its latest `occurrences` entry, if re-opened) and its cluster never met
383
+ the evidence standard:
384
+
385
+ - **Delete the fragment file** in the reflect PR — git history preserves
386
+ it; the deletion is reviewable like any other change. This prune is the
387
+ one sanctioned deletion in `logs/` (the AGENTS.md rule against deleting
388
+ findings guards gate-bypass, not consolidation) — it happens only here,
389
+ only in a reflect PR, only with a rollup line.
390
+ - **Record the rollup line** under `archived:` in the stamp: fragment
391
+ path, one-line summary, `uncorroborated after N reflect cycles`.
392
+ - `archived` is **not a status** — `log-entry.js` has no such transition
393
+ and hard-errors on it; never try to transition a fragment there.
394
+
395
+ If an archived signal was real, it will fire again — the new fragment
396
+ starts a fresh cluster with a fresh count, and the rollup note in git
397
+ history is the paper trail that it aged out once before.
@@ -0,0 +1,32 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "urn:unknown-knowledge:schema:catalog",
4
+ "title": "Store catalog (_catalog.yaml)",
5
+ "description": "The navigational entry point every store shares: _catalog.yaml → _rules.yaml → class files / tree leaves (PRD §3). The map is never the fact — catalogs point at files, they never embed record content. Entry ids follow the owning store's id grammar (K-NNN, notation, D-NNN), so they are plain strings here; id shape and file resolution are structural-validator checks (KK-05). Schema evolution is additive-only (§3.5, D-013).",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["schema-version", "store", "entries"],
9
+ "properties": {
10
+ "schema-version": { "type": "integer", "minimum": 1 },
11
+ "store": { "enum": ["ontology", "knowledge", "decisions"] },
12
+ "entries": {
13
+ "type": "array",
14
+ "items": { "$ref": "#/$defs/catalogEntry" }
15
+ }
16
+ },
17
+ "$defs": {
18
+ "catalogEntry": {
19
+ "type": "object",
20
+ "additionalProperties": false,
21
+ "required": ["id", "title", "file"],
22
+ "properties": {
23
+ "id": { "type": "string" },
24
+ "title": { "type": "string" },
25
+ "file": {
26
+ "type": "string",
27
+ "description": "Path relative to the store root, or a documented pending marker while the record is being imported."
28
+ }
29
+ }
30
+ }
31
+ }
32
+ }