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.
- package/LICENSE +202 -0
- package/NOTICE +5 -0
- package/README.md +167 -0
- package/cli/.gitkeep +0 -0
- package/cli/commands/init-copy.js +90 -0
- package/cli/commands/init.js +386 -0
- package/cli/init-copy.js +24 -0
- package/cli/init.js +24 -0
- package/cli/kit.manifest.yaml +256 -0
- package/cli/lib/copy-payload.js +334 -0
- package/cli/lib/generate-wrappers.js +162 -0
- package/package.json +36 -0
- package/payload/adapter-fixtures/README.md +53 -0
- package/payload/adapter-fixtures/html/EXPECTED.yaml +50 -0
- package/payload/adapter-fixtures/html/sample.html +38 -0
- package/payload/adapter-fixtures/md/EXPECTED.yaml +65 -0
- package/payload/adapter-fixtures/md/sample.md +32 -0
- package/payload/adapter-fixtures/pdf/EXPECTED.yaml +45 -0
- package/payload/adapter-fixtures/pdf/sample.pdf +0 -0
- package/payload/adapter-fixtures/txt/EXPECTED.yaml +31 -0
- package/payload/adapter-fixtures/txt/sample.txt +18 -0
- package/payload/docs/README.md +102 -0
- package/payload/docs/boundaries.md +60 -0
- package/payload/docs/ci-wiring.md +109 -0
- package/payload/docs/steward-guide.md +238 -0
- package/payload/engine/audit.js +34 -0
- package/payload/engine/commands/audit.js +289 -0
- package/payload/engine/commands/derive.js +334 -0
- package/payload/engine/commands/ingest.js +124 -0
- package/payload/engine/commands/log-entry.js +85 -0
- package/payload/engine/commands/phoenix.js +206 -0
- package/payload/engine/commands/preflight.js +530 -0
- package/payload/engine/commands/resolve.js +1678 -0
- package/payload/engine/commands/survey-map.js +351 -0
- package/payload/engine/commands/validate-values.js +315 -0
- package/payload/engine/commands/validate.js +1426 -0
- package/payload/engine/derive.js +34 -0
- package/payload/engine/ingest.js +34 -0
- package/payload/engine/lib/anchor-signatures.js +126 -0
- package/payload/engine/lib/boot.js +39 -0
- package/payload/engine/lib/call-numbers.js +133 -0
- package/payload/engine/lib/cli.js +147 -0
- package/payload/engine/lib/coverage.js +849 -0
- package/payload/engine/lib/decomposition.js +225 -0
- package/payload/engine/lib/derived.js +494 -0
- package/payload/engine/lib/engine-refusal.js +40 -0
- package/payload/engine/lib/exit-codes.js +14 -0
- package/payload/engine/lib/extractor-kinds.js +955 -0
- package/payload/engine/lib/format-adapters.js +802 -0
- package/payload/engine/lib/id-grammars.js +178 -0
- package/payload/engine/lib/iso-date.js +55 -0
- package/payload/engine/lib/kit-root.js +101 -0
- package/payload/engine/lib/load-stores.js +1624 -0
- package/payload/engine/lib/log-entry.js +196 -0
- package/payload/engine/lib/phoenix.js +628 -0
- package/payload/engine/lib/scoring.js +150 -0
- package/payload/engine/lib/suppressions.js +172 -0
- package/payload/engine/lib/time-verdicts.js +282 -0
- package/payload/engine/lib/usage-error.js +14 -0
- package/payload/engine/lib/validate-record.js +504 -0
- package/payload/engine/log-entry.js +34 -0
- package/payload/engine/phoenix.js +39 -0
- package/payload/engine/preflight.js +34 -0
- package/payload/engine/resolve.js +34 -0
- package/payload/engine/survey-map.js +34 -0
- package/payload/engine/validate-values.js +34 -0
- package/payload/engine/validate.js +34 -0
- package/payload/extractor-fixtures/.gitkeep +0 -0
- package/payload/extractor-fixtures/README.md +29 -0
- package/payload/extractor-fixtures/swift/strings-keys/EXPECTED.yaml +8 -0
- package/payload/extractor-fixtures/swift/strings-keys/sample.strings +15 -0
- package/payload/extractor-fixtures/swift/swift-const-array/EXPECTED.yaml +7 -0
- package/payload/extractor-fixtures/swift/swift-const-array/sample.swift +21 -0
- package/payload/extractor-fixtures/swift/swift-enum/EXPECTED.yaml +8 -0
- package/payload/extractor-fixtures/swift/swift-enum/sample.swift +30 -0
- package/payload/extractor-fixtures/swift/yaml-keys/EXPECTED.yaml +6 -0
- package/payload/extractor-fixtures/swift/yaml-keys/sample.yaml +23 -0
- package/payload/extractor-fixtures/swift/yaml-map-keys/EXPECTED.yaml +7 -0
- package/payload/extractor-fixtures/swift/yaml-map-keys/sample.yaml +15 -0
- package/payload/extractor-fixtures/ts/dir-modules/EXPECTED.yaml +12 -0
- package/payload/extractor-fixtures/ts/dir-modules/sample-modules/alpha.widget.ts +1 -0
- package/payload/extractor-fixtures/ts/dir-modules/sample-modules/beta.widget.ts +1 -0
- package/payload/extractor-fixtures/ts/dir-modules/sample-modules/gamma.widget.ts +1 -0
- package/payload/extractor-fixtures/ts/dir-modules/sample-modules/helpers/format.ts +3 -0
- package/payload/extractor-fixtures/ts/dir-modules/sample-modules/widgets.test.ts +2 -0
- package/payload/extractor-fixtures/ts/json-keys/EXPECTED.yaml +7 -0
- package/payload/extractor-fixtures/ts/json-keys/sample.json +5 -0
- package/payload/extractor-fixtures/ts/json-map-keys/EXPECTED.yaml +7 -0
- package/payload/extractor-fixtures/ts/json-map-keys/sample.json +13 -0
- package/payload/extractor-fixtures/ts/ts-const-array/EXPECTED.yaml +6 -0
- package/payload/extractor-fixtures/ts/ts-const-array/sample.ts +13 -0
- package/payload/extractor-fixtures/ts/ts-enum/EXPECTED.yaml +7 -0
- package/payload/extractor-fixtures/ts/ts-enum/sample.ts +11 -0
- package/payload/extractor-fixtures/ts/ts-object-keys/EXPECTED.yaml +6 -0
- package/payload/extractor-fixtures/ts/ts-object-keys/sample.tsx +23 -0
- package/payload/extractor-fixtures/ts/ts-union/EXPECTED.yaml +5 -0
- package/payload/extractor-fixtures/ts/ts-union/sample.ts +9 -0
- package/payload/hooks/pre-commit +37 -0
- package/payload/hooks/reverse-lookup +66 -0
- package/payload/package.json +3 -0
- package/payload/protocol/.gitkeep +0 -0
- package/payload/protocol/AGENTS.md +239 -0
- package/payload/protocol/derived-layer.md +174 -0
- package/payload/protocol/new-kind-pipeline.md +179 -0
- package/payload/protocol/registry-warrant.md +162 -0
- package/payload/protocol/skills/kb-build.md +303 -0
- package/payload/protocol/skills/knowledge-audit.md +183 -0
- package/payload/protocol/skills/knowledge-bootstrap.md +229 -0
- package/payload/protocol/skills/knowledge-reflect.md +397 -0
- package/payload/schemas/catalog.schema.json +32 -0
- package/payload/schemas/decision-entry.schema.json +122 -0
- package/payload/schemas/finding.schema.json +77 -0
- package/payload/schemas/gap.schema.json +52 -0
- package/payload/schemas/graduation-categories.schema.json +64 -0
- package/payload/schemas/knowledge-leaf.schema.json +194 -0
- package/payload/schemas/miss.schema.json +45 -0
- package/payload/schemas/ontology-concept.schema.json +115 -0
- package/payload/schemas/phoenix-event.schema.json +76 -0
- package/payload/schemas/registry.schema.json +57 -0
- package/payload/schemas/rules.schema.json +14 -0
- package/payload/schemas/survey-scope.schema.json +23 -0
- package/payload/templates/decisions/_catalog.yaml +7 -0
- package/payload/templates/decisions/_registries/graduation-categories.yaml +42 -0
- package/payload/templates/decisions/phoenix-event.yaml +74 -0
- package/payload/templates/decisions/reflect-mint-proposal.yaml +100 -0
- package/payload/templates/decisions/registry-minting.yaml +58 -0
- package/payload/templates/decisions/trust-graduation.yaml +120 -0
- package/payload/templates/decisions/trust-revocation.yaml +106 -0
- package/payload/templates/knowledge/_catalog.yaml +9 -0
- package/payload/templates/knowledge/_registries/anchor.yaml +42 -0
- package/payload/templates/knowledge/_registries/authority-tiers.yaml +32 -0
- package/payload/templates/knowledge/_registries/domains.yaml +43 -0
- package/payload/templates/knowledge/_registries/form.yaml +38 -0
- package/payload/templates/knowledge/_registries/jurisdictions.yaml +20 -0
- package/payload/templates/knowledge/_registries/operations.yaml +18 -0
- package/payload/templates/knowledge/_registries/stage.yaml +53 -0
- package/payload/templates/knowledge/_rules.yaml +6 -0
- package/payload/templates/new-kind/README.md +107 -0
- package/payload/templates/new-kind/descriptor.example.yaml +18 -0
- package/payload/templates/new-kind/fixture/EXPECTED.yaml +6 -0
- package/payload/templates/new-kind/fixture/demo-anchor.list +2 -0
- package/payload/templates/new-kind/fixture/sample.list +7 -0
- package/payload/templates/new-kind/parser.example.js +98 -0
- package/payload/templates/ontology/_catalog.yaml +6 -0
- package/payload/templates/ontology/_rules.yaml +6 -0
- package/payload/wrappers/cursor.mdc +15 -0
- 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
|
+
}
|