@polycode-projects/the-mechanical-code-talker 1.8.17 → 1.8.18

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/ask.mjs +38 -16
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polycode-projects/the-mechanical-code-talker",
3
- "version": "1.8.17",
3
+ "version": "1.8.18",
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.",
package/src/ask.mjs CHANGED
@@ -3514,22 +3514,44 @@ export function traverse(graph, parsed, { contextId = null, prev = null, pinnedO
3514
3514
  widenNote = `, widened to ${siblingClass} subjects (no ${entityType} recorded)`;
3515
3515
  }
3516
3516
  }
3517
- // "touches" up-refine composability (HANDOVER 2026-07-12, Class-to-module
3518
- // up-refinement): an EMPTY touchesSymbol lookup on a resolved CLASS is not
3519
- // decisive the way it is for a Function/Method a Class reads naturally as
3520
- // "the file/unit that holds it", so "who touched <Class>" with no recorded
3521
- // symbol-precise touch should still answer from the class's containing
3522
- // module's real touches, rather than a confident-looking-but-possibly-wrong
3523
- // "nothing touched it". Fall through to the grain-aware up-refine below
3524
- // ONLY for that Class case. Deliberately NOT widened to every
3525
- // FINE_ENTITY_TYPES member: "how many commits touched fnAlpha" (a Function)
3526
- // is pinned elsewhere (ask-combo.test.mjs's grain-aware COUNT lever) to stay
3527
- // an honest 0 rather than a module-grain false hit symbol-level counting
3528
- // precision for functions/methods is a deliberate, separate guarantee this
3529
- // change must not erode. `calls` is untouched either way: an empty
3530
- // callsSymbol result stays decisive (call parsing isn't a best-effort
3531
- // heuristic the way commit-diff symbol attribution is).
3532
- if (matches.length || !(kind === "touches" && objMatch.class === "Class")) {
3517
+ // "touches"/"calls" up-refine composability (HANDOVER 2026-07-12, Class-to-
3518
+ // module up-refinement; extended to `calls` in the 2026-07-12 follow-up
3519
+ // "who calls Router" wrongly returned empty for a Class whose calls are only
3520
+ // recorded at module-coarse grain): an EMPTY touchesSymbol/callsSymbol lookup
3521
+ // on a resolved CLASS is not decisive the way it is for a Function/Method — a
3522
+ // Class reads naturally as "the file/unit that holds it", so "who touched
3523
+ // <Class>"/"who calls <Class>" with no recorded symbol-precise edge should
3524
+ // still answer from the class's containing module's real touches/calls,
3525
+ // rather than a confident-looking-but-possibly-wrong "nothing touched/calls
3526
+ // it". Deliberately NOT widened to every FINE_ENTITY_TYPES member: "how many
3527
+ // commits touched fnAlpha" (a Function) is pinned elsewhere (ask-combo.test.mjs's
3528
+ // grain-aware COUNT lever) to stay an honest 0 rather than a module-grain
3529
+ // false hit symbol-level counting precision for functions/methods is a
3530
+ // deliberate, separate guarantee this change must not erode.
3531
+ //
3532
+ // Up-refine eligibility is gated on the Class having NO recorded `contains`
3533
+ // members: a class the extractor actually populated (Widget, Logger — both
3534
+ // carry real `contains` edges) reads as a fully-modeled unit whose own empty
3535
+ // symbol-grain scan IS the honest answer; a class with zero recorded members
3536
+ // (Router, Button — the extractor only ever saw its declaration, never a
3537
+ // body) is exactly the shape where the coarser module-level edge is the only
3538
+ // real signal recorded at all.
3539
+ //
3540
+ // Even when eligible, only actually fall through to the shared grain-aware
3541
+ // up-refine block below when a containing module can be resolved RIGHT NOW.
3542
+ // That block's own "no containing module found" case renders a DIFFERENT,
3543
+ // wrongGrainMiss-shaped honest miss ("'Button' resolved to the class Button,
3544
+ // but this question needs a module…") than the plain zero-match miss this
3545
+ // symbol-grain scan already renders ("No modules found whose module directly
3546
+ // calls Button…"). A Class with no `contains` members AND no `defines` edge
3547
+ // naming its module (the synthetic chatflow-tier2 fixture's Button, which the
3548
+ // extractor recorded a bare declaration for but never wired into `defines`)
3549
+ // must keep the latter, plain-miss wording — eligibility alone doesn't
3550
+ // guarantee the up-refine can actually complete.
3551
+ const upRefineEligible = (kind === "touches" || kind === "calls") && objMatch.class === "Class"
3552
+ && !edgesOfKind(graph, "contains").some((e) => e.subject === objMatch.id);
3553
+ const upRefineModule = upRefineEligible ? graph.byId.get(moduleIdOf(graph, objMatch) || "") : null;
3554
+ if (matches.length || !(upRefineEligible && upRefineModule)) {
3533
3555
  return { matches, objMatch, candidates, traversal: `${symbolKind} edges where object = ${objMatch.label}${widenNote}`, ambiguous, matchedVia };
3534
3556
  }
3535
3557
  }