@prestack/thread 0.0.3 → 0.1.1

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 (183) hide show
  1. package/CHANGELOG.md +310 -0
  2. package/dist/core/{asking.d.ts → answers.d.ts} +6 -14
  3. package/dist/core/{asking.js → answers.js} +13 -332
  4. package/dist/core/{asking.ts → answers.ts} +14 -357
  5. package/dist/core/attach.js +2 -2
  6. package/dist/core/attach.ts +2 -3
  7. package/dist/core/chain.js +4 -11
  8. package/dist/core/chain.ts +4 -3
  9. package/dist/core/client.d.ts +57 -0
  10. package/dist/core/client.js +243 -0
  11. package/dist/core/client.ts +279 -0
  12. package/dist/core/descend.d.ts +2 -0
  13. package/dist/core/descend.js +109 -16
  14. package/dist/core/descend.ts +120 -16
  15. package/dist/core/files.d.ts +45 -0
  16. package/dist/core/files.js +145 -0
  17. package/dist/core/files.ts +193 -0
  18. package/dist/core/init.d.ts +1 -0
  19. package/dist/core/init.js +10 -2
  20. package/dist/core/init.ts +8 -2
  21. package/dist/core/invoked.js +1 -1
  22. package/dist/core/invoked.ts +1 -1
  23. package/dist/core/rows.d.ts +2 -0
  24. package/dist/core/rows.js +65 -0
  25. package/dist/core/rows.ts +58 -0
  26. package/dist/core/tree.js +1 -1
  27. package/dist/core/tree.ts +1 -1
  28. package/dist/derivation/attach.js +0 -12
  29. package/dist/derivation/attach.ts +0 -12
  30. package/dist/derivation/generators/build.js +8 -7
  31. package/dist/derivation/generators/build.ts +8 -7
  32. package/dist/derivation/generators/release.d.ts +1 -1
  33. package/dist/derivation/generators/release.js +15 -23
  34. package/dist/derivation/generators/release.ts +15 -15
  35. package/dist/integrations/attach.js +1 -19
  36. package/dist/integrations/attach.ts +1 -19
  37. package/dist/integrations/generators/read.js +2 -2
  38. package/dist/integrations/generators/read.ts +4 -4
  39. package/dist/integrations/generators/vault.js +4 -12
  40. package/dist/integrations/generators/vault.ts +4 -4
  41. package/dist/integrations/module.js +5 -3
  42. package/dist/integrations/module.ts +5 -3
  43. package/dist/integrations/vault.js +5 -5
  44. package/dist/integrations/vault.ts +5 -5
  45. package/dist/integrity/attach.js +0 -12
  46. package/dist/integrity/attach.ts +0 -12
  47. package/dist/integrity/check-threads.js +1 -1
  48. package/dist/integrity/check-threads.ts +1 -1
  49. package/dist/integrity/constraint.d.ts +11 -2
  50. package/dist/integrity/constraint.js +163 -20
  51. package/dist/integrity/constraint.ts +174 -22
  52. package/dist/integrity/generators/compiler.js +7 -1
  53. package/dist/integrity/generators/compiler.ts +10 -1
  54. package/dist/integrity/generators/gate.js +23 -18
  55. package/dist/integrity/generators/gate.ts +29 -9
  56. package/dist/integrity/generators/publish.js +14 -26
  57. package/dist/integrity/generators/publish.ts +14 -17
  58. package/dist/integrity/generators/trial.d.ts +2 -2
  59. package/dist/integrity/generators/trial.js +108 -50
  60. package/dist/integrity/generators/trial.ts +107 -48
  61. package/dist/integrity/lint.js +3 -3
  62. package/dist/integrity/lint.ts +3 -3
  63. package/dist/integrity/lsp/diagnostics.js +1 -1
  64. package/dist/integrity/lsp/diagnostics.ts +1 -1
  65. package/dist/integrity/lsp/lint.js +1 -1
  66. package/dist/integrity/lsp/lint.ts +1 -1
  67. package/dist/integrity/slopnator.d.ts +1 -14
  68. package/dist/integrity/slopnator.js +8 -40
  69. package/dist/integrity/slopnator.ts +9 -51
  70. package/dist/integrity/slopnators/sl_broken_lineage.js +4 -2
  71. package/dist/integrity/slopnators/sl_broken_lineage.ts +4 -2
  72. package/dist/integrity/slopnators/sl_declared_elsewhere.js +24 -3
  73. package/dist/integrity/slopnators/sl_declared_elsewhere.ts +24 -3
  74. package/dist/integrity/slopnators/sl_derived_drift.js +10 -4
  75. package/dist/integrity/slopnators/sl_derived_drift.ts +10 -4
  76. package/dist/integrity/slopnators/sl_inert_island.js +7 -13
  77. package/dist/integrity/slopnators/sl_inert_island.ts +7 -16
  78. package/dist/integrity/slopnators/sl_inert_slopnator.js +8 -8
  79. package/dist/integrity/slopnators/sl_inert_slopnator.ts +8 -8
  80. package/dist/integrity/slopnators/sl_missing_row.js +14 -9
  81. package/dist/integrity/slopnators/sl_missing_row.ts +14 -10
  82. package/dist/integrity/slopnators/sl_no_child_process.js +8 -8
  83. package/dist/integrity/slopnators/sl_no_child_process.ts +8 -8
  84. package/dist/integrity/slopnators/sl_no_import_cycle.js +1 -1
  85. package/dist/integrity/slopnators/sl_no_import_cycle.ts +1 -1
  86. package/dist/integrity/slopnators/sl_no_such_command.js +20 -5
  87. package/dist/integrity/slopnators/sl_no_such_command.ts +22 -5
  88. package/dist/integrity/slopnators/sl_only_takes_a_literal.js +8 -8
  89. package/dist/integrity/slopnators/sl_only_takes_a_literal.ts +8 -8
  90. package/dist/integrity/slopnators/sl_stale_path.js +91 -18
  91. package/dist/integrity/slopnators/sl_stale_path.ts +97 -18
  92. package/dist/integrity/slopnators/sl_twice_meant.js +25 -2
  93. package/dist/integrity/slopnators/sl_twice_meant.ts +28 -2
  94. package/dist/integrity/slopnators/sl_twice_parented.js +26 -5
  95. package/dist/integrity/slopnators/sl_twice_parented.ts +26 -5
  96. package/dist/integrity/slopnators/sl_undeclared_node.js +18 -2
  97. package/dist/integrity/slopnators/sl_undeclared_node.ts +18 -2
  98. package/dist/integrity/slopnators/sl_undeclared_operation.js +3 -1
  99. package/dist/integrity/slopnators/sl_undeclared_operation.ts +3 -1
  100. package/dist/integrity/slopnators/sl_undeclared_variant.js +6 -2
  101. package/dist/integrity/slopnators/sl_undeclared_variant.ts +6 -2
  102. package/dist/integrity/slopnators/sl_ungoverned_island.js +3 -4
  103. package/dist/integrity/slopnators/sl_ungoverned_island.ts +3 -4
  104. package/dist/integrity/slopnators/sl_unkept_rows.js +1 -1
  105. package/dist/integrity/slopnators/sl_unkept_rows.ts +1 -1
  106. package/dist/integrity/slopnators/sl_unpaired_member.js +13 -8
  107. package/dist/integrity/slopnators/sl_unpaired_member.ts +16 -8
  108. package/dist/integrity/slopnators/sl_unpaired_slopnator.js +16 -3
  109. package/dist/integrity/slopnators/sl_unpaired_slopnator.ts +16 -3
  110. package/dist/integrity/slopnators/sl_unthreaded.d.ts +1 -0
  111. package/dist/integrity/slopnators/sl_unthreaded.js +18 -10
  112. package/dist/integrity/slopnators/sl_unthreaded.ts +19 -11
  113. package/dist/integrity/slopnators/sl_untranslated_key.js +1 -1
  114. package/dist/integrity/slopnators/sl_untranslated_key.ts +1 -1
  115. package/dist/integrity/slopnators/sl_unversioned.js +16 -2
  116. package/dist/integrity/slopnators/sl_unversioned.ts +16 -2
  117. package/dist/integrity/sweep.js +17 -3
  118. package/dist/integrity/sweep.ts +20 -3
  119. package/dist/knowledge/attach.js +0 -6
  120. package/dist/knowledge/attach.ts +0 -6
  121. package/dist/knowledge/generators/docsgen.js +12 -3
  122. package/dist/knowledge/generators/docsgen.ts +12 -2
  123. package/dist/knowledge/generators/glossary.js +1 -1
  124. package/dist/knowledge/generators/glossary.ts +1 -1
  125. package/dist/operations/attach.js +2 -26
  126. package/dist/operations/attach.ts +2 -27
  127. package/dist/operations/dispatch.js +2 -3
  128. package/dist/operations/dispatch.ts +2 -3
  129. package/dist/operations/generators/capability.js +4 -7
  130. package/dist/operations/generators/capability.ts +3 -6
  131. package/dist/operations/generators/compile.js +8 -13
  132. package/dist/operations/generators/compile.ts +8 -4
  133. package/dist/operations/generators/permits.js +4 -13
  134. package/dist/operations/generators/permits.ts +3 -3
  135. package/dist/operations/generators/seed.js +5 -2
  136. package/dist/operations/generators/seed.ts +6 -2
  137. package/dist/operations/generators/trial.js +10 -10
  138. package/dist/operations/generators/trial.ts +10 -10
  139. package/dist/operations/http.js +4 -1
  140. package/dist/operations/http.ts +6 -1
  141. package/package.json +2 -2
  142. package/dist/core/cli.d.ts +0 -2
  143. package/dist/core/cli.js +0 -1761
  144. package/dist/core/cli.ts +0 -2004
  145. package/dist/core/generators/meta.d.ts +0 -2
  146. package/dist/core/generators/meta.js +0 -829
  147. package/dist/core/generators/meta.ts +0 -1006
  148. package/dist/derivation/generators/derivation.d.ts +0 -3
  149. package/dist/derivation/generators/derivation.js +0 -273
  150. package/dist/derivation/generators/derivation.ts +0 -347
  151. package/dist/derivation/makers/release.d.ts +0 -1
  152. package/dist/derivation/makers/release.js +0 -107
  153. package/dist/derivation/makers/release.ts +0 -135
  154. package/dist/integrations/generators/secret.d.ts +0 -1
  155. package/dist/integrations/generators/secret.js +0 -233
  156. package/dist/integrations/generators/secret.ts +0 -289
  157. package/dist/integrations/generators/target.d.ts +0 -1
  158. package/dist/integrations/generators/target.js +0 -775
  159. package/dist/integrations/generators/target.ts +0 -944
  160. package/dist/integrity/generators/rule.d.ts +0 -2
  161. package/dist/integrity/generators/rule.js +0 -213
  162. package/dist/integrity/generators/rule.ts +0 -271
  163. package/dist/integrity/generators/seal.d.ts +0 -4
  164. package/dist/integrity/generators/seal.js +0 -101
  165. package/dist/integrity/generators/seal.ts +0 -117
  166. package/dist/integrity/sealed.d.ts +0 -1
  167. package/dist/integrity/sealed.js +0 -56
  168. package/dist/integrity/sealed.ts +0 -56
  169. package/dist/knowledge/generators/skills.d.ts +0 -1
  170. package/dist/knowledge/generators/skills.js +0 -222
  171. package/dist/knowledge/generators/skills.ts +0 -263
  172. package/dist/operations/generators/grant.d.ts +0 -1
  173. package/dist/operations/generators/grant.js +0 -267
  174. package/dist/operations/generators/grant.ts +0 -336
  175. package/dist/operations/generators/operation.d.ts +0 -1
  176. package/dist/operations/generators/operation.js +0 -811
  177. package/dist/operations/generators/operation.ts +0 -972
  178. package/dist/operations/makers/capability.d.ts +0 -1
  179. package/dist/operations/makers/capability.js +0 -117
  180. package/dist/operations/makers/capability.ts +0 -166
  181. package/dist/operations/makers/trial.d.ts +0 -1
  182. package/dist/operations/makers/trial.js +0 -413
  183. package/dist/operations/makers/trial.ts +0 -520
@@ -1,4 +1,11 @@
1
1
  import { named } from "../core/scope.js";
2
+ export function shown(proof) {
3
+ if (typeof proof === 'string')
4
+ return proof;
5
+ const target = proof.files.find((one) => one.at === proof.at) ?? proof.files[0];
6
+ const beside = proof.files.filter((one) => one.at !== proof.at).map((one) => one.at);
7
+ return `${target?.source ?? ''}${beside.length > 0 ? ` (in a tree holding ${beside.join(', ')})` : ''}`;
8
+ }
2
9
  export const CONSTRAINTS = [
3
10
  {
4
11
  id: 'no-comment',
@@ -181,7 +188,14 @@ export const CONSTRAINTS = [
181
188
  bans: 'one `at` claimed by two `declares` rows, wherever the two rows live',
182
189
  because: '`declares` is exactly one parent ever, and with two the child\'s owner, its shape and its origin each have two answers while the tree gives none. Measured 2026-08-14: the two rows may sit in different files, so no slopnator that sees one file can find this — check-threads found it only by comparing parents across the whole descent, which is why it was a check rather than a rule. Measured 2026-08-15: this rule read `threads/` and nothing else, so a file outside the tree that declared a child the tree already declares was invisible to it — and a file outside the tree may name children, which is the whole premise `broken-lineage` rests on. It now counts parents from every file the tree reaches, not from one directory',
183
190
  instead: 'keep the row that owns it and delete the other. If both are true, one of them is a `derives` or a `seeds`, which may have many parents because neither of them owns the child',
184
- wrong: "const M: Threaded = {\n declares: [\n {\n at: 'threads/one.ts',\n },\n {\n at: 'threads/one.ts',\n },\n ],\n}",
191
+ wrong: {
192
+ files: [
193
+ { at: 'thread/core/thread.ts', source: "export interface Threaded {\n readonly id?: string\n readonly at?: string\n readonly describes?: string\n readonly declares?: readonly Threaded[]\n readonly derives?: readonly Threaded[]\n readonly seeds?: readonly Threaded[]\n}\n" },
194
+ { at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n {\n at: 'threads/_probe_parented_row.ts',\n },\n ],\n}\n" },
195
+ { at: 'threads/_probe_parented_row.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const M: Threaded = {\n declares: [\n {\n at: 'threads/one.ts',\n },\n {\n at: 'threads/one.ts',\n },\n ],\n}\n" },
196
+ ],
197
+ at: 'threads/_probe_parented_row.ts',
198
+ },
185
199
  right: "const M: Threaded = {\n declares: [\n {\n at: 'threads/one.ts',\n },\n {\n at: 'threads/two.ts',\n },\n ],\n}",
186
200
  cites: [{ on: '2026-08-14', said: 'Cause we need unthreaded, redthread, broken lineage, and the request/produce errors....' }],
187
201
  seeds: [
@@ -238,11 +252,17 @@ export const CONSTRAINTS = [
238
252
  at: './constraint.ts',
239
253
  describes: 'The refusal of a written address that is not on disk — a file moves and every sentence naming it becomes a confident lie that still reads as correct, and counting those without judging them is worse than never scanning, because the number reads as a result.',
240
254
  version: 1,
241
- where: { paths: ['.'], except: ['slopnators/', '_generated/', 'thread/integrity/sealed.ts'] },
255
+ where: { paths: ['.'], except: ['slopnators/', '_generated/'] },
242
256
  bans: 'a string naming a path in this repository that is not on disk',
243
257
  because: 'measured 2026-08-10: 91 distinct paths across 240 mentions named files that had moved under `packages/core/`, and every one of them read as correct. Measured 2026-08-14: paths:check scanned 505 files, found 59 more and judged none of them, printing "no findings" while holding the count — a class counted and unjudged is worse than one never scanned, because the number reads as a result',
244
258
  instead: 'fix the path, or delete the sentence naming it. A path that carries a `{variable}` states the directory it writes into, and that directory must exist',
245
- wrong: "const at = 'threads/nope.ts'",
259
+ wrong: {
260
+ files: [
261
+ { at: 'threads/_probe_there.ts', source: 'export const THERE = 1\n' },
262
+ { at: 'probe.tsx', source: "const at = 'threads/nope.ts'\n" },
263
+ ],
264
+ at: 'probe.tsx',
265
+ },
246
266
  right: "const at = 'packages/core/integrity/src/constraint.ts'",
247
267
  cites: [{ on: '2026-08-14', said: 'I want you to transform every single thing that currently is a check into a law, so we can retire checks and we can have SSOT for laws, and slopnators only for execution.' }],
248
268
  seeds: [
@@ -303,7 +323,13 @@ export const CONSTRAINTS = [
303
323
  bans: 'a `runs` or `by` naming a script this repository does not have',
304
324
  because: 'the gate is the commands these name, so one that package.json lacks fails as `Command "nope:check" not found` at the moment it is reached — after every step before it has already run, and only when somebody runs the gate. Measured 2026-08-14: sixteen rows name sixteen commands and nothing has ever compared them against package.json; they are correct today by nobody having mistyped one',
305
325
  instead: 'add the script to package.json, or fix the name. A row naming a command is a promise that the command exists',
306
- wrong: "const M = {\n runs: 'pnpm nope:check',\n}",
326
+ wrong: {
327
+ files: [
328
+ { at: 'package.json', source: '{\n "scripts": {\n "typecheck": "tsc"\n }\n}\n' },
329
+ { at: 'threads/_probe_command.ts', source: "const M = {\n runs: 'pnpm nope:check',\n}\n" },
330
+ ],
331
+ at: 'threads/_probe_command.ts',
332
+ },
307
333
  right: "const M = {\n runs: 'pnpm typecheck',\n}",
308
334
  cites: [{ on: '2026-08-14', said: 'I think you need to study how a file gets parented and make proper error handling for everything. No at is just one.' }],
309
335
  seeds: [
@@ -333,7 +359,13 @@ export const CONSTRAINTS = [
333
359
  because: 'a rule is a row and a slopnator is a function, and nothing but their names holds the two together — so either half can go missing and the other reads as working. A rule whose slopnator is absent registers, loads, reports nothing, and is indistinguishable from a rule being obeyed. A slopnator no rule names is code that runs in no gate and governs nothing, while looking like enforcement to anybody reading the directory. Measured 2026-08-15: this rule was real and enforced, but it lived as a gate step in ci.ts rather than as a rule — one more place where a guarantee could be turned off by editing a list, which is the thing the rules exist to stop. Moving it here deletes the step rather than adding a check',
334
360
  instead: 'write the rule that needs the slopnator, or delete the slopnator. `pnpm slopnator:seed` writes the scaffold for a rule that has none, and a rename is a `git mv` rather than a second file',
335
361
  wrong: "const CONSTRAINTS = [\n {\n id: 'nothing-names-this-slopnator',\n },\n]",
336
- right: "const CONSTRAINTS = [\n {\n id: 'no-comment',\n },\n]",
362
+ right: {
363
+ files: [
364
+ { at: 'threads/_probe_paired.ts', source: "const CONSTRAINTS = [\n {\n id: 'no-comment',\n },\n]\n" },
365
+ { at: 'threads/slopnators/sl_no_comment.ts', source: 'export const slopnator = { on: [], holds: () => false }\n' },
366
+ ],
367
+ at: 'threads/_probe_paired.ts',
368
+ },
337
369
  cites: [
338
370
  { on: '2026-08-14', said: 'A law is written, names a slopnator. Slopnator is seeded magically. Someone write the function. An enforcer picks it up and it just works.' },
339
371
  { on: '2026-08-15', said: 'Does a slopnator without a law or a law without a slopnator exist? This should be an error....' },
@@ -364,7 +396,14 @@ export const CONSTRAINTS = [
364
396
  bans: 'two rows coining the same id while stating different `describes`',
365
397
  because: 'the glossary is derived: a word\'s meaning is the `describes` on the row that coins it, so a word coined twice with two descriptions has two meanings and the file can hold one. Repetition is not the defect — measured 2026-08-15, the tree coins `slopnator` on twelve rows and `trial` on twelve, deliberately, because the founder asked for the category in the glossary rather than each instance under its own name. Disagreement is the defect: those same twelve rows each stated a different sentence, so the tree asserted twelve meanings for one word and nothing looked. Five words were split this way — `slopnator`, `trial`, `ci`, `build` and `glossary` — and three of them were the same sentence written twice, which is the SSOT failure wearing a second coat',
366
398
  instead: 'state the category meaning on every row that coins the word, because the word is what the row names. Where two rows describe the same file from different heights, the row that declares it wins and the node repeats it. Where a row needs to say something only true of its own instance, it needs its own id — and if you do not want that word in the glossary, the sentence does not belong on the row',
367
- wrong: "const M: Threaded = {\n declares: [\n {\n id: 'one',\n describes: 'The first thing.',\n },\n {\n id: 'one',\n describes: 'A different thing entirely.',\n },\n ],\n}",
399
+ wrong: {
400
+ files: [
401
+ { at: 'thread/core/thread.ts', source: "export interface Threaded {\n readonly id?: string\n readonly at?: string\n readonly describes?: string\n readonly declares?: readonly Threaded[]\n readonly derives?: readonly Threaded[]\n readonly seeds?: readonly Threaded[]\n}\n" },
402
+ { at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n {\n at: 'threads/_probe_meant_row.ts',\n },\n ],\n}\n" },
403
+ { at: 'threads/_probe_meant_row.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const M: Threaded = {\n declares: [\n {\n id: 'one',\n describes: 'The first thing.',\n },\n {\n id: 'one',\n describes: 'A different thing entirely.',\n },\n ],\n}\n" },
404
+ ],
405
+ at: 'threads/_probe_meant_row.ts',
406
+ },
368
407
  right: "const M: Threaded = {\n declares: [\n {\n id: 'one',\n describes: 'The first thing.',\n },\n {\n id: 'one',\n describes: 'The first thing.',\n },\n ],\n}",
369
408
  cites: [
370
409
  { on: '2026-08-15', said: 'the ids thing. We can\'t have that, the id\'s being the same.' },
@@ -530,8 +569,24 @@ export const CONSTRAINTS = [
530
569
  bans: 'dispatch handed a name nothing declares, or a name this file does not write down',
531
570
  because: 'measured 2026-08-25: nothing anywhere holds a dispatched name against a declaration. `packages/ui/src/bindings.tsx` exposes `run(workflow: string, input?)`, and that workflow is a bare string passed to a context the UI package never sees — so a name that matches no operation reaches the browser and fails at whatever the host does with an unknown id. The second half is measured from the other side: `dispatch` in packages/core/operations/src/dispatch.ts takes an Operation object rather than a name, so no shipped call site names an operation as a string at all, and the only place a name was written down was that `run`. Measured again the same day, after `packages/ui/src/bindings.tsx` was deleted: `dispatch` is now a seam on the UI config, named once, handed a name — so from here on a dispatched name is always written where it can be read, and this rule is what holds it to a declaration. A name assembled at runtime is the same hole with a lid on it — it reads from every check exactly like a name that was checked and found',
532
571
  instead: 'write the name where it can be read, and declare the operation it names — `dispatch(\'wf_send_invoice\')` against a row whose `at` is `operations/wf_send_invoice.ts`. Where the name genuinely varies, the choosing belongs in the declaration, not in a template string',
533
- wrong: "dispatch('wf_not_declared')",
534
- right: "dispatch('wf_send_invoice')",
572
+ wrong: {
573
+ files: [
574
+ { at: 'thread/core/thread.ts', source: "export interface Threaded {\n readonly id?: string\n readonly at?: string\n readonly describes?: string\n readonly declares?: readonly Threaded[]\n readonly derives?: readonly Threaded[]\n readonly seeds?: readonly Threaded[]\n}\n" },
575
+ { at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n {\n at: 'threads/_probe_operations.ts',\n },\n ],\n}\n" },
576
+ { at: 'threads/_probe_operations.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const OPERATIONS: Threaded = {\n id: 'operations',\n declares: [\n {\n id: 'wf_send_invoice',\n at: 'operations/wf_send_invoice.ts',\n },\n ],\n}\n" },
577
+ { at: 'app/_probe_wrong.tsx', source: "dispatch('wf_not_declared')" },
578
+ ],
579
+ at: 'app/_probe_wrong.tsx',
580
+ },
581
+ right: {
582
+ files: [
583
+ { at: 'thread/core/thread.ts', source: "export interface Threaded {\n readonly id?: string\n readonly at?: string\n readonly describes?: string\n readonly declares?: readonly Threaded[]\n readonly derives?: readonly Threaded[]\n readonly seeds?: readonly Threaded[]\n}\n" },
584
+ { at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n {\n at: 'threads/_probe_operations.ts',\n },\n ],\n}\n" },
585
+ { at: 'threads/_probe_operations.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const OPERATIONS: Threaded = {\n id: 'operations',\n declares: [\n {\n id: 'wf_send_invoice',\n at: 'operations/wf_send_invoice.ts',\n },\n ],\n}\n" },
586
+ { at: 'app/_probe_right.tsx', source: "dispatch('wf_send_invoice')" },
587
+ ],
588
+ at: 'app/_probe_right.tsx',
589
+ },
535
590
  cites: [
536
591
  { on: '2026-08-25', said: 'And make sure the composition can only dispatch existing operations.' },
537
592
  { on: '2026-08-25', said: 'Yes — reading a view is dispatching too' },
@@ -563,8 +618,24 @@ export const CONSTRAINTS = [
563
618
  bans: 'an element wearing a member that no variant row declares, or that a variant row declares for a different element',
564
619
  because: 'measured 2026-08-25: the founder wrote `<Button.primary text="translation.key" />` as the shape a composition takes, and the fourteen variants in packages/ui/src/variants.ts hold no row `primary` — the one that fits is `button-primary`. `element()` in packages/ui/src/wrappers.tsx resolves any member through a proxy, so that call renders a button carrying the class `thread-primary`, which the stylesheet generator never writes because no variant of that name exists to write it from. It renders, it is unstyled, and nothing in the repository says a word. The pairing half is the same hole from the other side: `card` declares fifteen elements that wear it and `Button` is not among them, and nothing stops `<Button.card />` either. Both are the silent-pass shape — a check reporting success because it measured nothing',
565
620
  instead: 'declare the variant, and name the element in that row\'s `wears` — the row is what makes the pairing legal, which is why `<Button.buttonPrimary />` holds and `<Button.primary />` does not',
566
- wrong: '<Button.primary />',
567
- right: '<Button.buttonPrimary />',
621
+ wrong: {
622
+ files: [
623
+ { at: 'thread/core/thread.ts', source: "export interface Threaded {\n readonly id?: string\n readonly at?: string\n readonly describes?: string\n readonly declares?: readonly Threaded[]\n readonly derives?: readonly Threaded[]\n readonly seeds?: readonly Threaded[]\n}\n" },
624
+ { at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n {\n at: 'threads/_probe_worn.ts',\n },\n ],\n}\n" },
625
+ { at: 'threads/_probe_worn.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const ELEMENTS: Threaded = {\n id: 'element',\n declares: [\n {\n id: 'button',\n at: 'app/_probe_button.tsx',\n },\n ],\n}\n\nexport const VARIANTS: Threaded = {\n id: 'variant',\n declares: [\n {\n id: 'button-primary',\n at: 'app/_probe_variant.ts',\n wears: ['Button'],\n },\n ],\n}\n" },
626
+ { at: 'app/_probe_wrong.tsx', source: '<Button.primary />' },
627
+ ],
628
+ at: 'app/_probe_wrong.tsx',
629
+ },
630
+ right: {
631
+ files: [
632
+ { at: 'thread/core/thread.ts', source: "export interface Threaded {\n readonly id?: string\n readonly at?: string\n readonly describes?: string\n readonly declares?: readonly Threaded[]\n readonly derives?: readonly Threaded[]\n readonly seeds?: readonly Threaded[]\n}\n" },
633
+ { at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n {\n at: 'threads/_probe_worn.ts',\n },\n ],\n}\n" },
634
+ { at: 'threads/_probe_worn.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const ELEMENTS: Threaded = {\n id: 'element',\n declares: [\n {\n id: 'button',\n at: 'app/_probe_button.tsx',\n },\n ],\n}\n\nexport const VARIANTS: Threaded = {\n id: 'variant',\n declares: [\n {\n id: 'button-primary',\n at: 'app/_probe_variant.ts',\n wears: ['Button'],\n },\n ],\n}\n" },
635
+ { at: 'app/_probe_right.tsx', source: '<Button.buttonPrimary />' },
636
+ ],
637
+ at: 'app/_probe_right.tsx',
638
+ },
568
639
  cites: [
569
640
  { on: '2026-08-25', said: 'Variants themselves declare what elements they relate to, in their declarations. Then the composition says button.primary and if that matches the combination of variant and element it\'s acceptable.' },
570
641
  { on: '2026-08-25', said: 'If there\'s no primary variant.....that\' an error moron. Didn\'t I said that it can\'t name a variant that doesn\'t exist?' },
@@ -630,8 +701,22 @@ export const CONSTRAINTS = [
630
701
  bans: 'a member declared in an island\'s tree file whose file is not on disk, and a file in an island\'s folder that no row declares',
631
702
  because: 'a member is added by declaring it and then written, never the other way round — the same order a rule follows to its slopnator, and for the same reason: the row is what everything downstream reads, so a file that arrived without one is a thing the runtime, the API, the agent tools and the trials have all never heard of, while it sits there able to act. The two halves are held together by nothing but a name, so either can go missing and the other reads as working. Absence is the finding in both directions because there is no third place to look: the rows are what exists, the folder is what was written, and any gap between them is one somebody has to close on purpose. The rule reaches by shape and not by name — it reads every island that seeds its members from the tree, so an island added tomorrow is governed without editing this row',
632
703
  instead: 'add the row to the island\'s tree file first and run `pnpm member:seed`, which writes the file that row brings into existence — then write the member into the template it left',
633
- wrong: "export const OPERATIONS: readonly Member[] = [\n {\n id: 'wf_nothing_seeded_this',\n },\n]",
634
- right: "export const OPERATIONS: readonly Member[] = []",
704
+ wrong: {
705
+ files: [
706
+ { at: 'threads/thread.ts', source: "export const THREAD: Threaded = {\n declares: [\n { id: 'operations', at: 'src/islands/operations.ts', governs: ['operations/'] },\n { id: 'operation', at: 'threads/operations.ts' },\n ],\n}\n" },
707
+ { at: 'src/islands/operations.ts', source: 'export const OperationsIslandSchema = {}\n' },
708
+ { at: 'threads/operations.ts', source: "export const OPERATIONS: readonly Threaded[] = [\n {\n id: 'wf_nothing_seeded_this',\n },\n]\n" },
709
+ ],
710
+ at: 'threads/operations.ts',
711
+ },
712
+ right: {
713
+ files: [
714
+ { at: 'threads/thread.ts', source: "export const THREAD: Threaded = {\n declares: [\n { id: 'operations', at: 'src/islands/operations.ts', governs: ['operations/'] },\n { id: 'operation', at: 'threads/operations.ts' },\n ],\n}\n" },
715
+ { at: 'src/islands/operations.ts', source: 'export const OperationsIslandSchema = {}\n' },
716
+ { at: 'threads/operations.ts', source: 'export const OPERATIONS: readonly Threaded[] = []\n' },
717
+ ],
718
+ at: 'threads/operations.ts',
719
+ },
635
720
  cites: [
636
721
  { on: '2026-08-17', said: 'FIRST we add the new row in workflows.ts, which seeds a new wf_{id} file in a workflows folder.' },
637
722
  { on: '2026-08-17', said: 'The file is not authored but seeded. We can write the operation after, but it must have a template with instructions maybe of how to build. Like slopnators.' },
@@ -697,7 +782,14 @@ export const CONSTRAINTS = [
697
782
  bans: 'a declaration of a kind an island governs, written anywhere but where that island governs',
698
783
  because: 'the loader opens the paths an island governs and nothing else, so a declaration written beside the code that needs it is not a smaller declaration — it is no declaration, and every surface downstream reports success over it: it typechecks, the editor completes it, and the generator emits nothing. This is one rule rather than one per island because the rule never differed — measured 2026-08-17: the same sentence was written eleven times, and the island-to-path fact it depends on appeared 130 times across 56 authored files, so eleven copies of a rule drifted against a fact stated everywhere. Here the island states what it governs once, and the slopnator reads it. The call it looks for is read out of the island itself rather than derived from its name, because no convention survives contact with the real set: `sql` exposes `defineSqlFunctions` and `navigation` exposes `defineMap`. An island that governs only folders is one whose kind is derived and authored nowhere, so the call is refused everywhere — which is what `AUTHORED_MAP` did for the map at build.ts:150, now stated on the island instead of in the CLI',
699
784
  instead: 'declare it in the file the island governs, and import it where it is needed',
700
- wrong: "export default definePermissions({ specVersion: 1, entities: [{ entity: 'ent_order' }] })",
785
+ wrong: {
786
+ files: [
787
+ { at: 'threads/thread.ts', source: "export const THREAD: Threaded = {\n declares: [\n { id: 'permissions', at: 'threads/_island_permissions.ts', governs: ['app/_declare/permissions.ts'] },\n ],\n}\n" },
788
+ { at: 'threads/_island_permissions.ts', source: 'export const PermissionsIslandSchema = {}\nexport function definePermissions() {}\n' },
789
+ { at: 'probe.tsx', source: "export default definePermissions({ specVersion: 1, entities: [{ entity: 'ent_order' }] })\n" },
790
+ ],
791
+ at: 'probe.tsx',
792
+ },
701
793
  right: "import permissions from 'app/_declare/permissions'",
702
794
  cites: [
703
795
  { on: '2026-08-17', said: 'If there\'s something that is govern by an island but it\'s propelry done through island, error.' },
@@ -730,8 +822,20 @@ export const CONSTRAINTS = [
730
822
  bans: 'an island that states nothing it governs',
731
823
  because: 'what an island governs is the one fact about it that nothing else can supply. It is not derivable from the island\'s name — measured 2026-08-17: `navigation` governs `app/screens/` and its schema lives in `map.ts`, and `sql` exposes `defineSqlFunctions`, so two of thirteen break any convention a generator could apply. Stated as nothing, the rule that refuses a declaration elsewhere has no elsewhere to refuse: no file is the place its kind belongs, which makes every file the place. The island still parses, still typechecks, still appears in the tree, and governs nobody — inert rather than absent, which is the harder kind to notice',
732
824
  instead: 'state what the island governs — a file where its kind is declared, or a folder whose implementation it governs',
733
- wrong: "const M: Islanding = { declares: [{ id: 'events', at: 'packages/core/spec/src/islands/events.ts' }] }",
734
- right: "const M: Islanding = { declares: [{ id: 'events', at: 'packages/core/spec/src/islands/events.ts', governs: ['app/_declare/events.ts'] }] }",
825
+ wrong: {
826
+ files: [
827
+ { at: 'threads/_inert_probe.ts', source: "export const M: Threaded = {\n declares: [\n { id: 'events', at: 'packages/core/spec/src/islands/events.ts' },\n ],\n}\n" },
828
+ { at: 'packages/core/spec/src/islands/events.ts', source: 'export const EventsIslandSchema = {}\n' },
829
+ ],
830
+ at: 'threads/_inert_probe.ts',
831
+ },
832
+ right: {
833
+ files: [
834
+ { at: 'threads/_inert_probe.ts', source: "export const M: Threaded = {\n declares: [\n { id: 'events', at: 'packages/core/spec/src/islands/events.ts', governs: ['app/_declare/events.ts'] },\n ],\n}\n" },
835
+ { at: 'packages/core/spec/src/islands/events.ts', source: 'export const EventsIslandSchema = {}\n' },
836
+ ],
837
+ at: 'threads/_inert_probe.ts',
838
+ },
735
839
  cites: [
736
840
  { on: '2026-08-17', said: 'An island extends threader. It declares what it governs.' },
737
841
  { on: '2026-08-17', said: 'If there\'s an island that doesn\'t state what it governs, error.' },
@@ -797,7 +901,14 @@ export const CONSTRAINTS = [
797
901
  because: 'a rule that names a kind nobody declares is the most convincing kind of gap: somebody can point at the rule, the plugin registers it, the trial passes, and the thing it governs does not exist. It is the mirror of the silent pass — enforcement over an empty set. This rule used to refuse the other direction too, an island no rule named, and that direction retired on 2026-08-17 when the eleven per-island placement rules collapsed into `declared-elsewhere`: one rule reading what each island governs, so a kind added tomorrow is enforced the moment its row exists. What was checked became structural, and a check that can no longer fail is a check worth deleting rather than keeping for the shape of it',
798
902
  instead: 'name an island the tree declares, or drop the `governs` — a rule about a kind nobody can declare is a rule about nothing',
799
903
  wrong: "export const L: Constraint = {\n id: 'a-rule',\n governs: 'no-island-by-this-name',\n}",
800
- right: "export const L: Constraint = {\n id: 'a-rule',\n governs: 'entities',\n}",
904
+ right: {
905
+ files: [
906
+ { at: 'threads/thread.ts', source: "export const THREAD: Threaded = {\n declares: [\n { id: 'entities', at: 'threads/_island_entities.ts', governs: ['app/_declare/entities.ts'] },\n ],\n}\n" },
907
+ { at: 'threads/_island_entities.ts', source: 'export const EntitiesIslandSchema = {}\nexport function defineEntities() {}\n' },
908
+ { at: 'threads/_probe_governed.ts', source: "export const L: Constraint = {\n id: 'a-rule',\n governs: 'entities',\n}\n" },
909
+ ],
910
+ at: 'threads/_probe_governed.ts',
911
+ },
801
912
  cites: [
802
913
  { on: '2026-08-16', said: 'Here\'s a new law I need you to add: No island without law.' },
803
914
  { on: '2026-08-16', said: 'So also we need a "law doesn\'t match island" if this is to be done entirely by hand so we catch drifts.' },
@@ -960,8 +1071,24 @@ export const CONSTRAINTS = [
960
1071
  bans: 'an operation reaching a node under `nodes/` that the tree does not declare',
961
1072
  because: 'measured 2026-08-22: `OperationNode` stated `node` as a bare string, so a body naming `nd_teleport` compiled, loaded and dispatched, and nothing anywhere compared that name against a declared node. The first reading of this rule matched that string. Measured again 2026-08-26, after a node began building its own span: an operation writes no such string any more — it imports a node and calls it — so the reading matched nothing on any real file, and its own proofs were written in the same retired shape, so it passed its trial while measuring nothing. Both halves drifted together, which is the only way a rule goes quiet without anybody noticing. Proved by planting a node body no row declares and running a real app: it ran, answered `ok`, and the sweep named the file for being unthreaded and said nothing about the operation built from it. What an operation says today is the import it reaches its node through, so that is what is read — resolved to a path and checked against the descent, which also catches an import that resolves to nothing at all, where the only other answer was a module-not-found at run time. Measured a third time the same day, an hour after that reading shipped: it matched `import … from` and nothing else, so `await import(...)`, `require(...)` and `export … from` each walked through it, each tried against a real tree and each unremarked. Twice now this rule has enumerated a form instead of stating its rule, which is how a refusal ends up being a list of whatever somebody happened to try. The rule is about the PATH: a string that resolves under `nodes/` names a node, and a node the descent never reaches is a step nobody agreed to — whether it arrives by import, by require, by re-export, or sitting in a const waiting to be used',
962
1073
  instead: 'declare the node in the tree and then build from it. What an app can be asked to do grows by declaring a node, never by importing a file nothing brought into existence',
963
- wrong: "import { node as ghost } from '../nodes/nd_ghost.ts'",
964
- right: "import { node as read } from '../nodes/nd_read.ts'",
1074
+ wrong: {
1075
+ files: [
1076
+ { at: 'thread/core/thread.ts', source: "export interface Threaded {\n readonly id?: string\n readonly at?: string\n readonly describes?: string\n readonly declares?: readonly Threaded[]\n readonly derives?: readonly Threaded[]\n readonly seeds?: readonly Threaded[]\n}\n" },
1077
+ { at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n {\n at: 'threads/_probe_nodes.ts',\n },\n ],\n}\n" },
1078
+ { at: 'threads/_probe_nodes.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const NODES: Threaded = {\n id: 'nodes',\n declares: [\n {\n id: 'read',\n at: 'nodes/nd_read.ts',\n },\n ],\n}\n" },
1079
+ { at: 'operations/_probe_wrong.ts', source: "import { node as ghost } from '../nodes/nd_ghost.ts'" },
1080
+ ],
1081
+ at: 'operations/_probe_wrong.ts',
1082
+ },
1083
+ right: {
1084
+ files: [
1085
+ { at: 'thread/core/thread.ts', source: "export interface Threaded {\n readonly id?: string\n readonly at?: string\n readonly describes?: string\n readonly declares?: readonly Threaded[]\n readonly derives?: readonly Threaded[]\n readonly seeds?: readonly Threaded[]\n}\n" },
1086
+ { at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n {\n at: 'threads/_probe_nodes.ts',\n },\n ],\n}\n" },
1087
+ { at: 'threads/_probe_nodes.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const NODES: Threaded = {\n id: 'nodes',\n declares: [\n {\n id: 'read',\n at: 'nodes/nd_read.ts',\n },\n ],\n}\n" },
1088
+ { at: 'operations/_probe_right.ts', source: "import { node as read } from '../nodes/nd_read.ts'" },
1089
+ ],
1090
+ at: 'operations/_probe_right.ts',
1091
+ },
965
1092
  cites: [
966
1093
  { on: '2026-08-22', said: 'Operations are bilt of nodes, only nodes that are declared.' },
967
1094
  { on: '2026-08-22', said: 'That\'s why it grows. We declare, operations can do more.' },
@@ -993,8 +1120,24 @@ export const CONSTRAINTS = [
993
1120
  bans: 'a row in a tree file\'s `MEMBERS` whose id does not carry a prefix that list takes — `wf_`, `vw_` or `agt_` in the operations tree, `nd_` in the nodes tree',
994
1121
  because: 'measured 2026-08-24: a prefix is not bound to a file. Both tree files export a list called `MEMBERS`, both are arrays of rows carrying a string id, and nothing in TypeScript or in any check separated them — a node id placed in the operations list typechecked, attached, seeded a body and read as agreed, and an operation id placed in the nodes list did the same. The prefix is the only thing that says which of the two a row is, and it was decoration. Measured again 2026-08-25, after the first reading of this was deleted for a different reason: the same gap was still open, and the founder named it as one of five mismatches against their stated requirements. Depth is what the reading turns on — a member row states its own id, and the `seeds` and `derives` rows beneath it state theirs, which are bodies rather than operations or nodes, so `wf_send_invoice_body` carries no prefix and must not be refused for it. Only rows sitting directly in the list are read',
995
1122
  instead: 'name the row for what it is. An operation is `wf_`, `vw_` or `agt_` and belongs in the operations tree; a node is `nd_` and belongs in the nodes tree. A row in the wrong list is not a naming slip, it is a declaration of the wrong thing',
996
- wrong: "export const MEMBERS: readonly Member[] = [\n {\n id: 'nd_teleport',\n at: '_threads/operations.ts',\n describes: 'a node wearing an operation list',\n version: 1,\n },\n]",
997
- right: "export const MEMBERS: readonly Member[] = [\n {\n id: 'wf_send_invoice',\n at: '_threads/operations.ts',\n describes: 'sends an invoice',\n version: 1,\n },\n]",
1123
+ wrong: {
1124
+ files: [
1125
+ {
1126
+ at: 'threads/operations.ts',
1127
+ source: "export const MEMBERS: readonly Member[] = [\n {\n id: 'nd_teleport',\n at: '_threads/operations.ts',\n describes: 'a node wearing an operation list',\n version: 1,\n },\n]\n",
1128
+ },
1129
+ ],
1130
+ at: 'threads/operations.ts',
1131
+ },
1132
+ right: {
1133
+ files: [
1134
+ {
1135
+ at: 'threads/operations.ts',
1136
+ source: "export const MEMBERS: readonly Member[] = [\n {\n id: 'wf_send_invoice',\n at: '_threads/operations.ts',\n describes: 'sends an invoice',\n version: 1,\n },\n]\n",
1137
+ },
1138
+ ],
1139
+ at: 'threads/operations.ts',
1140
+ },
998
1141
  cites: [
999
1142
  { on: '2026-08-24', said: 'operations.ts, where it declares nodes.ts and dispatch.ts, and member rows in operations are wf_,vw_ and agt_ files.' },
1000
1143
  { on: '2026-08-25', said: '3 fix it please.' },