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,162 @@
1
+ /**
2
+ * Platform wrapper generation (KK-18, PRD §6/§9.2) — the second half of the
3
+ * init copy layer: thin pointer files at each selected agent platform's
4
+ * conventional path, aiming the platform at <root>/protocol/AGENTS.md. Like
5
+ * copy-payload.js this is the ENGINE only; the multi-select prompt is
6
+ * KK-19's, callers pass the chosen platform ids.
7
+ *
8
+ * The registry is DATA, not code: the `platforms:` section of
9
+ * cli/kit.manifest.yaml (id → { name, template, target, mode }). Adding a
10
+ * platform later = adding a manifest entry (+ a template under
11
+ * payload/wrappers/ if the generic pointer doesn't fit). Templates live in
12
+ * payload/ under the same D-007 constructional guards as copy sources;
13
+ * targets are CLIENT-REPO-ROOT-relative conventional paths (dotted dirs
14
+ * allowed — the §6 never-dotted rule governs the seeded root only).
15
+ * Wrappers duplicate nothing: protocol/AGENTS.md stays the single source of
16
+ * truth; a wrapper is a few pointer lines rendered from its template with
17
+ * `{{root}}` replaced by the seeded root name.
18
+ *
19
+ * Collision policy (§6), per the registry's `mode`:
20
+ * shared — ecosystem-shared instruction files (root AGENTS.md,
21
+ * .github/copilot-instructions.md). Fresh file → created
22
+ * holding just the sentinel block. Existing file without
23
+ * sentinels → APPENDED between sentinel markers, existing
24
+ * content byte-preserved. Existing well-formed sentinel block
25
+ * → content REPLACED WITHIN the sentinels (the idempotent
26
+ * choice: re-generation converges instead of refusing, and a
27
+ * root rename updates the pointer in place). Malformed
28
+ * sentinels (unbalanced/reversed/multiple) → skip-and-report,
29
+ * never guess.
30
+ * dedicated — single-tool files (CLAUDE.md, GEMINI.md, .cursor rule).
31
+ * Created if absent; an existing file is the user's own —
32
+ * skip-and-report, never clobbered.
33
+ * Symlinked targets are skipped, never written through (D-007 posture).
34
+ *
35
+ * Deterministic by construction: pure render of template x root name,
36
+ * platforms processed sorted, no wall-clock reads. Skips are REPORTED
37
+ * results, not errors — a partial wrapper set never fails the seed.
38
+ */
39
+ import { lstatSync, mkdirSync, readFileSync, writeFileSync, existsSync } from 'node:fs';
40
+ import { dirname, join, resolve } from 'node:path';
41
+ import { DEFAULT_ROOT, loadManifest, validateRootName } from './copy-payload.js';
42
+
43
+ export const SENTINEL_BEGIN = '<!-- unknown-knowledge:begin -->';
44
+ export const SENTINEL_END = '<!-- unknown-knowledge:end -->';
45
+
46
+ /** Refuse unknown platform ids against the manifest registry (pre-seed check). */
47
+ export function assertKnownPlatforms(manifest, ids) {
48
+ const known = Object.keys(manifest.platforms).sort();
49
+ for (const id of ids) {
50
+ if (!known.includes(id)) {
51
+ throw new Error(`unknown platform ${JSON.stringify(id)} — manifest defines: ${known.join(', ') || '(none)'}`);
52
+ }
53
+ }
54
+ }
55
+
56
+ /** Pure template render: {{root}} → seeded root name; leftovers are a template bug. */
57
+ function renderTemplate(text, rootName, templateRel) {
58
+ const out = text.replaceAll('{{root}}', rootName);
59
+ const leftover = out.match(/\{\{[a-z0-9-]+\}\}/);
60
+ if (leftover) throw new Error(`wrapper template ${templateRel}: unresolved placeholder ${leftover[0]}`);
61
+ return out;
62
+ }
63
+
64
+ function sentinelBlock(body) {
65
+ return `${SENTINEL_BEGIN}\n${body.trimEnd()}\n${SENTINEL_END}\n`;
66
+ }
67
+
68
+ /** lstat that treats absence as null (a dangling symlink still counts as present). */
69
+ function lstatOrNull(path) {
70
+ try {
71
+ return lstatSync(path);
72
+ } catch {
73
+ return null;
74
+ }
75
+ }
76
+
77
+ const countOf = (text, needle) => text.split(needle).length - 1;
78
+
79
+ /** §6 shared-file policy: sentinel-append / replace-within-sentinels, never clobber. */
80
+ function writeShared(targetAbs, body) {
81
+ const block = sentinelBlock(body);
82
+ const stat = lstatOrNull(targetAbs);
83
+ if (stat === null) {
84
+ mkdirSync(dirname(targetAbs), { recursive: true });
85
+ writeFileSync(targetAbs, block);
86
+ return { action: 'created' };
87
+ }
88
+ if (stat.isSymbolicLink()) {
89
+ return { action: 'skipped', reason: 'target is a symlink — wrappers are never written through links' };
90
+ }
91
+ const text = readFileSync(targetAbs, 'utf8');
92
+ const begins = countOf(text, SENTINEL_BEGIN);
93
+ const ends = countOf(text, SENTINEL_END);
94
+ if (begins === 0 && ends === 0) {
95
+ const sep = text.length === 0 ? '' : text.endsWith('\n') ? '\n' : '\n\n';
96
+ writeFileSync(targetAbs, `${text}${sep}${block}`);
97
+ return { action: 'appended' };
98
+ }
99
+ if (begins === 1 && ends === 1 && text.indexOf(SENTINEL_BEGIN) < text.indexOf(SENTINEL_END)) {
100
+ const start = text.indexOf(SENTINEL_BEGIN);
101
+ let after = text.indexOf(SENTINEL_END) + SENTINEL_END.length;
102
+ if (text[after] === '\n') after += 1; // the block carries its own trailing newline
103
+ writeFileSync(targetAbs, text.slice(0, start) + block + text.slice(after));
104
+ return { action: 'replaced' };
105
+ }
106
+ return {
107
+ action: 'skipped',
108
+ reason: `existing sentinel markers are malformed (expected exactly one ${SENTINEL_BEGIN} before one ${SENTINEL_END}) — resolve the file by hand, then re-add the pointer`,
109
+ };
110
+ }
111
+
112
+ /** §6 dedicated-file policy: create if absent; an existing file is never touched. */
113
+ function writeDedicated(targetAbs, body, rootName) {
114
+ if (lstatOrNull(targetAbs) !== null) {
115
+ return {
116
+ action: 'skipped',
117
+ reason: `already exists — dedicated wrapper targets are never overwritten (§6); add the pointer yourself: read ${rootName}/protocol/AGENTS.md before working`,
118
+ };
119
+ }
120
+ mkdirSync(dirname(targetAbs), { recursive: true });
121
+ writeFileSync(targetAbs, body);
122
+ return { action: 'created' };
123
+ }
124
+
125
+ /**
126
+ * Generate wrappers for the selected platforms into targetDir (the client
127
+ * repo root — wrapper targets are root-relative conventional paths, NOT
128
+ * inside the seeded kit dir).
129
+ *
130
+ * @param {object} options
131
+ * @param {string} options.kitRoot kit repo root (holds payload/ + cli/)
132
+ * @param {string} options.targetDir client repo root receiving the wrappers
133
+ * @param {string} [options.rootName] seeded kit dir name the pointers cite
134
+ * @param {string[]} [options.platforms] selected registry ids
135
+ * @param {string} [options.manifestPath] override, for tests
136
+ * @returns {Array<{ platform: string, name: string, target: string, mode: string,
137
+ * action: 'created'|'appended'|'replaced'|'skipped', reason?: string }>}
138
+ */
139
+ export function generateWrappers({ kitRoot, targetDir, rootName = DEFAULT_ROOT, platforms = [], manifestPath }) {
140
+ validateRootName(rootName);
141
+ const manifest = loadManifest(kitRoot, manifestPath);
142
+ assertKnownPlatforms(manifest, platforms);
143
+ if (!existsSync(resolve(targetDir))) {
144
+ throw new Error(`target dir ${resolve(targetDir)} does not exist`);
145
+ }
146
+
147
+ const results = [];
148
+ for (const id of [...new Set(platforms)].sort()) {
149
+ const spec = manifest.platforms[id];
150
+ const templateAbs = resolve(manifest.payloadRoot, spec.template);
151
+ if (!existsSync(templateAbs)) {
152
+ throw new Error(`platforms.${id}: template ${spec.template} missing from payload/ — the registry and the payload tree have drifted`);
153
+ }
154
+ const body = renderTemplate(readFileSync(templateAbs, 'utf8'), rootName, spec.template);
155
+ const targetAbs = join(resolve(targetDir), spec.target);
156
+ const outcome = spec.mode === 'shared'
157
+ ? writeShared(targetAbs, body)
158
+ : writeDedicated(targetAbs, body, rootName);
159
+ results.push({ platform: id, name: spec.name, target: spec.target, mode: spec.mode, ...outcome });
160
+ }
161
+ return results;
162
+ }
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "unknown-knowledge",
3
+ "version": "2.1.0",
4
+ "description": "Free OSS kit that stands up self-improving knowledge-base + ontology structures in any codebase: three governed YAML stores, a deterministic engine, and an agent protocol.",
5
+ "license": "Apache-2.0",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/doterodesign/unknown-knowledge.git"
9
+ },
10
+ "bugs": {
11
+ "url": "https://github.com/doterodesign/unknown-knowledge/issues"
12
+ },
13
+ "homepage": "https://github.com/doterodesign/unknown-knowledge#readme",
14
+ "type": "module",
15
+ "bin": {
16
+ "unknown-knowledge": "cli/init.js"
17
+ },
18
+ "files": [
19
+ "cli/",
20
+ "payload/",
21
+ "LICENSE",
22
+ "NOTICE",
23
+ "README.md"
24
+ ],
25
+ "engines": {
26
+ "node": ">=22.0.0"
27
+ },
28
+ "scripts": {
29
+ "test": "node --test \"tests/**/*.test.js\"",
30
+ "lint": "node scripts/lint.js",
31
+ "acceptance": "node acceptance/run.js"
32
+ },
33
+ "dependencies": {
34
+ "js-yaml": "^5.4.1"
35
+ }
36
+ }
@@ -0,0 +1,53 @@
1
+ # adapter-fixtures — the sample/expected pairs for format adapters
2
+
3
+ One directory per format, each carrying a `sample.<ext>` document and the
4
+ `EXPECTED.yaml` intermediate representation the adapter must produce from it.
5
+ The shape is deliberately IDENTICAL to the extractor-kind pairs — sample plus
6
+ expectation, pinned by kit CI — because these fixtures do the same double
7
+ duty: they are the runnable gate, and they are the authoring template a client
8
+ imitates when drafting an adapter for a format the kit does not ship (D-005:
9
+ only vendored, versioned, test-covered code ever runs).
10
+
11
+ Every sample is adversarial-but-adaptable on purpose: reflowed paragraphs,
12
+ code whose indentation is content, entities, nested block elements, PDF
13
+ kerning arrays and escapes. Gnarly input that stays INSIDE the envelope.
14
+ Out-of-envelope shapes (a `.docx` submission, an encrypted PDF, a scanned page
15
+ with no text) are NOT here: those hard-error by design, so there is no IR to
16
+ expect from them.
17
+
18
+ `EXPECTED.yaml` fields: `format` + `file` + `adapter` (the versioned name, e.g.
19
+ `md@1`) + `hash` (FNV-1a over the sample's bytes) + `blocks`. Each block is
20
+ `kind` + `text` + `locator`, plus `level` on headings.
21
+
22
+ **Order is contract.** Extractor pairs compare value SETS (§3.5, order is
23
+ presentation); adapter pairs compare an ORDERED SEQUENCE, because the IR
24
+ preserves document order and everything downstream reads it that way.
25
+
26
+ **Locators** are per-format, and each is deterministic:
27
+
28
+ | format | locator | meaning |
29
+ |--------|---------|---------|
30
+ | md, txt, html | `{ line, endLine }` | 1-based inclusive lines in the sample |
31
+ | pdf | `{ page, object }` | 1-based page ordinal, then text-operation ordinal within that page's content stream |
32
+
33
+ A PDF has no lines to point at, so it gets the coordinates it actually has.
34
+
35
+ ## Authoring a new adapter
36
+
37
+ 1. Add a versioned entry to `ADAPTERS` in `engine/lib/format-adapters.js`: a
38
+ pure `adapt(source) -> Block[]`, lexical only — no client-code execution,
39
+ no subprocess, no network, no eval (D-014).
40
+ 2. Declare its envelope in the module doc comment, and make everything outside
41
+ that envelope a HARD ERROR that names what was encountered. Never a partial
42
+ parse: a document reported as covered when half of it was never read is the
43
+ false-all-clear failure class (D-005/D-012), and it is worse than no
44
+ coverage at all.
45
+ 3. Ship a pair here, and pin it in the kit's tests the way the shipped four
46
+ are pinned.
47
+
48
+ **Versioning.** Bump an adapter's version when the same input would produce
49
+ different IR bytes (a new block kind, a changed locator scheme, different
50
+ splitting or ordering) — maps built under different versions are not
51
+ comparable, and the version is what says so. Do not bump for a widened
52
+ envelope that leaves previously-accepted input byte-identical. A bump updates
53
+ the fixture pair in the same commit.
@@ -0,0 +1,50 @@
1
+ # Expected IR for sample.html — the html@1 adapter (UCS-1153).
2
+ #
3
+ # Locators are 1-based inclusive line numbers, derived from the byte offsets of
4
+ # each element's open and close tags.
5
+ #
6
+ # What this pair pins: <script> and <style> bodies NEVER reach the IR (they are
7
+ # code and presentation, and feeding CSS to a lexicon would manufacture matches
8
+ # from selectors) — note that the sample's style block contains the word
9
+ # "settled" and its script contains it too, yet neither appears below; inline
10
+ # markup inside a block is stripped to text; the five XML entities decode
11
+ # (&amp; -> &, &lt;not&gt; -> <not>); a <p> nested inside an <li> closes the
12
+ # list item, so the item is emitted once, not twice; <pre> keeps its newlines;
13
+ # and table cells are joined with the same " | " separator the md adapter uses,
14
+ # so a table row means the same thing whatever format it arrived in.
15
+ #
16
+ # D-014: this adapter never executes anything. No DOM is built, no script runs,
17
+ # no resource is fetched. The document is bytes.
18
+ format: html
19
+ file: sample.html
20
+ adapter: html@1
21
+ hash: fnv1a64:062e49c39e1bf5c1
22
+ blocks:
23
+ - kind: heading
24
+ level: 1
25
+ text: Font licensing onboarding
26
+ locator: {line: 11, endLine: 11}
27
+ - kind: paragraph
28
+ text: The foundry must expose a CDN endpoint and a self-hosted fallback. Render budget is 400ms & measured end-to-end.
29
+ locator: {line: 13, endLine: 14}
30
+ - kind: list-item
31
+ text: CDN endpoint, TLS only
32
+ locator: {line: 22, endLine: 22}
33
+ - kind: paragraph
34
+ text: Self-hosted fallback for offline
35
+ locator: {line: 23, endLine: 23}
36
+ - kind: code
37
+ text: |-
38
+ foundry:
39
+ name: typeco
40
+ timeout-ms: 400
41
+ locator: {line: 26, endLine: 28}
42
+ - kind: table-row
43
+ text: Field | Meaning
44
+ locator: {line: 31, endLine: 31}
45
+ - kind: table-row
46
+ text: pending | license requested, <not> granted
47
+ locator: {line: 32, endLine: 32}
48
+ - kind: paragraph
49
+ text: Page the design-ops rotation when the CDN degrades for more than ninety seconds.
50
+ locator: {line: 35, endLine: 36}
@@ -0,0 +1,38 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <title>Font licensing onboarding</title>
5
+ <style>h1 { color: rebeccapurple; content: "granted"; }</style>
6
+ </head>
7
+ <body>
8
+ <!-- Adapter fixture — html. Adversarial-but-adaptable: script and style
9
+ bodies that must never reach the IR, inline markup inside blocks,
10
+ XML entities, a nested block element, and a table. -->
11
+ <h1>Font licensing onboarding</h1>
12
+
13
+ <p>The foundry must expose a <strong>CDN</strong> endpoint and a
14
+ self-hosted fallback. Render budget is 400ms &amp; measured end-to-end.</p>
15
+
16
+ <script>
17
+ // Executing this would be a D-014 violation. It is discarded as text.
18
+ document.title = "granted";
19
+ </script>
20
+
21
+ <ul>
22
+ <li>CDN endpoint, TLS only</li>
23
+ <li><p>Self-hosted fallback for offline</p></li>
24
+ </ul>
25
+
26
+ <pre>foundry:
27
+ name: typeco
28
+ timeout-ms: 400</pre>
29
+
30
+ <table>
31
+ <tr><th>Field</th><th>Meaning</th></tr>
32
+ <tr><td>pending</td><td>license requested, &lt;not&gt; granted</td></tr>
33
+ </table>
34
+
35
+ <blockquote>Page the design-ops rotation when the CDN degrades for more
36
+ than ninety seconds.</blockquote>
37
+ </body>
38
+ </html>
@@ -0,0 +1,65 @@
1
+ # Expected IR for sample.md — the md@1 adapter (UCS-1153).
2
+ #
3
+ # Ordered blocks with kinds and source locators. Order is CONTRACT here (unlike
4
+ # the extractor pairs, whose values are sets): the IR preserves document order,
5
+ # and a coverage map reads it that way. Locators are 1-based inclusive line
6
+ # numbers into the ORIGINAL sample, so a reader can open the file at the line.
7
+ #
8
+ # What this pair pins beyond the happy path: a paragraph reflowed from three
9
+ # source lines onto one block (its locator keeps the full span), fenced code
10
+ # whose newlines and indentation SURVIVE (whitespace is semantic in code, so it
11
+ # is never flattened), all three list markers, the table delimiter row dropped
12
+ # as presentation, and the trailing hashes of a closed ATX heading stripped.
13
+ format: md
14
+ file: sample.md
15
+ adapter: md@1
16
+ hash: fnv1a64:261b6b2f06931422
17
+ blocks:
18
+ - kind: heading
19
+ level: 1
20
+ text: Font licensing onboarding
21
+ locator: {line: 1, endLine: 1}
22
+ - kind: paragraph
23
+ text: 'Adapter fixture — markdown. Adversarial-but-adaptable on purpose: a paragraph wrapped across several source lines, a fenced code block whose indentation is content, both list markers, a table, and a heading with trailing hashes.'
24
+ locator: {line: 3, endLine: 5}
25
+ - kind: heading
26
+ level: 2
27
+ text: Foundry requirements
28
+ locator: {line: 7, endLine: 7}
29
+ - kind: paragraph
30
+ text: The foundry must expose a CDN endpoint and a self-hosted fallback. Render budget is four hundred milliseconds end-to-end, measured from request to paint.
31
+ locator: {line: 9, endLine: 11}
32
+ - kind: list-item
33
+ text: CDN endpoint, TLS only
34
+ locator: {line: 13, endLine: 13}
35
+ - kind: list-item
36
+ text: Self-hosted fallback for offline
37
+ locator: {line: 14, endLine: 14}
38
+ - kind: list-item
39
+ text: Trial license before production
40
+ locator: {line: 15, endLine: 15}
41
+ - kind: paragraph
42
+ text: 'Configuration lives in one block:'
43
+ locator: {line: 17, endLine: 17}
44
+ - kind: code
45
+ text: |-
46
+ foundry:
47
+ name: typeco
48
+ timeout-ms: 400
49
+ locator: {line: 19, endLine: 23}
50
+ - kind: table-row
51
+ text: Field | Meaning
52
+ locator: {line: 25, endLine: 25}
53
+ - kind: table-row
54
+ text: '`pending` | license requested, not granted'
55
+ locator: {line: 27, endLine: 27}
56
+ - kind: table-row
57
+ text: '`granted` | license final'
58
+ locator: {line: 28, endLine: 28}
59
+ - kind: heading
60
+ level: 3
61
+ text: Escalation
62
+ locator: {line: 30, endLine: 30}
63
+ - kind: paragraph
64
+ text: Page the design-ops rotation when the CDN degrades for more than ninety seconds.
65
+ locator: {line: 32, endLine: 32}
@@ -0,0 +1,32 @@
1
+ # Font licensing onboarding
2
+
3
+ Adapter fixture — markdown. Adversarial-but-adaptable on purpose: a paragraph
4
+ wrapped across several source lines, a fenced code block whose indentation is
5
+ content, both list markers, a table, and a heading with trailing hashes.
6
+
7
+ ## Foundry requirements ##
8
+
9
+ The foundry must expose a CDN endpoint and a self-hosted fallback. Render
10
+ budget is four hundred milliseconds end-to-end, measured from request to
11
+ paint.
12
+
13
+ - CDN endpoint, TLS only
14
+ * Self-hosted fallback for offline
15
+ 1. Trial license before production
16
+
17
+ Configuration lives in one block:
18
+
19
+ ```yaml
20
+ foundry:
21
+ name: typeco
22
+ timeout-ms: 400
23
+ ```
24
+
25
+ | Field | Meaning |
26
+ |-------|---------|
27
+ | `pending` | license requested, not granted |
28
+ | `granted` | license final |
29
+
30
+ ### Escalation
31
+
32
+ Page the design-ops rotation when the CDN degrades for more than ninety seconds.
@@ -0,0 +1,45 @@
1
+ # Expected IR for sample.pdf — the pdf@1 adapter (UCS-1153).
2
+ #
3
+ # LOCATOR SCHEME differs from the text formats, because a PDF is not line-
4
+ # addressable: { page, object } is the 1-based page ordinal in document order
5
+ # plus the 1-based ordinal of the text-showing operation within that page's
6
+ # content stream. Both derive from content-stream order, never from object
7
+ # numbering or enumeration order, so they are stable for identical bytes.
8
+ #
9
+ # The sample is hand-crafted by scripts/make-pdf-fixture.js so this expectation
10
+ # is genuinely reviewable rather than checked against an opaque binary. It
11
+ # exercises exactly the pdf@1 envelope: page 1 is an UNCOMPRESSED stream using
12
+ # Tj and a TJ kerning array (whose fragments join into one block), with an
13
+ # escaped paren \( and an octal \050 both decoding to literal parens; page 2 is
14
+ # a FLATEDECODE stream (inflated with node:zlib — stdlib decompression, never
15
+ # execution) using the ' (next-line-and-show) operator and a hex <...> string.
16
+ #
17
+ # Every kind here is a paragraph: PDF has no paragraph or heading concept, and
18
+ # clustering text by coordinates to invent one would be a guess. One text-
19
+ # showing operation, one block, one locator.
20
+ format: pdf
21
+ file: sample.pdf
22
+ adapter: pdf@1
23
+ hash: fnv1a64:0a290a783b026774
24
+ blocks:
25
+ - kind: paragraph
26
+ text: Font licensing onboarding
27
+ locator: {page: 1, object: 1}
28
+ - kind: paragraph
29
+ text: The foundry must expose a CDN endpoint and a self-hosted fallback.
30
+ locator: {page: 1, object: 2}
31
+ - kind: paragraph
32
+ text: Render budget is 400ms (end-to-end), measured from request to paint.
33
+ locator: {page: 1, object: 3}
34
+ - kind: paragraph
35
+ text: Pending (not granted) is a distinct state.
36
+ locator: {page: 1, object: 4}
37
+ - kind: paragraph
38
+ text: Escalation
39
+ locator: {page: 2, object: 1}
40
+ - kind: paragraph
41
+ text: Page the design-ops rotation when the CDN degrades for more than ninety seconds.
42
+ locator: {page: 2, object: 2}
43
+ - kind: paragraph
44
+ text: Foundry credentials are sandboxed.
45
+ locator: {page: 2, object: 3}
@@ -0,0 +1,31 @@
1
+ # Expected IR for sample.txt — the txt@1 adapter (UCS-1153).
2
+ #
3
+ # Every block is a paragraph. That is the whole point of this pair: plain text
4
+ # carries no marked-up structure, and the adapter INVENTS NONE. The sample
5
+ # deliberately tempts a heuristic parser twice — a four-space-indented run that
6
+ # a markdown adapter would call code, and an ALL-CAPS line that looks like a
7
+ # heading — and both stay paragraphs. Guessed structure would put sections in a
8
+ # coverage map that the document never had.
9
+ format: txt
10
+ file: sample.txt
11
+ adapter: txt@1
12
+ hash: fnv1a64:4c921cb8fa2f2059
13
+ blocks:
14
+ - kind: paragraph
15
+ text: Font licensing onboarding
16
+ locator: {line: 1, endLine: 1}
17
+ - kind: paragraph
18
+ text: 'Adapter fixture — plain text. Plain text carries no marked-up structure, so every block is a paragraph: the adapter invents no headings and no code, and this sample is shaped to tempt it into doing both.'
19
+ locator: {line: 3, endLine: 5}
20
+ - kind: paragraph
21
+ text: This line is indented four spaces. A markdown adapter would call it code. The txt adapter calls it prose, because plain text has no code fences and guessing would manufacture structure the document never had.
22
+ locator: {line: 7, endLine: 9}
23
+ - kind: paragraph
24
+ text: FOUNDRY REQUIREMENTS
25
+ locator: {line: 11, endLine: 11}
26
+ - kind: paragraph
27
+ text: The line above is shouting, and a heuristic adapter would promote it to a heading. It stays a paragraph. Degraded structure, identical process — the coverage map is bounded by content richness, and this is the honest floor.
28
+ locator: {line: 13, endLine: 15}
29
+ - kind: paragraph
30
+ text: Render budget is four hundred milliseconds end-to-end, measured from request to paint.
31
+ locator: {line: 17, endLine: 18}
@@ -0,0 +1,18 @@
1
+ Font licensing onboarding
2
+
3
+ Adapter fixture — plain text. Plain text carries no marked-up structure, so
4
+ every block is a paragraph: the adapter invents no headings and no code, and
5
+ this sample is shaped to tempt it into doing both.
6
+
7
+ This line is indented four spaces. A markdown adapter would call it code.
8
+ The txt adapter calls it prose, because plain text has no code fences and
9
+ guessing would manufacture structure the document never had.
10
+
11
+ FOUNDRY REQUIREMENTS
12
+
13
+ The line above is shouting, and a heuristic adapter would promote it to a
14
+ heading. It stays a paragraph. Degraded structure, identical process — the
15
+ coverage map is bounded by content richness, and this is the honest floor.
16
+
17
+ Render budget is four hundred milliseconds end-to-end, measured from request
18
+ to paint.
@@ -0,0 +1,102 @@
1
+ # unknown-knowledge — this repo's knowledge base
2
+
3
+ This directory was seeded once by the [unknown-knowledge](https://github.com/doterodesign/unknown-knowledge)
4
+ kit and is now yours (D-001): three YAML stores that map the system
5
+ (`ontology/`, `knowledge/`, `decisions/`), a deterministic engine that checks
6
+ the map (`engine/`), and the agent protocol that runs the loop (`protocol/`).
7
+ There is no service, no runtime, and no update channel — everything is files
8
+ in this repo, branched and merged by your normal PRs.
9
+
10
+ One idea governs everything here: **the map is never the fact.** Stores hold
11
+ claims and pointers; your source files hold facts. The engine's job is to
12
+ diff the two and say exactly where they disagree.
13
+
14
+ > Commands run from the **repo root** with this directory at its default
15
+ > name `unknown-knowledge/`; substitute your chosen name if it differs.
16
+
17
+ ## What you own vs. what was vendored
18
+
19
+ Everything is client-owned after seeding; the zone map in
20
+ `kit.manifest.yaml` records what an uninstall or audit needs to know — which
21
+ paths arrived from the kit and which your own loop produced.
22
+
23
+ | Zone | Paths | Meaning |
24
+ |---|---|---|
25
+ | seeded | `engine/`, `protocol/`, `schemas/`, `templates/`, `kit.manifest.yaml` | vendored at init: engine code, schemas, protocol markdown. Protocol conduct policy is explicitly yours to edit (see `docs/boundaries.md`) |
26
+ | client | `ontology/`, `knowledge/`, `decisions/`, `logs/`, `survey-scope.yaml` | your team's data. The kit shipped only empty scaffolding here; everything else was written by your loop |
27
+
28
+ ## Running the gates
29
+
30
+ The engine is plain Node (≥ 22, no build step) with one library
31
+ dependency, `js-yaml`, resolved from your repo like any other package: if
32
+ your repo does not already carry it, run `npm install --save-dev js-yaml`
33
+ once.
34
+
35
+ The blocking-grade checks all take `--root` as the **repo root** and share
36
+ one exit-code contract: 0 = clean, 1 = findings/quarantines, 2 = the check
37
+ never ran. **A check that never ran is a blocking defect, never a silent
38
+ pass** — treat exit 2 as a stop, not a shrug.
39
+
40
+ ```
41
+ node unknown-knowledge/engine/validate.js --root . # structure: ids, refs, pointers
42
+ node unknown-knowledge/engine/validate-values.js --root . # enumerated values vs. source
43
+ node unknown-knowledge/engine/preflight.js --root . # store health + per-concept verdicts
44
+ ```
45
+
46
+ `engine/audit.js` is different in kind — advisory (never blocking). It
47
+ proposes draft concepts for anchors the map does not cover yet; a human
48
+ reviews every draft. Run it on the steward cadence, never as a gate
49
+ (`docs/steward-guide.md`).
50
+
51
+ ## How the loop works
52
+
53
+ The runtime contract — `RESOLVE → PREFLIGHT → GATHER → ACT → RECORD` — lives
54
+ in [`protocol/AGENTS.md`](protocol/AGENTS.md). That file is the single
55
+ source of truth for how agents navigate the stores, what a verdict obliges,
56
+ and when findings get appended; start every integration question there. The
57
+ procedures live in the skills under `protocol/skills/`, referenced by name
58
+ (D-019): `/knowledge-bootstrap` (first population), `/knowledge-reflect`
59
+ (consolidating findings into reviewed fixes), `/kb-build` (cited knowledge
60
+ writes), `/knowledge-audit` (the loop's heartbeat report).
61
+
62
+ ## Extractor fixtures and later stacks (D-009)
63
+
64
+ Extractor fixtures for the stacks selected at init are included under
65
+ `engine/tests`. If you adopt another stack later, you author your own pack
66
+ from the included template (`templates/new-kind/`, plus the fixture
67
+ authoring README seeded beside the packs) — there is no update channel. The
68
+ governed path for teaching the engine a new anchor shape is
69
+ `protocol/new-kind-pipeline.md`.
70
+
71
+ ## CI
72
+
73
+ Session-level preflight is a sufficient gate for a small team, not for
74
+ hundreds of engineers. At team scale, wire the validators into CI — copyable
75
+ templates and the PR drift-attribution recipe are in
76
+ [`docs/ci-wiring.md`](docs/ci-wiring.md). Init never wires CI for you
77
+ (D-006).
78
+
79
+ ## Version stamp and license
80
+
81
+ `kit-version` in `kit.manifest.yaml` records the kit version that seeded
82
+ this directory — a birth certificate, not a dependency pin (D-021): it says
83
+ which schema revision, extractor-kind set, and fixture vintage the seed was
84
+ born with, and implies no update channel. The vendored code is Apache-2.0
85
+ (D-020); the seeded `LICENSE` and `NOTICE` carry the terms and the
86
+ attribution. Your stores and logs are your own content, not the kit's.
87
+
88
+ ## Uninstalling
89
+
90
+ Delete this directory and remove the thin per-platform wrapper files init
91
+ generated at their conventional paths (e.g. under `.claude/`) — that is the
92
+ whole uninstall; nothing else in your repo belongs to the kit. Before you
93
+ delete, mind the zone map above: `ontology/`, `knowledge/`, `decisions/`,
94
+ `logs/`, and `survey-scope.yaml` are your team's data, and `logs/` in
95
+ particular is the recorded history of where your map and your code
96
+ disagreed — export what you want to keep first.
97
+
98
+ ## Further reading
99
+
100
+ - [`docs/ci-wiring.md`](docs/ci-wiring.md) — CI templates + PR drift attribution (D-012)
101
+ - [`docs/steward-guide.md`](docs/steward-guide.md) — the steward role, cadence, CODEOWNERS, hygiene recipes
102
+ - [`docs/boundaries.md`](docs/boundaries.md) — what the kit guarantees, and what it will never catch