@polycode-projects/the-mechanical-code-talker 1.9.2 → 1.10.1

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 (80) hide show
  1. package/README.md +441 -202
  2. package/bin/tmct.mjs +126 -1
  3. package/package.json +4 -2
  4. package/src/answer-variants.mjs +8 -36
  5. package/src/ask-browser-entry.mjs +5 -23
  6. package/src/ask-browser.bundle.js +1 -2
  7. package/src/ask-nlp.mjs +9 -23
  8. package/src/ask-vocab.mjs +139 -589
  9. package/src/ask.mjs +627 -1729
  10. package/src/chat.mjs +1684 -2872
  11. package/src/cli-args.mjs +14 -28
  12. package/src/codegraph.mjs +236 -644
  13. package/src/completions/complete.mjs +18 -62
  14. package/src/completions/graph-adapter.mjs +14 -60
  15. package/src/completions/group.mjs +12 -68
  16. package/src/completions/infer.mjs +38 -126
  17. package/src/completions/prune.mjs +17 -70
  18. package/src/completions/rank.mjs +16 -69
  19. package/src/completions/search.mjs +8 -31
  20. package/src/concept.mjs +32 -88
  21. package/src/conformance.mjs +11 -15
  22. package/src/corpus/conceptnet.mjs +31 -89
  23. package/src/corpus/templates.mjs +19 -45
  24. package/src/corpus/unknown-ingest.mjs +31 -92
  25. package/src/embed.mjs +10 -22
  26. package/src/extensions.mjs +50 -154
  27. package/src/finish.mjs +35 -91
  28. package/src/grammar/ace.mjs +16 -40
  29. package/src/grammar/assert.mjs +1 -1
  30. package/src/grammar/lexicon-core.json +1 -1
  31. package/src/grammar/lexicon.mjs +9 -27
  32. package/src/graph-merge.mjs +2 -3
  33. package/src/hash.mjs +6 -14
  34. package/src/index.mjs +6 -10
  35. package/src/init.mjs +38 -125
  36. package/src/interpret/fuzzy.mjs +10 -29
  37. package/src/interpret/merge.mjs +9 -27
  38. package/src/interpret/normalize.mjs +137 -585
  39. package/src/interpret/pipeline.mjs +23 -71
  40. package/src/interpret/strategies/ace.mjs +7 -31
  41. package/src/interpret/strategies/constructions.mjs +14 -41
  42. package/src/interpret/strategies/grammar.mjs +21 -60
  43. package/src/interpret/strategies/keywords.mjs +42 -131
  44. package/src/interpret/strategies/noise-strip.mjs +18 -89
  45. package/src/memory/bias.mjs +11 -54
  46. package/src/memory/blocks.mjs +18 -69
  47. package/src/memory/core.mjs +171 -591
  48. package/src/memory/fold.mjs +0 -0
  49. package/src/memory/inspect.mjs +7 -25
  50. package/src/memory/shacl.mjs +10 -39
  51. package/src/memory/trust.mjs +26 -127
  52. package/src/memory-ask-browser-entry.mjs +7 -30
  53. package/src/memory-ask-browser.bundle.js +1 -1
  54. package/src/paraphrase.mjs +20 -53
  55. package/src/planning.mjs +15 -157
  56. package/src/prose-nlp.mjs +4 -17
  57. package/src/prose.mjs +19 -67
  58. package/src/providers/bootstrap.mjs +1 -2
  59. package/src/providers/fixture.mjs +1 -2
  60. package/src/providers/graph-service.mjs +28 -59
  61. package/src/repository-interface.mjs +6 -8
  62. package/src/router/drive.mjs +183 -0
  63. package/src/router/goal-reasoner.mjs +66 -231
  64. package/src/router/guardrail.mjs +20 -58
  65. package/src/router/planner.mjs +15 -46
  66. package/src/router/registry.mjs +13 -43
  67. package/src/router/resolver.mjs +46 -131
  68. package/src/router/results.mjs +231 -0
  69. package/src/schema-docs.mjs +10 -27
  70. package/src/server-http.mjs +10 -19
  71. package/src/server.mjs +22 -28
  72. package/src/sessions.mjs +15 -30
  73. package/src/source-slice.mjs +5 -7
  74. package/src/source.mjs +10 -20
  75. package/src/syllogise.mjs +187 -575
  76. package/src/telemetry.mjs +3 -3
  77. package/src/toml-config.mjs +4 -4
  78. package/src/tui/app.mjs +9 -19
  79. package/src/viz.mjs +66 -123
  80. package/src/wink-model.mjs +10 -24
Binary file
@@ -1,19 +1,7 @@
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.
1
+ // memory/inspect.mjs — seeing into the memory as TEXT: one renderer serves
2
+ // both the `/memory` chat command and the `tmct memory` CLI, terse or
3
+ // verbose. Pure renderers + one thin I/O wrapper (inspectMemory); an empty
4
+ // memory renders as the empty story, never an error.
17
5
 
18
6
  import { loadMemory, UTTERANCE_CLASS, IN_REPLY_TO_PROP, readFactRows, findContradictions } from "./core.mjs";
19
7
  import { loadBlockIndex } from "./blocks.mjs";
@@ -67,10 +55,7 @@ export function renderMemory({ memory, blocks }, { verbose = false } = {}) {
67
55
  for (const ind of balancedSample(of, k)) lines.push(` ${truncate(ind.label, textCap)}`);
68
56
  }
69
57
 
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.
58
+ // ---- top facts by computed trust (source prior + corroboration + recency) ----
74
59
  const ranked = readFactRows(memory)
75
60
  .filter((r) => r.sourceIds.length || r.provenance)
76
61
  .sort((a, b) => b.trust - a.trust
@@ -128,14 +113,11 @@ export function renderMemory({ memory, blocks }, { verbose = false } = {}) {
128
113
  lines.push("", "blocks — none folded yet (a session folds when it ends).");
129
114
  }
130
115
 
131
- // ---- explore hooks: real, runnable example queries built from what's actually
132
- // stored, so /memory is a springboard for drilling in, not just a dump ----
116
+ // ---- explore hooks: runnable example queries built from what's stored ----
133
117
  if (individuals.length) {
134
118
  const clean = (t) => typeof t === "string" && /^[a-z][a-z0-9]+(?: [a-z0-9]{2,}){0,2}$/.test(t) && t.length <= 22 && !/^\d+$/.test(t);
135
119
  const facts = readFactRows(memory);
136
- // Rank candidate "what is a X" terms by CATEGORY SIZE (how many facts point at
137
- // them) so the hooks land on rich, recognisable categories (function, class, …),
138
- // not a lone ConceptNet oddity.
120
+ // Rank "what is a X" candidates by category size (how many facts point at them).
139
121
  const freq = new Map();
140
122
  for (const f of facts) if (clean(f.object)) freq.set(f.object, (freq.get(f.object) || 0) + 1);
141
123
  const terms = [...freq.entries()]
@@ -1,37 +1,9 @@
1
- // memory/shacl.mjs — the declarative SHACL-STYLE ingest gate for tmct's own
2
- // memory graph (PLAN_AGENTS.md §2.1 "Declarative SHACL ingest gate (c)").
3
- //
4
- // ontology/memory-shapes.ttl is the canonical, standards-based (SHACL/
5
- // Turtle) declarative SPEC for these three shapes written first, kept as
6
- // the human-readable/auditable contract, modelled on marginalia's own
7
- // app/ontology/shapes.ttl. This file is a small, HAND-ROLLED validator that
8
- // implements exactly what that spec describes, in plain JS, against
9
- // memory/core.mjs's own {id, label, class, attributes} individual shape.
10
- //
11
- // Deliberately NOT wired to a real SHACL/RDF-JS engine. `shacl-engine` +
12
- // `rdf-ext` were tried first (the plan's named tooling, matching
13
- // marginalia's own choice) and rejected on measurement: shacl-engine
14
- // transitively pulls in `@comunica/query-sparql-rdfjs-lite` — a full
15
- // federated SPARQL query engine — across 560+ packages (~7700 added
16
- // package-lock.json lines), wildly disproportionate to tmct's "pure-JS,
17
- // minimal-deps" floor (5 runtime deps before this) and to what three closed,
18
- // bounded shapes actually need. `src/conformance.mjs` already proves this
19
- // project is comfortable with imperative shape assertions instead of a
20
- // general engine (a Repository-Interface contract-test suite, not a memory
21
- // gate — a DIFFERENT thing from this file, see its own header); this module
22
- // is the same discipline applied to memory-write validation. Keep
23
- // ontology/memory-shapes.ttl and this file in sync BY HAND when either shape
24
- // changes — the .ttl is documentation here, not machine-read.
25
- //
26
- // Every shape below is PERMISSIVE beyond memory/core.mjs's own existing
27
- // structural floor (appendFact/appendRule already throw before ever reaching
28
- // mutateMemory if subject/predicate/object or name/kind/slots are missing —
29
- // this gate mirrors, not tightens, that floor) and treats every OPTIONAL
30
- // attribute (provenance chief among them — appendFact's own signature
31
- // defaults `provenance` to `""`, and real call sites/tests legitimately omit
32
- // it, e.g. re-writing createdAt without re-asserting provenance) as OPTIONAL
33
- // here too: a violation only fires on genuine structural malformation, never
34
- // on a legitimately sparse-but-valid write or upsert of existing data.
1
+ // memory/shacl.mjs — SHACL-style ingest gate for tmct's own memory graph.
2
+ // Hand-rolled validator mirroring ontology/memory-shapes.ttl (kept in sync by
3
+ // hand) against memory/core.mjs's {id, label, class, attributes} shape — not
4
+ // wired to a real SHACL/RDF-JS engine (disproportionate deps for three closed
5
+ // shapes). Permissive beyond core.mjs's own structural floor: a violation
6
+ // only fires on genuine malformation, never a sparse-but-valid write.
35
7
 
36
8
  const MEMORY_CLASSES = new Set(["Utterance", "Fact", "Session", "Source", "Rule"]);
37
9
  const RULE_KINDS = new Set(["compose2", "filter", "recursive"]);
@@ -62,8 +34,8 @@ function checkIndividual(ind, violations) {
62
34
 
63
35
  /** FactShape (mgx:FactShape): the reified subject/predicate/object, each
64
36
  * present and non-empty; mgx:factProvenance, WHEN PRESENT, must be
65
- * non-empty (optional at this gate — see file header on why: appendFact's
66
- * own API allows an empty/omitted provenance). */
37
+ * non-empty (optional at this gate — appendFact's own API allows an
38
+ * empty/omitted provenance). */
67
39
  function checkFact(ind, violations) {
68
40
  for (const prop of ["rdf:subject", "rdf:predicate", "rdf:object"]) {
69
41
  if (!nonEmpty(attrValue(ind, prop))) violations.push(`a Fact needs a non-empty ${prop}`);
@@ -101,9 +73,8 @@ export function validateIndividual(ind) {
101
73
 
102
74
  /** The ingest gate: throw a clear, aggregated error if `ind` violates the
103
75
  * shape contract, so a malformed Fact/Rule never reaches mutateMemory's
104
- * write. Synchronous (no engine/file I/O) — safe to `await` regardless (a
105
- * synchronous throw inside an async caller's body still rejects that
106
- * caller's promise correctly; a non-throwing sync return awaits to itself). */
76
+ * write. Synchronous, but safe to `await` a sync throw inside an async
77
+ * caller still rejects its promise correctly. */
107
78
  export function assertIndividualValid(ind) {
108
79
  const r = validateIndividual(ind);
109
80
  if (!r.ok) {
@@ -1,64 +1,15 @@
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 > teach > provider > corpus > corpusWeak >
8
- // extracted > web > entailed);
9
- // - CORROBORATION over the fact's distinct Sources by noisy-OR
10
- // (1 − Π(1 − wᵢ), capped at 1) — two independent web sources (0.4) reach
11
- // 0.64, a lone operator fact is already 1.0;
12
- // - a bounded RECENCY nudge in [0.9, 1.0] from createdAt, half-life decayed —
13
- // the codegraph "capped nudge" philosophy, so recency breaks ties and
14
- // freshens but never flips a source-type ordering by itself.
15
- //
16
- // A fourth, per-Source bounded nudge folds into the type-prior term above (not a
17
- // separate multiplicative stage): each Source may carry mgx:sourceReliability in
18
- // [0.5, 1.5] (neutral 1.0 when absent, true of every Source until a session's
19
- // actor-level trust — sessionReliabilityFrom, core.mjs's recomputeSourceReliability —
20
- // starts writing it), so a session with a track record of corroborated facts
21
- // nudges its own Source's contribution up, one contradicted repeatedly nudges it
22
- // down — additive and safe: absent, every existing score is byte-identical.
23
- //
24
- // For ENTAILED facts (tier-5): trust = min(premise trusts) × rule-confidence — a
25
- // conclusion is only as trustworthy as its weakest premise. Premises may be
26
- // absent for now, so this is a documented HOOK: pass opts.premiseTrusts (and
27
- // opts.ruleConfidence) and it engages; otherwise an entailed fact scores off its
28
- // bare 0.3 prior like any other Source.
29
- //
30
- // This module is PURE and import-free of core.mjs (no cycle): it reads Source
31
- // individuals by their attribute props and returns { score, inputs }. core.mjs
32
- // materialises the score onto the Fact (mgx:trustScore) plus the inputs it was
33
- // computed from (mgx:trustInputs), so every score is reproducible and auditable.
1
+ // memory/trust.mjs — deterministic, explainable, auditable trust over a
2
+ // Fact's Sources. Trust = f(source-type prior, noisy-OR corroboration,
3
+ // recency decay), nudged by each Source's mgx:sourceReliability (neutral 1.0
4
+ // when absent). Entailed facts: min(premise trusts) × rule-confidence when
5
+ // premises are supplied, else the bare entailed prior. Pure, import-free of
6
+ // core.mjs; core.mjs materialises the result onto the Fact.
34
7
 
35
8
  export const TRUST_SCORE_PROP = "mgx:trustScore";
36
9
  export const TRUST_INPUTS_PROP = "mgx:trustInputs";
37
10
 
38
- /** Source-type priors — the ordering operator > teach > provider-graph >
39
- * curated-corpus > weak-corpus > extracted-document > web > unverified-
40
- * entailment. `teach` is the chat teach lane's natural-frame writes
41
- * ("remember that …", "<Name> owns <X>") — still operator speech, but
42
- * through a looser recognizer than the ACE-parsed operator assert, so it
43
- * sits just below the operator prior. The entailed value is a FLOOR before
44
- * premise adjustment (see the entailed hook below).
45
- *
46
- * `corpusWeak` is for corpus-sourced facts whose underlying relation is real
47
- * but low-precision (ConceptNet's /r/RelatedTo — ambiguous, undirected
48
- * association, unlike the specific typed relations the plain `corpus` prior
49
- * covers) — still a curated, committed dataset (above `web`), just not
50
- * asserting the same strength of claim (below `corpus`).
51
- *
52
- * `extracted` is scripts/extract-facts-from-text.mjs's batch reader: it runs
53
- * the SAME deterministic teach/assert recognizer as `teach`/`operator`, but
54
- * unattended over an arbitrary document nobody in-session vetted sentence by
55
- * sentence — the recognizer is exact/closed-set (no guessing), but the
56
- * SOURCE DOCUMENT is unreviewed, so it sits just above `web` (also an
57
- * unreviewed external source) and below `corpusWeak`/`corpus` (curated,
58
- * committed datasets) and `teach` (a human typing into the live chat).
59
- *
60
- * Both are computed from the Source's type exactly like every other tier —
61
- * never hand-set on a Fact directly. */
11
+ /** Source-type priors — computed from the Source's type only, never hand-set
12
+ * on a Fact directly. */
62
13
  export const SOURCE_PRIOR = Object.freeze({
63
14
  operator: 1.0,
64
15
  teach: 0.95,
@@ -73,14 +24,9 @@ export const SOURCE_PRIOR = Object.freeze({
73
24
  export const RECENCY_HALF_LIFE_MS = 30 * 24 * 60 * 60 * 1000; // 30 days
74
25
  export const RECENCY_FLOOR = 0.9; // recency multiplier stays within [0.9, 1.0]
75
26
 
76
- // Actor-level (session-scoped) trust — a bounded NUDGE on top of a Source's
77
- // type prior, read off the same already-passed Source individual a fact's
78
- // corroboration already resolves via sourceTypeOf. `mgx:sourceReliability`
79
- // lives in [SOURCE_RELIABILITY_MIN, SOURCE_RELIABILITY_MAX], NEUTRAL (1.0,
80
- // exactly — no rounding drift) when absent, which is true of every Source
81
- // until core.mjs's recomputeSourceReliability starts writing it. Neutral-when-
82
- // absent makes this safely additive: every existing score is byte-identical
83
- // until something actually writes the attribute.
27
+ // Actor-level (session-scoped) trust — a bounded nudge on a Source's type
28
+ // prior. Neutral (1.0) until core.mjs's recomputeSourceReliability writes it,
29
+ // so this stays additive-safe.
84
30
  export const SOURCE_RELIABILITY_MIN = 0.5;
85
31
  export const SOURCE_RELIABILITY_MAX = 1.5;
86
32
  export const SOURCE_RELIABILITY_NEUTRAL = 1.0;
@@ -88,10 +34,8 @@ export const SOURCE_RELIABILITY_NEUTRAL = 1.0;
88
34
  const round = (n, p = 6) => Number(n.toFixed(p));
89
35
  const sourceTypeOf = (s) => (s?.attributes || []).find((a) => a.prop === "mgx:sourceType")?.value || "";
90
36
 
91
- /** A Source's reliability multiplier: the raw mgx:sourceReliability attribute,
92
- * clamped into [SOURCE_RELIABILITY_MIN, SOURCE_RELIABILITY_MAX] (a corrupt or
93
- * out-of-range stored value is defended against, never trusted blindly), or
94
- * the neutral 1.0 when the attribute is absent/unparseable. */
37
+ /** A Source's reliability multiplier: raw mgx:sourceReliability, clamped to
38
+ * [SOURCE_RELIABILITY_MIN, MAX], or neutral 1.0 when absent/unparseable. */
95
39
  function sourceReliabilityOf(s) {
96
40
  const raw = (s?.attributes || []).find((a) => a.prop === "mgx:sourceReliability")?.value;
97
41
  if (raw === undefined) return SOURCE_RELIABILITY_NEUTRAL;
@@ -100,12 +44,8 @@ function sourceReliabilityOf(s) {
100
44
  return Math.max(SOURCE_RELIABILITY_MIN, Math.min(SOURCE_RELIABILITY_MAX, n));
101
45
  }
102
46
 
103
- /**
104
- * Bounded recency multiplier in [RECENCY_FLOOR, 1] from an ISO-8601 createdAt.
105
- * A half-life decay: freshly written ≈ 1.0, ancient → RECENCY_FLOOR. An unknown
106
- * or unparseable timestamp yields 1.0 (no penalty) — recency only ever nudges
107
- * down from a full score, it never invents one.
108
- */
47
+ /** Bounded recency multiplier in [RECENCY_FLOOR, 1], half-life decayed from
48
+ * createdAt. An unparseable timestamp yields 1.0 (no penalty). */
109
49
  export function recencyNudge(createdAt, now = Date.now(), halfLifeMs = RECENCY_HALF_LIFE_MS) {
110
50
  const t = Date.parse(createdAt);
111
51
  if (!Number.isFinite(t)) return 1;
@@ -115,31 +55,18 @@ export function recencyNudge(createdAt, now = Date.now(), halfLifeMs = RECENCY_H
115
55
 
116
56
  /**
117
57
  * Pure trust for one Fact. `fact` supplies `{ sourceIds: [...], createdAt }`;
118
- * the Source individuals are resolved from `sourcesById` (a plain { id: Source }
119
- * map — exactly what a memory payload's Source individuals key into). Distinct
58
+ * Source individuals resolve from `sourcesById` ({ id: Source }). Distinct
120
59
  * sources are corroborated by noisy-OR over their type priors and nudged by
121
60
  * recency. Deterministic given the same inputs and `opts.now`.
122
61
  *
123
- * opts:
124
- * - now (ms) reference time for recency; default Date.now()
125
- * - halfLifeMs recency half-life override
126
- * - premiseTrusts entailed hook: [trusts] of the conclusion's premise Facts
127
- * - ruleConfidence entailed hook: the rule's confidence in [0,1] (default 1)
128
- *
129
- * Returns { score, inputs } — `inputs` (the source-type multiset, corroboration
130
- * count, createdAt and the recency multiplier) is stored alongside the score so
131
- * "why does this rank high?" is answerable from the record.
62
+ * opts: now, halfLifeMs, premiseTrusts/ruleConfidence (entailed hook).
63
+ * Returns { score, inputs } inputs recorded for audit.
132
64
  */
133
65
  export function computeTrust(fact, sourcesById = {}, opts = {}) {
134
66
  const now = typeof opts.now === "number" ? opts.now : Date.now();
135
67
  const ids = Array.isArray(fact?.sourceIds) ? fact.sourceIds : [];
136
68
 
137
- // distinct sources their type priors, nudged by each Source's own
138
- // mgx:sourceReliability (neutral 1.0 when absent — see sourceReliabilityOf).
139
- // `types` stays the plain type multiset (the audit-trail shape callers/tests
140
- // already read off `inputs.sourceTypes` is unchanged); `priors` is the
141
- // per-source EFFECTIVE prior (type prior × reliability, clamped to [0,1])
142
- // the noisy-OR below actually corroborates over.
69
+ // distinct sources -> type priors × mgx:sourceReliability, clamped to [0,1]
143
70
  const seen = new Set();
144
71
  const types = [];
145
72
  const priors = [];
@@ -179,43 +106,15 @@ export function computeTrust(fact, sourcesById = {}, opts = {}) {
179
106
  return { score, inputs };
180
107
  }
181
108
 
182
- // How many facts a session needs to have amassed before its track record can
183
- // swing mgx:sourceReliability with full confidence toward an extreme. Without
184
- // this, a SINGLE data point saturates the score immediately (asserted=1,
185
- // contradicted=0 → the bare max 1.5) — measured in practice, this broke the
186
- // standing invariant that a lone, uncontradicted teach-sourced fact must
187
- // still score below the operator prior (0.95 × 1.5 clamps past 1.0). This
188
- // pseudo-count keeps a thin track record close to NEUTRAL and only lets a
189
- // session earn a confident nudge once it has a real history — the classic
190
- // Bayesian-smoothing shape (Laplace/"add-k" pseudo-count) for small-sample
191
- // rates.
109
+ // Laplace/"add-k" pseudo-count: without it a single data point would saturate
110
+ // mgx:sourceReliability to the bare max/min immediately.
192
111
  export const RELIABILITY_CONFIDENCE_PSEUDOCOUNT = 19;
193
112
 
194
- /**
195
- * Pure actor-level (session-scoped) reliability from one session's track
196
- * record: how many facts it asserted vs. how many of those later turned out
197
- * to be CONTRADICTED (findContradictions, core.mjs). Bounded to
198
- * [SOURCE_RELIABILITY_MIN, SOURCE_RELIABILITY_MAX] — the same range
199
- * mgx:sourceReliability lives in (B1 above), so the result of this function is
200
- * exactly what a caller materialises onto a session's Source individual.
201
- *
202
- * Monotonic in the right direction: more uncontradicted assertions → closer to
203
- * the max (1.5); more contradicted ones → closer to the min (0.5) — but
204
- * CONFIDENCE-SCALED by sample size, so a session with only one or two
205
- * assertions stays close to neutral (1.0) either way, and only a real track
206
- * record (many assertions) earns a confident swing toward an extreme. Zero
207
- * assertions is exactly neutral (1.0) — no track record, no opinion, matching
208
- * the neutral default a Source without this attribute at all already gets
209
- * (sourceReliabilityOf above). The shape:
210
- * net = clamp[-1,1]((asserted − 2×contradicted) / max(1, asserted))
211
- * confidence = asserted / (asserted + RELIABILITY_CONFIDENCE_PSEUDOCOUNT)
212
- * ratio = (net × confidence + 1) / 2 → [0, 1], 0.5 at confidence 0
213
- * reliability = MIN + (MAX − MIN) × ratio
214
- * Each contradicted fact costs DOUBLE an asserted fact's worth of `net` (the
215
- * `2×` term), so a session that is right twice and wrong once nets a positive
216
- * but reduced score rather than a wash — corroboration should count for less
217
- * than the reputational cost of a contradiction. Deterministic, no I/O.
218
- */
113
+ /** Pure actor-level reliability from a session's asserted-vs-contradicted
114
+ * track record (findContradictions, core.mjs), confidence-scaled by sample
115
+ * size so a thin record stays near neutral (1.0). Bounded to
116
+ * [SOURCE_RELIABILITY_MIN, MAX]. A contradicted fact costs double an
117
+ * asserted one's weight. */
219
118
  export function sessionReliabilityFrom({ factsAsserted = 0, factsContradicted = 0 } = {}) {
220
119
  const asserted = Math.max(0, Number(factsAsserted) || 0);
221
120
  const contradicted = Math.max(0, Number(factsContradicted) || 0);
@@ -1,36 +1,13 @@
1
1
  // memory-ask-browser-entry.mjs — the esbuild entry for `tmct viz`'s embedded
2
- // "Ask the graph" panel's MEMORY-graph engine (PLAN_VIZ_MEMORY.md Bug 1 fix).
2
+ // "Ask the graph" panel's memory-graph engine.
3
3
  //
4
- // Bug 1: the panel bundled ONLY ask.mjs tmct's code-graph query engine
5
- // ("which modules import X"), which has no concept of Facts/corpus data at
6
- // all, so a memory-graph question like "what is a dog" always missed even
7
- // though the page's own embedded payload had the answer. This is the SECOND,
8
- // narrow browser entry point src/ask-browser-entry.mjs's own doc comment
9
- // anticipates: re-exports just `factAnswer` (src/chat.mjs) — tmct's REAL
10
- // memory-graph answer engine, the same one `npm run chat` uses — plus
11
- // `createInMemoryStore` (src/memory/core.mjs), which is how the panel hands
12
- // `factAnswer` the page's already-embedded PAYLOAD with ZERO fs I/O: a
13
- // Backend-B handle's `loadMemory` branch returns `handle.payload` directly, no
14
- // bundle-time module shimming needed (see factAnswer's own doc comment,
15
- // src/chat.mjs, for the full reasoning — a simpler, more robust mechanism than
16
- // intercepting loadMemory at bundle time, since it reuses machinery the
17
- // codebase already ships and tests, rather than a new esbuild-only code path
18
- // that could drift from the real one).
19
- //
20
- // `factAnswer` is called with `envelope: null, miss: true` — the exact,
21
- // already-documented "no envelope available" bootstrap path (chat.mjs's own
22
- // comments: "the FIRST turn of a graph-less session... leaves `envelope` null
23
- // for the rest of THIS turn's processing" — a real, tested code path, not a
24
- // hack) — which arms factAnswer's own bare-question regex fallbacks
25
- // (BARE_WHATIS_RE and friends) to parse the query directly, with no
26
- // dependency on the much larger structural-graph parse pipeline
27
- // (dispatchTool/loadGraph, server.mjs) that pipeline needs a real --repo code
28
- // index for and this panel has no use for.
4
+ // Re-exports `factAnswer` (src/chat.mjs) and `createInMemoryStore`
5
+ // (src/memory/core.mjs), which lets the panel hand `factAnswer` the page's
6
+ // already-embedded payload with zero fs I/O. Called with `envelope: null,
7
+ // miss: true` to arm factAnswer's bare-question regex fallbacks directly,
8
+ // bypassing the structural-graph parse pipeline this panel has no use for.
29
9
  import { factAnswer } from "./chat.mjs";
30
10
  import { createInMemoryStore, normFactTerm } from "./memory/core.mjs";
31
11
 
32
- // normFactTerm is re-exported too viz.mjs's client-side "focus follows the
33
- // memory-engine's answer" heuristic (guessTermIdFromQuery) needs the SAME
34
- // term normalization the CLI's own `--term` seed flag and factAnswer's own
35
- // subject/object matching use, never a second hand-rolled copy.
12
+ // normFactTerm is re-exported too, for viz.mjs's client-side term normalization.
36
13
  globalThis.tmctMemoryAsk = { factAnswer, createInMemoryStore, normFactTerm };
@@ -2630,7 +2630,7 @@ CREATE INDEX IF NOT EXISTS edges_by_prop ON edges(prop);
2630
2630
  // (neither recognizes the bare "is in" idiom once "else" sits in front of it).
2631
2631
  // The only rescue was the relaxation cascade's drop-unmatched layer — but that
2632
2632
  // layer refuses to accept a relaxed reading that still renders an honest EMPTY
2633
- // (by design: relaxation must turn a miss into a real answer, never into
2633
+ // (: relaxation must turn a miss into a real answer, never into
2634
2634
  // another kind of miss), so a genuinely empty class ("what else is in
2635
2635
  // Task.complete" — a method, no members) bottomed out at the bare grammar
2636
2636
  // wall instead of the specific "no contains edges" receipt. Routing this
@@ -1,48 +1,23 @@
1
- // paraphrase.mjs — PLAN_BREADTH_FIRST_NLU.md (c) / ROADMAP.md "Ambition":
2
- // "Paraphrase alongside the original, verified, never instead of it." A
3
- // surface-realization variant of an isa-family (`rdfs:subClassOf`) teach
4
- // confirmation, shown NEXT TO the original literal confirmation, never
5
- // replacing it and never shown at all unless its accuracy is checked by
6
- // running tmct's OWN deterministic inference machinery (`src/syllogise.mjs`)
7
- // against both the original and the paraphrase.
1
+ // paraphrase.mjs — a surface-realization variant of an isa-family
2
+ // (`rdfs:subClassOf`) teach confirmation, shown alongside the original, never
3
+ // replacing it, and only when verified against tmct's own inference engine
4
+ // (src/syllogise.mjs). Scoped to isa-family facts only the one predicate
5
+ // family deriveSubClassClosure reasons over.
8
6
  //
9
- // Scope, deliberately narrow: isa-family (`rdfs:subClassOf`) facts only the
10
- // one predicate family `syllogise.mjs`'s `deriveSubClassClosure` actually
11
- // reasons over. tmct's other taught predicate families (someValuesFrom,
12
- // disjointWith, cardinality) have their own entailment rules in syllogise.mjs
13
- // too, but this pass covers the single most common teach shape ("X is a kind
14
- // of Y") the plan's own worked example (ROADMAP.md's Ambition section, the
15
- // `PLAN_BREADTH_FIRST_NLU.md` §8 canonical example) already anchors on;
16
- // widening to the other predicate families is a natural, separately-scoped
17
- // follow-on once this shape is proven live.
18
- //
19
- // The generator and the recognizer are a MATCHED PAIR by construction — every
20
- // template `paraphraseSubClass` can produce has a corresponding branch in
21
- // `recoverSubClassTriple` that parses it back to exactly the same
22
- // {subject, object} pair. This is a CLOSED set (never open-ended NLP), so
23
- // recognition is exact, not fuzzy. "Verified" means: re-derive the
24
- // `rdfs:subClassOf` transitive closure (the real conclusions this fact would
25
- // license, via `deriveSubClassClosure`) once seeded with the ORIGINAL triple
26
- // and once seeded with the triple RECOVERED FROM the paraphrase, over the
27
- // SAME existing taught edges — the two closures must be identical (both
28
- // derived edge SETS byte-for-byte equal). A generator bug that silently
29
- // swapped subject/object (a real risk for a passive-voice template) would be
30
- // caught here even in a graph with no other taught facts at all, because a
31
- // swapped pair changes the closure's own subject/object roles the moment any
32
- // OTHER edge touches either term — exactly the "must entail the same
33
- // conclusions, neither may contradict the other" check the Ambition asks for,
34
- // not a shallower string-equality stand-in for it.
7
+ // Verification: re-derive the rdfs:subClassOf closure once from the original
8
+ // triple and once from the triple recovered by parsing the paraphrase back —
9
+ // over the same existing taught edges, the two closures must be identical.
10
+ // This catches a generator bug that silently swapped subject/object, since a
11
+ // swap changes the closure's roles as soon as any other edge touches either
12
+ // term.
35
13
 
36
14
  import { deriveSubClassClosure } from "./syllogise.mjs";
37
15
  import { normFactTerm } from "./memory/core.mjs";
38
16
  import { fnv1aHex } from "./hash.mjs";
39
17
 
40
18
  // Every template reads "SUBJECT ⊑ OBJECT" left to right — no passive/reordered
41
- // form is offered, since a reordered form is exactly the shape most likely to
42
- // invert subject/object under a naive regex recognizer; keeping the pair's
43
- // left-to-right order fixed across every template keeps the recognizer trivial
44
- // AND correct by construction, rather than needing the closure check to catch
45
- // a bug the generator could have avoided entirely.
19
+ // form, since that's the shape most likely to invert subject/object under a
20
+ // naive regex recognizer.
46
21
  const articleFor = (word) => (/^[aeiou]/i.test(String(word || "")) ? "an" : "a");
47
22
  const SUBCLASS_TEMPLATES = [
48
23
  (s, o) => `${s} is a kind of ${o}`,
@@ -51,10 +26,8 @@ const SUBCLASS_TEMPLATES = [
51
26
  (s, o) => `${s} counts as ${articleFor(o)} ${o}`,
52
27
  ];
53
28
 
54
- // One regex per template above, in the SAME order deliberately paired by
55
- // index rather than derived/inverted from the generator, so a change to one
56
- // side can never silently desync from the other without a test catching it
57
- // (see paraphrase.test.mjs's own round-trip check over every template).
29
+ // One regex per template above, in the SAME order (paired by index, not
30
+ // derived from the generator).
58
31
  const SUBCLASS_RECOGNIZERS = [
59
32
  /^(.+?)\s+is\s+a\s+kind\s+of\s+(.+)$/i,
60
33
  /^(.+?)\s+is\s+a\s+type\s+of\s+(.+)$/i,
@@ -92,17 +65,11 @@ export function recoverSubClassTriple(text) {
92
65
  return null;
93
66
  }
94
67
 
95
- /** The actual verification step: does re-deriving the rdfs:subClassOf closure
96
- * from the paraphrase's own recovered triple produce the SAME entailed edge
97
- * set as deriving it from the original triple, over the same pre-existing
98
- * taught edges? `existingEdges` is `[[a,b], …]` the SAME already-normalized
99
- * pair-list shape `deriveSubClassClosure` itself takes (subClassEdges read
100
- * straight off taught Fact rows elsewhere in this codebase). Returns
101
- * `{verified, closure}` — `closure` is the original triple's own derived
102
- * conclusions (handed back so a caller can show/log them), `verified` is
103
- * false when the paraphrase's recovered triple doesn't reparse, doesn't match
104
- * the original's normalized (subject, object), or derives a different
105
- * closure — any one of those means the paraphrase must NOT be shown. */
68
+ /** Verify: re-derive the rdfs:subClassOf closure from the original triple and
69
+ * from the paraphrase's recovered triple, over the same existing taught
70
+ * edges. Returns `{verified, closure}` `verified` is false if the
71
+ * paraphrase doesn't reparse, doesn't match the original (subject, object),
72
+ * or derives a different closure. */
106
73
  export function verifySubClassParaphrase(subject, object, paraphraseText, existingEdges = []) {
107
74
  const origSubj = normFactTerm(subject);
108
75
  const origObj = normFactTerm(object);