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,225 @@
1
+ /**
2
+ * Query decomposition (UCS-1152) — turning an ask into joins against the
3
+ * governed vocabularies, and saying what did not join.
4
+ *
5
+ * The deterministic core of the resolution pipeline. A query is decomposed
6
+ * along three axes, each joining a DIFFERENT governed vocabulary, and none of
7
+ * them guessing:
8
+ *
9
+ * verb -> the `knowledge/operations` registry ("add a tool" -> add-tool)
10
+ * noun -> concept terms and aliases ("tool" -> K-101)
11
+ * place -> the `knowledge/jurisdictions` registry ("eu eaa" -> eu-eaa)
12
+ *
13
+ * What makes this the deterministic core rather than a search box is that every
14
+ * axis lands in a MINTED vocabulary value. The operations registry says which
15
+ * verbs exist; the ontology says which nouns do; the jurisdictions registry
16
+ * says which places do. A token that matches none of them is not silently
17
+ * dropped and not fuzzily coerced into the nearest thing — it is RESIDUE, named
18
+ * as such, with the context that did resolve attached, so the gap is a finding
19
+ * somebody can close rather than a bad answer nobody can trace.
20
+ *
21
+ * Three outputs no search box produces, and each exists because its absence is
22
+ * a specific failure the pipeline had:
23
+ *
24
+ * exclusions a leaf whose `applies.jurisdictions` excludes the query's place
25
+ * is EXCLUDED WITH ITS REASON, never silently absent. Silence
26
+ * here is indistinguishable from "no such knowledge exists", and
27
+ * the two demand opposite conduct from the reader.
28
+ * near-miss a vocabulary entry that shares tokens with the query but did
29
+ * not clear the match threshold. The answer that was nearly
30
+ * right is exactly what a reader needs to see when the right one
31
+ * is missing — and hiding it is how a store's vocabulary drifts
32
+ * from its users' without anyone noticing.
33
+ * residue the unconsumed non-stopword tokens. The store's own record of
34
+ * what it does not yet know.
35
+ *
36
+ * MATCH SEMANTICS. A vocabulary entry matches when EVERY word of its phrase is
37
+ * present in the query, each consuming a distinct token. Distinctness matters:
38
+ * "tool tool" must not satisfy a two-word phrase from one token. Words match
39
+ * with a naive singular/plural fold (`tool` ~ `tools`) and nothing else — no
40
+ * stemmer, no edit distance, no synonym expansion. That is a deliberate floor,
41
+ * not an unfinished feature: aliases are the governed, warranted mechanism for
42
+ * "these words mean the same thing", and a stemmer that quietly joined `tooling`
43
+ * to `tool` would be an ungoverned vocabulary decision made by a regex.
44
+ *
45
+ * NEAR-MISS is the same test relaxed from every-word to any-word, over entries
46
+ * that did NOT match. It reports the overlap, so a reader sees which word
47
+ * carried it.
48
+ */
49
+
50
+ /**
51
+ * The pinned stopword list.
52
+ *
53
+ * PROVENANCE: the list is fixed rather than re-derived per store, because the
54
+ * residue output built on it is what the goldens mirror, and a list that
55
+ * drifted would make the fixtures disagree with the behavior they pin.
56
+ *
57
+ * PINNED AND NOT CONFIGURABLE, for the same reason the volatility thresholds
58
+ * are (UCS-1150): residue is a governance signal — it is what gets logged as a
59
+ * retrieval-miss and eventually minted by literary warrant — and a per-store
60
+ * stopword list would make "unresolved" mean something different in every repo.
61
+ * A token's absence from residue must mean "this word carries no query intent
62
+ * anywhere", not "somebody's config happened to hide it here".
63
+ *
64
+ * MINIMAL on purpose. It holds articles, prepositions, auxiliaries, first-person
65
+ * pronouns, and the handful of process words that appear in asks without
66
+ * narrowing them ("process", "launch"). It deliberately does NOT hold domain
67
+ * words: a store that stopworded its own vocabulary would report clean
68
+ * resolution for asks it never understood, which is precisely the silent miss
69
+ * this ticket exists to make impossible. Over-inclusion is the dangerous
70
+ * direction, so the list errs short — a false residue token is a finding a
71
+ * steward reads and dismisses, while a wrongly stopworded one is a gap nobody
72
+ * ever sees.
73
+ *
74
+ * @type {ReadonlySet<string>}
75
+ */
76
+ export const STOPWORDS = Object.freeze(new Set([
77
+ 'a', 'an', 'and', 'at', 'do', 'for', 'how', 'i', 'in', 'is', 'launch', 'my',
78
+ 'of', 'on', 'our', 'process', 'should', 'the', 'this', 'to', 'we', 'with',
79
+ ]));
80
+
81
+ /**
82
+ * Split a query into tokens.
83
+ *
84
+ * The character class keeps `/`, `.`, `_` and `-` INSIDE a token, so
85
+ * `src/registry/export-formats.ts` survives as one token rather than
86
+ * shattering into five. That is what lets the resolver notice a path-shaped
87
+ * ask; splitting it first would destroy the only evidence that it was one.
88
+ *
89
+ * @param {string} query the raw query text
90
+ * @returns {string[]} lowercased tokens, in query order, duplicates kept
91
+ */
92
+ export const tokenize = (query) => (String(query).toLowerCase().match(/[a-z0-9./_-]+/g) ?? []);
93
+
94
+ /**
95
+ * Do a query token and a vocabulary word name the same thing?
96
+ *
97
+ * Exact, or differing by a single trailing `s`. That fold is the whole of the
98
+ * morphology here — see the module header for why it stops there.
99
+ *
100
+ * @param {string} token a query token
101
+ * @param {string} word a word from a vocabulary phrase
102
+ * @returns {boolean}
103
+ */
104
+ export const sameWord = (token, word) => token === word || token === `${word}s` || `${token}s` === word;
105
+
106
+ /** Split a vocabulary phrase ("eu eaa", "Blend mode") into its words. */
107
+ export const phraseWords = (phrase) => String(phrase).toLowerCase().split(/\s+/).filter(Boolean);
108
+
109
+ /**
110
+ * Which query tokens satisfy this phrase, or null when it is not satisfied.
111
+ *
112
+ * Every word must find a DISTINCT OCCURRENCE — the returned array is the tokens
113
+ * consumed, which is what makes residue computable: a token that satisfied some
114
+ * phrase is by definition not unresolved.
115
+ *
116
+ * Distinctness is tracked by INDEX, not by token text, and that is the whole
117
+ * subtlety. A query can legitimately repeat a word, and two occurrences of
118
+ * "tool" are two things the user typed — matching them by value would let the
119
+ * first occurrence be found again and rejected as already-used, so a phrase
120
+ * needing two would fail against a query that actually supplied two. Indexing
121
+ * also forecloses the opposite error: one occurrence can never satisfy two
122
+ * words, because its index is consumed the first time it is taken.
123
+ *
124
+ * Greedy first-fit, which is sound because vocabulary phrases are short. Where
125
+ * it cannot satisfy a phrase it returns null rather than backtracking, and that
126
+ * conservative direction is deliberate: under-matching leaves residue a steward
127
+ * can see, while over-matching silently swallows tokens nothing resolved.
128
+ *
129
+ * @param {string[]} phrase the vocabulary phrase's words
130
+ * @param {string[]} tokens the query's tokens
131
+ * @returns {string[]|null} the tokens consumed, in phrase order, or null
132
+ */
133
+ export function phraseHit(phrase, tokens) {
134
+ if (!phrase.length) return null; // an empty phrase matches nothing, never everything
135
+ const takenIndexes = new Set();
136
+ const used = [];
137
+ for (const word of phrase) {
138
+ const index = tokens.findIndex((t, i) => !takenIndexes.has(i) && sameWord(t, word));
139
+ if (index === -1) return null;
140
+ takenIndexes.add(index);
141
+ used.push(tokens[index]);
142
+ }
143
+ return used;
144
+ }
145
+
146
+ /**
147
+ * The tokens a phrase OVERLAPS, for near-miss reporting.
148
+ *
149
+ * The match test relaxed from every-word to any-word. An entry with overlap but
150
+ * no match is the near-miss: it shares vocabulary with the ask and still did not
151
+ * clear the threshold, which is exactly the case a reader needs to see when the
152
+ * right answer is missing.
153
+ *
154
+ * @param {string[]} phrase the vocabulary phrase's words
155
+ * @param {string[]} tokens the query's tokens
156
+ * @returns {string[]} the overlapping tokens, de-duplicated, in query order
157
+ */
158
+ export function phraseOverlap(phrase, tokens) {
159
+ const overlap = [];
160
+ for (const token of tokens) {
161
+ if (!overlap.includes(token) && phrase.some((word) => sameWord(token, word))) overlap.push(token);
162
+ }
163
+ return overlap;
164
+ }
165
+
166
+ /**
167
+ * The minted values of one registry, sorted (UCS-1148).
168
+ *
169
+ * SUPPRESSED VALUES ARE NOT JOINED. A suppressed value is one a steward
170
+ * explicitly refused, and joining an ask to it would resolve a query through
171
+ * vocabulary the store has disowned. It is not treated as unknown either: the
172
+ * registry accounted for it, which is a different fact from never having heard
173
+ * of it, and a near-miss is where that distinction can be shown without acting
174
+ * on it.
175
+ *
176
+ * A registry the store does not carry yields an empty list rather than throwing.
177
+ * Registry absence is the whole installed base (UCS-1148) — a store that governs
178
+ * no operations simply resolves no verbs, and demanding the file would fail
179
+ * every store written before this ticket.
180
+ *
181
+ * @param {object} model the loaded store model
182
+ * @param {string} key the registry key, e.g. 'knowledge/operations'
183
+ * @returns {string[]} minted values, lexicographically sorted
184
+ */
185
+ export function mintedValues(model, key) {
186
+ const registry = model.registries?.get(key);
187
+ return registry ? [...registry.minted].sort() : [];
188
+ }
189
+
190
+ /**
191
+ * Suppressed values of one registry, sorted — the disowned vocabulary.
192
+ *
193
+ * @param {object} model the loaded store model
194
+ * @param {string} key the registry key
195
+ * @returns {string[]}
196
+ */
197
+ export function suppressedValues(model, key) {
198
+ const registry = model.registries?.get(key);
199
+ return registry ? [...registry.suppressed].sort() : [];
200
+ }
201
+
202
+ /**
203
+ * The phrases a registry value answers to.
204
+ *
205
+ * A minted value is an identifier, and identifiers are written for machines:
206
+ * `add-tool`, `eu-eaa`. So a value contributes TWO phrases — the value itself
207
+ * as one word, and the value with its separators opened into spaces, which is
208
+ * what a human types. "add tool" reaching `add-tool` is not fuzzy matching;
209
+ * it is reading the identifier's own internal structure, which the author put
210
+ * there precisely to be legible.
211
+ *
212
+ * Both are returned so the caller can report WHICH spelling matched.
213
+ *
214
+ * @param {string} value a registry value
215
+ * @returns {Array<{spelling: string, words: string[]}>}
216
+ */
217
+ export function valuePhrases(value) {
218
+ const lower = String(value).toLowerCase();
219
+ const opened = lower.split(/[-_/.]+/).filter(Boolean);
220
+ const phrases = [{ spelling: lower, words: [lower] }];
221
+ if (opened.length > 1 || opened[0] !== lower) {
222
+ phrases.push({ spelling: opened.join(' '), words: opened });
223
+ }
224
+ return phrases;
225
+ }