@polycode-projects/the-mechanical-code-talker 0.2.0 → 0.4.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/README.md +77 -3
- package/ROADMAP.md +416 -3
- package/bin/tmct.mjs +308 -12
- package/corpus/README.md +52 -0
- package/corpus/conceptnet/LICENSE-NOTICE +37 -0
- package/corpus/conceptnet/README.md +103 -0
- package/corpus/conceptnet/fetch-slice.mjs +136 -0
- package/corpus/conceptnet/filter-dump.mjs +89 -0
- package/corpus/conceptnet/slice.jsonl +14258 -0
- package/data/phrasebook/software-phrases.txt +231 -0
- package/data/templates/grammar-rules.toml +89 -0
- package/data/templates/responses.jsonl +68 -0
- package/package.json +40 -3
- package/src/ask-nlp.mjs +22 -10
- package/src/ask-vocab.mjs +35 -1
- package/src/ask.mjs +171 -494
- package/src/chat.mjs +709 -81
- package/src/corpus/conceptnet-map.toml +251 -0
- package/src/corpus/conceptnet.mjs +167 -0
- package/src/corpus/templates.mjs +188 -0
- package/src/finish.mjs +443 -0
- package/src/grammar/ace.mjs +341 -0
- package/src/grammar/assert.mjs +40 -0
- package/src/grammar/lexicon-core.json +287 -0
- package/src/grammar/lexicon.mjs +202 -0
- package/src/hash.mjs +32 -0
- package/src/index.mjs +21 -5
- package/src/init.mjs +264 -0
- package/src/interpret/fuzzy.mjs +89 -0
- package/src/interpret/merge.mjs +148 -0
- package/src/interpret/normalize.mjs +151 -0
- package/src/interpret/pipeline.mjs +112 -0
- package/src/interpret/strategies/grammar.mjs +137 -0
- package/src/interpret/strategies/keywords.mjs +241 -0
- package/src/interpret/strategies/noise-strip.mjs +114 -0
- package/src/memory/blocks.mjs +221 -0
- package/src/memory/core.mjs +533 -0
- package/src/memory/fold.mjs +0 -0
- package/src/memory/inspect.mjs +141 -0
- package/src/memory/trust.mjs +113 -0
- package/src/prose-nlp.mjs +14 -16
- package/src/providers/bootstrap.mjs +24 -0
- package/src/providers/fixture.mjs +118 -0
- package/src/providers/graph-service.mjs +312 -0
- package/src/repository-interface.mjs +318 -0
- package/src/server.mjs +44 -28
- package/src/sessions.mjs +137 -4
- package/src/source.mjs +44 -5
- package/src/syllogise.mjs +0 -0
- package/src/toml-config.mjs +14 -0
- package/src/tui/app.mjs +173 -0
- package/src/wink-model.mjs +74 -0
- package/bin/cli.mjs +0 -226
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// memory/inspect.mjs — seeing into the memory as TEXT (ROADMAP Phase 4,
|
|
2
|
+
// "Memory inspection"). One renderer serves both surfaces — the `/memory` chat
|
|
3
|
+
// command and the `tmct memory` CLI — in a terse (default) and a verbose form:
|
|
4
|
+
//
|
|
5
|
+
// - the memory graph grouped by OWL superclass (Fact / Utterance / Session,
|
|
6
|
+
// plus any other class present), counts with BALANCED samples scaled
|
|
7
|
+
// log-wise to class size (a 10,000-fact class shows ~8 exemplars, a
|
|
8
|
+
// 3-session class shows all 3);
|
|
9
|
+
// - top facts ranked by PROVENANCE BREADTH (a fact the corpus AND the chat
|
|
10
|
+
// both asserted outranks a single-writer fact), provenance verbatim;
|
|
11
|
+
// - recent Q→A utterance pairs (read off the mgx:inReplyTo edges);
|
|
12
|
+
// - the block-index summary (blocks, indexed tokens, top PageRank blocks).
|
|
13
|
+
//
|
|
14
|
+
// Pure renderers over loaded payloads + one thin I/O wrapper (inspectMemory).
|
|
15
|
+
// Everything degrades honestly: an empty memory renders as the empty story,
|
|
16
|
+
// never an error.
|
|
17
|
+
|
|
18
|
+
import { loadMemory, UTTERANCE_CLASS, IN_REPLY_TO_PROP, readFactRows, findContradictions } from "./core.mjs";
|
|
19
|
+
import { loadBlockIndex } from "./blocks.mjs";
|
|
20
|
+
|
|
21
|
+
/** Log-scaled sample count for a class of `n` individuals: 2·log10(n), floored
|
|
22
|
+
* at 3, never more than n (10,000 → 8; 500 → 5; 3 → 3; 1 → 1). Verbose doubles. */
|
|
23
|
+
export function sampleSize(n, { verbose = false } = {}) {
|
|
24
|
+
const base = Math.max(3, Math.round(2 * Math.log10(Math.max(1, n))));
|
|
25
|
+
return Math.min(n, verbose ? base * 2 : base);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Evenly-spaced (balanced) deterministic sample of k items — spans the class
|
|
29
|
+
* start to end rather than showing the first k. */
|
|
30
|
+
export function balancedSample(items, k) {
|
|
31
|
+
const n = items.length;
|
|
32
|
+
if (n <= k) return items.slice();
|
|
33
|
+
if (k <= 1) return [items[0]];
|
|
34
|
+
const out = [];
|
|
35
|
+
for (let i = 0; i < k; i += 1) out.push(items[Math.round((i * (n - 1)) / (k - 1))]);
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const attrOf = (ind, key) => (ind?.attributes || []).find((a) => a.key === key)?.value || "";
|
|
40
|
+
const truncate = (s, cap) => {
|
|
41
|
+
const t = String(s ?? "").replace(/\s+/g, " ").trim();
|
|
42
|
+
return t.length > cap ? `${t.slice(0, cap - 1)}…` : t;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/** Render a loaded memory payload + block index into the inspection text.
|
|
46
|
+
* Pure. `verbose` widens every cap and stops truncating provenance/text. */
|
|
47
|
+
export function renderMemory({ memory, blocks }, { verbose = false } = {}) {
|
|
48
|
+
const individuals = memory?.individuals || [];
|
|
49
|
+
const lines = [];
|
|
50
|
+
const textCap = verbose ? 400 : 100;
|
|
51
|
+
|
|
52
|
+
if (!individuals.length) {
|
|
53
|
+
lines.push("memory is empty — nothing remembered yet (facts, utterances and sessions land in .tmct/memory/ as you chat).");
|
|
54
|
+
} else {
|
|
55
|
+
// ---- classes: counts + balanced log-scaled samples ----
|
|
56
|
+
const byClass = new Map();
|
|
57
|
+
for (const ind of individuals) {
|
|
58
|
+
const cls = ind?.class || "(unclassified)";
|
|
59
|
+
if (!byClass.has(cls)) byClass.set(cls, []);
|
|
60
|
+
byClass.get(cls).push(ind);
|
|
61
|
+
}
|
|
62
|
+
const classes = [...byClass.entries()].sort((a, b) => b[1].length - a[1].length);
|
|
63
|
+
lines.push(`memory — ${individuals.length} individuals: ${classes.map(([c, of]) => `${of.length} ${c}`).join(", ")}.`);
|
|
64
|
+
for (const [cls, of] of classes) {
|
|
65
|
+
const k = sampleSize(of.length, { verbose });
|
|
66
|
+
lines.push("", `${cls} — ${of.length} (showing ${k})`);
|
|
67
|
+
for (const ind of balancedSample(of, k)) lines.push(` ${truncate(ind.label, textCap)}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ---- top facts by COMPUTED TRUST (upgraded from raw provenance breadth) ----
|
|
71
|
+
// Trust folds source-type prior + corroboration + recency, so a corroborated
|
|
72
|
+
// operator-stated fact outranks a lone web scrape by construction; provenance
|
|
73
|
+
// rides along (verbatim in verbose) for the audit trail.
|
|
74
|
+
const ranked = readFactRows(memory)
|
|
75
|
+
.filter((r) => r.sourceIds.length || r.provenance)
|
|
76
|
+
.sort((a, b) => b.trust - a.trust
|
|
77
|
+
|| b.sourceIds.length - a.sourceIds.length
|
|
78
|
+
|| `${a.subject} ${a.predicate} ${a.object}`.localeCompare(`${b.subject} ${b.predicate} ${b.object}`));
|
|
79
|
+
if (ranked.length) {
|
|
80
|
+
lines.push("", "top facts by trust:");
|
|
81
|
+
for (const r of ranked.slice(0, verbose ? 8 : 3)) {
|
|
82
|
+
const n = r.sourceIds.length || (r.provenance ? r.provenance.split(" | ").filter(Boolean).length : 0);
|
|
83
|
+
const label = `${r.subject} ${r.predicate} ${r.object}`;
|
|
84
|
+
lines.push(` ${truncate(label, textCap)} — trust ${r.trust.toFixed(2)}, ${n} source${n === 1 ? "" : "s"}: ${verbose ? r.provenance : truncate(r.provenance, 80)}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ---- contradictions: same (subject,predicate), differing object, both above
|
|
89
|
+
// the trust floor → surface BOTH with provenance, never silently pick ----
|
|
90
|
+
const contradictions = findContradictions(memory);
|
|
91
|
+
if (contradictions.length) {
|
|
92
|
+
lines.push("", `contradictions (${contradictions.length} — both kept, never silently resolved):`);
|
|
93
|
+
for (const group of contradictions.slice(0, verbose ? 8 : 3)) {
|
|
94
|
+
lines.push(` ${group[0].subject} ${group[0].predicate}?`);
|
|
95
|
+
for (const r of group) {
|
|
96
|
+
lines.push(` ${truncate(r.object, textCap)} (trust ${r.trust.toFixed(2)}; ${verbose ? r.provenance : truncate(r.provenance, 60)})`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ---- recent Q→A pairs (off the inReplyTo edges) ----
|
|
102
|
+
const byId = new Map(individuals.map((i) => [i.id, i]));
|
|
103
|
+
const replyGroup = (memory.objectProperties || []).find((g) => g?.prop === IN_REPLY_TO_PROP);
|
|
104
|
+
const pairs = (replyGroup?.examples || [])
|
|
105
|
+
.map((e) => ({ a: byId.get(e.subject), q: byId.get(e.object) }))
|
|
106
|
+
.filter((p) => p.a && p.q && p.a.class === UTTERANCE_CLASS)
|
|
107
|
+
.sort((x, y) => attrOf(y.a, "ts").localeCompare(attrOf(x.a, "ts")));
|
|
108
|
+
if (pairs.length) {
|
|
109
|
+
lines.push("", `recent Q→A pairs (${pairs.length} recorded):`);
|
|
110
|
+
for (const p of pairs.slice(0, verbose ? 8 : 3)) {
|
|
111
|
+
lines.push(` Q: ${truncate(attrOf(p.q, "text"), textCap)}`);
|
|
112
|
+
lines.push(` A: ${truncate(attrOf(p.a, "text"), textCap)}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ---- block-index summary ----
|
|
118
|
+
const entries = Object.entries(blocks?.blocks || {});
|
|
119
|
+
if (entries.length) {
|
|
120
|
+
const tokens = entries.reduce((n, [, b]) => n + (b.tokens?.length || 0), 0);
|
|
121
|
+
const top = entries
|
|
122
|
+
.slice()
|
|
123
|
+
.sort((a, b) => (b[1].rank ?? 0) - (a[1].rank ?? 0) || a[0].localeCompare(b[0]))
|
|
124
|
+
.slice(0, verbose ? 8 : 3);
|
|
125
|
+
lines.push("", `blocks — ${entries.length} folded session block${entries.length === 1 ? "" : "s"}, ${tokens} indexed tokens.`);
|
|
126
|
+
lines.push(` top by rank: ${top.map(([id, b]) => `${String(id).slice(0, 8)} (${(b.rank ?? 0).toFixed(3)})`).join(", ")}`);
|
|
127
|
+
} else {
|
|
128
|
+
lines.push("", "blocks — none folded yet (a session folds when it ends).");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return lines.join("\n");
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Load + render a repo's memory (the one thin I/O wrapper both the `/memory`
|
|
135
|
+
* chat command and the `tmct memory` CLI call). Never throws on a missing
|
|
136
|
+
* store — that is the honest empty story. */
|
|
137
|
+
export async function inspectMemory(dir, { verbose = false } = {}) {
|
|
138
|
+
const memory = await loadMemory(dir);
|
|
139
|
+
const blocks = await loadBlockIndex(dir);
|
|
140
|
+
return renderMemory({ memory, blocks }, { verbose });
|
|
141
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// memory/trust.mjs — deterministic, explainable, auditable trust over a Fact's
|
|
2
|
+
// Sources (PLAN_PROVENANCE_TRUST step (c)).
|
|
3
|
+
//
|
|
4
|
+
// Trust is a COMPUTED attribute of a Fact — never hand-set — a pure function of
|
|
5
|
+
// its Source edges, those Sources' types, and its mgx:createdAt. Three inputs
|
|
6
|
+
// combine:
|
|
7
|
+
// - a Source-TYPE PRIOR (operator > provider > corpus > web > entailed);
|
|
8
|
+
// - CORROBORATION over the fact's distinct Sources by noisy-OR
|
|
9
|
+
// (1 − Π(1 − wᵢ), capped at 1) — two independent web sources (0.4) reach
|
|
10
|
+
// 0.64, a lone operator fact is already 1.0;
|
|
11
|
+
// - a bounded RECENCY nudge in [0.9, 1.0] from createdAt, half-life decayed —
|
|
12
|
+
// the codegraph "capped nudge" philosophy, so recency breaks ties and
|
|
13
|
+
// freshens but never flips a source-type ordering by itself.
|
|
14
|
+
//
|
|
15
|
+
// For ENTAILED facts (tier-5): trust = min(premise trusts) × rule-confidence — a
|
|
16
|
+
// conclusion is only as trustworthy as its weakest premise. Premises may be
|
|
17
|
+
// absent for now, so this is a documented HOOK: pass opts.premiseTrusts (and
|
|
18
|
+
// opts.ruleConfidence) and it engages; otherwise an entailed fact scores off its
|
|
19
|
+
// bare 0.3 prior like any other Source.
|
|
20
|
+
//
|
|
21
|
+
// This module is PURE and import-free of core.mjs (no cycle): it reads Source
|
|
22
|
+
// individuals by their attribute props and returns { score, inputs }. core.mjs
|
|
23
|
+
// materialises the score onto the Fact (mgx:trustScore) plus the inputs it was
|
|
24
|
+
// computed from (mgx:trustInputs), so every score is reproducible and auditable.
|
|
25
|
+
|
|
26
|
+
export const TRUST_SCORE_PROP = "mgx:trustScore";
|
|
27
|
+
export const TRUST_INPUTS_PROP = "mgx:trustInputs";
|
|
28
|
+
|
|
29
|
+
/** Source-type priors — the ordering operator > provider-graph > curated-corpus
|
|
30
|
+
* > web > unverified-entailment. The entailed value is a FLOOR before premise
|
|
31
|
+
* adjustment (see the entailed hook below). */
|
|
32
|
+
export const SOURCE_PRIOR = Object.freeze({
|
|
33
|
+
operator: 1.0,
|
|
34
|
+
provider: 0.9,
|
|
35
|
+
corpus: 0.7,
|
|
36
|
+
web: 0.4,
|
|
37
|
+
entailed: 0.3,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
export const RECENCY_HALF_LIFE_MS = 30 * 24 * 60 * 60 * 1000; // 30 days
|
|
41
|
+
export const RECENCY_FLOOR = 0.9; // recency multiplier stays within [0.9, 1.0]
|
|
42
|
+
|
|
43
|
+
const round = (n, p = 6) => Number(n.toFixed(p));
|
|
44
|
+
const sourceTypeOf = (s) => (s?.attributes || []).find((a) => a.prop === "mgx:sourceType")?.value || "";
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Bounded recency multiplier in [RECENCY_FLOOR, 1] from an ISO-8601 createdAt.
|
|
48
|
+
* A half-life decay: freshly written ≈ 1.0, ancient → RECENCY_FLOOR. An unknown
|
|
49
|
+
* or unparseable timestamp yields 1.0 (no penalty) — recency only ever nudges
|
|
50
|
+
* down from a full score, it never invents one.
|
|
51
|
+
*/
|
|
52
|
+
export function recencyNudge(createdAt, now = Date.now(), halfLifeMs = RECENCY_HALF_LIFE_MS) {
|
|
53
|
+
const t = Date.parse(createdAt);
|
|
54
|
+
if (!Number.isFinite(t)) return 1;
|
|
55
|
+
const ageMs = Math.max(0, now - t);
|
|
56
|
+
return RECENCY_FLOOR + (1 - RECENCY_FLOOR) * Math.pow(0.5, ageMs / halfLifeMs);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Pure trust for one Fact. `fact` supplies `{ sourceIds: [...], createdAt }`;
|
|
61
|
+
* the Source individuals are resolved from `sourcesById` (a plain { id: Source }
|
|
62
|
+
* map — exactly what a memory payload's Source individuals key into). Distinct
|
|
63
|
+
* sources are corroborated by noisy-OR over their type priors and nudged by
|
|
64
|
+
* recency. Deterministic given the same inputs and `opts.now`.
|
|
65
|
+
*
|
|
66
|
+
* opts:
|
|
67
|
+
* - now (ms) reference time for recency; default Date.now()
|
|
68
|
+
* - halfLifeMs recency half-life override
|
|
69
|
+
* - premiseTrusts entailed hook: [trusts] of the conclusion's premise Facts
|
|
70
|
+
* - ruleConfidence entailed hook: the rule's confidence in [0,1] (default 1)
|
|
71
|
+
*
|
|
72
|
+
* Returns { score, inputs } — `inputs` (the source-type multiset, corroboration
|
|
73
|
+
* count, createdAt and the recency multiplier) is stored alongside the score so
|
|
74
|
+
* "why does this rank high?" is answerable from the record.
|
|
75
|
+
*/
|
|
76
|
+
export function computeTrust(fact, sourcesById = {}, opts = {}) {
|
|
77
|
+
const now = typeof opts.now === "number" ? opts.now : Date.now();
|
|
78
|
+
const ids = Array.isArray(fact?.sourceIds) ? fact.sourceIds : [];
|
|
79
|
+
|
|
80
|
+
// distinct sources → their type priors
|
|
81
|
+
const seen = new Set();
|
|
82
|
+
const types = [];
|
|
83
|
+
for (const id of ids) {
|
|
84
|
+
if (seen.has(id)) continue;
|
|
85
|
+
seen.add(id);
|
|
86
|
+
const t = sourceTypeOf(sourcesById[id]);
|
|
87
|
+
if (t) types.push(t);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// corroboration via noisy-OR over distinct-source priors, capped at 1
|
|
91
|
+
let base = 0;
|
|
92
|
+
let complement = 1;
|
|
93
|
+
for (const t of types) complement *= 1 - (SOURCE_PRIOR[t] ?? 0);
|
|
94
|
+
if (types.length) base = Math.min(1, 1 - complement);
|
|
95
|
+
|
|
96
|
+
// entailed hook (tier-5): a conclusion is only as trustworthy as its weakest
|
|
97
|
+
// premise × the rule confidence. Engages only when premises are supplied;
|
|
98
|
+
// otherwise an entailed fact rides its bare prior through the noisy-OR above.
|
|
99
|
+
if (types.includes("entailed") && Array.isArray(opts.premiseTrusts) && opts.premiseTrusts.length) {
|
|
100
|
+
const rc = typeof opts.ruleConfidence === "number" ? opts.ruleConfidence : 1;
|
|
101
|
+
base = Math.max(0, Math.min(1, Math.min(...opts.premiseTrusts) * rc));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const recency = recencyNudge(fact?.createdAt, now, opts.halfLifeMs);
|
|
105
|
+
const score = round(Math.min(1, base * recency));
|
|
106
|
+
const inputs = {
|
|
107
|
+
sourceTypes: types.slice().sort(),
|
|
108
|
+
corroboration: types.length,
|
|
109
|
+
createdAt: fact?.createdAt || "",
|
|
110
|
+
recency: round(recency),
|
|
111
|
+
};
|
|
112
|
+
return { score, inputs };
|
|
113
|
+
}
|
package/src/prose-nlp.mjs
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
// prose-nlp.mjs — the OPTIONAL wink-nlp lemma loader behind prose.mjs's LEMMA layer.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
// process either way.
|
|
3
|
+
// Kept SEPARATE from ask-nlp.mjs (its own `proseLemma` export shape) rather than an
|
|
4
|
+
// import of that ask-engine surface — but both now share the neutral leaf loader
|
|
5
|
+
// src/wink-model.mjs, so the wink model is resolved in ONE place. The former ~20
|
|
6
|
+
// duplicated createRequire lines are gone; the coupling this file avoids is to
|
|
7
|
+
// ask-nlp.mjs's export shape, not to a leaf model loader.
|
|
9
8
|
//
|
|
10
|
-
// BOUNDARY (same as ask-nlp.mjs, hard): Node-only, never inlined into the
|
|
11
|
-
// bundle. prose.mjs is itself never inlined by viz.mjs's askSource(), so
|
|
12
|
-
// browser-side can reach this module. wink
|
|
13
|
-
//
|
|
14
|
-
// the optional deps simply builds no lemma layer (honestly
|
|
9
|
+
// BOUNDARY (same as ask-nlp.mjs, hard): Node-only path, never inlined into the
|
|
10
|
+
// viewer bundle. prose.mjs is itself never inlined by viz.mjs's askSource(), so
|
|
11
|
+
// nothing browser-side can reach this module. The wink pair is loaded lazily (Node
|
|
12
|
+
// createRequire fallback, or the browser registration seam), failure cached as null:
|
|
13
|
+
// a checkout without the optional deps simply builds no lemma layer (honestly
|
|
14
|
+
// absent), it never throws.
|
|
15
15
|
//
|
|
16
16
|
// Determinism: wink's lemmatiser is a fixed trained model with no sampling — the
|
|
17
17
|
// same token always yields the same lemma across runs and processes, which is what
|
|
18
18
|
// lets the lemma layer meet the "byte-identical proseIndex across builds" contract.
|
|
19
19
|
|
|
20
|
-
import {
|
|
20
|
+
import { winkInstance } from "./wink-model.mjs";
|
|
21
21
|
|
|
22
22
|
let cached; // undefined = not tried yet; null = unavailable (tried once, honestly off)
|
|
23
23
|
|
|
@@ -27,10 +27,8 @@ let cached; // undefined = not tried yet; null = unavailable (tried once, honest
|
|
|
27
27
|
export function proseLemma() {
|
|
28
28
|
if (cached !== undefined) return cached;
|
|
29
29
|
try {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
const model = require("wink-eng-lite-web-model");
|
|
33
|
-
const nlp = winkNLP(model);
|
|
30
|
+
const nlp = winkInstance();
|
|
31
|
+
if (!nlp) { cached = null; return cached; }
|
|
34
32
|
const its = nlp.its;
|
|
35
33
|
const memo = new Map();
|
|
36
34
|
cached = (word) => {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// The BOOTSTRAP reference provider — the empty/degenerate graph a fresh repo
|
|
2
|
+
// "contains" before anything is indexed. PLAN_REPOSITORY_INTERFACE.md deliverable
|
|
3
|
+
// 2: "bootstrap returns honest empties".
|
|
4
|
+
//
|
|
5
|
+
// It implements every Repository-Interface service over the empty bootstrap
|
|
6
|
+
// payload (src/source.mjs emptyEntities): every id-taking service returns
|
|
7
|
+
// miss(UNRESOLVED_TERM) — there are no individuals — and every aggregate returns
|
|
8
|
+
// an honest empty (stats.total = 0, untested.modules = [], …). Nothing throws.
|
|
9
|
+
// This is the other end of the compatibility kit: the provider that has no data
|
|
10
|
+
// must still CONFORM.
|
|
11
|
+
|
|
12
|
+
import { parseEntities } from "../codegraph.mjs";
|
|
13
|
+
import { emptyEntities } from "../source.mjs";
|
|
14
|
+
import { createGraphService } from "./graph-service.mjs";
|
|
15
|
+
|
|
16
|
+
/** The parsed empty bootstrap graph. */
|
|
17
|
+
export function bootstrapGraph() {
|
|
18
|
+
return parseEntities(emptyEntities());
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** The bootstrap provider: every service over the empty graph — honest empties. */
|
|
22
|
+
export function bootstrapProvider() {
|
|
23
|
+
return createGraphService(bootstrapGraph());
|
|
24
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// The FIXTURE reference provider — a small, real, self-contained code graph that
|
|
2
|
+
// implements every Repository-Interface service. PLAN_REPOSITORY_INTERFACE.md
|
|
3
|
+
// deliverable 2: "the executable specification an external producer reads first".
|
|
4
|
+
//
|
|
5
|
+
// It is a degenerate provider in the sense that its graph is tiny and its source
|
|
6
|
+
// bodies are absent (snippet/context answer NO_SOURCE) — but every OTHER service
|
|
7
|
+
// returns real graph truth. The contract suite (test/repository-interface.test.mjs)
|
|
8
|
+
// runs the whole compatibility kit against it.
|
|
9
|
+
//
|
|
10
|
+
// The payload is embedded (not read from test/) so this ships as a runnable spec
|
|
11
|
+
// inside the library. Its shape is exactly a parseEntities() input.
|
|
12
|
+
|
|
13
|
+
import { parseEntities } from "../codegraph.mjs";
|
|
14
|
+
import { createGraphService } from "./graph-service.mjs";
|
|
15
|
+
|
|
16
|
+
/** A compact but type-complete entities payload: modules, a class hierarchy
|
|
17
|
+
* (Base ← Widget ← Button), a method with a full signature, an attribute, a
|
|
18
|
+
* module global, and a commit — wired by one edge of every closed kind. */
|
|
19
|
+
export const FIXTURE_ENTITIES = Object.freeze({
|
|
20
|
+
generated_at: "2026-07-05T00:00:00.000Z",
|
|
21
|
+
bootstrap: false,
|
|
22
|
+
prefixes: { seon: "http://se-on.org/ontologies/seon.owl#", mgx: "urn:tmct:mgx#" },
|
|
23
|
+
classes: [
|
|
24
|
+
{ name: "Module", count: 5, sample: ["pkg/core/graph.mjs"] },
|
|
25
|
+
{ name: "Class", count: 3, sample: ["Base", "Widget", "Button"] },
|
|
26
|
+
{ name: "Method", count: 1, sample: ["Widget.render"] },
|
|
27
|
+
{ name: "Attribute", count: 1, sample: ["Widget.name"] },
|
|
28
|
+
{ name: "Function", count: 1, sample: ["parseNode"] },
|
|
29
|
+
{ name: "Commit", count: 1, sample: ["a1b2c3d"] },
|
|
30
|
+
],
|
|
31
|
+
vocabulary: [],
|
|
32
|
+
objectProperties: [
|
|
33
|
+
{ predicate: "imports", prop: "mgx:importsNamespace", count: 3, examples: [
|
|
34
|
+
{ subject: "mod:view.mjs", object: "mod:graph.mjs", subjectLabel: "pkg/ui/view.mjs", objectLabel: "pkg/core/graph.mjs" },
|
|
35
|
+
{ subject: "mod:widget.mjs", object: "mod:graph.mjs", subjectLabel: "pkg/ui/widget.mjs", objectLabel: "pkg/core/graph.mjs" },
|
|
36
|
+
{ subject: "mod:button.mjs", object: "mod:widget.mjs", subjectLabel: "pkg/ui/button.mjs", objectLabel: "pkg/ui/widget.mjs" },
|
|
37
|
+
] },
|
|
38
|
+
{ predicate: "calls", prop: "mgx:callsCoarse", count: 1, examples: [
|
|
39
|
+
{ subject: "mod:script.mjs", object: "mod:graph.mjs", subjectLabel: "scripts/build.mjs", objectLabel: "pkg/core/graph.mjs" },
|
|
40
|
+
] },
|
|
41
|
+
{ predicate: "callsSymbol", prop: "mgx:callsSymbol", count: 1, examples: [
|
|
42
|
+
{ subject: "m:render", object: "fn:parseNode", subjectLabel: "Widget.render", objectLabel: "parseNode" },
|
|
43
|
+
] },
|
|
44
|
+
{ predicate: "defines", prop: "seon:declaresMethod", count: 3, examples: [
|
|
45
|
+
{ subject: "mod:graph.mjs", object: "fn:parseNode", subjectLabel: "pkg/core/graph.mjs", objectLabel: "parseNode" },
|
|
46
|
+
{ subject: "mod:widget.mjs", object: "cls:widget", subjectLabel: "pkg/ui/widget.mjs", objectLabel: "Widget" },
|
|
47
|
+
{ subject: "mod:widget.mjs", object: "g:register", subjectLabel: "pkg/ui/widget.mjs", objectLabel: "register" },
|
|
48
|
+
] },
|
|
49
|
+
{ predicate: "tests", prop: "mgx:testsCoverage", count: 1, examples: [
|
|
50
|
+
{ subject: "mod:widget.test.mjs", object: "mod:widget.mjs", subjectLabel: "pkg/test/widget.test.mjs", objectLabel: "pkg/ui/widget.mjs" },
|
|
51
|
+
] },
|
|
52
|
+
{ predicate: "touches", prop: "mgx:touchedByCommit", count: 1, examples: [
|
|
53
|
+
{ subject: "commit:a1b2c3d", object: "mod:widget.mjs", subjectLabel: "a1b2c3d", objectLabel: "pkg/ui/widget.mjs" },
|
|
54
|
+
] },
|
|
55
|
+
{ predicate: "touchesSymbol", prop: "mgx:touchesSymbol", count: 1, examples: [
|
|
56
|
+
{ subject: "commit:a1b2c3d", object: "m:render", subjectLabel: "a1b2c3d", objectLabel: "Widget.render" },
|
|
57
|
+
] },
|
|
58
|
+
{ predicate: "contains", prop: "seon:containsCodeEntity", count: 2, examples: [
|
|
59
|
+
{ subject: "cls:widget", object: "m:render", subjectLabel: "Widget", objectLabel: "render" },
|
|
60
|
+
{ subject: "cls:widget", object: "a:name", subjectLabel: "Widget", objectLabel: "name" },
|
|
61
|
+
] },
|
|
62
|
+
{ predicate: "inherits", prop: "seon:hasSuperType", count: 2, examples: [
|
|
63
|
+
{ subject: "cls:widget", object: "cls:base", subjectLabel: "Widget", objectLabel: "Base" },
|
|
64
|
+
{ subject: "cls:button", object: "cls:widget", subjectLabel: "Button", objectLabel: "Widget" },
|
|
65
|
+
] },
|
|
66
|
+
{ predicate: "cochange", prop: "mgx:changeCoupledWith", count: 1, examples: [
|
|
67
|
+
{ subject: "mod:widget.mjs", object: "mod:graph.mjs", subjectLabel: "pkg/ui/widget.mjs", objectLabel: "pkg/core/graph.mjs", weight: 3 },
|
|
68
|
+
] },
|
|
69
|
+
{ predicate: "reexports", prop: "mgx:reExports", count: 1, examples: [
|
|
70
|
+
{ subject: "mod:widget.mjs", object: "cls:widget", subjectLabel: "pkg/ui/widget.mjs", objectLabel: "Widget" },
|
|
71
|
+
] },
|
|
72
|
+
],
|
|
73
|
+
individuals: [
|
|
74
|
+
{ id: "mod:graph.mjs", label: "pkg/core/graph.mjs", class: "Module", derived_from: ["git:a1b2c3d"], mentions: [] },
|
|
75
|
+
{ id: "mod:view.mjs", label: "pkg/ui/view.mjs", class: "Module", derived_from: [], mentions: [] },
|
|
76
|
+
{ id: "mod:widget.mjs", label: "pkg/ui/widget.mjs", class: "Module", derived_from: ["git:a1b2c3d"], mentions: [] },
|
|
77
|
+
{ id: "mod:button.mjs", label: "pkg/ui/button.mjs", class: "Module", derived_from: [], mentions: [] },
|
|
78
|
+
{ id: "mod:script.mjs", label: "scripts/build.mjs", class: "Module", derived_from: [], mentions: [] },
|
|
79
|
+
{ id: "mod:widget.test.mjs", label: "pkg/test/widget.test.mjs", class: "Module", derived_from: [], mentions: [] },
|
|
80
|
+
{ id: "fn:parseNode", label: "parseNode", class: "Function", derived_from: [], mentions: [], attributes: [
|
|
81
|
+
{ prop: "seon:startsAt", key: "site", value: "pkg/core/graph.mjs:10-24" },
|
|
82
|
+
{ prop: "seon:hasParameter", key: "params", value: "node, depth=0" },
|
|
83
|
+
{ prop: "seon:hasReturnType", key: "returns", value: "Node" },
|
|
84
|
+
] },
|
|
85
|
+
{ id: "cls:base", label: "Base", class: "Class", derived_from: [], mentions: [], attributes: [{ prop: "seon:startsAt", key: "site", value: "pkg/core/graph.mjs:1-6" }] },
|
|
86
|
+
{ id: "cls:widget", label: "Widget", class: "Class", derived_from: [], mentions: [], attributes: [{ prop: "seon:startsAt", key: "site", value: "pkg/ui/widget.mjs:1-40" }] },
|
|
87
|
+
{ id: "cls:button", label: "Button", class: "Class", derived_from: [], mentions: [], attributes: [{ prop: "seon:startsAt", key: "site", value: "pkg/ui/button.mjs:1-12" }] },
|
|
88
|
+
{ id: "m:render", label: "Widget.render", class: "Method", derived_from: [], mentions: [], attributes: [
|
|
89
|
+
{ prop: "seon:startsAt", key: "site", value: "pkg/ui/widget.mjs:8-20" },
|
|
90
|
+
{ prop: "mgx:decorator", key: "decorators", value: "property" },
|
|
91
|
+
{ prop: "seon:hasParameter", key: "params", value: "self, mode='full'" },
|
|
92
|
+
{ prop: "seon:hasReturnType", key: "returns", value: "str" },
|
|
93
|
+
{ prop: "seon:throwsException", key: "raises", value: "ValueError" },
|
|
94
|
+
{ prop: "seon:accessesField", key: "self_fields", value: "name, size" },
|
|
95
|
+
{ prop: "seon:hasDoc", key: "doc", value: "Render the widget." },
|
|
96
|
+
] },
|
|
97
|
+
{ id: "a:name", label: "Widget.name", class: "Attribute", derived_from: [], mentions: [], attributes: [{ prop: "seon:startsAt", key: "site", value: "pkg/ui/widget.mjs:2" }] },
|
|
98
|
+
{ id: "g:register", label: "register", class: "GlobalVariable", derived_from: [], mentions: [], attributes: [
|
|
99
|
+
{ prop: "seon:startsAt", key: "site", value: "pkg/ui/widget.mjs:1" },
|
|
100
|
+
{ prop: "mgx:value", key: "value", value: "Library()" },
|
|
101
|
+
] },
|
|
102
|
+
{ id: "commit:a1b2c3d", label: "a1b2c3d", class: "Commit", derived_from: [], mentions: [], attributes: [
|
|
103
|
+
{ prop: "mgx:commitAuthor", key: "author", value: "Grace Hopper" },
|
|
104
|
+
{ prop: "mgx:commitDate", key: "date", value: "2026-07-01" },
|
|
105
|
+
{ prop: "mgx:commitMessage", key: "message", value: "render the widget in full mode" },
|
|
106
|
+
] },
|
|
107
|
+
],
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
/** The parsed fixture graph (shared, immutable truth). */
|
|
111
|
+
export function fixtureGraph() {
|
|
112
|
+
return parseEntities(FIXTURE_ENTITIES);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** The fixture provider: a Repository-Interface service over the small real graph. */
|
|
116
|
+
export function fixtureProvider() {
|
|
117
|
+
return createGraphService(fixtureGraph());
|
|
118
|
+
}
|