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,196 @@
1
+ /**
2
+ * Log-entry append/transition helper (KK-13) — how agents write to the
3
+ * fragment-based logs (PRD §3.4, D-010) without hand-editing YAML.
4
+ *
5
+ * logs/findings/ finding.schema.json five capture triggers (§3.4)
6
+ * logs/misses/ miss.schema.json unextractable anchors → extractor backlog
7
+ * logs/gaps/ gap.schema.json requests no protocol/skill could route
8
+ *
9
+ * ONE FILE PER ENTRY: `createEntry` mints `logs/<log>/<date>-<hex8>.yaml`,
10
+ * so hundreds of concurrent sessions on hundreds of branches append without
11
+ * a single merge conflict — the file name is the entry id. Eight hex chars
12
+ * (2^32 ids per date per log): 'wx' only guards one checkout, so the suffix
13
+ * space itself must keep cross-branch same-day collisions negligible (D-010).
14
+ *
15
+ * Status lifecycle (§3.4/§8): open → proposed → resolved | rejected, and
16
+ * re-open-not-duplicate — a recurrence transitions the SAME entry back to
17
+ * open (appending the date to `occurrences`), never mints a sibling.
18
+ * `transitionStatus` enforces the legal table and THROWS on an illegal move;
19
+ * resolving stamps `verified`, rejecting requires a `reason`, re-opening
20
+ * drops both — the old outcome no longer holds.
21
+ *
22
+ * Dates are injectable — callers pass the ISO date; nothing in here reads the
23
+ * wall clock, so diffable output never depends on when a test ran (PRD §5).
24
+ *
25
+ * Capture content policy (§3.4/KK-20): entries carry concept IDs and file
26
+ * paths only — never verbatim user text, quoted session content, or secrets.
27
+ * Committed fragments are permanent git history in the client's repo.
28
+ *
29
+ * Reflect-side contract (implementation lands with KK-22): /knowledge-reflect
30
+ * consolidates fragments via these same transitions — approved + validator
31
+ * re-run pass → resolved (+ verified date); fail → re-opened; rejected with
32
+ * reason; recurrences re-open, never duplicate. `open` entries still
33
+ * uncorroborated after N reflect cycles are pruned: archived with a rollup
34
+ * note (the fragment file is deleted; the rollup is reflect output, not a
35
+ * status — this module never deletes).
36
+ */
37
+ import { randomBytes } from 'node:crypto';
38
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
39
+ import { join, resolve, sep } from 'node:path';
40
+ import { load, dump } from 'js-yaml';
41
+ import { validateRecord } from './validate-record.js';
42
+ import { isCalendarDate } from './iso-date.js';
43
+
44
+ /** Log directory name (under logs/) → record kind / schema document. */
45
+ export const LOGS = Object.freeze({
46
+ findings: 'finding',
47
+ misses: 'miss',
48
+ gaps: 'gap',
49
+ });
50
+
51
+ /** The §3.4 lifecycle: open → proposed → resolved/rejected; re-open-not-duplicate. */
52
+ export const LEGAL_TRANSITIONS = Object.freeze({
53
+ open: Object.freeze(['proposed']),
54
+ proposed: Object.freeze(['rejected', 'resolved']),
55
+ rejected: Object.freeze(['open']),
56
+ resolved: Object.freeze(['open']),
57
+ });
58
+
59
+ const SCHEMA_VERSION = 1;
60
+ /** Keys the helper stamps itself; hand-supplying one is a caller bug. */
61
+ const HELPER_OWNED = ['schema-version', 'date', 'status', 'verified', 'reason', 'occurrences'];
62
+
63
+ function assertDate(date, what = 'date') {
64
+ // A real day, not just four-two-two: the date is stamped into the fragment's
65
+ // filename and its status transitions, so `2026-02-30` would live forever in
66
+ // an audit trail as a day that never happened.
67
+ if (!isCalendarDate(date)) {
68
+ throw new Error(`${what} must be an injected ISO date (YYYY-MM-DD), got ${JSON.stringify(date)} — the helper never reads the wall clock (PRD §5)`);
69
+ }
70
+ }
71
+
72
+ function kindFor(log) {
73
+ const kind = LOGS[log];
74
+ if (!kind) throw new Error(`unknown log "${log}" (expected one of: ${Object.keys(LOGS).join(', ')})`);
75
+ return kind;
76
+ }
77
+
78
+ function assertValid(kind, entry, file) {
79
+ const { errors } = validateRecord(kind, entry);
80
+ if (errors.length > 0) {
81
+ const detail = errors.map((e) => `${e.path}: ${e.code} — ${e.message}`).join('; ');
82
+ throw new Error(`${file}: entry does not validate against ${kind}.schema.json: ${detail}`);
83
+ }
84
+ }
85
+
86
+ /** Serialize with stable options so fragments diff cleanly run-over-run. */
87
+ function serialize(entry) {
88
+ return dump(entry, { lineWidth: 100, noRefs: true, sortKeys: false });
89
+ }
90
+
91
+ /**
92
+ * Append one entry to logs/<log>/ — one file per entry (D-010).
93
+ *
94
+ * @param {object} options
95
+ * @param {string} options.root kit root (the dir containing logs/)
96
+ * @param {string} options.log 'findings' | 'misses' | 'gaps'
97
+ * @param {string} options.date injected ISO date — never wall-clock
98
+ * @param {object} options.fields kind-specific fields (trigger/summary/…);
99
+ * concept IDs and paths only — never verbatim user text or secrets
100
+ * @param {string} [options.suffix] 8-hex filename suffix (default: random)
101
+ * @returns {{ file: string, entry: object }} root-relative fragment path + entry
102
+ */
103
+ export function createEntry({ root, log, date, fields = {}, suffix } = {}) {
104
+ const kind = kindFor(log);
105
+ assertDate(date);
106
+ for (const key of HELPER_OWNED) {
107
+ if (Object.hasOwn(fields, key)) {
108
+ throw new Error(`field "${key}" is helper-owned — createEntry stamps it; entries are born open with schema-version ${SCHEMA_VERSION}`);
109
+ }
110
+ }
111
+ if (suffix !== undefined && !/^[0-9a-f]{8}$/.test(suffix)) {
112
+ throw new Error(`suffix must be 8 lowercase hex chars, got ${JSON.stringify(suffix)}`);
113
+ }
114
+ const id = suffix ?? randomBytes(4).toString('hex');
115
+ const file = `logs/${log}/${date}-${id}.yaml`;
116
+ const entry = { 'schema-version': SCHEMA_VERSION, date, status: 'open', ...fields };
117
+ assertValid(kind, entry, file);
118
+ mkdirSync(join(root, 'logs', log), { recursive: true });
119
+ // 'wx' = exclusive create: a suffix collision is a hard error, never an
120
+ // overwrite — published fragments are immutable except via transitions.
121
+ writeFileSync(join(root, file), serialize(entry), { flag: 'wx' });
122
+ return { file, entry };
123
+ }
124
+
125
+ /**
126
+ * Move one fragment through the lifecycle; hard-errors on an illegal move
127
+ * and leaves the file untouched.
128
+ *
129
+ * @param {object} options
130
+ * @param {string} options.root kit root (the dir containing logs/)
131
+ * @param {string} options.file root-relative fragment path (as createEntry returned)
132
+ * @param {string} options.to target status
133
+ * @param {string} options.date injected ISO date — never wall-clock
134
+ * @param {string} [options.reason] required when rejecting
135
+ * @returns {{ file: string, entry: object }} the rewritten entry
136
+ */
137
+ /** Derived from LOGS so a fourth log can't be creatable-but-untransitionable. */
138
+ const FRAGMENT_PATH = new RegExp(`^logs/(${Object.keys(LOGS).join('|')})/[^/]+\\.yaml$`);
139
+
140
+ /**
141
+ * What each target status does to the lifecycle fields (§3.4/§8) — one table
142
+ * so field ownership lives in one place, not scattered through an if/else
143
+ * chain. `stamp` writes fields; `drop` removes the ones the new status must
144
+ * not carry (the validation layer enforces the same invariants).
145
+ */
146
+ const STATUS_EFFECTS = Object.freeze({
147
+ proposed: {},
148
+ resolved: {
149
+ // the validator re-run passed (§8)
150
+ stamp: ({ date }) => ({ verified: date }),
151
+ },
152
+ rejected: {
153
+ stamp: ({ file, reason }) => {
154
+ if (typeof reason !== 'string' || reason === '') {
155
+ throw new Error(`${file}: rejecting requires a reason — rejections record the reason (§8)`);
156
+ }
157
+ return { reason };
158
+ },
159
+ },
160
+ open: {
161
+ // Re-open, not duplicate: same entry, occurrence date appended; the old
162
+ // resolution/rejection outcome (verified, reason) no longer holds.
163
+ drop: ['verified', 'reason'],
164
+ stamp: ({ date, entry }) => ({ occurrences: [...(entry.occurrences ?? []), date] }),
165
+ },
166
+ });
167
+
168
+ export function transitionStatus({ root, file, to, date, reason } = {}) {
169
+ const match = FRAGMENT_PATH.exec(file ?? '');
170
+ if (!match) throw new Error(`not a log fragment path: ${JSON.stringify(file)} (expected logs/<${Object.keys(LOGS).join('|')}>/<entry>.yaml, relative to root)`);
171
+ const kind = kindFor(match[1]);
172
+ if (!Object.hasOwn(LEGAL_TRANSITIONS, to ?? '')) {
173
+ throw new Error(`unknown target status ${JSON.stringify(to)} (expected one of: ${Object.keys(LEGAL_TRANSITIONS).join(', ')})`);
174
+ }
175
+ assertDate(date);
176
+ // Confine the read/write inside root's logs/ dir: a crafted relative path
177
+ // ('../logs/…', 'foo/../../logs/…') must never reach a sibling tree.
178
+ const logsDir = resolve(root, 'logs');
179
+ const absolute = resolve(root, file);
180
+ if (!absolute.startsWith(logsDir + sep)) {
181
+ throw new Error(`fragment path ${JSON.stringify(file)} escapes the kit root — transitions only touch files under ${logsDir}`);
182
+ }
183
+ const entry = load(readFileSync(absolute, 'utf8'));
184
+ assertValid(kind, entry, file);
185
+ const from = entry.status;
186
+ if (!LEGAL_TRANSITIONS[from].includes(to)) {
187
+ throw new Error(`${file}: illegal transition ${from} → ${to} (legal from ${from}: ${LEGAL_TRANSITIONS[from].join(', ') || 'none'}) — the lifecycle is open → proposed → resolved/rejected; re-open-not-duplicate (§3.4)`);
188
+ }
189
+ const effects = STATUS_EFFECTS[to];
190
+ entry.status = to;
191
+ for (const key of effects.drop ?? []) delete entry[key];
192
+ Object.assign(entry, effects.stamp?.({ file, date, reason, entry }));
193
+ assertValid(kind, entry, file);
194
+ writeFileSync(absolute, serialize(entry));
195
+ return { file, entry };
196
+ }