@polycode-projects/the-mechanical-code-talker 0.8.1 → 0.9.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.
@@ -43,8 +43,8 @@
43
43
 
44
44
  import { backwardChain, extractEntity } from "./resolver.mjs";
45
45
  import { capabilityByName, effectsOf } from "./registry.mjs";
46
- import { hallucinationsIn } from "../../agentbench/grade.mjs";
47
- import { intersect } from "../../agentbench/results.mjs";
46
+ import { hallucinationsIn } from "./call-validator.mjs";
47
+ import { intersect } from "./set-algebra.mjs";
48
48
 
49
49
  // Hard OUTER-tick budget — the meta-loop runs at most this many ticks, then
50
50
  // REFUSES (escalate). Independent of BDI convergence and of the monotone
@@ -55,8 +55,23 @@ export const MAX_TICKS = 16;
55
55
  // ---- the DECLARED goal model (data, mirroring registry.mjs's STRIPS operators)
56
56
  // A goal-rule is a maintenance INVARIANT over the graph, plus the epistemic
57
57
  // sub-goals whose facts decide whether it is violated and the DECLARED priority
58
- // that breaks ties in first-step arbitration. Growing this set is the "long-chain
58
+ // that settles ties in first-step arbitration. Growing this set is the "long-chain
59
59
  // deduction library" the RFC flags — same discipline as syllogise's rule set.
60
+ //
61
+ // Each rule declares (pure frozen data, no code):
62
+ // focusClass — the entity class the scoped reading binds its focus to.
63
+ // modes — which deduced scopes the rule covers ("scoped" = a bound focus;
64
+ // "global" = whole-graph keystone arbitration). A rule whose
65
+ // sub-goals are all entity-scoped has no global reading.
66
+ // subGoals — the epistemic facts to gather, IN DECLARED ORDER (each
67
+ // backward-chains to a capability, exactly like an NL intent).
68
+ // compose — the declarative fold of the gathered facts into the scoped
69
+ // answer: intersect(a, b), each side naming a gathered topic,
70
+ // optionally bound to the focus (`of:"focus"`), optionally with
71
+ // the focus itself unioned in (`withFocus` — the change footprint).
72
+ // priorityTopic / coverageTopic — the global keystone arbitration keys
73
+ // (argmax |priority(m)| over the coverage-violating set).
74
+ // achieves — the meta-goal topic the composed answer achieves.
60
75
  export const GOAL_RULES = Object.freeze([
61
76
  Object.freeze({
62
77
  id: "coverage-invariant",
@@ -65,8 +80,8 @@ export const GOAL_RULES = Object.freeze([
65
80
  // closure) MUST have direct test coverage. A Module that is untested AND
66
81
  // impactful VIOLATES it — an active goal to close the coverage gap.
67
82
  invariant: "an impactful module must be tested",
68
- // the epistemic facts a plan must gather to evaluate the invariant (each
69
- // backward-chains to a capability, exactly like the resolver's NL intents).
83
+ focusClass: "Module",
84
+ modes: Object.freeze(["scoped", "global"]),
70
85
  subGoals: Object.freeze(["impact", "untested"]),
71
86
  // the DECLARED priority key for first-step arbitration: a violation's
72
87
  // priority is its blast radius |impact(module)| — the wider the reach, the
@@ -74,9 +89,41 @@ export const GOAL_RULES = Object.freeze([
74
89
  priorityTopic: "impact",
75
90
  // the coverage predicate the invariant screens on.
76
91
  coverageTopic: "untested",
92
+ // scoped fold: untested ∩ ({focus} ∪ impact(focus)) — the change footprint.
93
+ compose: Object.freeze({
94
+ op: "intersect",
95
+ a: Object.freeze({ topic: "untested" }),
96
+ b: Object.freeze({ topic: "impact", of: "focus", withFocus: true }),
97
+ names: "the change's untested footprint",
98
+ empty: "no coverage gap",
99
+ }),
77
100
  // the meta-goal topic the composed answer achieves (backward-chained below).
78
101
  achieves: "coverage-gap",
79
102
  }),
103
+ Object.freeze({
104
+ id: "cochange-risk-invariant",
105
+ kind: "maintenance",
106
+ // INVARIANT: a module CHANGE-COUPLED with the focus (they historically land
107
+ // in the same commits) MUST have direct test coverage. A coupled module that
108
+ // is untested VIOLATES it — an active goal over the focus's coupling set.
109
+ invariant: "a module change-coupled with the focus must be tested",
110
+ focusClass: "Module",
111
+ // scoped ONLY: both sub-goals are read relative to a bound focus; there is
112
+ // no whole-graph keystone reading declared for change-coupling.
113
+ modes: Object.freeze(["scoped"]),
114
+ subGoals: Object.freeze(["cochanges", "untested"]),
115
+ priorityTopic: "cochanges",
116
+ coverageTopic: "untested",
117
+ // scoped fold: cochanges(focus) ∩ untested — the coupled-but-untested set.
118
+ compose: Object.freeze({
119
+ op: "intersect",
120
+ a: Object.freeze({ topic: "cochanges", of: "focus" }),
121
+ b: Object.freeze({ topic: "untested" }),
122
+ names: "the change-coupled untested set",
123
+ empty: "every change-coupled module is tested",
124
+ }),
125
+ achieves: "cochange-risk",
126
+ }),
80
127
  ]);
81
128
 
82
129
  /** Backward-chain a meta-goal topic to the declared goal-rule that achieves it —
@@ -85,6 +132,28 @@ export function backwardChainGoal(topic) {
85
132
  return GOAL_RULES.find((r) => r.achieves === topic) || null;
86
133
  }
87
134
 
135
+ /** THE RULE-SELECTION DEDUCTION (replaces the old single-rule hard-wiring): a
136
+ * declared goal-rule APPLIES to a request iff
137
+ * (1) every one of its epistemic sub-goal topics backward-chains to a
138
+ * capability IN the declared toolset (the closed-world groundability
139
+ * screen — the meta-level twin of "never select an out-of-set call"),
140
+ * (2) the deduced mode is one of the rule's declared modes, and
141
+ * (3) a scoped reading's bound focus is of the rule's declared focusClass.
142
+ * Pure over the goal model + registry: nothing here reads the request string.
143
+ * The caller REFUSES on zero matches (the open-world goal-generation seam) and
144
+ * on more than one (an ambiguous meta-goal — arbitration between meta-goals is
145
+ * undeclared, so guessing one would be an invented goal). */
146
+ export function applicableRules(declaredTools, focus, mode) {
147
+ const declared = Array.isArray(declaredTools) ? declaredTools : [];
148
+ return GOAL_RULES.filter((rule) =>
149
+ rule.modes.includes(mode)
150
+ && (mode !== "scoped" || (focus != null && focus.class === rule.focusClass))
151
+ && rule.subGoals.every((topic) => {
152
+ const cap = backwardChain(topic);
153
+ return Boolean(cap && declared.includes(cap.name));
154
+ }));
155
+ }
156
+
88
157
  const refuse = (why, driver) => ({ calls: [], refused: true, terminated: true, proof: [], composed: null, driver, why });
89
158
 
90
159
  /** THREATS lifted to the meta-level: any pending intention whose needed condition
@@ -130,11 +199,12 @@ async function groundSubGoal(topic, entityLabel, tools, ctx) {
130
199
  }
131
200
 
132
201
  /** BDI DROP CONDITIONS (Rao & Georgeff): an intention persists until it is
133
- * achieved / impossible / its goal lapses. Returns the reason string, or null
134
- * to KEEP committing to it. Pureunit-testable in isolation. */
135
- export function dropCondition(intention, observed, mode, focus) {
202
+ * achieved / impossible / its goal lapses. `focusClass` is the SELECTED rule's
203
+ * declared focus class (never a literal here the rule is the authority).
204
+ * Returns the reason string, or null to KEEP committing to it. Pure. */
205
+ export function dropCondition(intention, observed, mode, focus, focusClass) {
136
206
  if (observed.has(intention.key)) return "achieved"; // fact now gathered
137
- if (mode === "scoped" && (!focus || focus.class !== "Module")) return "lapsed"; // focus moved
207
+ if (mode === "scoped" && (!focus || focus.class !== focusClass)) return "lapsed"; // focus moved
138
208
  return null; // else keep the commitment
139
209
  }
140
210
 
@@ -148,21 +218,32 @@ export function dropCondition(intention, observed, mode, focus) {
148
218
  * ctx: { dispatch(name,input)->{ok,result}, resolve(term)->{match,ambiguous} }. */
149
219
  export async function goalReason(request, tools, ctx, { driver = "goal-0.8.1" } = {}) {
150
220
  const declared = Array.isArray(tools) ? tools : [];
151
- const rule = backwardChainGoal("coverage-gap");
152
- if (!rule) return refuse("no declared goal-rule achieves the meta-goal — escalate", driver);
153
221
 
154
- // STEP 1 — deduce the goal scope from the DECLARED model + a bound focus.
222
+ // STEP 1 — deduce the goal scope from the DECLARED model + a bound focus,
223
+ // then SELECT the goal-rule by pure applicability (no request keyword ever):
224
+ // a bound focus reads scoped, no focus reads global (keystone arbitration).
155
225
  const focus = focusOf(request, ctx);
156
- let mode;
157
- if (focus && focus.class === "Module") mode = "scoped"; // assess the focus module's change footprint
158
- else if (focus) mode = "escalate"; // a non-Module focus: no declared rule covers it
159
- else mode = "global"; // no focus => rank the whole codebase (keystone)
226
+ const mode = focus ? "scoped" : "global";
160
227
 
161
- // The open-world goal-generation seam, named honestly: a resolved focus the
162
- // declared goal model does not cover is REFUSED, never given an invented goal.
163
- if (mode === "escalate") {
164
- return refuse(`open-world: no declared goal-rule covers a ${focus.class} focus (the coverage-invariant is Module-scoped) escalate`, driver);
228
+ // The open-world goal-generation seam, named honestly: a resolved focus whose
229
+ // class NO declared goal-rule scopes is REFUSED, never given an invented goal.
230
+ if (focus && !GOAL_RULES.some((r) => r.focusClass === focus.class)) {
231
+ const covered = [...new Set(GOAL_RULES.map((r) => r.focusClass))].join("/");
232
+ return refuse(`open-world: no declared goal-rule covers a ${focus.class} focus (the declared goal-rules are ${covered}-scoped) — escalate`, driver);
233
+ }
234
+
235
+ // Rule selection is a DEDUCTION over the goal model + the declared toolset:
236
+ // 0 applicable rules => the same open-world seam (nothing declared grounds the
237
+ // request's scope in this toolset); >1 => an AMBIGUOUS meta-goal (arbitration
238
+ // between meta-goals is undeclared) — both are honest refusals, never a guess.
239
+ const applicable = applicableRules(declared, focus, mode);
240
+ if (!applicable.length) {
241
+ 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
+ }
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);
165
245
  }
246
+ const rule = applicable[0];
166
247
 
167
248
  // the glass-box WHY, citing the declared goal-rule by backward-chain (the C2
168
249
  // twin of resolver.mjs's "backward-chain => <capability>" provenance).
@@ -175,15 +256,22 @@ export async function goalReason(request, tools, ctx, { driver = "goal-0.8.1" }
175
256
  const calls = [];
176
257
  const observed = new Map(); // intention.key -> gathered result set
177
258
 
178
- // STEP 2/3 — the pending INTENTIONS (epistemic sub-goals), each carrying its
179
- // declared execution order (arbitration is least-commitment: min order first,
180
- // the keystone selection over the gathered facts happens at compose).
181
- // scoped: gather impact(focus) then the untested coverage-scan.
182
- // global: gather untested first, then EXPAND to impact-of-each (GDA replan).
183
- const pending = mode === "scoped"
184
- ? [{ topic: "impact", of: focus.label, key: `impact:${focus.label}`, order: 0 },
185
- { topic: "untested", of: null, key: "untested", order: 1 }]
186
- : [{ topic: "untested", of: null, key: "untested", order: 0 }];
259
+ // STEP 2/3 — the pending INTENTIONS: the rule's epistemic sub-goals IN
260
+ // DECLARED ORDER (arbitration is least-commitment: min order first, the
261
+ // keystone selection over the gathered facts happens at compose). A topic
262
+ // binds the focus iff its capability declares a REQUIRED parameter (read from
263
+ // the registry, never special-cased by topic name); in global mode there is
264
+ // no focus to bind, so entity-scoped topics are deferred to the GDA expansion
265
+ // (gather the coverage scan first, then EXPAND to priority-of-each violator).
266
+ const bindsEntity = (topic) => {
267
+ const cap = backwardChain(topic);
268
+ return Boolean(cap && cap.parameters.some((p) => p.required));
269
+ };
270
+ const pending = rule.subGoals
271
+ .filter((topic) => mode === "scoped" || !bindsEntity(topic))
272
+ .map((topic, i) => (mode === "scoped" && bindsEntity(topic)
273
+ ? { topic, of: focus.label, key: `${topic}:${focus.label}`, order: i }
274
+ : { topic, of: null, key: topic, order: i }));
187
275
 
188
276
  let committed = null; // the persisted BDI intention (not re-derived each tick)
189
277
  let expanded = false; // one-shot guard: the single bounded GDA expansion
@@ -197,7 +285,7 @@ export async function goalReason(request, tools, ctx, { driver = "goal-0.8.1" }
197
285
  // (3b) PERSISTENCE — keep the committed intention unless a BDI drop condition
198
286
  // fires; only THEN re-arbitrate. This is the "commitment, not recomputed
199
287
  // preference" that stops the loop thrashing.
200
- if (committed && dropCondition(committed, observed, mode, focus)) committed = null;
288
+ if (committed && dropCondition(committed, observed, mode, focus, rule.focusClass)) committed = null;
201
289
  if (!committed || !pending.includes(committed)) {
202
290
  // (3a) FIRST-STEP ARBITRATION — least-commitment: the lowest declared order
203
291
  // among pending. Threat-aware: skip a step that would clobber another
@@ -220,17 +308,17 @@ export async function goalReason(request, tools, ctx, { driver = "goal-0.8.1" }
220
308
  pending.splice(pending.indexOf(committed), 1);
221
309
  committed = null;
222
310
 
223
- // GDA EXPANSION (monitor -> replan), ONCE: on observing the untested set in
224
- // global mode, expand to the priority sub-goal (impact) for each violating
225
- // module, so arbitration can rank them. Bounded by |untested| (finite) and
226
- // fired at most once (the `expanded` guard) => the pending set still
227
- // converges.
311
+ // GDA EXPANSION (monitor -> replan), ONCE: on observing the coverage set in
312
+ // global mode (guarded on the rule DECLARING a global reading), expand to
313
+ // the priority sub-goal for each violating module, so arbitration can rank
314
+ // them. Bounded by the finite coverage set and fired at most once (the
315
+ // `expanded` guard) => the pending set still converges.
228
316
  let expandedThisTick = false;
229
- if (mode === "global" && achievedTopic === rule.coverageTopic && !expanded) {
317
+ if (mode === "global" && rule.modes.includes("global") && achievedTopic === rule.coverageTopic && !expanded) {
230
318
  expanded = true;
231
319
  expandedThisTick = true;
232
- const untested = observed.get("untested") || [];
233
- untested.forEach((m, i) => pending.push({ topic: rule.priorityTopic, of: m, key: `impact:${m}`, order: 100 + i }));
320
+ const violating = observed.get(rule.coverageTopic) || [];
321
+ violating.forEach((m, i) => pending.push({ topic: rule.priorityTopic, of: m, key: `${rule.priorityTopic}:${m}`, order: 100 + i }));
234
322
  }
235
323
 
236
324
  // the invariant, enforced mechanically: the pending set shrank by one this
@@ -245,21 +333,31 @@ export async function goalReason(request, tools, ctx, { driver = "goal-0.8.1" }
245
333
  // facts (all INSIDE the driver's timeout guard; no unbounded post-work).
246
334
  let composed;
247
335
  if (mode === "scoped") {
248
- // the coverage-gap of the focus: the untested modules in its change
249
- // FOOTPRINT ({focus} ∪ its impact closure) — a real composed set (∅ = no gap).
250
- const footprint = [focus.label, ...(observed.get(`impact:${focus.label}`) || [])];
251
- composed = intersect(observed.get("untested") || [], footprint);
252
- why.push(`compose: untested ({${focus.label}} impact) = the change's untested footprint (${composed.length ? composed.join(", ") : "∅ — no coverage gap"})`);
336
+ // interpret the rule's DECLARATIVE compose spec: intersect two gathered
337
+ // sides, each a topic (optionally focus-bound, optionally with the focus
338
+ // itself unioned in — the change-footprint shape). is a real answer.
339
+ const sideSet = (side) => {
340
+ const key = side.of === "focus" ? `${side.topic}:${focus.label}` : side.topic;
341
+ const set = observed.get(key) || [];
342
+ return side.withFocus ? [focus.label, ...set] : set;
343
+ };
344
+ const sideDesc = (side) => (side.withFocus
345
+ ? `({${focus.label}} ∪ ${side.topic})`
346
+ : side.of === "focus" ? `${side.topic}(${focus.label})` : side.topic);
347
+ const spec = rule.compose;
348
+ composed = intersect(sideSet(spec.a), sideSet(spec.b));
349
+ why.push(`compose: ${sideDesc(spec.a)} ∩ ${sideDesc(spec.b)} = ${spec.names} (${composed.length ? composed.join(", ") : `∅ — ${spec.empty}`})`);
253
350
  } else {
254
- // KEYSTONE arbitration: among the coverage violations (untested modules), pick
255
- // the highest declared priority — the widest blast radius |impact(m)| — tie
256
- // broken by label order. The single most-worth-covering module.
257
- const untested = observed.get("untested") || [];
258
- const ranked = untested
259
- .map((m) => ({ m, weight: (observed.get(`impact:${m}`) || []).length }))
351
+ // KEYSTONE arbitration: among the coverage violations, pick the highest
352
+ // declared priority — the widest |priority(m)| set — tie broken by label
353
+ // order. The single most-worth-covering module. Only a rule declaring a
354
+ // global mode ever reaches here (applicability screened on rule.modes).
355
+ const violating = observed.get(rule.coverageTopic) || [];
356
+ const ranked = violating
357
+ .map((m) => ({ m, weight: (observed.get(`${rule.priorityTopic}:${m}`) || []).length }))
260
358
  .sort((a, b) => b.weight - a.weight || String(a.m).localeCompare(String(b.m)));
261
359
  composed = ranked.length ? [ranked[0].m] : [];
262
- why.push(`keystone: argmax |impact| over ${untested.length} untested module(s) => ${composed.length ? `${composed[0]} (weight ${ranked[0].weight})` : "∅"}`);
360
+ why.push(`keystone: argmax |${rule.priorityTopic}| over ${violating.length} ${rule.coverageTopic} module(s) => ${composed.length ? `${composed[0]} (weight ${ranked[0].weight})` : "∅"}`);
263
361
  }
264
362
 
265
363
  return { calls, refused: false, terminated: true, proof, why, composed, driver, observed: `goal(${mode}): ${calls.map((c) => c.name).join(" -> ")}` };
@@ -28,7 +28,7 @@
28
28
  // Pure over its inputs + ctx.resolve (the binding oracle). No network, no Date.now.
29
29
 
30
30
  import { capabilityByName, preconditionsOf, PRECOND } from "./registry.mjs";
31
- import { hallucinationsIn } from "../../agentbench/grade.mjs";
31
+ import { hallucinationsIn } from "./call-validator.mjs";
32
32
 
33
33
  /** Validate a proposed tool_use. Returns a glass-box verdict:
34
34
  * { ok, tool, denied:[{reason,detail}], steps:[{pred,ok,...}], provenance }
@@ -74,7 +74,28 @@ export function decompose(request) {
74
74
  };
75
75
  }
76
76
 
77
- // METHOD 3 — SEQUENCING: split on the ordered connectives. Least commitment:
77
+ // METHOD 3 — the MEMBER-FILTER recipe: "which/what methods|members of X
78
+ // (end up|eventually)? calling/reaching Y". A C1 surface-syntax recipe like the
79
+ // conditional and relative-filter methods above (the C1 discipline: a closed,
80
+ // authored shape — NOT the C2 goal-reasoner's deduction). Decomposes to
81
+ // [enumerate members(X), filter by bounded transitive call-reach of Y]. The
82
+ // second segment is the filter TARGET, role "member-filter": the DRIVER owns
83
+ // the per-member callees hop + the reachability fold (driver-resolver.mjs) —
84
+ // segment 2 is not a resolvable leaf sub-goal on its own.
85
+ const mem = raw.match(
86
+ /^(?:which|what)\s+(?:methods?|members?)\s+of\s+(.+?)\s+(?:(?:end\s+up|eventually)\s+)?(?:calls?|calling|reach(?:es|ing)?|invokes?|invoking)\s+(.+?)\s*\??$/i,
87
+ );
88
+ if (mem) {
89
+ return {
90
+ method: "member-filter",
91
+ segments: [
92
+ { text: `members ${mem[1].trim()}`, role: "action", thread: false },
93
+ { text: mem[2].trim(), role: "member-filter", thread: true },
94
+ ],
95
+ };
96
+ }
97
+
98
+ // METHOD 4 — SEQUENCING: split on the ordered connectives. Least commitment:
78
99
  // we only split where a connective actually is.
79
100
  const parts = raw.split(/\s*(?:,\s*then\s+|,\s+and\s+then\s+|\s+and\s+then\s+|\s+then\s+|,\s+|\s+and\s+)\s*/i)
80
101
  .map((s) => s.replace(/^(?:then\s+|and\s+then\s+|and\s+|also\s+|check\s+|next\s+)/i, "").trim())
@@ -44,7 +44,7 @@ import { selectTool } from "../server-http.mjs";
44
44
  import {
45
45
  capabilities, capabilityByName, preconditionsOf, effectsOf, PRECOND,
46
46
  } from "./registry.mjs";
47
- import { hallucinationsIn } from "../../agentbench/grade.mjs";
47
+ import { hallucinationsIn } from "./call-validator.mjs";
48
48
 
49
49
  // ---- the ask-kind -> epistemic-topic MAPPING (the Stage-1 core) --------------
50
50
  // Keyed `${shape}:${kind}` off parseQuery's simple-clause output. The VALUE is
@@ -0,0 +1,31 @@
1
+ // src/router/set-algebra.mjs — the COMPOSITION OPERATORS: pure set-algebra a
2
+ // multi-step plan needs to fold its threaded step result-sets into ONE composed
3
+ // answer. Shared by the product router (goal-reasoner's relative-filter fold)
4
+ // and the bench result-execution layer (agentbench/results.mjs re-exports
5
+ // these) — the bench imports the product, never the other way round. The
6
+ // resolver DRIVER picks the operator from the router's OWN HTN method
7
+ // (relative-filter -> intersect; conditional -> fallback/guard) and applies it;
8
+ // grade.mjs never imports these (it only value-compares the driver's composed
9
+ // answer to the STATIC expect.result literal — no circular re-derivation).
10
+ // No I/O, no Date.now, no LLM.
11
+
12
+ /** Dedupe + locale-stable sort — the canonical label-set normal form. */
13
+ export const uniqSort = (xs) => [...new Set(xs)].sort((a, b) => String(a).localeCompare(String(b)));
14
+
15
+ /** a ∩ b — the relative-filter fold ("of the <set>, which are <Y>"). */
16
+ export function intersect(a = [], b = []) {
17
+ const bs = new Set(b);
18
+ return uniqSort([...new Set(a)].filter((x) => bs.has(x)));
19
+ }
20
+
21
+ /** if a is empty -> b, else a — the conditional "… <action> instead" recipe
22
+ * ("if X has no tests, list what covers Y instead"). */
23
+ export function fallbackIfEmpty(a = [], b = []) {
24
+ return uniqSort(a.length ? a : b);
25
+ }
26
+
27
+ /** if a is empty -> b, else ∅ — the guarded conditional ("if X is untested,
28
+ * <action> it"): the action's result fires ONLY when the guard set is empty. */
29
+ export function guardIfEmpty(a = [], b = []) {
30
+ return uniqSort(a.length ? [] : b);
31
+ }