@polycode-projects/the-mechanical-code-talker 0.9.8 → 0.9.10
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 +9 -9
- package/package.json +1 -1
- package/src/ask-vocab.mjs +6 -0
- package/src/ask.mjs +15 -0
- package/src/chat.mjs +14 -1
- package/src/router/goal-reasoner.mjs +20 -7
package/ROADMAP.md
CHANGED
|
@@ -27,8 +27,8 @@ the file has been deleted.
|
|
|
27
27
|
- **Cluster C** — `PLAN_CHAT_FEEL.md` item 6, the pronoun/temporal/discourse-count trio
|
|
28
28
|
(measured red sets from an earlier advisor tick; re-measured against the current graded pool
|
|
29
29
|
before any fix).
|
|
30
|
-
-
|
|
31
|
-
|
|
30
|
+
- ~~**Cluster D** — `PLAN_CODE.md` Track 1~~ **shipped** (see "Now" below). Tracks 2-4 remain
|
|
31
|
+
unsigned-off and untouched.
|
|
32
32
|
- **Cluster E** — `PLAN_INFERENCE_TESTING.md` Stage 1 (`cax-sco` rule) + Stage 2 (proof-chain
|
|
33
33
|
receipts) in `src/syllogise.mjs`, targeting the measured chat-A2 50% ceiling.
|
|
34
34
|
|
|
@@ -82,13 +82,13 @@ In priority order (full detail and measured targets in `HANDOVER.md`):
|
|
|
82
82
|
unscoped lister shows matches. Found dogfooding a 191k-entity monorepo graph.
|
|
83
83
|
3. **Bug 7.** A modal auxiliary ("should") survives the fuzzy-correction cascade and misreads as
|
|
84
84
|
"hold". The diagnosed fix is adding modals to `STOPWORDS`.
|
|
85
|
-
4.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
85
|
+
4. ~~`PLAN_TMCT_ECOSYSTEM_INTEGRATION.md`~~ **shipped** — see "Now" above.
|
|
86
|
+
5. ~~`PLAN_CODE.md` Track 1 sign-off~~ **signed off and shipped** — see "Now" above.
|
|
87
|
+
6. **Still open**: the function-grain forward-shape gap (Bug C+D's grain resolution doesn't
|
|
88
|
+
cover the "forward" traversal shape), 8 remaining temporal red ids (each a distinct small
|
|
89
|
+
grammar gap), the un-flagged `C1:presupposition`/`C2:garden-path` regression, the
|
|
90
|
+
`edgesOfKind` by-subject/by-object endpoint indices (memoization itself is done). Full
|
|
91
|
+
detail in `HANDOVER.md`.
|
|
92
92
|
7. **`SKILL_PLAYTEST_SPRINT.md`, in progress.** A capped, delegated, chained playtest loop (each
|
|
93
93
|
round a background chat session against `examples/mini-webapp`, appraised and fixed+shipped
|
|
94
94
|
live). Rounds 1-3 shipped 3 real fixes (0.9.3-0.9.5); cap raised from 3 to 8 rounds mid-run;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polycode-projects/the-mechanical-code-talker",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.10",
|
|
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-vocab.mjs
CHANGED
|
@@ -157,6 +157,12 @@ export const RELATIONS = {
|
|
|
157
157
|
"change alongside", "shares commits with", "share commits with",
|
|
158
158
|
"tends to change together with", "tend to change together with",
|
|
159
159
|
"moves together with", "move together with",
|
|
160
|
+
// Track-1 trio (temporal lever): the bare "changed/change together with" form
|
|
161
|
+
// (no "tends to"/"tend to" prefix) was missing outright — "which modules
|
|
162
|
+
// changed together with X" fell through to the "touch(ed)" verb instead (a
|
|
163
|
+
// Commit->Module kind, structurally unable to match a Module subject), always
|
|
164
|
+
// producing a confidently-empty answer regardless of real cochange data.
|
|
165
|
+
"changed together with", "change together with", "changes together with",
|
|
160
166
|
],
|
|
161
167
|
},
|
|
162
168
|
reexports: {
|
package/src/ask.mjs
CHANGED
|
@@ -2159,6 +2159,21 @@ export function traverse(graph, parsed, { contextId = null, prev = null } = {})
|
|
|
2159
2159
|
// Module individuals and a FINER entityType was asked for do we refine via `defines`
|
|
2160
2160
|
// (imports) — never blindly treat an edge's subject id as if it were always a module id.
|
|
2161
2161
|
let edges = kindsFor(kind).flatMap((k) => edgesOfKind(graph, k)).filter((e) => e.object === gObjMatch.id);
|
|
2162
|
+
// cochange (Module<->Module, mgx:changeCoupledWith) is a SYMMETRIC relation but
|
|
2163
|
+
// stored as ONE directed edge per pair (extractor convention, not a meaningful
|
|
2164
|
+
// subject/object direction) — "which modules cochange with X" must also match
|
|
2165
|
+
// when X is the STORED SUBJECT of the pair, reading the OTHER endpoint (Track-1
|
|
2166
|
+
// trio, temporal lever). Flip subject<->object on that side so the subjects-
|
|
2167
|
+
// collection loop below (which reads e.subject) picks up the partner uniformly;
|
|
2168
|
+
// the object-side match above is untouched, so an existing non-empty answer is
|
|
2169
|
+
// byte-identical.
|
|
2170
|
+
if (kind === "cochange") {
|
|
2171
|
+
edges = edges.concat(
|
|
2172
|
+
kindsFor(kind).flatMap((k) => edgesOfKind(graph, k))
|
|
2173
|
+
.filter((e) => e.subject === gObjMatch.id)
|
|
2174
|
+
.map((e) => ({ ...e, subject: e.object, object: e.subject })),
|
|
2175
|
+
);
|
|
2176
|
+
}
|
|
2162
2177
|
let extNote = "";
|
|
2163
2178
|
if (!edges.length && gObjMatch.class) {
|
|
2164
2179
|
// Unresolved ext:<Name> endpoints with the SAME name as the resolved entity:
|
package/src/chat.mjs
CHANGED
|
@@ -2478,13 +2478,26 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
|
|
|
2478
2478
|
note(trace, `lane: (1) META/SELF — bare self/session question recognized, answered via="${meta.via}"`);
|
|
2479
2479
|
}
|
|
2480
2480
|
}
|
|
2481
|
-
if (!handled && miss && isConversational(query)) {
|
|
2481
|
+
if (!handled && miss && !envelope?.parsed && isConversational(query)) {
|
|
2482
2482
|
// A conversational miss (a greeting, "what can you do", a very short non-code
|
|
2483
2483
|
// line) gets the friendly orientation (module-aware: empty → --repo/tmct init).
|
|
2484
2484
|
// Bug B1 (0.8.2 follow-up): this branch carries via:"template" and never
|
|
2485
2485
|
// reaches the composed-only wall-shortening gate below, so a second
|
|
2486
2486
|
// identical orientation-class turn used to repeat the full blurb verbatim —
|
|
2487
2487
|
// collapse to a one-liner on that repeat, mirroring WALL_REPEAT_ONELINER.
|
|
2488
|
+
//
|
|
2489
|
+
// `!envelope?.parsed` (Track-1 trio, pronoun/focus binding — "biggest movable
|
|
2490
|
+
// mass"): isConversational() is a TEXT-ONLY heuristic (≤3 words, no code-ish
|
|
2491
|
+
// token) — it has no way to know the query already compiled to a real
|
|
2492
|
+
// structural AST shape. A pronoun-shortened follow-up ("who touched it"/
|
|
2493
|
+
// "that"/"this") is exactly 3 words and "touched" isn't in STRUCT_WORDS, so
|
|
2494
|
+
// isConversational used to fire AFTER ask() had already parsed the reverse
|
|
2495
|
+
// "touches" shape, resolved the pronoun off the focus, run the traversal, and
|
|
2496
|
+
// composed an honest (possibly empty) answer — discarding that correct answer
|
|
2497
|
+
// for the generic orientation wall. When envelope.parsed stood, the query WAS
|
|
2498
|
+
// structural (by construction, not word-count guesswork); its own composed
|
|
2499
|
+
// answer — hit, honest empty, or "it needs a referent" — is always more
|
|
2500
|
+
// truthful than the orientation card.
|
|
2488
2501
|
const orientation = orientationAnswer(templates, graph);
|
|
2489
2502
|
const repeat = last?.answer === orientation;
|
|
2490
2503
|
answer = repeat ? ORIENTATION_REPEAT_ONELINER : orientation;
|
|
@@ -164,9 +164,16 @@ export function backwardChainGoal(topic) {
|
|
|
164
164
|
* The caller REFUSES on zero matches (the open-world goal-generation seam) and
|
|
165
165
|
* on more than one (an ambiguous meta-goal — arbitration between meta-goals is
|
|
166
166
|
* undeclared, so guessing one would be an invented goal). */
|
|
167
|
-
|
|
167
|
+
// `ruleSet` defaults to the real, shipped GOAL_RULES — every existing caller
|
|
168
|
+
// (driver-goal.mjs, this module's own goalReason, every test) is unaffected.
|
|
169
|
+
// The override exists ONLY for PLAN_CODE.md Track 1 (§1.4): the synthesis
|
|
170
|
+
// oracle clones a CANDIDATE rule into its own array and runs it through this
|
|
171
|
+
// SAME trusted function — never a re-derivation — to check it decides
|
|
172
|
+
// applicability exactly like a hand-written rule would. synthbench/ is the
|
|
173
|
+
// only caller that ever passes a non-default ruleSet.
|
|
174
|
+
export function applicableRules(declaredTools, focus, mode, ruleSet = GOAL_RULES) {
|
|
168
175
|
const declared = Array.isArray(declaredTools) ? declaredTools : [];
|
|
169
|
-
return
|
|
176
|
+
return ruleSet.filter((rule) =>
|
|
170
177
|
rule.modes.includes(mode)
|
|
171
178
|
&& (mode !== "scoped" || (focus != null && focus.class === rule.focusClass))
|
|
172
179
|
&& rule.subGoals.every((topic) => {
|
|
@@ -255,8 +262,14 @@ export function dropCondition(intention, observed, mode, focus, focusClass) {
|
|
|
255
262
|
* the keystone module globally) or an HONEST REFUSE at the open-world
|
|
256
263
|
* goal-generation seam.
|
|
257
264
|
*
|
|
258
|
-
* ctx: { dispatch(name,input)->{ok,result}, resolve(term)->{match,ambiguous} }.
|
|
259
|
-
|
|
265
|
+
* ctx: { dispatch(name,input)->{ok,result}, resolve(term)->{match,ambiguous} }.
|
|
266
|
+
*
|
|
267
|
+
* `ruleSet` defaults to the real, shipped GOAL_RULES (every product call site
|
|
268
|
+
* omits it). PLAN_CODE.md Track 1's synthesis oracle (synthbench/rules/
|
|
269
|
+
* oracle.mjs) is the one caller that overrides it, with a CLONED array
|
|
270
|
+
* holding a candidate rule — the candidate then runs through this exact same
|
|
271
|
+
* meta-loop a hand-written rule does, never a parallel re-implementation. */
|
|
272
|
+
export async function goalReason(request, tools, ctx, { driver = "goal-0.8.1", ruleSet = GOAL_RULES } = {}) {
|
|
260
273
|
const declared = Array.isArray(tools) ? tools : [];
|
|
261
274
|
|
|
262
275
|
// STEP 1 — deduce the goal scope from the DECLARED model + a bound focus,
|
|
@@ -267,8 +280,8 @@ export async function goalReason(request, tools, ctx, { driver = "goal-0.8.1" }
|
|
|
267
280
|
|
|
268
281
|
// The open-world goal-generation seam, named honestly: a resolved focus whose
|
|
269
282
|
// class NO declared goal-rule scopes is REFUSED, never given an invented goal.
|
|
270
|
-
if (focus && !
|
|
271
|
-
const covered = [...new Set(
|
|
283
|
+
if (focus && !ruleSet.some((r) => r.focusClass === focus.class)) {
|
|
284
|
+
const covered = [...new Set(ruleSet.map((r) => r.focusClass))].join("/");
|
|
272
285
|
return refuse(`open-world: no declared goal-rule covers a ${focus.class} focus (the declared goal-rules are ${covered}-scoped) — escalate`, driver);
|
|
273
286
|
}
|
|
274
287
|
|
|
@@ -276,7 +289,7 @@ export async function goalReason(request, tools, ctx, { driver = "goal-0.8.1" }
|
|
|
276
289
|
// 0 applicable rules => the same open-world seam (nothing declared grounds the
|
|
277
290
|
// request's scope in this toolset); >1 => an AMBIGUOUS meta-goal (arbitration
|
|
278
291
|
// between meta-goals is undeclared) — both are honest refusals, never a guess.
|
|
279
|
-
const applicable = applicableRules(declared, focus, mode);
|
|
292
|
+
const applicable = applicableRules(declared, focus, mode, ruleSet);
|
|
280
293
|
|
|
281
294
|
// THE GLOBAL-MODE DOMAIN GATE (Bug 8 fix, see the module header). SCOPED mode
|
|
282
295
|
// already proved relevance via a bound graph entity; GLOBAL mode has not, so
|