@polycode-projects/the-mechanical-code-talker 1.9.2 → 1.10.0

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 (80) hide show
  1. package/README.md +441 -202
  2. package/bin/tmct.mjs +126 -1
  3. package/package.json +4 -2
  4. package/src/answer-variants.mjs +8 -36
  5. package/src/ask-browser-entry.mjs +5 -23
  6. package/src/ask-browser.bundle.js +1 -2
  7. package/src/ask-nlp.mjs +9 -23
  8. package/src/ask-vocab.mjs +139 -589
  9. package/src/ask.mjs +627 -1729
  10. package/src/chat.mjs +1684 -2872
  11. package/src/cli-args.mjs +14 -28
  12. package/src/codegraph.mjs +236 -644
  13. package/src/completions/complete.mjs +18 -62
  14. package/src/completions/graph-adapter.mjs +14 -60
  15. package/src/completions/group.mjs +12 -68
  16. package/src/completions/infer.mjs +38 -126
  17. package/src/completions/prune.mjs +17 -70
  18. package/src/completions/rank.mjs +16 -69
  19. package/src/completions/search.mjs +8 -31
  20. package/src/concept.mjs +32 -88
  21. package/src/conformance.mjs +11 -15
  22. package/src/corpus/conceptnet.mjs +31 -89
  23. package/src/corpus/templates.mjs +19 -45
  24. package/src/corpus/unknown-ingest.mjs +31 -92
  25. package/src/embed.mjs +10 -22
  26. package/src/extensions.mjs +50 -154
  27. package/src/finish.mjs +35 -91
  28. package/src/grammar/ace.mjs +16 -40
  29. package/src/grammar/assert.mjs +1 -1
  30. package/src/grammar/lexicon-core.json +1 -1
  31. package/src/grammar/lexicon.mjs +9 -27
  32. package/src/graph-merge.mjs +2 -3
  33. package/src/hash.mjs +6 -14
  34. package/src/index.mjs +6 -10
  35. package/src/init.mjs +38 -125
  36. package/src/interpret/fuzzy.mjs +10 -29
  37. package/src/interpret/merge.mjs +9 -27
  38. package/src/interpret/normalize.mjs +137 -585
  39. package/src/interpret/pipeline.mjs +23 -71
  40. package/src/interpret/strategies/ace.mjs +7 -31
  41. package/src/interpret/strategies/constructions.mjs +14 -41
  42. package/src/interpret/strategies/grammar.mjs +21 -60
  43. package/src/interpret/strategies/keywords.mjs +42 -131
  44. package/src/interpret/strategies/noise-strip.mjs +18 -89
  45. package/src/memory/bias.mjs +11 -54
  46. package/src/memory/blocks.mjs +18 -69
  47. package/src/memory/core.mjs +171 -591
  48. package/src/memory/fold.mjs +0 -0
  49. package/src/memory/inspect.mjs +7 -25
  50. package/src/memory/shacl.mjs +10 -39
  51. package/src/memory/trust.mjs +26 -127
  52. package/src/memory-ask-browser-entry.mjs +7 -30
  53. package/src/memory-ask-browser.bundle.js +1 -1
  54. package/src/paraphrase.mjs +20 -53
  55. package/src/planning.mjs +15 -157
  56. package/src/prose-nlp.mjs +4 -17
  57. package/src/prose.mjs +19 -67
  58. package/src/providers/bootstrap.mjs +1 -2
  59. package/src/providers/fixture.mjs +1 -2
  60. package/src/providers/graph-service.mjs +28 -59
  61. package/src/repository-interface.mjs +6 -8
  62. package/src/router/drive.mjs +183 -0
  63. package/src/router/goal-reasoner.mjs +66 -231
  64. package/src/router/guardrail.mjs +20 -58
  65. package/src/router/planner.mjs +15 -46
  66. package/src/router/registry.mjs +13 -43
  67. package/src/router/resolver.mjs +46 -131
  68. package/src/router/results.mjs +231 -0
  69. package/src/schema-docs.mjs +10 -27
  70. package/src/server-http.mjs +10 -19
  71. package/src/server.mjs +22 -28
  72. package/src/sessions.mjs +15 -30
  73. package/src/source-slice.mjs +5 -7
  74. package/src/source.mjs +10 -20
  75. package/src/syllogise.mjs +187 -575
  76. package/src/telemetry.mjs +3 -3
  77. package/src/toml-config.mjs +4 -4
  78. package/src/tui/app.mjs +9 -19
  79. package/src/viz.mjs +66 -123
  80. package/src/wink-model.mjs +10 -24
package/README.md CHANGED
@@ -3,161 +3,117 @@
3
3
  `@polycode-projects/the-mechanical-code-talker`
4
4
 
5
5
  A pure-JS, **no-LLM**, offline, **$0** chatbot in the ELIZA/PARRY lineage:
6
- pattern-driven, best-efforts, and obsessed with software the way PARRY was
7
- obsessed with the mafia. No model calls anywhere.
6
+ pattern-driven, best-efforts, and focused on software as its subject matter.
7
+ It makes no model calls.
8
8
 
9
9
  tmct turns natural language directly into a graph database. On first run it
10
- seeds an everyday **human-world persona** people, places, objects, nature,
11
- time so it already has a vocabulary before you teach it anything. A
10
+ seeds an everyday **human-world persona**: people, places, objects, nature,
11
+ time. It already has a vocabulary before you teach it anything. A
12
12
  code-focused persona is available as an opt-in alternative: a software
13
13
  **ontology** (real definitions), a **lexicon** (everyday words mapped onto
14
14
  it), and a wider ConceptNet **corpus**. Point tmct at a real codebase's graph
15
- and it reasons over that too, whichever persona is active. Teach it a fact in
16
- plain English and it mints a node. Ask it a question and it answers from what
17
- it was seeded with, what you taught it, and what it can derive by rule from
18
- both. Every answer is either grounded or an honest miss.
15
+ and it reasons over that too, whichever persona is active.
16
+
17
+ Teach it a fact in plain English and it mints a node. Ask it a question and
18
+ it answers from what it was seeded with, what you taught it, and what it can
19
+ derive by rule from both. Every answer is either grounded or an honest miss.
19
20
 
20
21
  ## Teach it, then ask it to reason
21
22
 
22
23
  This is real, runnable output. No cherry-picking, no model anywhere in the
23
- loop. Copy it into a file and run it:
24
+ loop. The script lives at `examples/teach-and-infer.mjs` in this repo; run it
25
+ yourself with `node examples/teach-and-infer.mjs`, or copy the source below:
24
26
 
25
27
  ```js
26
28
  import { runChat } from "@polycode-projects/the-mechanical-code-talker";
27
29
  import { Readable, PassThrough } from "node:stream";
28
- import { mkdtemp, mkdir, writeFile } from "node:fs/promises";
30
+ import { mkdtemp, rm } from "node:fs/promises";
29
31
  import { tmpdir } from "node:os";
30
32
  import { join } from "node:path";
31
33
 
32
- // A graph producer feeds tmct a code graph like this one (see "The repository
33
- // interface" below). This is a 4-node slice: two modules, a base class, and a
34
- // class that inherits it.
35
- const graph = {
36
- individuals: [
37
- { id: "mod:src/handlers/base.mjs", label: "src/handlers/base.mjs", class: "Module" },
38
- { id: "mod:src/handlers/tasks.mjs", label: "src/handlers/tasks.mjs", class: "Module" },
39
- { id: "fn:src/handlers/base.mjs#Controller", label: "Controller", class: "Class" },
40
- { id: "fn:src/handlers/tasks.mjs#TaskController", label: "TaskController", class: "Class" },
41
- ],
42
- objectProperties: [{
43
- predicate: "inherits", prop: "seon:hasSuperType", count: 1,
44
- examples: [{ subject: "fn:src/handlers/tasks.mjs#TaskController", object: "fn:src/handlers/base.mjs#Controller",
45
- subjectLabel: "TaskController", objectLabel: "Controller" }],
46
- }],
47
- };
48
-
49
- const repoPath = await mkdtemp(join(tmpdir(), "tmct-demo-"));
50
- await mkdir(join(repoPath, ".tmct"), { recursive: true });
51
- await writeFile(join(repoPath, ".tmct", "graph.json"), JSON.stringify(graph));
52
-
53
- // tmct has one API surface for both teaching and asking: a chat turn, in
54
- // English. Each call below is a short session over the same repo, so what
55
- // gets taught in the first call is still remembered in the second.
56
- async function tell(line) {
57
- const out = new PassThrough();
58
- let transcript = "";
59
- out.on("data", (chunk) => { transcript += chunk; });
60
- await runChat({ repoPath, input: Readable.from([line + "\n", "/exit\n"]), output: out });
61
- return transcript.split("\n").find((l) => l.startsWith("tmct> "));
62
- }
63
-
64
- await tell("a controller is a kind of handler"); // Learn
65
- console.log(await tell("is TaskController a handler")); // Infer from learnings
34
+ const TEACH = [
35
+ "ahab is the father of john",
36
+ "john is the father of ishmael",
37
+ "a father is a kind of parent",
38
+ "remember that ahab is male",
39
+ "a grandparent is a parent of a parent",
40
+ "a grandfather is a grandparent who is male",
41
+ ];
42
+ const ASK = "is ahab the grandfather of ishmael";
43
+
44
+ // memoryBackend: "memory" keeps this whole session in the live handle only —
45
+ // no repo, no graph file, no disk write.
46
+ const repoPath = await mkdtemp(join(tmpdir(), "tmct-example-"));
47
+ const output = new PassThrough();
48
+ let transcript = "";
49
+ output.on("data", (chunk) => { transcript += chunk; });
50
+ const lines = [...TEACH, ASK].map((line) => line + "\n");
51
+ await runChat({ repoPath, memoryBackend: "memory", input: Readable.from([...lines, "/exit\n"]), output });
66
52
  ```
67
53
 
54
+ (`examples/teach-and-infer.mjs` adds the parsing that turns `transcript` into
55
+ the answers below, plus cleanup — see the file for the full script.)
56
+
68
57
  Output, captured from an actual run:
69
58
 
70
59
  ```
71
- tmct> yes the code graph says TaskController inherits Controller, and you
72
- told me: controller is a kind of handler (source: ace:chat:<session-id>@<timestamp>)
73
- ```
60
+ tmct> ahab is the father of john
61
+ noted — remembered: ahab fathers john
74
62
 
75
- Nothing here was told that "handler" and "Controller" relate. tmct combined a
76
- fact already in the graph (`TaskController inherits Controller`) with a fact
77
- you just taught it in English (`controller is a kind of handler`) and wrote
78
- the connecting sentence itself, citing both sources. The `source: ace:chat:…`
79
- part is a real provenance receipt. Every fact tmct stores records where it
80
- came from and when (more on that below).
63
+ Goal (inferred): Teach/remember a new fact.
81
64
 
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.
65
+ tmct> john is the father of ishmael
66
+ noted — remembered: john fathers ishmael
87
67
 
88
- ```
89
- $ tmct
90
- tmct> what talks to the payment module?
91
-
92
- tmct> /callers checkout
93
-
94
- tmct> /exit
95
- ```
68
+ Goal (inferred): Teach/remember a new fact.
96
69
 
97
- **[Try it live in your browser →](https://polycode-projects.gitlab.io/the-mechanical-code-talker/)**
98
- is a real, interactive chat demo running client-side. Your browser runs the
99
- actual query engine against a small example codebase, no server, no install.
70
+ tmct> a father is a kind of parent
71
+ noted remembered 1 fact: father rdfs:subClassOf parent (father is a type of parent)
100
72
 
101
- ## Compared to other JS libraries
73
+ Goal (inferred): Teach/remember a new fact.
102
74
 
103
- Nothing found in the JS ecosystem does this exact combination: teach in
104
- English, reason over a seeded ontology, answer in English, with no model call
105
- anywhere. Three kinds of library each cover one piece of it well.
75
+ Canonical: does "father" inherits "parent"? ask(inherits, subject="father", "parent")
106
76
 
107
- **[compromise](https://github.com/spencermountain/compromise)** (`npm i
108
- compromise`, 12k+ GitHub stars, describes itself as "modest natural language
109
- processing") is the general-purpose NLP toolkit of the three. Its own README
110
- example:
77
+ tmct> remember that ahab is male
78
+ noted remembered: ahab is male
111
79
 
112
- ```js
113
- import nlp from 'compromise'
114
- let doc = nlp('she sells seashells by the seashore.')
115
- doc.verbs().toPastTense()
116
- doc.text()
117
- // 'she sold seashells by the seashore.'
118
- ```
80
+ Goal (inferred): Teach/remember a new fact.
119
81
 
120
- It tags part-of-speech, conjugates verbs, and parses fractions and money far
121
- more broadly than tmct attempts to. It has no graph, no ontology, and no
122
- memory between calls. Each call is stateless text in, text out.
82
+ tmct> a grandparent is a parent of a parent
83
+ noted remembered: a grandparent is a parent of a parent
123
84
 
124
- **[N3.js](https://github.com/rdfjs/N3.js)** (`npm i n3`) is a mature,
125
- spec-compliant RDF/OWL toolkit: parsing, writing, and in-memory storage of
126
- triples. Its own README example:
85
+ Goal (inferred): Teach/remember a new fact.
127
86
 
128
- ```js
129
- const parser = new N3.Parser();
130
- parser.parse(tomAndJerry, (error, quad, prefixes) => {
131
- if (quad) console.log(quad);
132
- else console.log("That's all, folks!", prefixes);
133
- });
87
+ tmct> a grandfather is a grandparent who is male
88
+ noted remembered: a grandfather is a grandparent who is male
89
+
90
+ Goal (inferred): Teach/remember a new fact.
91
+
92
+ tmct> is ahab the grandfather of ishmael
93
+ yes — you told me: ahab fathers john (source: teach:chat:<session-id>@<timestamp>); father is a kind of parent (source: ace:chat:<session-id>@<timestamp>); you told me: john fathers ishmael (source: teach:chat:<session-id>@<timestamp>); you told me: ahab is male (source: teach:chat:<session-id>@<timestamp>)
134
94
  ```
135
95
 
136
- (`tomAndJerry` is Turtle text the caller writes by hand: `c:Tom a c:Cat.
137
- c:Jerry a c:Mouse; c:smarterThan c:Tom.`) N3.js is the right choice if you
138
- already have RDF and need to parse or serialize it fast. It has no
139
- natural-language front end (you write the triples yourself) and no built-in
140
- reasoning beyond an optional, limited basic-graph-pattern reasoner.
96
+ Nobody told tmct that ahab is ishmael's grandfather. It combined four facts
97
+ taught across six turns: the two father facts, the father-is-a-kind-of-parent
98
+ alias, and the ahab-is-male property, then cited all four. The `source: …`
99
+ parts are real provenance receipts. Every fact tmct stores records where it
100
+ came from and when (more on that below).
141
101
 
142
- **[elizabot](https://github.com/tkafka/node-elizabot)** (`npm i elizabot`) is
143
- the direct ELIZA lineage in JS, the same territory tmct's chat surface sits
144
- in. Its own README example:
102
+ `test/examples-teach-and-infer.test.mjs` pins this exact output. If the
103
+ underlying chat behavior ever drifts, that test fails and says so.
145
104
 
146
- ```js
147
- var eliza = new ElizaBot();
148
- var initial = eliza.getInitial();
149
- var reply = eliza.transform(inputstring);
105
+ ```
106
+ $ tmct
107
+ tmct> what talks to the payment module?
108
+
109
+ tmct> /callers checkout
110
+
111
+ tmct> /exit
150
112
  ```
151
113
 
152
- It is deterministic and needs no model, same as tmct. But it has no graph and
153
- no persistent memory: a fact from one line never carries into the next, and
154
- its "reasoning" is pattern substitution, not a stored, queryable fact.
155
-
156
- Put together: broad NLP without a graph (compromise), a graph without a
157
- natural-language front end (N3.js), or a conversational front end without a
158
- graph (elizabot). Combining ontology-seeded graph memory, English teaching,
159
- and rule-based inference into one no-model pipeline is what looks distinctive
160
- about tmct as of this writing.
114
+ **[Try it live in your browser →](https://polycode-projects.gitlab.io/the-mechanical-code-talker/)**
115
+ is a real, interactive chat demo running client-side. Your browser runs the
116
+ actual query engine against a small example codebase, no server, no install.
161
117
 
162
118
  ## How it interprets you
163
119
 
@@ -168,7 +124,7 @@ results are grouped by class.
168
124
  One of the strategies is an **ACE-inspired controlled grammar**: when your
169
125
  text fits the controlled fragment, tmct emits OWL-labelled triples from it.
170
126
  Those triples are statements it can store, retrieve, and answer from later.
171
- Text that doesn't fit the grammar still gets the tolerant strategies; nothing
127
+ Text that doesn't fit the grammar still gets the tolerant strategies. Nothing
172
128
  is rejected for being loose, fuzzy, or misspelled.
173
129
 
174
130
  **Everyday question shapes.** Bare "what is Commit" (no article) now resolves
@@ -182,9 +138,9 @@ you to remember X" teaches like bare "remember X"; "please tell me about X"
182
138
  and "search for X" describe and find exactly as their direct forms do.
183
139
 
184
140
  **Negation and passive.** "Which modules do *not* import X?" computes a bounded
185
- **set complement** over the graph, and an honestly empty result stays a miss
186
- rather than a fabricated list (a non-enumerable type, like arbitrary *changes*,
187
- is refused outright). Reversible-passive questions traverse the right direction:
141
+ **set complement** over the graph. An empty result stays a miss rather than a
142
+ fabricated list (a non-enumerable type, like arbitrary *changes*, is refused
143
+ outright). Reversible-passive questions traverse the right direction:
188
144
  "what is imported by Y" and "what does Y import" are understood as opposite
189
145
  edges, not the same one.
190
146
 
@@ -223,13 +179,13 @@ still import Y" when it no longer does.
223
179
  spans: prose versus *protected* entities, paths, numbers, code, provenance, and
224
180
  receipts. A small data-driven grammar pass then runs on the prose spans only,
225
181
  under a guard that proves the protected spans came through byte-for-byte. Today
226
- that pass fixes the a/an article defect; broader voice and agreement rules are
182
+ that pass fixes the a/an article defect. Broader voice and agreement rules are
227
183
  implemented but parked until they earn their place on the benchmark.
228
184
 
229
185
  A frozen regression suite plays out full multi-turn dialogues built from these
230
- phrasings, at every complexity level this project defines, from a single
231
- question up to a messy, typo-ridden real user. Tier-by-tier detail is in
232
- `HANDOVER.md` and `ROADMAP.md`.
186
+ phrasings, at every complexity level this project defines. That ranges from a
187
+ single question up to a messy, typo-ridden real user. Tier-by-tier detail is
188
+ in `HANDOVER.md` and `ROADMAP.md`.
233
189
 
234
190
  ## How it guides you
235
191
 
@@ -250,11 +206,11 @@ bands instead of dead-ending:
250
206
 
251
207
  It fires for both **noun** concepts (class, module, function, method) and
252
208
  **relation** concepts (imports, calls, contains, inherits, tests), and only
253
- when tmct genuinely knows the concept *and* has instances of it. Otherwise the
254
- honest miss stands. The effect is a conversation that drills down from a vague
255
- opener to a useful answer without ever hitting a wall. Natural phrasings are
256
- routed to the capability you meant: *"what functions are in Task"* → its
257
- members, *"what defined saveStore"* → where it's defined.
209
+ when tmct knows the concept and has instances of it. Otherwise the honest miss
210
+ stands. This lets you start from a vague opener and still reach a useful
211
+ answer. Natural phrasings are routed to the capability you meant: *"what
212
+ functions are in Task"* its members, *"what defined saveStore"* → where
213
+ it's defined.
258
214
 
259
215
  ## Detailed, grounded answers
260
216
 
@@ -262,7 +218,7 @@ Ask a precise question and tmct gives you a precise answer. Ask for more and
262
218
  it gives you more: "give me a detailed summary of how X works" (or "explain
263
219
  in detail how X works", or "...detailed overview/explanation of X") gets a
264
220
  longer, multi-sentence account instead of one line. Every sentence in it is
265
- lifted from a real graph edge, attribute, or taught fact never generated
221
+ lifted from a real graph edge, attribute, or taught fact. tmct never generates
266
222
  free text.
267
223
 
268
224
  The wording varies a little too. A small, curated, deterministic pool swaps a
@@ -305,6 +261,73 @@ doc = In-memory record store. [seon:hasDoc]. Other matches: src/core/store.mjs
305
261
 
306
262
  Full pipeline design in `archive/PLAN_COMPLETIONS.md`.
307
263
 
264
+ ## Planning across the graph
265
+
266
+ Some questions need more than one lookup. `tmct plan` is a small STRIPS/PDDL-style
267
+ planner over the same read-only graph-query tools chat/serve use
268
+ (`src/router/*`): it decomposes a compound request, resolves and executes each
269
+ step in order with a provable causal-link proof chain, and folds the results
270
+ into one answer. A request neither the planner nor a single lookup can ground
271
+ escalates to a closed-world goal-reasoner, which deduces maintenance goals
272
+ (coverage gaps, change-coupling risk) straight from the graph — never from
273
+ keywords in your question. Anything none of that grounds is an honest "no plan
274
+ found", the same "grounded or an honest miss" rule as everywhere else in tmct.
275
+
276
+ ```
277
+ $ node bin/tmct.mjs plan "of the modules impacted by src/lib/http.mjs, which are untested" --repo examples/mini-webapp
278
+ tmct plan: "of the modules impacted by src/lib/http.mjs, which are untested"
279
+ driver: resolver-0.8.0
280
+
281
+ steps:
282
+ 1. tmct_impact {"module":"src/lib/http.mjs"}
283
+ Impact of changing src/lib/http.mjs (reverse closure over imports/calls edges, module- and function-level):
284
+ total: 5 dependent(s) across 2 depth level(s) (lists capped for brevity).
285
+ depth 1 (3 direct dependents):
286
+ - src/handlers/base.mjs (imports it) — tests: none recorded
287
+ - src/handlers/tasks.mjs (imports it) — tests: test/tasks.test.mjs
288
+ - src/server/router.mjs (imports it) — tests: none recorded
289
+ depth 2 (2):
290
+ - src/handlers/users.mjs (imports it) — tests: none recorded
291
+ - src/server/app.mjs (imports it) — tests: none recorded
292
+ 2. tmct_untested {}
293
+ 7 source module(s) with no covering test module:
294
+ src/core/validate.mjs
295
+ src/handlers/base.mjs
296
+ src/handlers/users.mjs
297
+ src/lib/http.mjs
298
+ src/lib/logger.mjs
299
+ src/server/app.mjs
300
+ src/server/router.mjs
301
+
302
+ composed answer (4): src/handlers/base.mjs, src/handlers/users.mjs, src/server/app.mjs, src/server/router.mjs
303
+ ```
304
+
305
+ tmct planned two calls (`tmct_impact` then `tmct_untested`), ran both against the
306
+ real graph, and intersected the results itself — you get the four modules that
307
+ are both downstream of the change AND missing coverage, not two separate lists
308
+ you'd have to cross-reference by hand.
309
+
310
+ Leave the entity out and ask a maintenance question instead, and the goal-reasoner
311
+ picks up where the planner refuses:
312
+
313
+ ```
314
+ $ node bin/tmct.mjs plan "what most needs a test in this codebase" --repo examples/mini-webapp
315
+ tmct plan: "what most needs a test in this codebase"
316
+ driver: goal-0.8.1
317
+ ...
318
+ composed answer (1): src/lib/http.mjs
319
+ ```
320
+
321
+ It deduced the goal ("an impactful module must be tested"), gathered every
322
+ untested module, ranked each by blast radius, and named the one worth testing
323
+ first — `src/lib/http.mjs`, the module with the widest reach.
324
+
325
+ `--tools tmct_impact,tmct_untested` restricts which capabilities the planner is
326
+ allowed to use; `--json` prints the full machine-readable loop result (calls,
327
+ proof chain, composed answer) for a caller that wants to consume this
328
+ programmatically rather than read the report. `tmct plan --help` has the full
329
+ flag reference.
330
+
308
331
  ## How it remembers
309
332
 
310
333
  tmct's memory has two layers, both fed by every parsed request and response and
@@ -318,10 +341,10 @@ by cleaned session logs:
318
341
  With no graph at all, tmct starts empty and remembers what you tell it. The
319
342
  `.tmct/` graph is created from the conversation. On a first run it seeds the
320
343
  committed vocabulary so it knows what it's talking about from turn one: an
321
- everyday **human-world** persona people, places, objects, nature, time/
322
- events, body/food and mind vocabulary hand-curated from Open English WordNet
323
- and bridged to Schema.org's top-level classes so "what is a dog?" answers
324
- offline, from disk, on turn one. A code-domain persona (a curated **SEON**
344
+ everyday **human-world** persona covering people, places, objects, nature,
345
+ time and events, body and food, and mind vocabulary. It's hand-curated from
346
+ Open English WordNet and bridged to Schema.org's top-level classes, so "what
347
+ is a dog?" answers offline, from disk, on turn one. A code-domain persona (a curated **SEON**
325
348
  software ontology plus the whole filtered **ConceptNet slice**, CC-BY-SA 4.0)
326
349
  is available opt-in: `tmct init --with-persona code`. `--ephemeral` (used by
327
350
  the shipped `npm run example:*` demos) reads a graph but writes nothing back.
@@ -338,7 +361,7 @@ Two more exist: `memory` keeps taught facts in the process only, nothing
338
361
  written to disk; `sqlite` persists them to a local SQLite file instead
339
362
  (`.tmct/memory/graph.sqlite`).
340
363
 
341
- Pick one at init time and it sticks `tmct init --memory-backend sqlite`
364
+ Pick one at init time and it sticks. `tmct init --memory-backend sqlite`
342
365
  writes the choice into `tmct.toml`, and every later `tmct chat` in that repo
343
366
  uses it with no flag needed:
344
367
 
@@ -360,10 +383,10 @@ New vocabulary compounds as you teach it. "redis is a cache" mints "redis" as
360
383
  a class-level concept even though it was never in the built-in lexicon, and a
361
384
  later "every cache is a store" does the same for "store," the other way
362
385
  round, as long as one side of the sentence is already grounded. tmct never
363
- mints a fact between two totally ungrounded terms; it declines and nudges you
364
- to ground one side first. Quantified teaching works too: "some functions are
365
- risky" stores the quantifier, and a later "how many functions are risky"
366
- answers "A few."
386
+ mints a fact between two totally ungrounded terms. It declines instead, and
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."
367
390
 
368
391
  Once you've taught a few facts, "how many facts are there" counts them back.
369
392
  That's the same count phrasing a code graph answers "how many classes are
@@ -376,9 +399,9 @@ first-class individuals: operator chat, a curated corpus, a provider graph, a
376
399
  web scrape, a rule-derived entailment. A fact links back to *all* of them
377
400
  (`mgx:derivedFrom` / `mgx:statedBy` / `mgx:canonicalisedFrom`), timestamped with
378
401
  `mgx:createdAt`. From those links tmct computes a **deterministic, explainable
379
- trust score**: a source-type prior combined with corroboration (how many
380
- independent sources agree) and recency. It is never hand-set, always traceable
381
- to its inputs. Retrieval then ranks by **relevance × trust**, so a corroborated,
402
+ trust score**, combining a source-type prior with corroboration (how many
403
+ independent sources agree) and recency. It is never hand-set. Every value
404
+ traces back to its inputs. Retrieval then ranks by **relevance × trust**, so a corroborated,
382
405
  operator-stated fact outranks a lone web scrape on the same question. When two
383
406
  trusted sources *disagree*, the `/memory` inspector shows **both sides with their
384
407
  provenance** rather than silently picking a winner.
@@ -408,7 +431,7 @@ Inside the chat: `/help` lists commands, `/memory` inspects what tmct remembers
408
431
  `TMCT_GRAPH_FILE` overrides the graph location.
409
432
 
410
433
  `tmct --help` (or `npm run help` from a clone of this repo) is the full,
411
- up-to-date flag reference for every subcommand a bare `npm run` only lists
434
+ up-to-date flag reference for every subcommand. A bare `npm run` only lists
412
435
  script names, so `npm run help` is the documented way in from there.
413
436
 
414
437
  `tmct init` is the onboarding surface for the repository interface below: it
@@ -419,57 +442,262 @@ package or a bare user gets a working install in one command.
419
442
  > Install-size note: tmct depends on wink-nlp's deterministic English language
420
443
  > model (~3.8 MB installed). That model is a lookup table, not an LLM.
421
444
 
422
- ### Flags, config, and multiple graphs
445
+ ### Full command reference (`tmct --help`)
423
446
 
424
- Every subcommand shares one flag/config resolver (`src/cli-args.mjs`). The
425
- graph-path precedence is the same everywhere: `--graph` flag(s) beat
426
- `TMCT_GRAPH_FILE`, which beats `tmct.toml`'s `graph_file`/`graph_files`, which
427
- beats the `--repo`-derived `<repo>/.tmct/graph.json` default.
447
+ `tmct --help` always prints the real, current flags. What follows is that
448
+ same output, split into one block per command with a short note on what each
449
+ one is for, so it is easier to scan than the raw dump.
428
450
 
429
- ```bash
430
- tmct init --repo /abs/path # scaffold a specific repo, not just cwd
431
- tmct init --corpus <id|path> # a tier-2 manifest id (aws|python|java|general)
432
- # or your own corpus jsonl file
433
- tmct init --ontology <name|path> # activate+seed an ontology bundle
434
- tmct init --lexicon <name|path> # activate a lexicon bundle (never seeded)
435
- tmct init --graph <path> [--graph <path> …] # set tmct.toml's graph_file/graph_files
436
- tmct init --config <path> # write to an alternate tmct.toml location
437
- tmct init --persona-size medium|large # grow the default persona (Small is default)
438
- tmct init --memory-backend default|memory|sqlite # write tmct.toml's [memory] backend
439
- # every later `tmct chat` here uses it
440
-
441
- tmct import --corpus <id|path> # activate+seed into an ALREADY-initialized
442
- tmct import --ontology <name|path> # repoany combination of these flags in
443
- tmct import --lexicon <name|path> # one call. --graph is a DIFFERENT, purely
444
- tmct import --graph <path> # additive op: it appends to graph_files,
445
- # never activates an extensions bundle.
446
- tmct import --memory-backend default|memory|sqlite # same knob as `tmct init`, for a
447
- # repo that's already initialized
448
-
449
- tmct chat --graph <path> [--graph <path> …] # explicit graph file(s) — multiple merge
450
- # (ids that collide across graphs are
451
- # auto-prefixed; see src/graph-merge.mjs)
452
- tmct chat --config <path> # an alternate tmct.toml (a file or a dir)
453
- tmct chat --memory-backend default|memory|sqlite # override tmct.toml's backend for
454
- # just this session
455
- tmct serve --graph <path> --config <path> # same two flags, for the HTTP endpoint
456
- ```
457
-
458
- `--corpus`/`--ontology`/`--lexicon` each take one value; chain multiple `tmct
459
- import` calls to combine several. `npm run init:large` in `package.json`
460
- chains one `init` and five `import --corpus` calls to combine every shipped
461
- bundle (human persona + seon + conceptnet + aws/python/java) into ~7,380
462
- facts on the default flat-JSON backend a working example to copy from.
463
-
464
- `tmct extend --validate <dir> --config <path>` validates a third-party
465
- extension pack against an alternate tmct.toml, without mutating anything.
451
+ Every subcommand shares one flag/config resolver (`src/cli-args.mjs`), which
452
+ is why `--repo`, `--graph`, and `--config` behave the same way everywhere.
453
+
454
+ The bare command and `tmct chat` open the interactive session:
455
+
456
+ ```
457
+ Usage:
458
+ tmct interactive chat (the headline surface)
459
+ tmct chat [--repo <abs>] chat over a specific repo's graph
460
+ [--graph <path>] explicit graph file (repeatable multiple graphs merge;
461
+ see src/graph-merge.mjs); wins over --repo/TMCT_GRAPH_FILE/tmct.toml
462
+ [--config <path>] an alternate tmct.toml location (a file or a directory)
463
+ [--ephemeral] read the graph but write nothing back (demo/read-only)
464
+ [--narrate] start with narrate mode ona verbose, developer-facing
465
+ trace of decision points/matched pattern/results/goal per
466
+ turn, appended under a "--- narrate ---" marker (also
467
+ TMCT_NARRATE=1; toggle mid-session with /narrate on|off)
468
+ [--plain] force the plain readline shell (the default when
469
+ stdin/stdout is not a terminal)
470
+ [--memory-backend <default|memory|sqlite>] storage backend for taught facts this
471
+ session (CLI flag > TMCT_MEMORY_BACKEND env > tmct.toml's
472
+ [memory] backend > "default", the flat .tmct/ JSON file)
473
+ ```
474
+
475
+ `tmct memory` is the CLI-side view of the same data the `/memory` chat command shows:
476
+
477
+ ```
478
+ tmct memory [--repo <abs>] what tmct remembers: facts, utterances, sessions,
479
+ [--config <path>] folded blocks (the /memory chat command, from the shell)
480
+ [--verbose]
481
+ ```
482
+
483
+ `tmct init` sets up a repo for the first time: `.tmct/`, `tmct.toml`, a seed, and a
484
+ provenance record. Most of its flags choose what gets seeded and where config is written:
485
+
486
+ ```
487
+ tmct init [--repo <abs>] initialize a repo for tmct (default: cwd): .tmct/,
488
+ [--force] tmct.toml, tier-1 corpus seed, provenance record
489
+ [--corpus <id|path>] also seed a corpus — a tier-2 manifest id (aws|python|java|
490
+ general) or a jsonl file path — opt-in, offline, $0
491
+ [--ontology <name|path>] activate+seed an ontology bundle (a recognized name or a path)
492
+ [--lexicon <name|path>] activate a lexicon bundle (recognized name or a path;
493
+ merged read-time, never seeded — see mergedLexiconExtra)
494
+ [--graph <path>] set graph_file/graph_files in tmct.toml (repeatable)
495
+ [--config <path>] write to an alternate tmct.toml location
496
+ [--detect] suggest a tier-2 corpus from the repo's manifests
497
+ (pyproject.toml → python, pom.xml → java); never seeds unasked
498
+ [--with-persona <name>] write an explicit [extensions]/[bias] preset into tmct.toml
499
+ ("code" — today's implicit default, made explicit)
500
+ [--persona-size <medium|large>] grow the default "human" persona's fact count
501
+ beyond Small (the default): "medium" activates
502
+ human-medium.jsonl (~1,608 facts total), "large" also
503
+ activates human-large.jsonl (~13,600 facts total,
504
+ with genuine multi-hop hypernym chains) — additive
505
+ size tiers of the SAME bundle, not separate personas
506
+ [--memory-backend <default|memory|sqlite>] write tmct.toml's [memory] backend
507
+ (same flag name as `tmct chat`) — a later `tmct chat`
508
+ in this repo picks it up with no flag needed
509
+ ```
510
+
511
+ `tmct import` does the same activation as `tmct init`, but against a repo that is
512
+ already set up. Its `--graph` flag works differently from the others: it appends to
513
+ `tmct.toml`'s `graph_files` array instead of activating a bundle.
514
+
515
+ ```
516
+ tmct import [--repo <abs>] activate+seed into an ALREADY-initialized repo (any
517
+ [--corpus <id|path>] combination of these flags in one call). --graph is a
518
+ [--ontology <name|path>] DIFFERENT operation from the others: it APPENDS to
519
+ [--lexicon <name|path>] tmct.toml's graph_files array (multi-graph growth),
520
+ [--graph <path>] never an extensions-bundle activation.
521
+ [--memory-backend <default|memory|sqlite>] same knob as `tmct init`
522
+ [--config <path>]
523
+ ```
524
+
525
+ `tmct extend --validate` checks a third-party extension pack's declared resources
526
+ before you switch any repo's `tmct.toml` over to it:
527
+
528
+ ```
529
+ tmct extend --validate <dir> validate a third-party extension pack's declared
530
+ [--config <path>] resources (corpus/lexicon/templates) before activating
531
+ it in any repo's tmct.toml; exits non-zero on failure
532
+ ```
533
+
534
+ `tmct syllogise` is the offline maintenance job described under "Speculative
535
+ inference" above:
536
+
537
+ ```
538
+ tmct syllogise [--repo <abs>] speculative inference (offline maintenance job): forward-
539
+ [--depth <n>] [--budget <n>] chain the memory's rdfs:subClassOf closure, materialising
540
+ [--config <path>] bounded, low-trust, retractable entailed facts (never on the chat path)
541
+ ```
542
+
543
+ `tmct viz` renders the memory graph to a single HTML file you can open in a browser:
544
+
545
+ ```
546
+ tmct viz [--repo <abs>] write one self-contained, navigable HTML file rendering the
547
+ [--focus <id>] memory graph: pan/zoom, click a node for its label/class/
548
+ [--term <word>] timestamps. Seeds from the most recently created individual
549
+ [--depth <n>] by default (--focus <id> or --term <word> override it);
550
+ [--limit <n>] --output defaults to graph.html in the cwd.
551
+ [--hub-degree <n>] --depth = max arcs (hops) from the focus node (default 3);
552
+ [--edge-kind <mode>] --limit = spiral length, total nodes walked (default 300);
553
+ [--output <path>] --hub-degree = stop expanding THROUGH a node above N
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.
560
+ ```
561
+
562
+ `tmct serve` runs an Anthropic Messages API-compatible HTTP endpoint over the graph,
563
+ so a tool-loop client can call tmct like a model, at $0:
564
+
565
+ ```
566
+ tmct serve [--repo <abs>] run the Anthropic Messages API-compatible endpoint
567
+ [--host <h>] [--port <n>] (POST /v1/messages) over the graph — a deterministic,
568
+ [--graph <path>] no-LLM "model" a tool-loop client can call; $0 usage.
569
+ [--config <path>] Defaults: host 127.0.0.1, port 8787. Ctrl+C to stop.
570
+ ```
571
+
572
+ `tmct cli` is a lower-level, carry-over surface for invoking a graph tool directly:
573
+
574
+ ```
575
+ tmct cli <tool> '{…}' invoke a graph tool directly (carry-over, de-emphasized)
576
+ tmct cli digest '{…}' architecture map + per-module context bundles
577
+ ```
578
+
579
+ Two precedence chains apply across every command above, in this order:
580
+
581
+ ```
582
+ Shared graph-path precedence (chat/serve; see src/cli-args.mjs): --graph flag(s) >
583
+ TMCT_GRAPH_FILE env > tmct.toml graph_file/graph_files > --repo-derived
584
+ <repo>/.tmct/graph.json > git-root/cwd default.
585
+
586
+ Memory-backend precedence (chat; see src/chat.mjs createSession): --memory-backend
587
+ flag > TMCT_MEMORY_BACKEND env > tmct.toml [memory] backend > "default" (the flat
588
+ .tmct/ JSON file). Set it once with `tmct init --memory-backend <...>` and every
589
+ later `tmct chat` in that repo picks it up with no flag needed.
590
+ ```
591
+
592
+ `npm run init:large` in `package.json` chains one `init` and five `import --corpus`
593
+ calls to combine every shipped bundle (human persona + seon + conceptnet +
594
+ aws/python/java) into ~7,380 facts on the default flat-JSON backend, a working
595
+ example to copy from.
596
+
597
+ ### tmct.toml reference
598
+
599
+ `tmct init` writes a sparse `tmct.toml` with just the keys it needs. The file
600
+ recognizes more keys than that default covers. Below is one config with every
601
+ recognized key set, so you can see the full surface in one place
602
+ (`src/toml-config.mjs` is the source of truth; `src/extensions.mjs` defines the
603
+ `[extensions.*]`/`[bias]` shape).
604
+
605
+ ```toml
606
+ # Newline-delimited-file form is also accepted: repositories = "repos.txt"
607
+ repositories = ["../other-service", "../another-service"]
608
+
609
+ # Where generated output (e.g. tmct viz's default graph.html) resolves to.
610
+ out_root = "./out"
611
+
612
+ # The code-graph JSON artifact. TMCT_GRAPH_FILE overrides this at runtime.
613
+ graph_file = ".tmct/graph.json"
614
+ # Extra graphs, merged alongside graph_file (ids that collide are auto-prefixed).
615
+ graph_files = [".tmct/graph.json", ".tmct/legacy-graph.json"]
616
+
617
+ [corpus]
618
+ # "tier1" (committed slice only, $0/offline, the default), "tier2" (also fetch
619
+ # growable corpora at seed time), or "tier3" (also consult live sources per query).
620
+ tier = "tier1"
621
+
622
+ [seed]
623
+ enabled = true # seed the committed corpus into .tmct/memory during init
624
+ limit = 500 # cap the seeded fact count (definitional band first); unset = no cap
625
+
626
+ # One [extensions.<name>] table per bundle. A recognized name (human, seon,
627
+ # conceptnet, human-medium, human-large, tier2-aws, tier2-python, tier2-java,
628
+ # tier2-general, wordnet-xl, wordnet-full, namenet) overrides that bundle's
629
+ # shipped defaults. Any other name declares a new bundle and must set `kind`.
630
+ [extensions.human]
631
+ active = true
632
+
633
+ [extensions.seon]
634
+ active = true
635
+
636
+ [extensions.conceptnet]
637
+ active = true
638
+
639
+ [extensions.tier2-aws]
640
+ active = true
641
+
642
+ [extensions.my-custom-pack]
643
+ kind = "pack" # corpus | lexicon | templates | pack | ontology
644
+ active = true
645
+ corpus_path = "./vendor/my-pack/corpus.jsonl"
646
+ lexicon_path = "./vendor/my-pack/lexicon.json"
647
+ templates_path = "./vendor/my-pack/templates"
648
+ phrasebook_path = "./vendor/my-pack/phrasebook.json"
649
+ provenance_prefix = "corpus:my-custom-pack"
650
+
651
+ # Flat bundle-name -> weight table, consumed by src/memory/bias.mjs's ranking.
652
+ [bias]
653
+ human = 1.0
654
+ seon = 0.8
655
+ conceptnet = 0.6
656
+ my-custom-pack = 1.2
657
+
658
+ [index]
659
+ languages = ["js", "py"] # restrict indexing to these languages
660
+ exclude = ["**/node_modules/**", "**/dist/**"]
661
+ secret_exclude = ["**/*.env", "**/secrets/**"] # never indexed, even under a broad include
662
+ history_depth = 200 # commits of git history to consider
663
+ # The five keys below parse and normalize but have no consumer wired up yet:
664
+ include_text = true
665
+ include_structure = true
666
+ respect_gitignore = true
667
+ markdown_sections = true
668
+ vue = true
669
+
670
+ [tune]
671
+ score_gap_k = 0.25 # retrieval score-gap threshold
672
+ literal_mention = true # boost literal-name mentions
673
+ demote_non_prod = true # rank test/fixture code below production code
674
+ call_adjacency = true # boost callers/callees of a matched symbol
675
+ impl_of_interface = true # boost an interface's implementations
676
+ beam_search = true # use beam search over the graph walk
677
+ beam_width = 8
678
+ embed_rank = false # rerank by embedding similarity (off by default)
679
+ prose_layers = 2 # how many prose-generation passes to run
680
+
681
+ [tune.expansion]
682
+ strategy = "beam" # graph-walk expansion strategy
683
+ nodes = 50 # node budget for the walk
684
+ q = 0.5 # expansion breadth parameter
685
+ depth = 3 # max hops
686
+
687
+ [telemetry]
688
+ enabled = false # local-only counters; never phones home
689
+
690
+ [memory]
691
+ retention_versions = 5 # snapshot generations memory/core.mjs keeps on manifest bootstrap
692
+ backend = "sqlite" # default | memory | sqlite (see "Memory backends" above)
693
+ ```
466
694
 
467
695
  ### Try it on an example graph
468
696
 
469
- tmct *consumes* a code graph at `<repo>/.tmct/graph.json`; it does not build
470
- one. Two ready-made example graphs live in `examples/` in this repo (not in the
471
- published npm package clone the repo to use them) so you can see it answer
472
- real questions with no setup:
697
+ tmct *consumes* a code graph at `<repo>/.tmct/graph.json`. It does not build
698
+ one. Two ready-made example graphs live in `examples/` in this repo (clone the
699
+ repo to use them; they are not in the published npm package), so you can see
700
+ it answer real questions with no setup:
473
701
 
474
702
  ```bash
475
703
  npm run example:mini # "Questboard" — a small task-tracker web app (12 modules)
@@ -507,28 +735,38 @@ import { runChat, ask, resolveObject, fetchEntities } from "@polycode-projects/t
507
735
  ```
508
736
 
509
737
  `runChat` is the full teach-and-ask surface (see "Teach it, then ask it to
510
- reason" above it works over injectable streams, so a script can drive a
511
- session the same way the tests do). `ask`/`resolveObject` are the lower-level,
738
+ reason" above). It works over injectable streams, so a script can drive a
739
+ session the same way the tests do. `ask`/`resolveObject` are the lower-level,
512
740
  read-only query primitives over an already-loaded graph, for a caller that
513
741
  wants to query without a chat session. The `exports` map and the chat
514
742
  primitives (`ask`, `resolveObject`, `relationKind`, `impactClosure`,
515
743
  `dispatchTool`, `fetchEntities`) are the extension surface.
516
744
 
745
+ ```js
746
+ import { buildCapabilityPlanCtx, runCapabilityPlan, declaredCapabilityNames } from "@polycode-projects/the-mechanical-code-talker/plan";
747
+ ```
748
+
749
+ The same planner `tmct plan` runs, callable directly: `buildCapabilityPlanCtx`
750
+ loads a repo's graph into a `{ dispatch, resolve, graph }` context,
751
+ `runCapabilityPlan(request, tools, ctx)` runs a request through it, and
752
+ `declaredCapabilityNames()` lists every capability a caller can declare. See
753
+ "Planning across the graph" above.
754
+
517
755
  ## The repository interface
518
756
 
519
757
  tmct is not an indexer, so it consumes a graph through a typed contract any
520
758
  producer can implement. That contract is first-class: a **versioned (1.0.0),
521
759
  OWL-grounded, machine-readable service definition** (`docs/repository-interface.md`
522
760
  plus a JSON schema) of every service, its arguments, result types, and error
523
- contract. A **miss is a value, not a throw**: the interface models "no answer"
524
- explicitly. tmct ships **reference providers** (a fixture graph and the
761
+ contract. The interface returns a miss as a normal value. It never throws to
762
+ signal "no answer." tmct ships **reference providers** (a fixture graph and the
525
763
  empty/bootstrap graph) that implement every service, and a **runnable conformance
526
764
  suite**. tmct's own providers pass it in `npm test`. Any external graph producer
527
765
  (seonix first) runs the same suite against its native implementation to claim
528
- conformance. Conformance is the suite, not prose. This inverts the original
529
- relationship: tmct was lifted out of seonix, and seonix now reorients as a *user*
530
- that imports the tmct library and exposes its graph to tmct as a service. The LLM
531
- agent stays outside tmct, as the no-LLM ethos requires.
766
+ conformance. Passing the suite is what conformance means here. This inverts the
767
+ original relationship: tmct was lifted out of seonix, and seonix now reorients
768
+ as a *user* that imports the tmct library and exposes its graph to tmct as a
769
+ service. The LLM agent stays outside tmct, as the no-LLM ethos requires.
532
770
 
533
771
  ## Security and supply chain
534
772
 
@@ -546,16 +784,17 @@ cross-version-stable fact-id contract has exactly one definition.
546
784
  ## Provenance
547
785
 
548
786
  tmct began as a whole-package lift of the seonix chat surface (v0.1.0, then
549
- published as `@polycode-projects/mct`), and was then reshaped: the LLM
550
- fallback, the code-extraction stack, and the MCP server were all removed; the
787
+ published as `@polycode-projects/mct`), and was then reshaped. The LLM
788
+ fallback, the code-extraction stack, and the MCP server were all removed. The
551
789
  naming, license, and memory model were reset to the vision above. See
552
790
  `ROADMAP.md` for the phase plan.
553
791
 
554
792
  ## Licensing
555
793
 
556
- **MPL-2.0.** Free for commercial use; if you modify the covered files and
794
+ **MPL-2.0.** Free for commercial use. If you modify the covered files and
557
795
  distribute them, you must publish those files' source under the MPL with
558
- attribution. The copyleft is file-level, not project-level. See `LICENSE`.
796
+ attribution. The copyleft applies file by file, not to the whole project. See
797
+ `LICENSE`.
559
798
 
560
799
  Corpus data carries its own licenses, separate from the code: the shipped
561
800
  ConceptNet slice is **CC-BY-SA 4.0**, with its own notice alongside it.