@polycode-projects/the-mechanical-code-talker 0.8.1 → 0.8.2
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 +51 -9
- package/data/templates/responses.jsonl +1 -1
- package/package.json +1 -1
- package/src/ask.mjs +115 -19
- package/src/chat.mjs +356 -20
- package/src/codegraph.mjs +109 -1
- package/src/interpret/merge.mjs +16 -1
- package/src/interpret/normalize.mjs +109 -4
- package/src/memory/core.mjs +8 -1
- package/src/memory/fold.mjs +0 -0
- package/src/memory/trust.mjs +8 -4
- package/src/router/call-validator.mjs +45 -0
- package/src/router/goal-reasoner.mjs +148 -50
- package/src/router/guardrail.mjs +1 -1
- package/src/router/planner.mjs +22 -1
- package/src/router/resolver.mjs +1 -1
- package/src/router/set-algebra.mjs +31 -0
package/ROADMAP.md
CHANGED
|
@@ -12,11 +12,29 @@ 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-
|
|
16
|
-
|
|
17
|
-
**Built: v0.8.
|
|
18
|
-
`npm test` green (**
|
|
19
|
-
|
|
15
|
+
## Where we are now (2026-07-07)
|
|
16
|
+
|
|
17
|
+
**Built: v0.8.2 — the chat-feel wave + rule-general C2 (0.8.1 published; 0.8.2 gated green, push =
|
|
18
|
+
the next operator action).** `npm test` green (**974**). 0.8.2 deepened both axes at once:
|
|
19
|
+
|
|
20
|
+
- **Chat feel (CHATBENCH_0.8.2, deterministic tier):** tier-1 **334/334 (draw A) + 285/285 (draw
|
|
21
|
+
B)**, zero regressions; the cycle-1 standing hard-fail `gq-functions-call-fnalpha` **flipped
|
|
22
|
+
green**. Landed (PLAN_CHAT_FEEL items 1–5, 7, 8): recall hygiene, preamble/politeness frames,
|
|
23
|
+
calls∪callsSymbol + grain/meta fallbacks, the author lane, wall kindness + honest capability
|
|
24
|
+
nudges, teach-lane widening + the taught-class↔inherits bridge, receipt tails prose→detail.
|
|
25
|
+
Plus a live-found scale hotfix (edgesOfKind argument-spread overflow past ~100k edges, from a
|
|
26
|
+
27,770-module monorepo). **The judged (LLM) re-judge of the touched tags is DEFERRED
|
|
27
|
+
post-release (ship-over-delay)** — 0.8.1 judged scores stay the judged record and are stale on
|
|
28
|
+
those tags; see CHATBENCH_0.8.2.
|
|
29
|
+
- **Agent (AGENTBENCH_0.8.2):** ladder 43→56 cases; goal driver **100% plan / 98% result / 0%
|
|
30
|
+
hallucination, all rungs gate-PASS** (one deliberate honest red: `ab-c2-what-to-test`); resolver
|
|
31
|
+
floor clean **A0–C1 100/100** via the new member-filter HTN method + per-member hop (flips
|
|
32
|
+
`ab-c1-widget-methods-calling` in both drivers). **C2 is now rule-general**: a second declared
|
|
33
|
+
goal-rule (`cochange-risk-invariant`) + pure `applicableRules` selection (0→refuse open-world,
|
|
34
|
+
>1→refuse ambiguous; grep-clean of request keywords). Bench-import inversion
|
|
35
|
+
(`src/router/call-validator.mjs` + `set-algebra.mjs`); bounded runner pool (`--concurrency` 8).
|
|
36
|
+
|
|
37
|
+
**Shipped in v0.8.1 (published):** the same demonstrated-vs-designed honesty discipline, summarized:
|
|
20
38
|
|
|
21
39
|
- **AGENTBENCH now grades the executed composed RESULT, not just the call-plan** (retires the headline
|
|
22
40
|
0.8.0 caveat). Under result-grading the resolver is **97% plan / 91% result / 0% hallucination**; the
|
|
@@ -46,9 +64,13 @@ registry + resolver/guardrail/planner (`AGENTBENCH_0.8.0_001`: 96% *plan* comple
|
|
|
46
64
|
closed-world C1); the three chat levers (`CHATBENCH_0.8.0`: tier-1 331→333); the `../bedrock-meter` `$0`
|
|
47
65
|
rung (rank 0 below nova-micro, £0 metered e2e); the playtest; and the Stage-2/Stage-5 research notes.
|
|
48
66
|
|
|
49
|
-
**Next:**
|
|
50
|
-
the
|
|
51
|
-
|
|
67
|
+
**Next (post-release, in order):** (1) the judged re-judge of the stale tags (the deferred half of
|
|
68
|
+
the 0.8.2 evidence); (2) the chat-feel fast-follows from the confirmation playtest (recall
|
|
69
|
+
conjunction enforcement, the fuzzy-entity FALSE-EMPTY confident-wrong class, function-grain
|
|
70
|
+
coverage, module-grain overview); (3) the Track-1 trio against the measured red set (pron ×18,
|
|
71
|
+
temporal b1×5 + c1×9, disc-count re-measure first); (4) `edgesOfKind` memoization + endpoint
|
|
72
|
+
indices (perf at monorepo scale); (5) the seonix cutover ping + wh-scale feedback loop. Full list
|
|
73
|
+
with targets in `HANDOVER.md`.
|
|
52
74
|
|
|
53
75
|
## The umbrella product definition (item 1)
|
|
54
76
|
|
|
@@ -635,7 +657,7 @@ you toward precision" promise on the conversational surface.
|
|
|
635
657
|
with new graded cells for the miss / empty-graph / concept-touch surfaces so these become
|
|
636
658
|
regression-protected levers, not one-off polish.
|
|
637
659
|
|
|
638
|
-
## Phase 11 — The capability router & the agentic bench (0.8.0 shipped · 0.8.1 deepened)
|
|
660
|
+
## Phase 11 — The capability router & the agentic bench (0.8.0 shipped · 0.8.1 deepened · 0.8.2 feel + rule-general C2)
|
|
639
661
|
|
|
640
662
|
*(Operator-directed 2026-07-06; built the same day across five concurrent tracks.)* tmct as a **deterministic, no-LLM
|
|
641
663
|
tool router** behind an Anthropic-compatible API — the workstream specified in
|
|
@@ -657,6 +679,14 @@ real deliverable. The five tracks below are all built; the two research-agent st
|
|
|
657
679
|
remain designed-not-built by intent.
|
|
658
680
|
|
|
659
681
|
### Track 1 — chat-surface levers (next CHATBENCH; all three)
|
|
682
|
+
|
|
683
|
+
> **STATUS (0.8.2):** the surrounding feel surface landed — PLAN_CHAT_FEEL items **1–5, 7, 8**
|
|
684
|
+
> (recall hygiene, preamble frames, call-relation self-consistency, author lane, wall kindness,
|
|
685
|
+
> teach-lane widening, honest nudges) shipped and gate-verified deterministically. **The trio
|
|
686
|
+
> below is DEFERRED post-release with measured targets** (advisor tick-4): pronoun red set = 18
|
|
687
|
+
> g-b1-pron ids; temporal = g-b1-temp ×5 + g-c1-temp ×9; discourse-count re-measure first — it
|
|
688
|
+
> sampled 0/5 red and is likely already green. See HANDOVER follow-up #3.
|
|
689
|
+
|
|
660
690
|
The three levers `CHATBENCH_0.7.1` measured + ranked — which **double as router prerequisites** (they
|
|
661
691
|
gate the A2→B1→C1 rungs, per Phase B of the router plan):
|
|
662
692
|
1. **Pronoun / focus binding** — the "it → Commit" mis-bind (`B1 pron 1.24`); biggest movable mass.
|
|
@@ -666,6 +696,12 @@ gate the A2→B1→C1 rungs, per Phase B of the router plan):
|
|
|
666
696
|
Land all three (not just #1); they raise the chat floor *and* the router's floor at once.
|
|
667
697
|
|
|
668
698
|
### Track 2 — the router build (the within-horizon slice, in order)
|
|
699
|
+
|
|
700
|
+
> **STATUS (0.8.2):** the C1 composition gap closed — the **member-filter HTN method + per-member
|
|
701
|
+
> callees hop** flips the standing C1 red in both drivers (resolver floor A0–C1 all 100/100); the
|
|
702
|
+
> ladder grew 43→56 fixture-linted cases; the bench-import smell is inverted
|
|
703
|
+
> (`src/router/call-validator.mjs` + `set-algebra.mjs`).
|
|
704
|
+
|
|
669
705
|
Buildable now with a frontier model as co-author (see PLAN §"solved vs unsolved"):
|
|
670
706
|
- **Phase A — the shim.** An Anthropic Messages API endpoint (`/v1/messages`, `tool_use`/`tool_result`
|
|
671
707
|
blocks). **Extended:** also present as a **`bedrock-meter`-compatible routing target** (see below).
|
|
@@ -704,6 +740,12 @@ critical build path:
|
|
|
704
740
|
- **Stage 5 — goal-reasoner, closed-world C2** — BDI + Goal-Driven Autonomy: deduce-goals (long-chain
|
|
705
741
|
deduction) → plan-each (C1) → threat-aware, *persistent* first-step arbitration.
|
|
706
742
|
|
|
743
|
+
> **STATUS (0.8.2):** both research stages are now BUILT and measured. Stage 2 landed in 0.8.1_002;
|
|
744
|
+
> Stage 5's 0.8.1 "one thin rule" caveat is retired — **C2 is rule-general**: two declared
|
|
745
|
+
> goal-rules (`coverage-invariant`, `cochange-risk-invariant`) selected by pure `applicableRules`
|
|
746
|
+
> deduction with honest refusals at both failure modes (0 applicable = open-world, >1 = ambiguous),
|
|
747
|
+
> zero request keywords. Goal driver: 100% plan / 98% result / 0% hallucination over 56 cases.
|
|
748
|
+
|
|
707
749
|
## Phase LATER — recognized, deferred, not now
|
|
708
750
|
|
|
709
751
|
Features we have deliberately shaped seams for but will not build until the phases above have
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
{"id":"conversational-greeting-good-evening","class":"conversational","register":"friendly","template":"Good evening. Ask me about this codebase, or /help."}
|
|
61
61
|
{"id":"conversational-thanks","class":"conversational","register":"friendly","template":"Any time. Ask another, or /help for what I can do."}
|
|
62
62
|
{"id":"conversational-farewell","class":"conversational","register":"friendly","template":"Bye — flushing the session log. Come back with a question any time."}
|
|
63
|
-
{"id":"orientation-friendly","class":"orientation","register":"friendly","template":"I answer questions about THIS codebase's structure — imports, calls, definitions,\nhistory and counts. For example:\n which modules import
|
|
63
|
+
{"id":"orientation-friendly","class":"orientation","register":"friendly","template":"I answer questions about THIS codebase's structure — imports, calls, definitions,\nhistory and counts. For example:\n which modules import {example1}\n what calls {example2}\n how many classes are there\n/help for commands, /stats for an overview of the graph."}
|
|
64
64
|
{"id":"miss-no-previous-answer","class":"miss","register":"friendly","template":"No previous answer to expand yet — ask me a question first, then say \"why\" or \"say more\"."}
|
|
65
65
|
{"id":"conversational-greeting-empty","class":"conversational","register":"friendly","template":"Hi. There's no code graph loaded here — for code structure (imports, calls, definitions) I need a `.tmct/graph.json`: point me at one with `--repo <path>`, or try the shipped example `npm run example:mini`. (tmct reads graphs; it doesn't index code itself.) For general vocabulary, `tmct init` seeds concepts — try \"what is a cache\". /help for commands."}
|
|
66
66
|
{"id":"orientation-empty","class":"orientation","register":"friendly","template":"There's no code graph loaded here, so I can't answer structure questions (imports, calls, definitions) yet.\nFor those I need a `.tmct/graph.json` produced by a graph producer — point me at one with `--repo <path>`, or try the shipped example `npm run example:mini`. tmct reads graphs; it doesn't index code itself.\nFor general vocabulary, `tmct init` seeds concepts — try \"what is a cache\". /help for commands, /memory for what I remember."}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polycode-projects/the-mechanical-code-talker",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
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
|
@@ -77,7 +77,12 @@ import { nlpAdapter } from "./ask-nlp.mjs";
|
|
|
77
77
|
* classification, so they cannot drift in meaning). */
|
|
78
78
|
function edgesOfKind(graph, kind) {
|
|
79
79
|
const out = [];
|
|
80
|
-
|
|
80
|
+
// Plain-loop append, NOT out.push(...g.edges): argument spread overflows the call
|
|
81
|
+
// stack past ~100k edges on graph-scale relation groups (see codegraph.mjs twin).
|
|
82
|
+
for (const g of graph.relations) {
|
|
83
|
+
if (relationKind(g) !== kind) continue;
|
|
84
|
+
for (const e of g.edges) out.push(e);
|
|
85
|
+
}
|
|
81
86
|
return out;
|
|
82
87
|
}
|
|
83
88
|
|
|
@@ -89,6 +94,12 @@ function edgesOfKind(graph, kind) {
|
|
|
89
94
|
// "which functions call X" should read off callsSymbol (fn->fn), not the module-coarse "calls".
|
|
90
95
|
const SYMBOL_GRAIN_SIBLING = { calls: "callsSymbol", touches: "touchesSymbol" };
|
|
91
96
|
const FINE_ENTITY_TYPES = new Set(["Function", "Method", "Class", "Attribute", "GlobalVariable"]);
|
|
97
|
+
// The fn/method FAMILY (0.8.2 WS1): callers of a symbol are recorded at whichever
|
|
98
|
+
// grain the extractor saw (a method Widget.render is class "Method"), but a person
|
|
99
|
+
// asking "which functions call X" means the callable family, not the storage class.
|
|
100
|
+
// Used ONLY as an empty-result fallback (see traverse's reverse symbol-grain path):
|
|
101
|
+
// an exact-class answer is never widened, so every non-empty answer is byte-stable.
|
|
102
|
+
const FINE_CLASS_SIBLING = { Function: "Method", Method: "Function" };
|
|
92
103
|
|
|
93
104
|
// Query-side UNION families (2026-07-02 query families): a parsed kind that is not
|
|
94
105
|
// itself a stored predicate but a curated union of stored kinds — "what uses X"
|
|
@@ -125,6 +136,24 @@ function verbFor(kind) {
|
|
|
125
136
|
return REVERSE_MISS_VERB[kind] || kind;
|
|
126
137
|
}
|
|
127
138
|
|
|
139
|
+
// Leading-relation-verb strip for the tests-kind honest empty (0.8.2 WS1): the
|
|
140
|
+
// keyword strategy can match the "tests" NOUN as the relation verb and leave the
|
|
141
|
+
// user's OWN verb at the head of the object term ("do any tests touch f.mjs" →
|
|
142
|
+
// object "touch f.mjs"), which the old ^cover-only strip missed ("No tests cover
|
|
143
|
+
// touch app/lib/f.mjs."). The closed list is read from ask-vocab.mjs's exported
|
|
144
|
+
// VERB_TO_KIND (derived from the RELATIONS verb table — the source of truth,
|
|
145
|
+
// including the `tests` kind's own verbs: cover/check/verify/exercise/…), longest
|
|
146
|
+
// phrase first so multi-word verbs strip whole; a bare optional s/ing/ed tail keeps
|
|
147
|
+
// the previously-stripped inflections ("covering") without enumerating them. Only
|
|
148
|
+
// ever applied to the tests-kind zero-hit template's object — never to resolution.
|
|
149
|
+
const LEADING_RELATION_VERB_RE = new RegExp(
|
|
150
|
+
`^(?:${Object.keys(VERB_TO_KIND)
|
|
151
|
+
.sort((a, b) => b.length - a.length)
|
|
152
|
+
.map((v) => v.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
|
|
153
|
+
.join("|")})(?:s|ing|ed)?\\s+`,
|
|
154
|
+
"i",
|
|
155
|
+
);
|
|
156
|
+
|
|
128
157
|
// ---- the parsing strategies + normalization + fuzzy service formerly defined
|
|
129
158
|
// here now live in src/interpret/ (items 8/10/13): interpret/normalize.mjs
|
|
130
159
|
// (normalizeQuery, applyNegationFrames, STOPWORDS, splitWords), interpret/
|
|
@@ -1416,7 +1445,29 @@ export function traverse(graph, parsed, { contextId = null, prev = null } = {})
|
|
|
1416
1445
|
const token = (i.attributes || []).find((a) => a.key === "token")?.value;
|
|
1417
1446
|
return token && String(token).toLowerCase() === termLc;
|
|
1418
1447
|
});
|
|
1419
|
-
if (!match)
|
|
1448
|
+
if (!match) {
|
|
1449
|
+
// META FALLBACK TO REAL ENTITIES (0.8.2 WS1): "what is a Record" used to say
|
|
1450
|
+
// "'Record' isn't a term in this graph's own vocabulary" even when Record is a
|
|
1451
|
+
// code-graph Class individual. After the SchemaClass/SchemaPredicate miss, try
|
|
1452
|
+
// an exact case-insensitive UNIQUE label match against class === "Class"
|
|
1453
|
+
// individuals; a unique hit renders a describe-style one-liner (see render's
|
|
1454
|
+
// metaCodeClass branch). Anything less than a unique exact hit keeps the
|
|
1455
|
+
// honest vocabulary miss — never a guess.
|
|
1456
|
+
const classHits = (graph.individuals || []).filter((i) => i.class === "Class" && String(i.label).toLowerCase() === termLc);
|
|
1457
|
+
if (classHits.length === 1) {
|
|
1458
|
+
const hit = classHits[0];
|
|
1459
|
+
const mid = moduleIdOf(graph, hit);
|
|
1460
|
+
const modLabel = (mid && graph.byId.get(mid)?.label)
|
|
1461
|
+
|| String((hit.attributes || []).find((a) => a.key === "site")?.value || "").split(":")[0]
|
|
1462
|
+
|| null;
|
|
1463
|
+
return {
|
|
1464
|
+
matches: [hit], objMatch: hit, candidates: [], ambiguous: false,
|
|
1465
|
+
metaCodeClass: true, metaModuleLabel: modLabel,
|
|
1466
|
+
traversal: `schema lookup for "${term}" (miss), then unique Class individual by label`,
|
|
1467
|
+
};
|
|
1468
|
+
}
|
|
1469
|
+
return { matches: [], objMatch: null, candidates: [], traversal: `schema lookup for "${term}"`, ambiguous: false };
|
|
1470
|
+
}
|
|
1420
1471
|
return {
|
|
1421
1472
|
matches: [match], objMatch: match, candidates: [],
|
|
1422
1473
|
traversal: `schema lookup for "${term}"`, ambiguous: false,
|
|
@@ -1536,9 +1587,22 @@ export function traverse(graph, parsed, { contextId = null, prev = null } = {})
|
|
|
1536
1587
|
}
|
|
1537
1588
|
|
|
1538
1589
|
if (shape === "forward") {
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1590
|
+
// FORWARD CALL UNION (0.8.2 WS1): a kind with a symbol-grain sibling scans the
|
|
1591
|
+
// UNION coarse+sibling when the resolved SUBJECT is itself a fine symbol —
|
|
1592
|
+
// "what does Widget.render call" lives on callsSymbol (fn->fn), which the
|
|
1593
|
+
// module-coarse scan alone can never reach (a coarse edge's subject is a
|
|
1594
|
+
// module, so a Function/Method subject rendered a false "no calls edges" while
|
|
1595
|
+
// the reverse direction answered). Module subjects never carry a sibling edge,
|
|
1596
|
+
// so their scan — and the traversal receipt — stays byte-identical. The receipt
|
|
1597
|
+
// names what was actually scanned ("calls+callsSymbol edges where subject = X").
|
|
1598
|
+
const fwdSibling = SYMBOL_GRAIN_SIBLING[kind];
|
|
1599
|
+
const subjIsFineSymbol = !!(fwdSibling && objMatch.class && FINE_ENTITY_TYPES.has(objMatch.class));
|
|
1600
|
+
const fwdKinds = subjIsFineSymbol ? [...new Set([...kindsFor(kind), fwdSibling])] : kindsFor(kind);
|
|
1601
|
+
const edges = fwdKinds.flatMap((k) => edgesOfKind(graph, k)).filter((e) => e.subject === objMatch.id);
|
|
1602
|
+
const targets = edges.map((e) => graph.byId.get(e.object)).filter(Boolean);
|
|
1603
|
+
// dedupe only on the widened scan — the coarse-only path keeps its exact shape.
|
|
1604
|
+
const matches = subjIsFineSymbol ? uniqueById(targets) : targets;
|
|
1605
|
+
return { matches, objMatch, candidates, traversal: `${fwdKinds.join("+")} edges where subject = ${objMatch.label}`, ambiguous, matchedVia };
|
|
1542
1606
|
}
|
|
1543
1607
|
|
|
1544
1608
|
// reverse + transitive (PLAN_MECHANICAL_CHAT.md P1): the gate above guarantees kind is
|
|
@@ -1577,8 +1641,23 @@ export function traverse(graph, parsed, { contextId = null, prev = null } = {})
|
|
|
1577
1641
|
if (symbolKind && (FINE_ENTITY_TYPES.has(entityType) || objIsFineSymbol)) {
|
|
1578
1642
|
const edges = edgesOfKind(graph, symbolKind).filter((e) => e.object === objMatch.id);
|
|
1579
1643
|
const subjects = uniqueById(edges.map((e) => graph.byId.get(e.subject)).filter(Boolean));
|
|
1580
|
-
|
|
1581
|
-
|
|
1644
|
+
let matches = (!entityType || entityType === "Change") ? subjects : subjects.filter((i) => i.class === entityType);
|
|
1645
|
+
let widenNote = "";
|
|
1646
|
+
// FINE-GRAIN FAMILY FALLBACK (0.8.2 WS1): when the exact-class filter comes back
|
|
1647
|
+
// EMPTY and the asked grain is Function/Method, retry with the family sibling —
|
|
1648
|
+
// "which functions call fnAlpha" must not hide the recorded caller Widget.render
|
|
1649
|
+
// just because the extractor stored it as class Method. Fallback-only by
|
|
1650
|
+
// construction (the exact filter must be empty first), so every currently
|
|
1651
|
+
// non-empty answer is byte-identical; the widening is said in the traversal.
|
|
1652
|
+
const siblingClass = FINE_CLASS_SIBLING[entityType];
|
|
1653
|
+
if (!matches.length && siblingClass) {
|
|
1654
|
+
const widened = subjects.filter((i) => i.class === siblingClass);
|
|
1655
|
+
if (widened.length) {
|
|
1656
|
+
matches = widened;
|
|
1657
|
+
widenNote = `, widened to ${siblingClass} subjects (no ${entityType} recorded)`;
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
return { matches, objMatch, candidates, traversal: `${symbolKind} edges where object = ${objMatch.label}${widenNote}`, ambiguous, matchedVia };
|
|
1582
1661
|
}
|
|
1583
1662
|
|
|
1584
1663
|
// General case: some predicates are already fine-grained (inherits: Class->Class, contains:
|
|
@@ -1714,6 +1793,17 @@ function renderCore(parsed, result) {
|
|
|
1714
1793
|
miss: true, ambiguous: false,
|
|
1715
1794
|
};
|
|
1716
1795
|
}
|
|
1796
|
+
// meta fallback hit (0.8.2 WS1, see traverse's meta branch): the term is not
|
|
1797
|
+
// schema vocabulary but IS a unique code-graph Class — a describe-style
|
|
1798
|
+
// one-liner pointing at the real entity, instead of the false vocabulary miss.
|
|
1799
|
+
if (result.metaCodeClass) {
|
|
1800
|
+
const label = result.objMatch.label;
|
|
1801
|
+
const definedIn = result.metaModuleLabel ? `, defined in ${result.metaModuleLabel}` : "";
|
|
1802
|
+
return {
|
|
1803
|
+
content: `${label} is a class in this codebase${definedIn} — try "describe ${label}" or "which classes inherit from ${label}".`,
|
|
1804
|
+
miss: false, ambiguous: false, matches: result.matches,
|
|
1805
|
+
};
|
|
1806
|
+
}
|
|
1717
1807
|
const doc = (result.objMatch.attributes || []).find((a) => a.key === "doc")?.value || "";
|
|
1718
1808
|
const kindWord = result.objMatch.class === "SchemaClass" ? "a class in the graph's schema" : "a predicate (relation) in the graph's schema";
|
|
1719
1809
|
return { content: `${result.objMatch.label} is ${kindWord}: ${doc}`, miss: false, ambiguous: false, matches: result.matches };
|
|
@@ -1724,7 +1814,7 @@ function renderCore(parsed, result) {
|
|
|
1724
1814
|
if (result.mentionsShape) {
|
|
1725
1815
|
if (!result.matches.length) {
|
|
1726
1816
|
return {
|
|
1727
|
-
content: `"${parsed.object}" is not mentioned in any indexed identifier or doc-comment prose
|
|
1817
|
+
content: `"${parsed.object}" is not mentioned in any indexed identifier or doc-comment prose.`,
|
|
1728
1818
|
miss: true, ambiguous: false,
|
|
1729
1819
|
};
|
|
1730
1820
|
}
|
|
@@ -1781,7 +1871,7 @@ function renderCore(parsed, result) {
|
|
|
1781
1871
|
if (result.whenShape) {
|
|
1782
1872
|
const subject = result.objMatch.label;
|
|
1783
1873
|
if (!result.matches.length) {
|
|
1784
|
-
return { content: `no recorded commit touches ${subject} in this index
|
|
1874
|
+
return { content: `no recorded commit touches ${subject} in this index.`, miss: true, ambiguous: false };
|
|
1785
1875
|
}
|
|
1786
1876
|
const newest = result.matches[0];
|
|
1787
1877
|
const date = (newest.attributes || []).find((a) => a.key === "date")?.value || "";
|
|
@@ -1811,7 +1901,7 @@ function renderCore(parsed, result) {
|
|
|
1811
1901
|
const cite = `commit ${result.objMatch.label}`;
|
|
1812
1902
|
if (!result.matches.length) {
|
|
1813
1903
|
return {
|
|
1814
|
-
content: `${cite} touched nothing recorded in the index
|
|
1904
|
+
content: `${cite} touched nothing recorded in the index.`,
|
|
1815
1905
|
miss: true, ambiguous: false,
|
|
1816
1906
|
};
|
|
1817
1907
|
}
|
|
@@ -1829,8 +1919,11 @@ function renderCore(parsed, result) {
|
|
|
1829
1919
|
if (!result.objMatch || !result.subjMatch) {
|
|
1830
1920
|
return { content: `couldn't resolve one of the terms in this question.`, miss: true, ambiguous: false };
|
|
1831
1921
|
}
|
|
1922
|
+
// the yes render is plain words — the traversal string IS "<kind> edge from
|
|
1923
|
+
// <A> to <B>", so it reads as the sentence itself, not a parenthetical receipt
|
|
1924
|
+
// (the receipt still rides on the result's traversal field for why/verbose).
|
|
1832
1925
|
return {
|
|
1833
|
-
content: result.answer ? `Yes
|
|
1926
|
+
content: result.answer ? `Yes — ${result.traversal}.` : `No — no ${parsed.kind} edge found from ${result.subjMatch.label} to ${result.objMatch.label}.`,
|
|
1834
1927
|
miss: !result.answer, ambiguous: false,
|
|
1835
1928
|
};
|
|
1836
1929
|
}
|
|
@@ -1841,21 +1934,24 @@ function renderCore(parsed, result) {
|
|
|
1841
1934
|
// subject-first phrasing rather than reusing reverse's "found ... that OBJECT" template.
|
|
1842
1935
|
if (parsed.shape === "forward") {
|
|
1843
1936
|
return {
|
|
1844
|
-
content: `${result.objMatch.label} has no ${parsed.kind} edges in the index
|
|
1937
|
+
content: `${result.objMatch.label} has no ${parsed.kind} edges in the index.`,
|
|
1845
1938
|
miss: true, ambiguous: false,
|
|
1846
1939
|
};
|
|
1847
1940
|
}
|
|
1848
1941
|
// "what tests cover X" / "what tests X" — the tests themselves are the search
|
|
1849
1942
|
// target (no explicit entity keyword → entityType null), and "tests" reads as a
|
|
1850
1943
|
// verb phrase, so the generic "No <modules> found whose module directly tests <obj>"
|
|
1851
|
-
// template garbles: it mislabels the searched kind as "modules" and lets the
|
|
1852
|
-
//
|
|
1853
|
-
//
|
|
1854
|
-
//
|
|
1944
|
+
// template garbles: it mislabels the searched kind as "modules" and lets the user's
|
|
1945
|
+
// leaked verb ride into the object ("…tests cover touch X"). Any leading relation
|
|
1946
|
+
// verb (cover/touch/check/verify/… — LEADING_RELATION_VERB_RE, built from the
|
|
1947
|
+
// ask-vocab verb table) is stripped, so the honest empty reads as the natural
|
|
1948
|
+
// "No tests cover X." The frozen entity-keyword form ("which modules test X",
|
|
1949
|
+
// entityType="Module") keeps its pinned wording below.
|
|
1855
1950
|
if (parsed.kind === "tests" && !parsed.entityType) {
|
|
1856
|
-
const
|
|
1951
|
+
const stripped = String(parsed.object || "").replace(LEADING_RELATION_VERB_RE, "").trim();
|
|
1952
|
+
const obj = stripped || String(parsed.object || "").trim();
|
|
1857
1953
|
return {
|
|
1858
|
-
content: `No tests cover ${obj}
|
|
1954
|
+
content: `No tests cover ${obj}.`,
|
|
1859
1955
|
miss: true, ambiguous: false,
|
|
1860
1956
|
};
|
|
1861
1957
|
}
|
|
@@ -1865,7 +1961,7 @@ function renderCore(parsed, result) {
|
|
|
1865
1961
|
// append-only/sacred mid-arc, so the honest-miss phrasing stays as-is.
|
|
1866
1962
|
const entityWord = nounFor(parsed.entityType || "Module", 2);
|
|
1867
1963
|
return {
|
|
1868
|
-
content: `No ${entityWord} found whose module directly ${verbFor(parsed.kind)} ${parsed.object}
|
|
1964
|
+
content: `No ${entityWord} found whose module directly ${verbFor(parsed.kind)} ${parsed.object}.`,
|
|
1869
1965
|
miss: true, ambiguous: false,
|
|
1870
1966
|
};
|
|
1871
1967
|
}
|