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,60 @@
1
+ # Guarantees and boundaries
2
+
3
+ What the kit promises, what it deliberately does not, and which of its rules
4
+ are yours to change. Each section names the decision that pinned it — the
5
+ full record is in the kit's decision log, and the seeded copy of every rule
6
+ below lives in `protocol/AGENTS.md`.
7
+
8
+ ## What the kit will never catch (D-008)
9
+
10
+ The kit catches **conceptual drift**: names, value sets, and pointers — the
11
+ map disagreeing with the artifact. A registry gained a value no concept
12
+ knows; a concept enumerates a value the source dropped; a pointer dangles at
13
+ a file that moved.
14
+
15
+ It will **never** catch **behavioral drift**: logic bugs, inverted
16
+ conditions, a wrong calculation behind a correctly-named function, a
17
+ registry whose values are all present and all handled badly. Behavior is
18
+ your test suite's job. Budget your trust accordingly: a green validator run
19
+ means the map matches the code, not that the code is right.
20
+
21
+ ## The engine never executes your code (D-014)
22
+
23
+ The engine only ever **reads your files lexically**. It never imports,
24
+ evals, or runs repo content; JSON and YAML are parsed as data, never
25
+ loaded as code; it makes no network calls. The one child process the
26
+ engine spawns is `git` (in `survey-map.js`, to list tracked files) — it
27
+ reads git's index, never your code. This is the answer to "does this tool
28
+ run our code in CI?": no — guaranteed by design and pinned by a grep test
29
+ in the kit's own CI. Any future adapter that would break it (e.g. a
30
+ remote-config fetch at validate time) must be explicitly opt-in.
31
+
32
+ ## Findings privacy — the capture content policy
33
+
34
+ Findings, misses, and gaps are committed YAML fragments: permanent git
35
+ history **in your repo**, reviewable in PRs like any other change — and
36
+ they never leave it. The kit has no runtime and no telemetry; nothing is
37
+ collected or sent anywhere.
38
+
39
+ The capture policy is what keeps the fragments safe to commit: summaries
40
+ carry **concept IDs and file paths only** — never verbatim user text,
41
+ quoted session content, or secrets; the `session` field is an opaque ID.
42
+ This strips context by design — a finding is a signal that something
43
+ disagreed, not a transcript of the disagreement. The SSOT is the artifact
44
+ the finding points at; anyone judging a finding follows the pointer and
45
+ reads the source.
46
+
47
+ ## Preflight conduct is yours to set (D-011)
48
+
49
+ Verdicts are deterministic engine facts; **what a session does about a
50
+ verdict is policy in markdown you own**. The place to edit is the
51
+ CLIENT-EDITABLE conduct table in `protocol/AGENTS.md`
52
+ ("Conduct-on-verdict policy"): the recommended default is
53
+ quarantine-and-continue, and a stricter shop may edit `quarantined` to
54
+ fail-stop — that is the sanctioned edit, made by normal PR through your
55
+ steward gate.
56
+
57
+ The non-editable floor: the engine's verdicts, exit codes, and evidence are
58
+ computed facts, not policy — and no policy edit may tell an agent to bypass
59
+ a gate, trust a quarantined concept's claims, or treat exit 2 (a check that
60
+ never ran) as anything but a stop.
@@ -0,0 +1,109 @@
1
+ # CI wiring — validators in CI, attribution on PRs
2
+
3
+ > Commands run from the **repo root** with the kit dir at its default name
4
+ > `unknown-knowledge/`; substitute your chosen name if it differs. The CI
5
+ > snippets below are honest templates: written to be correct, **not executed
6
+ > against every CI product** — adapt runners, images, and paths to your
7
+ > setup, then watch the first run.
8
+
9
+ The kit never wires CI at init (D-006) — CI systems churn; your pipeline is
10
+ yours. But at team scale CI stops being optional in practice: session-level
11
+ preflight is a sufficient gate for a small team, not for hundreds of
12
+ engineers. Wire the two validators in.
13
+
14
+ ## What may gate, and what never does
15
+
16
+ - `engine/validate.js` and `engine/validate-values.js` are blocking-grade
17
+ and MAY gate a merge. Exit codes: 0 = clean, 1 = findings, 2 = the check
18
+ never ran. Fail the job on both 1 and 2 — **a check that never ran is a
19
+ blocking defect, never a silent pass** (a non-zero exit fails the job by
20
+ default in every system below, so the templates need no special casing).
21
+ - `engine/preflight.js` with no `--concepts` checks store health only — add
22
+ it as a third gating step if you want the store verdict pinned in CI.
23
+ - `engine/audit.js` is advisory (never blocking): it drafts proposals for
24
+ human review, and presenting it as a required check would flip the whole
25
+ protocol from proposal-first to gate-first. Keep it out of CI. Its
26
+ `--fail-on-findings` flag exists for a human's local run — never a CI
27
+ default.
28
+
29
+ The engine needs Node ≥ 22 and its one library dependency, `js-yaml`,
30
+ resolvable in the checkout (see the seeded README) — the templates below
31
+ install dependencies for that reason. It reads your files lexically — it
32
+ never executes your code and never touches the network
33
+ (`docs/boundaries.md`) — so the jobs need no secrets, tokens, or extra
34
+ permissions.
35
+
36
+ ## GitHub Actions
37
+
38
+ ```yaml
39
+ # .github/workflows/knowledge-gates.yml — untested template, adapt.
40
+ name: knowledge-gates
41
+ on: pull_request
42
+ jobs:
43
+ validate:
44
+ runs-on: ubuntu-latest
45
+ steps:
46
+ - uses: actions/checkout@v4
47
+ - uses: actions/setup-node@v4
48
+ with:
49
+ node-version: 22
50
+ - run: npm ci # js-yaml must resolve; use `npm install js-yaml` if you keep no lockfile
51
+ - run: node unknown-knowledge/engine/validate.js --root .
52
+ - run: node unknown-knowledge/engine/validate-values.js --root .
53
+ ```
54
+
55
+ ## GitLab CI
56
+
57
+ ```yaml
58
+ # .gitlab-ci.yml fragment — untested template, adapt.
59
+ knowledge-gates:
60
+ image: node:22
61
+ rules:
62
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event"
63
+ script:
64
+ - npm ci # js-yaml must resolve; use `npm install js-yaml` if you keep no lockfile
65
+ - node unknown-knowledge/engine/validate.js --root .
66
+ - node unknown-knowledge/engine/validate-values.js --root .
67
+ ```
68
+
69
+ ## Xcode Cloud
70
+
71
+ Xcode Cloud runs custom scripts from `ci_scripts/`; its images ship no Node
72
+ runtime, so the script installs one.
73
+
74
+ ```sh
75
+ #!/bin/sh
76
+ # ci_scripts/ci_post_clone.sh — untested template, adapt.
77
+ set -e
78
+ brew install node
79
+ npm ci --prefix "$CI_PRIMARY_REPOSITORY_PATH" # js-yaml must resolve
80
+ node "$CI_PRIMARY_REPOSITORY_PATH/unknown-knowledge/engine/validate.js" --root "$CI_PRIMARY_REPOSITORY_PATH"
81
+ node "$CI_PRIMARY_REPOSITORY_PATH/unknown-knowledge/engine/validate-values.js" --root "$CI_PRIMARY_REPOSITORY_PATH"
82
+ ```
83
+
84
+ ## PR drift attribution (D-012) — whose findings are these?
85
+
86
+ Filtering validation down to the concepts a PR touched is rejected by
87
+ design: any gap in the reverse index (folder pointers, renames, module
88
+ adds/deletes) turns the filter into a false all-clear, which is worse than
89
+ no check. Attribution instead diffs **two honest whole-store runs** — the
90
+ finding set at the PR's merge-base against the set at HEAD. Both validators
91
+ emit stable, sorted JSON precisely so this diff means something.
92
+
93
+ ```sh
94
+ git worktree add /tmp/kk-base "$(git merge-base HEAD origin/main)"
95
+
96
+ # Exit 1 just means findings exist — attribution wants the sets, not the gate.
97
+ node unknown-knowledge/engine/validate.js --json --root /tmp/kk-base > /tmp/base.json || true
98
+ node unknown-knowledge/engine/validate.js --json --root . > /tmp/head.json || true
99
+
100
+ # Right side only = drift this PR introduced; left side only = drift it fixed.
101
+ diff <(jq -S '.findings' /tmp/base.json) <(jq -S '.findings' /tmp/head.json)
102
+
103
+ git worktree remove /tmp/kk-base
104
+ ```
105
+
106
+ Run the same pair with `engine/validate-values.js` for value drift. In a CI
107
+ gate, fail on findings the diff attributes to the PR rather than on the raw
108
+ exit code — the baseline may legitimately be red while the map catches up,
109
+ and a PR should answer for its own drift, not the backlog's.
@@ -0,0 +1,238 @@
1
+ # Steward guide — owning the loop
2
+
3
+ > Commands run from the **repo root** with the kit dir at its default name
4
+ > `unknown-knowledge/`; substitute your chosen name if it differs.
5
+ > Procedures live in the skills and in `protocol/AGENTS.md` — this guide
6
+ > tells you what the steward decides and points at where each procedure is
7
+ > defined; it never restates one.
8
+
9
+ ## The role
10
+
11
+ A small group or rotation owns the improvement loop; the ordinary engineer
12
+ invokes nothing — the protocol rides along in their sessions. The steward:
13
+
14
+ - runs `/knowledge-reflect` on cadence — weekly, daily at high finding
15
+ volume (procedure: `protocol/skills/knowledge-reflect.md`);
16
+ - triages the reverse audit's drafted concepts — audit output is advisory
17
+ (never blocking), so every draft waits for your judgment;
18
+ - gates `/kb-build` promotions — the knowledge store's only write path is
19
+ cited and human-gated (`protocol/skills/kb-build.md`);
20
+ - accepts decision entries (final `D-NNN` assignment, below).
21
+
22
+ The heartbeat is `/knowledge-audit` (`protocol/skills/knowledge-audit.md`):
23
+ days-since-last-reflect, open-fragment counts, top quarantined concepts — a
24
+ lapsed rotation is visible instead of silent. The engine side of triage is
25
+ one command; `--today` is passed explicitly because diffable output never
26
+ reads the wall clock:
27
+
28
+ ```
29
+ node unknown-knowledge/engine/audit.js --root . --json --today 2026-07-09 --stale-days 90
30
+ ```
31
+
32
+ ## Reflect lands as a PR
33
+
34
+ Reflect emits a recommendation list — each item a concrete diff with its
35
+ corroborating findings attached; you approve or reject **per item**, and
36
+ approved diffs land as a normal PR. That is the design, not a convenience:
37
+ even the improvement loop's own changes pass the review and CI your org
38
+ already trusts, and rejections leave a recorded reason instead of a
39
+ disappeared suggestion.
40
+
41
+ ## CODEOWNERS — scope the human gate
42
+
43
+ Gate the stores, the protocol, and the engine; leave the logs alone:
44
+
45
+ ```
46
+ # CODEOWNERS — steward review scoped to the governed surfaces.
47
+ /unknown-knowledge/ontology/ @your-org/knowledge-stewards
48
+ /unknown-knowledge/knowledge/ @your-org/knowledge-stewards
49
+ /unknown-knowledge/decisions/ @your-org/knowledge-stewards
50
+ /unknown-knowledge/protocol/ @your-org/knowledge-stewards
51
+ /unknown-knowledge/engine/ @your-org/knowledge-stewards
52
+ # logs/ is deliberately absent: findings ride nearly every agent-assisted
53
+ # PR. Requiring steward review there drowns the rotation in rubber-stamping
54
+ # — or teaches engineers to delete fragments to unblock merges, and capture
55
+ # dies. Logs must never require steward review.
56
+ ```
57
+
58
+ ## Suppressions hygiene
59
+
60
+ `unknown-knowledge/suppressions.yaml` silences known-noise audit findings.
61
+ Each entry is **strictly** `{ term, sourcePath, reason, date }`, exact match
62
+ only — no patterns, no expiry (D-013). Two properties to lean on:
63
+
64
+ - **Fails open.** A malformed entry (or an unparseable file) warns and
65
+ suppresses nothing, so the findings it would have hidden resurface — a
66
+ suppression can hide less than you meant, never more.
67
+ - **Exact match is self-expiring.** A suppression stops matching the moment
68
+ the file moves or the term changes; that is a feature, not fragility.
69
+
70
+ The JSON report carries the full suppressed list — sweep it on reflect
71
+ cadence and prune entries whose `reason` no longer holds.
72
+
73
+ ## ID collision — two branches mint the same K-id
74
+
75
+ Published IDs are immutable — never renumbered (PRD §3.5). When two
76
+ branches both mint `K-210`, the merge is textually clean and the duplicate
77
+ surfaces on main; the **later-merging PR renumbers its own entry**, never
78
+ the one already published:
79
+
80
+ 1. Rebase onto main and run the structural validator — it reports the
81
+ `duplicate-id` finding:
82
+
83
+ ```
84
+ node unknown-knowledge/engine/validate.js --root .
85
+ ```
86
+
87
+ 2. Re-mint your entry at a free id within the owning class's range (mint
88
+ with gaps, per the store's `_rules.yaml`).
89
+ 3. Update your branch's own inbound refs to the new id — `used-by`,
90
+ `relates-to`, and any `consulted:` refs in findings your branch appended.
91
+ 4. Re-run both validators; green means the renumber is complete.
92
+
93
+ ## Decisions authoring
94
+
95
+ The write path is the "Decisions-authoring path" in `protocol/AGENTS.md`:
96
+ anyone — agent or human — drafts a `proposed` entry with a provisional
97
+ date-suffixed id through the normal PR gate. Your half is acceptance: assign
98
+ the final `D-NNN` within range, and hold the append-mostly line — status
99
+ transitions never rewrite `context`/`decision`, and supersession chains must
100
+ resolve and stay acyclic.
101
+
102
+ ## Phoenix events — re-filing a drifted subtree
103
+
104
+ Sometimes the material is fine and the shelf is wrong: one class holds two kinds
105
+ of thing that no longer share a reader, or a name stopped meaning what it says.
106
+ A phoenix event fixes that in bulk, under review, without breaking a single
107
+ citation.
108
+
109
+ It is a PR like any other, carrying four things: the Decisions entry that
110
+ sanctions it (copy `templates/decisions/phoenix-event.yaml`), the registry
111
+ mintings the new values need, the mapping at `knowledge/_phoenix/<event>.yaml`,
112
+ and the leaf rewrites — which you never make by hand:
113
+
114
+ ```
115
+ node unknown-knowledge/engine/phoenix.js P-001 --root . # dry run: plan it, write nothing
116
+ node unknown-knowledge/engine/phoenix.js P-001 --root . --apply # write, only if the plan is clean
117
+ ```
118
+
119
+ What to check when you review one:
120
+
121
+ - **The mapping is complete.** Every leaf in the declared scope must be mapped,
122
+ split, or explicitly carried forward. The engine refuses the event and names
123
+ the leaf otherwise — it will not half-apply, so a rejected event leaves the
124
+ store exactly as it found it.
125
+ - **Each row's `why` earns its move.** On a split this is the whole substance:
126
+ the class-level rule cannot say why L-000117 went to ingest and its neighbour
127
+ went to patterns, so the rows have to.
128
+ - **The diff is two lines per leaf.** `edition` and the one facet. If a leaf's
129
+ citations, body, or `id` appear in the diff, something is wrong — the engine
130
+ rewrites single frontmatter lines and copies every other byte through.
131
+ - **The mapping stays.** Do not delete it after the event lands. It is what lets
132
+ the validator confirm every edition above 1 was sanctioned, reading only the
133
+ working tree. A leaf's edition must equal `1 + the events that moved it`, and
134
+ `validate.js` reports `unaccounted-edition` when it does not.
135
+
136
+ Identity never moves. Accession ids are untouched, so every citation into the
137
+ subtree — decisions entries, catalog rows, leaf cross-references — stays valid
138
+ across the event. If a proposal needs an id renamed or reissued, it is not a
139
+ phoenix event.
140
+
141
+ ## Findings are lossy, by design
142
+
143
+ Keep the corroboration math honest by knowing what the log is not:
144
+
145
+ - **Capture strips context deliberately.** Summaries carry concept IDs and
146
+ file paths only — never verbatim user text (see `docs/boundaries.md`).
147
+ The SSOT is the artifact the finding points at, never the finding's
148
+ prose: to judge a cluster, follow the pointers and read the source.
149
+ - **Findings travel only with merged PRs.** Abandoned branches lose theirs.
150
+ Corroboration tolerates a lossy denominator — treat finding counts as
151
+ signal, never as census.
152
+
153
+ ## Trust graduation — narrowing inspection, and un-narrowing it
154
+
155
+ Autonomy is graduated per change-category, never global, never per leaf, and
156
+ never assumed (the gate rules in `protocol/AGENTS.md`). The category is the
157
+ unit because it is the only thing evidence can be about: "this leaf has been
158
+ right ten times" says nothing about the eleventh leaf, which is a different
159
+ claim by a different author, whereas "alias additions have been approved
160
+ unmodified ten cycles running" is evidence about a class of edit.
161
+
162
+ The observable basis is what reflect already records: **per-item approval
163
+ outcomes by category** (approved / approved-with-modification / rejected).
164
+
165
+ ### The category table
166
+
167
+ `decisions/_registries/graduation-categories.yaml` declares every change
168
+ category and its eligibility. It is filed under `decisions/` because
169
+ graduation governs the change *process* — the team's truth anchor (D-003) —
170
+ not the knowledge itself. Two shapes:
171
+
172
+ - **`eligible`**, with a threshold **N**: mechanical categories, where the
173
+ edit is checkable by reading it and a clean run is evidence about the next
174
+ one. Alias additions, verified bumps, `paths` edges, see-also links.
175
+ - **`gated`**, permanently: judgment categories. New domain classes,
176
+ contradicts/supersedes edges, authority assignments, and **anything
177
+ citation-bearing**. These never graduate, however long the streak — the
178
+ streak answers a different question than the one they ask. A correct
179
+ authority assignment ten times over says the assigner has been careful, not
180
+ that the eleventh source's trustworthiness can go unread.
181
+
182
+ Every row carries a warrant and cites a Decisions entry, like any registry
183
+ value. A malformed table is a hard error (exit 2), not a finding: graduation
184
+ checks judged against a table the engine could not read are checks that never
185
+ ran.
186
+
187
+ ### v1 analytics are MANUAL — the engine does not compute the counts
188
+
189
+ State this plainly to anyone reading a green validator: **the engine does not
190
+ compute approved-unmodified counts and never decides whether a threshold was
191
+ met.** It checks that the entry is well-formed, that its category is declared,
192
+ and that the category is not gated. *You* count the cycles and judge them
193
+ against N. The `observed-cycles` field is your written record of that count,
194
+ kept so a reviewer can weigh the judgment — no code asserts it. A clean
195
+ validation run is not agreement that a graduation was earned.
196
+
197
+ ### Recording one
198
+
199
+ Copy `templates/decisions/trust-graduation.yaml`, and record the graduation as
200
+ a `decisions/` entry of category `trust`. The entry carries a typed
201
+ `graduation:` block naming the category and your observed count.
202
+
203
+ ```
204
+ node unknown-knowledge/engine/validate.js --root .
205
+ ```
206
+
207
+ ### Revocation is automatic on ANY defect
208
+
209
+ **Any defect found in a graduated category revokes it.** Not "a serious
210
+ defect", not "a pattern of defects" — one is enough, and the response is
211
+ mechanical so it cannot be argued down. The warrant for sampling a category
212
+ was that it had stopped producing surprises; one surprise is the evidence that
213
+ it has not. Record it with `templates/decisions/trust-revocation.yaml`; the
214
+ category returns to full inspection and its streak restarts at zero.
215
+
216
+ Revoking is deliberately the cheapest entry in the store to write. Narrowing
217
+ inspection takes a counted streak and a written rationale; restoring it takes
218
+ noticing one defect. If revoking were as laborious as graduating, the
219
+ laborious thing would quietly not get done and the boundary would only ever
220
+ move one way. Revoking is always allowed — including for a gated category,
221
+ where it is a no-op that records the standing position. Only *graduating* a
222
+ gated category is refused.
223
+
224
+ ### Citation spot-checks stay in the sampling plan at every trust level
225
+
226
+ Whatever a category has earned, **citation spot-checks continue at every trust
227
+ level**, including the most graduated. A citation is a claim about the world
228
+ that the store cannot check for itself; no streak of clean mechanical edits is
229
+ evidence that the citations are sound. Graduation only ever answers the
230
+ mechanical question.
231
+
232
+ ### Provenance — tracing a bad skill revision
233
+
234
+ Entries carry `provenance` (`author`, `skill-version`), and the validator
235
+ surfaces it in both JSON and human output. When a defect turns out to come
236
+ from a bad skill vintage rather than a bad judgment, this is what makes every
237
+ entry that vintage wrote findable rather than guessed at — a bad skill
238
+ revision is traceable like any other defect.
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * engine/audit.js — the entry shim (UCS-956).
4
+ *
5
+ * This file statically imports NOTHING. That is its entire job.
6
+ *
7
+ * Node exits 1 on an unhandled ES module load error, and exit 1 means FINDINGS
8
+ * (PRD §5). A SyntaxError in a lib/ module, or a missing `js-yaml`, must not
9
+ * tell an agent that the check ran and found problems. So the engine is reached
10
+ * only through `import()`, where a load failure is an ordinary catchable
11
+ * rejection rather than a process-level crash.
12
+ *
13
+ * Both specifiers are string literals naming the engine's own files. D-014
14
+ * forbids importing REPO CONTENT — the client's code — and nothing here can
15
+ * name it: there is no variable to point somewhere else.
16
+ *
17
+ * The command lives in commands/audit.js. Its invocation path is unchanged.
18
+ */
19
+ try {
20
+ const [{ boot }, command] = await Promise.all([
21
+ import('./lib/boot.js'),
22
+ import('./commands/audit.js'),
23
+ ]);
24
+ // exitCode, never process.exit(): exit() drops queued async stdout writes, so
25
+ // piped --json output would truncate at the pipe buffer — corrupt output
26
+ // wearing a clean exit code. Node exits on its own once stdout drains.
27
+ process.exitCode = await boot('audit', command);
28
+ } catch (error) {
29
+ // The engine could not be loaded, so no check ran. Exit 2 — never 1.
30
+ // Hardcoded, because reading it from lib/exit-codes.js is the very thing
31
+ // that may have just failed.
32
+ process.stderr.write(`audit: internal failure — the engine could not be loaded\n${error?.stack ?? error}\n`);
33
+ process.exitCode = 2;
34
+ }