@polycode-projects/the-mechanical-code-talker 1.0.8 → 1.0.9

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/src/ask-vocab.mjs CHANGED
@@ -31,6 +31,54 @@
31
31
  * `verbs: [...]` shape) since ask.mjs only needs phrase -> kind, never the
32
32
  * register itself. A misparsed casual phrase costs nothing beyond an honest
33
33
  * object-miss (resolveObject never guesses), so breadth here is genuinely low-risk. */
34
+ // REVERSE inherits phrasings (Seonix Batch 2 Fix 2) — "is X a superclass of Y" /
35
+ // "is X a parent class of Y" name the SAME `inherits` relation as the forward
36
+ // "is X a subclass of Y" verbs above, but with subject/object semantically
37
+ // SWAPPED: the questioner's X is the base, not the derived class. The "a"-forms
38
+ // (below) are folded into RELATIONS.inherits.verbs so VERB_TO_KIND maps them to
39
+ // kind "inherits" exactly like every forward verb — every existing
40
+ // inherits-consuming template/renderer keeps working unmodified — but ALSO kept
41
+ // as this separate exported list so the strategies that build "ask" shapes
42
+ // (subject-before/object-after by regex capture POSITION, not semantic
43
+ // direction — see grammar.mjs T1 and keywords.mjs's decomposition) can detect a
44
+ // reverse verb and swap subject/object at parse time, before evaluation ever
45
+ // sees it.
46
+ //
47
+ // Bare "superclass"/"superclasses" (single word, no "is … of" wrapper) are ALSO
48
+ // folded in: keyword-spot's decomposition (keywords.mjs) finds a verb phrase as
49
+ // a CONTIGUOUS run of words, and the interrogative word order ("is Base a
50
+ // superclass of Widget") puts the subject BETWEEN "is" and "a superclass of",
51
+ // breaking that contiguity for the 4-word phrase — exactly mirroring why the
52
+ // forward direction already carries bare "subclass"/"subclasses" alongside its
53
+ // own "is a subclass of" (both above, in RELATIONS.inherits.verbs): the bare
54
+ // stem is what actually lets keyword-spot's decomposition recognize the
55
+ // aux-first question form ("is Foo a subclass of Bar" only resolves today via
56
+ // that same bare "subclass" stem — see grammar.mjs T1's own comment).
57
+ //
58
+ // The "the"-DEFINITE forms ("is the superclass of", "are the superclass of",
59
+ // "is the parent class of") are named in INHERITS_REVERSE_VERBS below (the
60
+ // caller-requested literal set) but deliberately NOT folded into
61
+ // RELATIONS.inherits.verbs / VERB_TO_KIND: ask.mjs's CONTENT_VOCAB is built by
62
+ // splitting every VERB_TO_KIND key into its individual words (wordsOf), so a
63
+ // verb phrase containing the bare word "the" would leak "the" itself into
64
+ // CONTENT_VOCAB — and the progressive-relaxation cascade's NOISE-STRIP layer
65
+ // treats anything in CONTENT_VOCAB as un-strippable content, not noise. Verified
66
+ // live: folding the "the"-forms in broke test/ask-cascade.test.mjs's pinned
67
+ // NOISE-STRIP/DROP-UNMATCHED/SYNONYM-NORMALISE cases (each expects "the" to stay
68
+ // strippable) and test/chatflow-tier2.test.mjs's ESL-pronoun case, all of which
69
+ // rely on the pre-existing invariant that NO verb phrase in this file's tables
70
+ // ever contains the bare word "the" (confirmed true before this change). So the
71
+ // "the"-forms stay honest misses for now — "is a superclass of"/"are a
72
+ // superclass of" (the forms this Batch's own tests exercise) work; reinstating
73
+ // the "the"-forms would need a CONTENT_VOCAB fix first, out of this fix's scope.
74
+ const INHERITS_REVERSE_VERB_LIST = [
75
+ "is a superclass of",
76
+ "are a superclass of",
77
+ "is a parent class of",
78
+ "are a parent class of",
79
+ "superclass", "superclasses",
80
+ ];
81
+
34
82
  export const RELATIONS = {
35
83
  imports: {
36
84
  comment: "Module -> Module: subject's import graph references object (usesComplexType).",
@@ -105,7 +153,18 @@ export const RELATIONS = {
105
153
  inherits: {
106
154
  comment: "Class -> Class: subject's declared base resolves to object (subclassOf).",
107
155
  verbs: [
108
- "inherits from", "inherit from", "extends", "extend", "subclasses", "subclass",
156
+ "inherits from", "inherit from",
157
+ // bare "inherits"/"inherit" (Tier 6 playtest, §3b surface-variation axis):
158
+ // this list's own SIBLING verb "extends"/"extend" already works bare, with
159
+ // no "from" required, but "inherits"/"inherit" — arguably the MORE common
160
+ // everyday phrasing of the two ("TaskController inherits Controller",
161
+ // "does TaskController inherit Controller") — had no bare form at all,
162
+ // only the "... from" variant. VERB_ALT's longest-first sort (already
163
+ // relied on elsewhere in this file for the same reason) means "inherits
164
+ // from"/"inherit from" still win whenever "from" actually follows, so
165
+ // this is purely additive.
166
+ "inherits", "inherit",
167
+ "extends", "extend", "subclasses", "subclass",
109
168
  "derives from", "derive from", "is a subclass of", "are a subclass of",
110
169
  "is a kind of", "are a kind of", "is built off", "are built off",
111
170
  "is built on top of", "are built on top of",
@@ -116,6 +175,12 @@ export const RELATIONS = {
116
175
  // the gerund-lead check reads) are listed; longest-match-first prefers the
117
176
  // two-word form when "from" follows.
118
177
  "extending", "inheriting from", "inheriting", "subclassing", "extends from",
178
+ // REVERSE phrasings (Seonix Batch 2 Fix 2, see INHERITS_REVERSE_VERB_LIST's own
179
+ // comment above): "is/are a|the superclass/parent class of" — folded in here so
180
+ // VERB_TO_KIND maps them to "inherits" like every other verb in this list; the
181
+ // subject/object SWAP their direction requires is handled at parse time by the
182
+ // strategies that build the "ask" shape, not here.
183
+ ...INHERITS_REVERSE_VERB_LIST,
119
184
  ],
120
185
  },
121
186
  touches: {
@@ -163,6 +228,13 @@ export const RELATIONS = {
163
228
  // Commit->Module kind, structurally unable to match a Module subject), always
164
229
  // producing a confidently-empty answer regardless of real cochange data.
165
230
  "changed together with", "change together with", "changes together with",
231
+ // Present-tense bare form (Seonix Batch 4/5 follow-up): only the past tense
232
+ // "changed with" existed above — "what changes with X"/"what usually changes
233
+ // with X" fell through entirely (ENTITY_TO_TYPE's own "changes"->"Change"
234
+ // pseudo-type noun risked consuming the word first; see parseRelationalOrQualified's
235
+ // guard against exactly that in ask.mjs).
236
+ "changes with", "change with", "tends to change with", "tend to change with",
237
+ "usually changes with",
166
238
  ],
167
239
  },
168
240
  reexports: {
@@ -180,6 +252,22 @@ export const RELATIONS = {
180
252
  },
181
253
  };
182
254
 
255
+ /** The closed set of reverse `inherits` verb phrasings a strategy that has already
256
+ * matched a verb phrase can check ("was this one of the reverse ones?") to decide
257
+ * whether to swap subject/object before returning its parsed shape (see the
258
+ * comment above INHERITS_REVERSE_VERB_LIST, right before RELATIONS, for the full
259
+ * story). Every phrase here EXCEPT the three "the"-definite forms is ALSO folded
260
+ * into RELATIONS.inherits.verbs (so VERB_TO_KIND routes it to kind "inherits"
261
+ * like any other inherits verb) — the "the"-forms are named here for
262
+ * completeness (matching the originally-specified closed set) but are not yet
263
+ * reachable through VERB_TO_KIND, so a strategy will never actually see one as
264
+ * a matched verb; keeping them in this list is harmless and future-proofs the
265
+ * swap check for whenever the CONTENT_VOCAB constraint is lifted. */
266
+ export const INHERITS_REVERSE_VERBS = Object.freeze([
267
+ ...INHERITS_REVERSE_VERB_LIST,
268
+ "is the superclass of", "are the superclass of", "is the parent class of",
269
+ ]);
270
+
183
271
  // ---- where/when/mentions markers (2026-07-02 query families) — the location and
184
272
  // prose-mention questions carry NO relation verb ("where is X defined", "where is
185
273
  // X mentioned"), so ask.mjs routes them by these marker words instead of
@@ -191,6 +279,34 @@ export const WHERE_MARKERS = Object.freeze(["defined", "declared", "located", "i
191
279
  /** Prose-mention markers: "where is X <marker>" -> the prose/mentions surface. */
192
280
  export const MENTION_MARKERS = Object.freeze(["mentioned", "referenced"]);
193
281
 
282
+ // ---- trailing scope filler (Seonix Batch 2 Fix 3) — a "what is a <noun phrase>"
283
+ // question sometimes tacks on a trailing clause that scopes the question back onto
284
+ // the graph/codebase ITSELF, not onto any additional term: "what is a Module in
285
+ // this graph" means exactly "what is a Module", not a literal lookup of the glued
286
+ // phrase "Module in this graph". Closed and curated like every other table here —
287
+ // an unlisted trailing clause is left alone (an honest literal lookup), never
288
+ // silently swallowed by a general heuristic. ----
289
+
290
+ /** Trailing filler clauses stripped from the END of a captured meta-whatis object,
291
+ * case-insensitive, before the term is used as a lookup key. */
292
+ export const TRAILING_SCOPE_FILLER = Object.freeze([
293
+ "in this graph", "in the graph", "in this codebase", "in the codebase",
294
+ "in this repo", "in the repo", "here",
295
+ ]);
296
+
297
+ const TRAILING_SCOPE_FILLER_RE = new RegExp(
298
+ `\\s+(?:${TRAILING_SCOPE_FILLER.join("|")})\\s*[?.!]*$`, "i",
299
+ );
300
+
301
+ /** Strip ONE trailing scope-filler clause (TRAILING_SCOPE_FILLER, above) off the end
302
+ * of a captured meta-whatis object — "what is a Module in this graph" resolves the
303
+ * same term as "what is a Module". Applied once, not in a loop: no worked phrasing
304
+ * stacks two filler clauses. Every TRAILING_SCOPE_FILLER entry is plain words (no
305
+ * regex metacharacters), so no escaping is needed building the alternation. */
306
+ export function stripTrailingScopeFiller(text) {
307
+ return text.replace(TRAILING_SCOPE_FILLER_RE, "").trim();
308
+ }
309
+
194
310
  /** relation token -> flat verb-phrase list, the shape ask.mjs's VERB_TO_KIND
195
311
  * table needs (phrase -> kind), derived once from RELATIONS. */
196
312
  export const VERB_TO_KIND = Object.freeze(
@@ -352,6 +468,14 @@ export const MISSPELLINGS = Object.freeze({
352
468
  // curated here so the intended trigger is restored BEFORE parsing (the general
353
469
  // bounded fuzzy path in ask.mjs's cascade is the backstop for uncurated typos).
354
470
  "manyn": "many", "mnay": "many", "amny": "many", "mnany": "many",
471
+ // "hwo" (Tier 6 playtest, §3b typo axis): a transposed-letter typo of "how" —
472
+ // "hwo many classes are there" used to lose the aggregate/list trigger
473
+ // outright ("how many" only reads as a count trigger when both words are
474
+ // exact), same failure class as "manyn"/"mnay" just above, one word to the
475
+ // left of it. "how" is grammar-owned via AGGREGATE_TRIGGERS' own "how many"/
476
+ // "how much" entries (test/ask-vocab.test.mjs's canonical-value check
477
+ // splits those multi-word triggers into individual words).
478
+ "hwo": "how",
355
479
  "coutn": "count", "conut": "count", "cuont": "count", "ocunt": "count",
356
480
  "numer": "number", "nubmer": "number", "numbr": "number", "nmuber": "number",
357
481
  "lst": "list", "lsit": "list", "ilst": "list",