@polycode-projects/the-mechanical-code-talker 1.8.14 → 1.8.15

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 (3) hide show
  1. package/README.md +34 -2
  2. package/package.json +1 -1
  3. package/src/chat.mjs +51 -2
package/README.md CHANGED
@@ -79,6 +79,12 @@ the connecting sentence itself, citing both sources. The `source: ace:chat:…`
79
79
  part is a real provenance receipt. Every fact tmct stores records where it
80
80
  came from and when (more on that below).
81
81
 
82
+ The first `tell()` call above replies too: `noted — remembered 1 fact:
83
+ controller rdfs:subClassOf handler (controller is a type of handler)`. The
84
+ part in parentheses is a paraphrase. tmct generates it and checks it against
85
+ its own inference rules before showing it, so it never just guesses at
86
+ prose.
87
+
82
88
  ```
83
89
  $ tmct
84
90
  tmct> what talks to the payment module?
@@ -188,6 +194,23 @@ exactly. It checks the type itself and its subclasses first. Only if nothing
188
194
  matches there does it widen to a related type, and when it does, it says so
189
195
  plainly rather than presenting the looser match as exact.
190
196
 
197
+ **Comparing two things.** "compare TaskController and UserController" or "how
198
+ is TaskController different from UserController" lines up both entities'
199
+ shared and differing edges side by side: *"Comparing TaskController and
200
+ UserController (both Class): inherits [seon:hasSuperType]: TaskController (1)
201
+ -> Controller; UserController (1) -> Controller"*. Every row is a real edge
202
+ or attribute from the graph, never a hand-written diff.
203
+
204
+ **Up-refining to a containing module.** A class rarely has its own
205
+ symbol-precise commit or import record. "who touched TaskController" answers
206
+ from its containing module's real history instead of a confident-looking but
207
+ wrong "nothing touched it".
208
+
209
+ **Following a list.** tmct remembers the last list it gave you. After
210
+ "which modules import src/core/model.mjs", "which of those are tested" or
211
+ "how many of those" resolves "those"/"them" against that list, not a fresh,
212
+ unresolved pronoun.
213
+
191
214
  **Synonyms and everyday phrasing.** tmct matches many of the words people
192
215
  actually use for the same idea, from a curated synonym list plus a filtered
193
216
  ConceptNet slice. A slightly different word for the same concept still
@@ -242,6 +265,11 @@ longer, multi-sentence account instead of one line. Every sentence in it is
242
265
  lifted from a real graph edge, attribute, or taught fact — never generated
243
266
  free text.
244
267
 
268
+ The wording varies a little too. A small, curated, deterministic pool swaps a
269
+ handful of connector words, like "defined in", "located in", or "found in".
270
+ The same fact doesn't read identically for every entity, but the same
271
+ question against the same entity always renders the same way.
272
+
245
273
  ```
246
274
  $ node bin/tmct.mjs chat --repo examples/mini-webapp --ephemeral
247
275
  tmct> give me a detailed overview of how the Store works
@@ -314,8 +342,8 @@ from the environment. There's no CLI flag yet — this is a library-level
314
342
  option for now, newer and less exercised than the default backend.
315
343
 
316
344
  Teaching isn't limited to the ACE grammar's fixed shapes. Tell tmct an
317
- arbitrary fact, like "margo eats ribs", and it mints a fact you can later ask
318
- about directly: "what does margo eat", or "does margo eat ribs".
345
+ arbitrary fact, like "margo really eats ribs", and it mints a fact you can
346
+ later ask about directly: "what does margo eat", or "does margo eat ribs".
319
347
 
320
348
  New vocabulary compounds as you teach it. "redis is a cache" mints "redis" as
321
349
  a class-level concept even though it was never in the built-in lexicon, and a
@@ -326,6 +354,10 @@ to ground one side first. Quantified teaching works too: "some functions are
326
354
  risky" stores the quantifier, and a later "how many functions are risky"
327
355
  answers "A few."
328
356
 
357
+ Once you've taught a few facts, "how many facts are there" counts them back.
358
+ That's the same count phrasing a code graph answers "how many classes are
359
+ there" with, just now reading tmct's own memory.
360
+
329
361
  ### Provenance and trust
330
362
 
331
363
  Every fact and text block records **where it came from and when**. Sources are
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polycode-projects/the-mechanical-code-talker",
3
- "version": "1.8.14",
3
+ "version": "1.8.15",
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/chat.mjs CHANGED
@@ -4879,6 +4879,34 @@ const HAS_METHOD_OPEN_RE = /^what\s+methods\s+does\s+([\w'-]+)\s+have[?.!\s]*$/i
4879
4879
  * cascade/orientation nudge that already handles it. */
4880
4880
  const IS_ADJECTIVE_YESNO_RE = /^(?:is|are|was|were)\s+(.+?)\s+([A-Za-z][\w-]*)[?.!\s]*$/i;
4881
4881
  const IS_ADJECTIVE_PRONOUN_RE = /^(?:it|this|that)$/i;
4882
+ /** BENCHMARK_CONVERSATION_1.8.14.md persona-sweep (2026-07-12), highest cross-
4883
+ * persona signal in the run (4 independent personas): IS_ADJECTIVE_YESNO_RE's
4884
+ * subject capture is unbounded/unrestricted (see its own docblock above), so
4885
+ * a pronoun-subject IDENTITY question ("are you happy", "are you like
4886
+ * chatgpt", "are you secretly ChatGPT or GPT-4") backtracks the pronoun
4887
+ * itself (plus any trailing filler word up to the last token) into the
4888
+ * SUBJECT capture — factReadBack then treats "you"/"you like"/"you secretly
4889
+ * chatgpt or" as a literal fact subject and offers to teach a fact ABOUT the
4890
+ * pronoun ("remember that you is happy"), exactly the grammatical category
4891
+ * error TEACH_PRONOUN_RE (above, chat.mjs:2648) was already built to reject
4892
+ * on the teach-lane side. Same pronoun set (you|i|they|he|she|we) reused
4893
+ * here, checked at the START of the subject capture only (not anchored to
4894
+ * the whole capture — a pronoun subject can carry trailing words, "you
4895
+ * like"/"you secretly … or", the same way TEACH_PRONOUN_RE's own `\s+\S+`
4896
+ * tail allows). "it" is deliberately EXCLUDED from this set, unlike
4897
+ * TEACH_PRONOUN_RE — IS_ADJECTIVE_PRONOUN_RE (just above) already gives "it"
4898
+ * its own correct, wanted behavior (anaphoric resolution against the
4899
+ * session's current FOCUS, "is it deprecated" → resolves off focusLabel),
4900
+ * which this guard must not shadow. Every call site below is expected to
4901
+ * test rawSubject (post-trim, pre-lowercasing) against this BEFORE treating
4902
+ * the match as a fact-subject candidate, and to fall through (never offer
4903
+ * unknownAdjectiveOffer, never attempt a fact lookup) on a hit — the same
4904
+ * "decline, don't misroute" discipline the rest of this reader already
4905
+ * follows for an honest miss, letting the query continue to whatever
4906
+ * handles identity/small-talk questions (isConversational's IDENTITY_PHRASES/
4907
+ * AI_IDENTITY_PHRASES/FEELINGS_PHRASES closed sets, or its own ≤3-word
4908
+ * catch-all) instead. */
4909
+ const IS_ADJECTIVE_YESNO_PRONOUN_SUBJECT_RE = /^(?:you|i|they|he|she|we)\b/i;
4882
4910
  /** The TEACH-OFFER for a subject IS_ADJECTIVE_YESNO_RE resolved but has no
4883
4911
  * fact about at all (Tier-5 playtest, cycle 2) — the offered "remember that
4884
4912
  * X is Y" phrasing is verified in-state: TEACH_PROPERTY_RE's own subject
@@ -5050,7 +5078,17 @@ async function factReadBack(memoryDir, query, envelope, miss, graph = null, focu
5050
5078
  // already stood; "is the checkout flow deprecated" (this branch's own
5051
5079
  // ORIGINAL T8 target — "deprecated" has no structural meaning at all)
5052
5080
  // has no envelope.parsed to defer to, so it is untouched.
5053
- if (subject && !/^there\b/i.test(subject) && !envelope?.parsed) {
5081
+ // Pronoun-subject guard (BENCHMARK_CONVERSATION_1.8.14.md persona-sweep,
5082
+ // 2026-07-12) — see IS_ADJECTIVE_YESNO_PRONOUN_SUBJECT_RE's own docblock
5083
+ // above IS_ADJECTIVE_YESNO_RE: "are you happy"/"are you like chatgpt"
5084
+ // backtrack a pronoun subject in here exactly like any other adjective
5085
+ // subject, so without this check unknownAdjectiveOffer would wrongly
5086
+ // offer to teach a fact about the literal pronoun. Checked on rawSubject
5087
+ // (before the IS_ADJECTIVE_PRONOUN_RE focus-resolution swap above, which
5088
+ // only ever fires for "it"/"this"/"that" — never a personal pronoun like
5089
+ // "you", so `subject` itself would already carry the pronoun verbatim).
5090
+ if (subject && !/^there\b/i.test(subject) && !envelope?.parsed
5091
+ && !IS_ADJECTIVE_YESNO_PRONOUN_SUBJECT_RE.test(rawSubject)) {
5054
5092
  return unknownAdjectiveOffer(subject, emptyIsAdj[2].trim().toLowerCase());
5055
5093
  }
5056
5094
  }
@@ -5816,7 +5854,18 @@ async function factReadBack(memoryDir, query, envelope, miss, graph = null, focu
5816
5854
  const isAdj = qHedge.match(IS_ADJECTIVE_YESNO_RE);
5817
5855
  if (isAdj) {
5818
5856
  const rawSubject = isAdj[1].trim();
5819
- const subject = IS_ADJECTIVE_PRONOUN_RE.test(rawSubject) ? (focusLabel || null) : rawSubject;
5857
+ // Pronoun-subject guard (BENCHMARK_CONVERSATION_1.8.14.md persona-sweep,
5858
+ // 2026-07-12) — see IS_ADJECTIVE_YESNO_PRONOUN_SUBJECT_RE's own docblock
5859
+ // above IS_ADJECTIVE_YESNO_RE: "are you happy"/"are you like chatgpt"/
5860
+ // "are you secretly ChatGPT or GPT-4" backtrack a personal-pronoun
5861
+ // subject in here just like any other adjective subject. Forcing
5862
+ // `subject` to null (the SAME "nothing to resolve" shape a bare "it"/
5863
+ // "this"/"that" with no standing focus already produces just below) lets
5864
+ // this whole reader decline HONESTLY — no fact lookup, no teach-offer —
5865
+ // and fall through to whatever handles identity/small-talk questions
5866
+ // instead, rather than special-casing a return here.
5867
+ const subject = IS_ADJECTIVE_YESNO_PRONOUN_SUBJECT_RE.test(rawSubject) ? null
5868
+ : IS_ADJECTIVE_PRONOUN_RE.test(rawSubject) ? (focusLabel || null) : rawSubject;
5820
5869
  const adjective = isAdj[2].trim().toLowerCase();
5821
5870
  if (subject) {
5822
5871
  const subjVariants = factTermVariants(normFactTerm, subject);