@polycode-projects/the-mechanical-code-talker 0.9.6 → 0.9.7

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/ROADMAP.md CHANGED
@@ -12,11 +12,28 @@ mapped into them (item numbers retained for traceability); the seven sketches
12
12
  formerly held in `code-talker-ideas.txt` are folded into items 8–11 below and
13
13
  the file has been deleted.
14
14
 
15
- ## Where we are now (2026-07-07)
15
+ ## Where we are now (2026-07-08)
16
16
 
17
- `npm test` green (**1042**). **v0.9.5, pushed** (0.8.2 → 0.9.0 for this session's main wave, then
18
- one patch per shipped playtest-sprint fix — see `HANDOVER.md` for the exact release chain and the
19
- "Playtest sprint" section there for what each patch fixed).
17
+ `npm test` green (**1055**). **v0.9.6, pushed** (0.8.2 → 0.9.6 across this session see
18
+ `HANDOVER.md` for the exact release chain).
19
+
20
+ ### Doing right now (5 background clusters, all worktree-isolated, merged sequentially as each lands)
21
+
22
+ - **Cluster A** — Bug 6 (scoped-listing false-empty) + Bug 7 (modal-in-fuzzy-cascade) + the
23
+ chat-feel residual pair (items 1-2 of the trio's siblings) + `edgesOfKind` memoization +
24
+ ontology numeric-vocabulary declaration.
25
+ - **Cluster B** — Bug 8 (goal-reasoner confident-wrong on off-domain requests) + refreshing
26
+ `PLAN_CAPABILITY_ROUTER.md`'s stale stage-5 status text.
27
+ - **Cluster C** — `PLAN_CHAT_FEEL.md` item 6, the pronoun/temporal/discourse-count trio
28
+ (measured red sets from an earlier advisor tick; re-measured against the current graded pool
29
+ before any fix).
30
+ - **Cluster D** — `PLAN_CODE.md` Track 1 (GOAL_RULE/PHRASING_FRAMES synthesis) — operator
31
+ sign-off given this session; Tracks 2-4 remain unsigned-off and untouched.
32
+ - **Cluster E** — `PLAN_INFERENCE_TESTING.md` Stage 1 (`cax-sco` rule) + Stage 2 (proof-chain
33
+ receipts) in `src/syllogise.mjs`, targeting the measured chat-A2 50% ceiling.
34
+
35
+ Also in flight: playtest sprint rounds 4-8 (3/3 rounds so far shipped a real fix), the strategy
36
+ advisor (restarted, 5-min re-arm).
20
37
 
21
38
  ### Now: shipped this session
22
39
 
@@ -44,16 +61,23 @@ one patch per shipped playtest-sprint fix — see `HANDOVER.md` for the exact re
44
61
  - **4 new plan docs.** `PLAN_ontology-hierarchies.md`, `PLAN_INFERENCE_TESTING.md` (revised so
45
62
  infbench generation is mechanical, not hand-authored), `archive/PLAN_PREDICATE_QUERIES.md`, and
46
63
  `PLAN_CODE.md` (new, program synthesis over tmct's closed DSLs, gated on explicit operator
47
- sign-off per track, not built yet).
64
+ sign-off per track — Track 1 signed off, see "Doing" above).
65
+ - **`PLAN_TMCT_ECOSYSTEM_INTEGRATION.md`**, landed — a code-grounded tmct/bedrock-meter/marginalia
66
+ integration plan. Corrects a standing wrong assumption (the `/v1/messages` shim was believed
67
+ unmerged; it's been on `main` since 0.8.0) and confirms seonix has already fully migrated onto
68
+ tmct as a real dependency.
69
+ - **A `/narrate` debug mode.** Opt-in (`/narrate on`, `--narrate`, `TMCT_NARRATE=1`) per-turn
70
+ decision narrative — which lane/pattern matched, results + sources, a deterministic goal
71
+ deduction — built for playtest-sprint debugging; zero cost when off (byte-identical regression
72
+ guard).
48
73
 
49
74
  ### Next: the open follow-ups
50
75
 
51
76
  In priority order (full detail and measured targets in `HANDOVER.md`):
52
77
 
53
- 1. **Bug 8.** The goal-reasoner's global-mode deduction answers requests unrelated to any deduced
54
- goal with false confidence, once no focus entity binds (`src/router/goal-reasoner.mjs:226`).
55
- The most serious open issue: a genuine confident-wrong failure in the flagship
56
- zero-hallucination capability. Needs a real semantic gate.
78
+ 1. **Bug 8 — FIXED.** A global-mode domain gate now requires the request itself (not just the
79
+ caller's declared toolset) to parse as a query about the deduced goal's domain, reusing
80
+ `ask.mjs`'s own grammar. Zero-hallucination baseline unchanged; independently re-verified.
57
81
  2. **Bug 6.** Scoped listing false-empty: "list modules in `<pkg>`" returns empty though the
58
82
  unscoped lister shows matches. Found dogfooding a 191k-entity monorepo graph.
59
83
  3. **Bug 7.** A modal auxiliary ("should") survives the fuzzy-correction cascade and misreads as
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polycode-projects/the-mechanical-code-talker",
3
- "version": "0.9.6",
3
+ "version": "0.9.7",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "The Mechanical Code Talker (tmct) — a tolerant, offline, $0 chat surface that guides you toward precision queries about a software repository. ELIZA/PARRY-style but domain-obsessed with code. No model calls; no codebase index of its own.",
@@ -40,11 +40,32 @@
40
40
  // (queries mutate nothing — the STRIPS closed world), so no step can delete a
41
41
  // condition another goal depends on. We compute this from the registry rather
42
42
  // than assume it (threatsAmong), so the guarantee is grounded, not asserted.
43
+ //
44
+ // THE GLOBAL-MODE DOMAIN GATE (Bug 8 fix). In SCOPED mode, relevance is already
45
+ // proven structurally: the focus is a REAL bound graph entity (resolveObject
46
+ // found it), so the request is provably about something in the graph. In GLOBAL
47
+ // mode there is no focus to bind, and `applicableRules` alone only screens the
48
+ // CALLER'S DECLARED TOOLSET — a caller-constant fact that says nothing about
49
+ // whether THIS request has any connection to the deduced goal (a caller who
50
+ // declares tmct_untested/tmct_impact once per session would ground
51
+ // coverage-invariant for every off-topic turn). The fix reuses ask.mjs's OWN
52
+ // compositional NL grammar (parseQuery — the SAME primitive the C1 resolver
53
+ // already parses every request with, see resolver.mjs mapParse) as a structural
54
+ // relevance check, never a new keyword table: does the request even COMPILE to a
55
+ // recognized graph-query shape naming a known entity kind, and does that kind
56
+ // match the rule's declared focusClass? A request parseQuery cannot place at all
57
+ // (null — "write a haiku about pizza") or places without landing on any
58
+ // recognized entity kind (a miss with no entity kind — "how many pizzas are
59
+ // there") is an honest "not about this graph" signal; a request parseQuery
60
+ // resolves to a real AST naming the rule's focus class ("which module is the
61
+ // biggest testing risk" -> {node:"superlative", entityType:"Module", ...})
62
+ // stays exactly as reachable as before. Zero request keywords added.
43
63
 
44
64
  import { backwardChain, extractEntity } from "./resolver.mjs";
45
65
  import { capabilityByName, effectsOf } from "./registry.mjs";
46
66
  import { hallucinationsIn } from "./call-validator.mjs";
47
67
  import { intersect } from "./set-algebra.mjs";
68
+ import { parseQuery } from "../ask.mjs";
48
69
 
49
70
  // Hard OUTER-tick budget — the meta-loop runs at most this many ticks, then
50
71
  // REFUSES (escalate). Independent of BDI convergence and of the monotone
@@ -178,6 +199,25 @@ function focusOf(request, ctx) {
178
199
  return r && r.match && !r.ambiguous ? r.match : null;
179
200
  }
180
201
 
202
+ /** The GLOBAL-MODE DOMAIN GATE's primitive: what entity CLASS (if any) did
203
+ * ask.mjs's own compositional NL grammar recognize in the request? Walks
204
+ * parseQuery's AST (the same shapes resolver.mjs's mapParse/mapFrame already
205
+ * consume) for its declared `entityType` field, unwrapping the wrapper nodes
206
+ * (`clause`, `inner`, `base`) that carry no entityType of their own. Returns the
207
+ * class name, or null when the grammar placed nothing (an outright non-parse) or
208
+ * placed a MISS with no recognized entity kind at all ("how many pizzas are
209
+ * there" -> {node:"miss", reason:"count needs a known entity kind..."} carries no
210
+ * entityType, same as a flat null). Pure; no request-string keyword table — it
211
+ * reads a field ask.mjs's grammar already computes for every request. */
212
+ function parsedEntityType(node) {
213
+ if (!node || typeof node !== "object") return null;
214
+ if (typeof node.entityType === "string") return node.entityType;
215
+ if (node.clause) return parsedEntityType(node.clause);
216
+ if (node.inner) return parsedEntityType(node.inner);
217
+ if (node.base) return parsedEntityType(node.base);
218
+ return null;
219
+ }
220
+
181
221
  /** Ground ONE epistemic sub-goal (a topic + optional bound entity) into a
182
222
  * grounded, EXECUTED call, or null when it is not groundable in the declared
183
223
  * toolset (=> the meta-loop escalates). Backward-chains topic->capability, binds
@@ -237,13 +277,31 @@ export async function goalReason(request, tools, ctx, { driver = "goal-0.8.1" }
237
277
  // request's scope in this toolset); >1 => an AMBIGUOUS meta-goal (arbitration
238
278
  // between meta-goals is undeclared) — both are honest refusals, never a guess.
239
279
  const applicable = applicableRules(declared, focus, mode);
240
- if (!applicable.length) {
280
+
281
+ // THE GLOBAL-MODE DOMAIN GATE (Bug 8 fix, see the module header). SCOPED mode
282
+ // already proved relevance via a bound graph entity; GLOBAL mode has not, so
283
+ // `applicable` alone (a pure function of the caller's DECLARED TOOLSET) is not
284
+ // enough — it says nothing about whether THIS request is even about the graph.
285
+ // Screen it against ask.mjs's own NL grammar: the request must parse to a shape
286
+ // naming the candidate rule's declared focusClass, or it is refused as honestly
287
+ // off-domain rather than answered with someone else's goal.
288
+ let domainRelevant = applicable;
289
+ if (mode === "global" && applicable.length) {
290
+ const requestClass = parsedEntityType(parseQuery(request));
291
+ domainRelevant = applicable.filter((r) => requestClass === r.focusClass);
292
+ if (!domainRelevant.length) {
293
+ const classes = [...new Set(applicable.map((r) => r.focusClass))].join("/");
294
+ return refuse(`open-world: the request does not parse as a query about ${classes} (ask.mjs's own NL grammar names ${requestClass || "no recognized entity kind"} in it) — global goal deduction needs the REQUEST ITSELF to be about the deduced goal's domain, not just a declared toolset that happens to ground it — escalate`, driver);
295
+ }
296
+ }
297
+
298
+ if (!domainRelevant.length) {
241
299
  return refuse(`open-world: no declared goal-rule is applicable in ${mode} mode (each needs a sub-goal capability outside the declared toolset, or a scope it does not declare) — escalate`, driver);
242
300
  }
243
- if (applicable.length > 1) {
244
- return refuse(`ambiguous meta-goal: ${applicable.length} declared goal-rules apply (${applicable.map((r) => r.id).join(", ")}) — meta-goal arbitration is undeclared, refuse rather than guess — escalate`, driver);
301
+ if (domainRelevant.length > 1) {
302
+ return refuse(`ambiguous meta-goal: ${domainRelevant.length} declared goal-rules apply (${domainRelevant.map((r) => r.id).join(", ")}) — meta-goal arbitration is undeclared, refuse rather than guess — escalate`, driver);
245
303
  }
246
- const rule = applicable[0];
304
+ const rule = domainRelevant[0];
247
305
 
248
306
  // the glass-box WHY, citing the declared goal-rule by backward-chain (the C2
249
307
  // twin of resolver.mjs's "backward-chain => <capability>" provenance).