@polycode-projects/the-mechanical-code-talker 1.10.14 → 1.11.5
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/README.md +121 -114
- package/ROADMAP.md +14 -4
- package/bin/tmct.mjs +167 -77
- package/data/games/crates.txt +24 -0
- package/data/games/hanoi-3.txt +30 -0
- package/data/games/river.txt +33 -0
- package/package.json +1 -1
- package/src/ask.mjs +119 -6
- package/src/chat.mjs +1515 -52
- package/src/codegraph.mjs +24 -11
- package/src/domain.mjs +350 -0
- package/src/import-file.mjs +86 -0
- package/src/init.mjs +46 -1
- package/src/interpret/normalize.mjs +46 -0
- package/src/interpret/strategies/keywords.mjs +13 -9
- package/src/ledger-viz.mjs +635 -0
- package/src/memory/core.mjs +100 -17
- package/src/memory/shacl.mjs +20 -7
- package/src/memory-ask-browser-entry.mjs +9 -7
- package/src/memory-ask-browser.bundle.js +5648 -1177
- package/src/plan-viz.mjs +409 -0
- package/src/router/drive.mjs +122 -13
- package/src/router/guardrail.mjs +5 -0
- package/src/router/registry.mjs +55 -11
- package/src/router/resolver.mjs +13 -0
- package/src/router/taught.mjs +84 -0
- package/src/sentences.mjs +19 -0
- package/src/syllogise.mjs +154 -38
- package/src/viz-theme.mjs +66 -0
- package/src/wink-model.mjs +12 -6
- package/src/ask-browser-entry.mjs +0 -19
- package/src/ask-browser.bundle.js +0 -5411
- package/src/viz.mjs +0 -959
package/README.md
CHANGED
|
@@ -114,6 +114,23 @@ tmct> /exit
|
|
|
114
114
|
**[Try it live in your browser →](https://polycode-projects.gitlab.io/the-mechanical-code-talker/)**
|
|
115
115
|
is a real, interactive chat demo running client-side. Your browser runs the
|
|
116
116
|
actual query engine against a small example codebase, no server, no install.
|
|
117
|
+
The page leads with the **memory ledger**: every fact as a readable sentence,
|
|
118
|
+
drill by clicking the terms inside them, and an in-page chat whose answers
|
|
119
|
+
focus the ledger.
|
|
120
|
+
|
|
121
|
+
Two more surfaces, both generated by tmct itself:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
npx tmct viz # ledger.html — your own memory as the same
|
|
125
|
+
# readable, self-contained explorer
|
|
126
|
+
npx tmct init
|
|
127
|
+
npx tmct import --file .tmct/imports/games/hanoi-3.txt
|
|
128
|
+
npx tmct chat --prompt 'disk-1 rests on disk-2. disk-2 rests on disk-3.
|
|
129
|
+
disk-3 rests on peg-a. the goal is that every disk rests on peg-c. solve it.' \
|
|
130
|
+
--render blocks --output plan.html # an animated replay of the solved plan
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
More on the game file and the planner under "Teach it a game" below.
|
|
117
134
|
|
|
118
135
|
## How it interprets you
|
|
119
136
|
|
|
@@ -127,65 +144,38 @@ Those triples are statements it can store, retrieve, and answer from later.
|
|
|
127
144
|
Text that doesn't fit the grammar still gets the tolerant strategies. Nothing
|
|
128
145
|
is rejected for being loose, fuzzy, or misspelled.
|
|
129
146
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
or attribute from the graph, never a hand-written diff.
|
|
159
|
-
|
|
160
|
-
**Up-refining to a containing module.** A class rarely has its own
|
|
161
|
-
symbol-precise commit or import record. "who touched TaskController" answers
|
|
162
|
-
from its containing module's real history instead of a confident-looking but
|
|
163
|
-
wrong "nothing touched it".
|
|
164
|
-
|
|
165
|
-
**Following a list.** tmct remembers the last list it gave you. After
|
|
166
|
-
"which modules import src/core/model.mjs", "which of those are tested" or
|
|
167
|
-
"how many of those" resolves "those"/"them" against that list, not a fresh,
|
|
168
|
-
unresolved pronoun.
|
|
169
|
-
|
|
170
|
-
**Synonyms and everyday phrasing.** tmct matches many of the words people
|
|
171
|
-
actually use for the same idea, from a curated synonym list plus a filtered
|
|
172
|
-
ConceptNet slice. A slightly different word for the same concept still
|
|
173
|
-
resolves. It also follows a few common sentence shapes: clauses starting with
|
|
174
|
-
*because/although/while*, and conditionals ("if X were removed, what
|
|
175
|
-
breaks"). It flags a question whose premise doesn't hold, too: "why does X
|
|
176
|
-
still import Y" when it no longer does.
|
|
177
|
-
|
|
178
|
-
**Response finishing.** Before an answer is printed it is segmented into typed
|
|
179
|
-
spans: prose versus *protected* entities, paths, numbers, code, provenance, and
|
|
180
|
-
receipts. A small data-driven grammar pass then runs on the prose spans only,
|
|
181
|
-
under a guard that proves the protected spans came through byte-for-byte. Today
|
|
182
|
-
that pass fixes the a/an article defect. Broader voice and agreement rules are
|
|
183
|
-
implemented but parked until they earn their place on the benchmark.
|
|
147
|
+
On top of that base, tmct reads the shapes people actually use, and each one
|
|
148
|
+
resolves to a real graph traversal or declines honestly:
|
|
149
|
+
|
|
150
|
+
- everyday question forms: "what is Commit", "what's model.mjs for",
|
|
151
|
+
"recent commits" as real dated history;
|
|
152
|
+
- polite or indirect framing: "I'd like you to remember X" teaches like bare
|
|
153
|
+
"remember X";
|
|
154
|
+
- negation as a bounded **set complement**: "which modules do *not* import
|
|
155
|
+
X?", with an empty result staying a miss, never a fabricated list;
|
|
156
|
+
- reversible passives: "what is imported by Y" and "what does Y import" are
|
|
157
|
+
opposite edges, not the same one;
|
|
158
|
+
- finding by description: "find me the payment class" checks the type and
|
|
159
|
+
its subclasses first, and says so plainly when it widens;
|
|
160
|
+
- comparison: "compare TaskController and UserController" lines up both
|
|
161
|
+
entities' real edges side by side, never a hand-written diff;
|
|
162
|
+
- list follow-ups: after "which modules import src/core/model.mjs",
|
|
163
|
+
"which of those are tested" resolves against that list;
|
|
164
|
+
- curated synonyms plus a filtered ConceptNet slice, clause openers
|
|
165
|
+
(*because/although/while*), conditionals, and false-premise flags ("why
|
|
166
|
+
does X still import Y" when it no longer does).
|
|
167
|
+
|
|
168
|
+
The full catalog with measured coverage lives in `CAPABILITIES_1.7.3.md` and
|
|
169
|
+
the `BENCHMARK_*.md` reports.
|
|
170
|
+
|
|
171
|
+
**Response finishing.** Before an answer prints, it is segmented into typed
|
|
172
|
+
spans: prose versus *protected* entities, paths, numbers, code, provenance,
|
|
173
|
+
and receipts. A small data-driven grammar pass runs on the prose spans only,
|
|
174
|
+
under a guard that proves the protected spans came through byte-for-byte.
|
|
184
175
|
|
|
185
176
|
A frozen regression suite plays out full multi-turn dialogues built from these
|
|
186
|
-
phrasings,
|
|
187
|
-
|
|
188
|
-
in `HANDOVER.md` and `ROADMAP.md`.
|
|
177
|
+
phrasings, from a single question up to a messy, typo-ridden real user.
|
|
178
|
+
Tier-by-tier detail is in `HANDOVER.md` and `ROADMAP.md`.
|
|
189
179
|
|
|
190
180
|
## How it guides you
|
|
191
181
|
|
|
@@ -250,13 +240,7 @@ const memory = await loadMemory(dir);
|
|
|
250
240
|
const graphService = createCompletionsGraphAdapter(graph, memory);
|
|
251
241
|
|
|
252
242
|
const { text } = await generateCompletion(dir, "Store", { query: "Store", graph, memory, graphService });
|
|
253
|
-
console.log(text);
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
```
|
|
257
|
-
Attribute: prose_tokens = memory record store [mgx:hasProseTokens]. Attribute:
|
|
258
|
-
doc = In-memory record store. [seon:hasDoc]. Other matches: src/core/store.mjs
|
|
259
|
-
(Module), loadStore (Function), saveStore (Function), testLoadStore (Function).
|
|
243
|
+
console.log(text); // prints the same text as the chat answer above
|
|
260
244
|
```
|
|
261
245
|
|
|
262
246
|
Full pipeline design in `archive/PLAN_COMPLETIONS.md`.
|
|
@@ -328,6 +312,45 @@ proof chain, composed answer) for a caller that wants to consume this
|
|
|
328
312
|
programmatically rather than read the report. `tmct plan --help` has the full
|
|
329
313
|
flag reference.
|
|
330
314
|
|
|
315
|
+
## Teach it a game, then ask it to plan
|
|
316
|
+
|
|
317
|
+
The planner above works over a fixed toolset. This one works over rules you
|
|
318
|
+
teach. A game definition is a plain-text file of controlled English — the
|
|
319
|
+
classes, the pieces, the ordering, and the legal moves as taught action
|
|
320
|
+
rules — with `#` comment lines carrying example prompts. `tmct init`
|
|
321
|
+
scaffolds one at `.tmct/imports/games/hanoi-3.txt`, and
|
|
322
|
+
`tmct import --file` teaches it sentence by sentence, reporting every line
|
|
323
|
+
and refusing (exit 1) if any sentence declines.
|
|
324
|
+
|
|
325
|
+
Then one message states the board and the goal, and "solve it" searches the
|
|
326
|
+
taught rules for the shortest move sequence:
|
|
327
|
+
|
|
328
|
+
```
|
|
329
|
+
tmct> disk-1 rests on disk-2. disk-2 rests on disk-3. disk-3 rests on peg-a.
|
|
330
|
+
the goal is that every disk rests on peg-c. solve it.
|
|
331
|
+
plan found — 7 moves (shortest):
|
|
332
|
+
1. move disk-1 onto peg-c
|
|
333
|
+
2. move disk-2 onto peg-b
|
|
334
|
+
3. move disk-1 onto disk-2
|
|
335
|
+
4. move disk-3 onto peg-c
|
|
336
|
+
5. move disk-1 onto peg-a
|
|
337
|
+
6. move disk-2 onto disk-3
|
|
338
|
+
7. move disk-1 onto disk-2
|
|
339
|
+
|
|
340
|
+
because — you taught me the "move onto" rule and 3 ordering facts. Say "next" to make move 1, or ask "what moves are legal now".
|
|
341
|
+
|
|
342
|
+
Goal (inferred): Plan a move sequence from the current state to the goal (7 moves).
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
"next" executes one move at a time, writing each board state into memory as
|
|
346
|
+
facts; the final step re-reads those facts and confirms the goal from them,
|
|
347
|
+
never assuming success. The search is genuine and domain-general: the test
|
|
348
|
+
suite teaches Towers of Hanoi purely as sentences for 1 to 8 disks and
|
|
349
|
+
asserts the plan is exactly 2^n − 1 moves every time, and a second game
|
|
350
|
+
(`crates.txt`, stacking crates with different rules and a two-goal
|
|
351
|
+
conjunction) solves with zero interpreter changes. `--render blocks` writes
|
|
352
|
+
the plan as a self-contained animated page (see "Two more surfaces" above).
|
|
353
|
+
|
|
331
354
|
## How it remembers
|
|
332
355
|
|
|
333
356
|
tmct's memory has two layers, both fed by every parsed request and response and
|
|
@@ -359,38 +382,21 @@ reasoning). Design detail and the full fact-count tables are in `archive/PLAN_SE
|
|
|
359
382
|
The default memory backend writes an OWL-labelled JSON file under `.tmct/`.
|
|
360
383
|
Two more exist: `memory` keeps taught facts in the process only, nothing
|
|
361
384
|
written to disk; `sqlite` persists them to a local SQLite file instead
|
|
362
|
-
(`.tmct/memory/graph.sqlite`).
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
```bash
|
|
369
|
-
tmct init --memory-backend sqlite # writes [memory] backend = "sqlite" to tmct.toml
|
|
370
|
-
tmct chat # picks it up automatically
|
|
371
|
-
tmct chat --memory-backend memory # override for just this session
|
|
372
|
-
```
|
|
373
|
-
|
|
374
|
-
Precedence is `--memory-backend` flag > `TMCT_MEMORY_BACKEND` env > tmct.toml's
|
|
375
|
-
`[memory] backend` > the default. A library caller sets the same thing
|
|
376
|
-
directly: `runChat({ memoryBackend: "sqlite" })`.
|
|
385
|
+
(`.tmct/memory/graph.sqlite`). `tmct init --memory-backend sqlite` writes the
|
|
386
|
+
choice into `tmct.toml` and every later `tmct chat` in that repo picks it up.
|
|
387
|
+
Precedence is `--memory-backend` flag > `TMCT_MEMORY_BACKEND` env >
|
|
388
|
+
tmct.toml's `[memory] backend` > the default. A library caller sets the same
|
|
389
|
+
thing directly: `runChat({ memoryBackend: "sqlite" })`.
|
|
377
390
|
|
|
378
391
|
Teaching isn't limited to the ACE grammar's fixed shapes. Tell tmct an
|
|
379
392
|
arbitrary fact, like "margo really eats ribs", and it mints a fact you can
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
nudges you to ground one side first. Quantified teaching works too: "some
|
|
388
|
-
functions are risky" stores the quantifier, and a later "how many functions
|
|
389
|
-
are risky" answers "A few."
|
|
390
|
-
|
|
391
|
-
Once you've taught a few facts, "how many facts are there" counts them back.
|
|
392
|
-
That's the same count phrasing a code graph answers "how many classes are
|
|
393
|
-
there" with, just now reading tmct's own memory.
|
|
393
|
+
ask about directly: "what does margo eat". New vocabulary compounds as you
|
|
394
|
+
teach: "redis is a cache" mints "redis" even though it was never in the
|
|
395
|
+
built-in lexicon, as long as one side of the sentence is already grounded —
|
|
396
|
+
tmct never mints a fact between two totally ungrounded terms; it declines and
|
|
397
|
+
nudges you to ground one side first. Quantified teaching stores the
|
|
398
|
+
quantifier ("some functions are risky" … "how many functions are risky" →
|
|
399
|
+
"A few."), and "how many facts are there" counts the store back.
|
|
394
400
|
|
|
395
401
|
### Provenance and trust
|
|
396
402
|
|
|
@@ -410,10 +416,8 @@ provenance** rather than silently picking a winner.
|
|
|
410
416
|
|
|
411
417
|
`tmct syllogise [--depth n] [--budget n]` is an offline, bounded, deterministic
|
|
412
418
|
batch that forward-chains the memory's `rdfs:subClassOf` closure into new
|
|
413
|
-
**entailed** facts
|
|
414
|
-
|
|
415
|
-
**low-trust and retractable** (never outranking a stated fact) and this never runs
|
|
416
|
-
on the chat's hot path.
|
|
419
|
+
**entailed** facts. They are low-trust and retractable, never outranking a
|
|
420
|
+
stated fact, and this never runs on the chat's hot path.
|
|
417
421
|
|
|
418
422
|
## Install & use
|
|
419
423
|
|
|
@@ -423,7 +427,7 @@ tmct # bare = chat (the headline)
|
|
|
423
427
|
tmct chat --repo /abs/path/to/repo # chat over a specific repo's graph
|
|
424
428
|
tmct init # scaffold .tmct/, tmct.toml, seed + provenance
|
|
425
429
|
tmct syllogise # offline: pre-derive entailed facts (maintenance)
|
|
426
|
-
npm run viz
|
|
430
|
+
npm run viz && open ledger.html # self-contained HTML memory-ledger explorer
|
|
427
431
|
```
|
|
428
432
|
|
|
429
433
|
Inside the chat: `/help` lists commands, `/memory` inspects what tmct remembers
|
|
@@ -461,6 +465,11 @@ Usage:
|
|
|
461
465
|
see src/graph-merge.mjs); wins over --repo/TMCT_GRAPH_FILE/tmct.toml
|
|
462
466
|
[--config <path>] an alternate tmct.toml location (a file or a directory)
|
|
463
467
|
[--ephemeral] read the graph but write nothing back (demo/read-only)
|
|
468
|
+
[--prompt "<text>"] one-shot: run the prompt's sentences as turns and print
|
|
469
|
+
the final answer (teach state first, trigger last)
|
|
470
|
+
[--render blocks] with --prompt: when the final turn produced a plan,
|
|
471
|
+
write it as a self-contained animated page
|
|
472
|
+
[--output <path>] the rendered page's path (default plan.html)
|
|
464
473
|
[--narrate] start with narrate mode on — a verbose, developer-facing
|
|
465
474
|
trace of decision points/matched pattern/results/goal per
|
|
466
475
|
turn, appended under a "--- narrate ---" marker (also
|
|
@@ -518,6 +527,9 @@ already set up. Its `--graph` flag works differently from the others: it appends
|
|
|
518
527
|
[--ontology <name|path>] DIFFERENT operation from the others: it APPENDS to
|
|
519
528
|
[--lexicon <name|path>] tmct.toml's graph_files array (multi-graph growth),
|
|
520
529
|
[--graph <path>] never an extensions-bundle activation.
|
|
530
|
+
[--file <definition.txt>] teach a plain-text definition file sentence by
|
|
531
|
+
sentence (# lines are comments); any declined
|
|
532
|
+
sentence exits non-zero with the sentence named
|
|
521
533
|
[--memory-backend <default|memory|sqlite>] same knob as `tmct init`
|
|
522
534
|
[--config <path>]
|
|
523
535
|
```
|
|
@@ -540,23 +552,18 @@ inference" above:
|
|
|
540
552
|
[--config <path>] bounded, low-trust, retractable entailed facts (never on the chat path)
|
|
541
553
|
```
|
|
542
554
|
|
|
543
|
-
`tmct viz` renders the memory graph
|
|
555
|
+
`tmct viz` renders the memory graph as the ledger explorer — a single,
|
|
556
|
+
self-contained HTML file you can open in a browser:
|
|
544
557
|
|
|
545
558
|
```
|
|
546
|
-
tmct viz [--repo <abs>] write one self-contained
|
|
547
|
-
[--focus <
|
|
548
|
-
[--term <word>]
|
|
549
|
-
[--
|
|
550
|
-
[--
|
|
551
|
-
[--
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
[--config <path>] connections, still shows it (default 40); --edge-kind =
|
|
555
|
-
meta|relation|both (default both) — which edge kinds the
|
|
556
|
-
walk follows (provenance-only, concept-relations-only, or
|
|
557
|
-
both — see the page's own edge-kind toggle to change this
|
|
558
|
-
live); --term <word> resolves to the Fact(s) whose subject/
|
|
559
|
-
object normalizes to that word and seeds from there.
|
|
559
|
+
tmct viz [--repo <abs>] write one self-contained HTML page: the memory graph as a
|
|
560
|
+
[--focus <term>] readable ledger of fact-sentences around one focus term,
|
|
561
|
+
[--term <word>] with segments, a two-hop minimap, and an in-page chat dock
|
|
562
|
+
[--limit <n>] that answers from the embedded graph. Focuses on the newest
|
|
563
|
+
[--output <path>] taught fact's subject by default (--focus <term> or
|
|
564
|
+
[--config <path>] --term <word> override it); --output defaults to
|
|
565
|
+
ledger.html in the cwd; --limit caps the embedded fact
|
|
566
|
+
rows; --term resolves via the same normalization chat uses.
|
|
560
567
|
```
|
|
561
568
|
|
|
562
569
|
`tmct serve` runs an Anthropic Messages API-compatible HTTP endpoint over the graph,
|
|
@@ -606,7 +613,7 @@ recognized key set, so you can see the full surface in one place
|
|
|
606
613
|
# Newline-delimited-file form is also accepted: repositories = "repos.txt"
|
|
607
614
|
repositories = ["../other-service", "../another-service"]
|
|
608
615
|
|
|
609
|
-
# Where generated output (e.g. tmct viz's default
|
|
616
|
+
# Where generated output (e.g. tmct viz's default ledger.html) resolves to.
|
|
610
617
|
out_root = "./out"
|
|
611
618
|
|
|
612
619
|
# The code-graph JSON artifact. TMCT_GRAPH_FILE overrides this at runtime.
|
package/ROADMAP.md
CHANGED
|
@@ -10,7 +10,10 @@ open items, session-scoped), see `HANDOVER.md` instead — this file doesn't dup
|
|
|
10
10
|
A tolerant, ELIZA/PARRY-style chat surface over a codebase, obsessed with software the way PARRY was
|
|
11
11
|
obsessed with the mafia — deterministic, zero-cost, **no LLM anywhere in the product path**. Guides a
|
|
12
12
|
user toward precision queries rather than guessing; every answer is grounded, restates every genuine
|
|
13
|
-
reading it finds in full, or is an honest miss when nothing grounds it at all.
|
|
13
|
+
reading it finds in full, or is an honest miss when nothing grounds it at all. Its visual surfaces —
|
|
14
|
+
the ledger explorer with its in-browser chat (`tmct viz`), the animated plan page
|
|
15
|
+
(`chat --prompt … --render blocks`), and the Pages homepage hero — are the same graph read out loud:
|
|
16
|
+
same engine, same provenance, no LLM.
|
|
14
17
|
|
|
15
18
|
## Ambition
|
|
16
19
|
|
|
@@ -48,9 +51,16 @@ getting silently traded away by inherited caution:
|
|
|
48
51
|
and an NPC turn scheduler. Design-only.
|
|
49
52
|
- **`PLAN_SYLLOGIST.md`** — retraction-aware consistency checking under a hard budget and trust
|
|
50
53
|
tiers, the one open piece of the reasoning engine's research horizon. Design-only.
|
|
51
|
-
- **`
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
- **`PLAN_HANOI.md`** — shipped in full, including the follow-ups: river-crossing (co-travel
|
|
55
|
+
effects, the forbidden-together constraint frame, 7-crossing oracle) and planner-side
|
|
56
|
+
consumption of `taught:` capability records (`/plan` in chat and bin). See its dated addenda.
|
|
57
|
+
- **`PLAN_GUESS_NUMBER.md`** — the closed-loop (observe-and-replan) planning domain for the same
|
|
58
|
+
kernels. Design-only.
|
|
59
|
+
- **`PLAN_VIZ_LEDGER.md`** — shipped in full, including the follow-ups, all resolved by
|
|
60
|
+
operator decision 2026-07-15: the ledger IS the `tmct viz` surface (node-link page removed),
|
|
61
|
+
`factAnswer`/`factReadBack` carry the additive `goal` field the dock renders, multi-valued
|
|
62
|
+
has/can facts are exempt from `findContradictions`, and page weight is budgeted (~561 KB
|
|
63
|
+
after this batch; revisit only if outgrown). See its dated addendum.
|
|
54
64
|
- **`PLAN_CODE.md`** — small JS-function and HTML/CSS-fragment synthesis via a sandboxed headless
|
|
55
65
|
browser (Track 1, program synthesis, already shipped). Blocked on a sandbox dependency decision.
|
|
56
66
|
- **`PLAN_AGENTS.md`** — the governing plan for tmct's broader multi-repo arc (marginalia, seonix,
|