@orkestrel/scaffold 0.0.63 → 0.0.64

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 (52) hide show
  1. package/README.md +18 -103
  2. package/dist/bin/main.js +95 -27
  3. package/dist/bin/main.js.map +1 -1
  4. package/dist/host/AGENTS.md +2 -2
  5. package/dist/host/CLAUDE.md +6 -0
  6. package/dist/host/agents/orchestration.md +23 -15
  7. package/dist/host/agents/skills/enterprise-bootstrap/references/bootstrap-reference.md +16 -16
  8. package/dist/host/agents/skills/enterprise-bootstrap/references/frontend-design.md +6 -6
  9. package/dist/host/agents/skills/enterprise-bootstrap/references/utilities.md +4 -4
  10. package/dist/host/agents/skills/orkestrel-debrief/references/field-testing.md +5 -5
  11. package/dist/host/agents/skills/orkestrel-falsify/references/reconcile.md +1 -1
  12. package/dist/host/agents/skills/orkestrel-publish/SKILL.md +15 -15
  13. package/dist/host/agents/skills/orkestrel-publish/references/wave.md +43 -17
  14. package/dist/host/agents/skills/orkestrel-publish/references/window.md +41 -16
  15. package/dist/host/claude/agents/orkestrel.md +56 -56
  16. package/dist/host/claude/agents/reviewer.md +13 -0
  17. package/dist/host/claude/rules/architecture.md +51 -45
  18. package/dist/host/claude/rules/documentation.md +18 -1
  19. package/dist/host/claude/rules/portability.md +2 -0
  20. package/dist/host/claude/rules/quality.md +1 -1
  21. package/dist/host/claude/rules/tests.md +12 -11
  22. package/dist/host/claude/rules/typescript.md +5 -0
  23. package/dist/host/claude/rules/workspace.md +23 -18
  24. package/dist/host/claude/rules/writing.md +4 -0
  25. package/dist/host/codex/agents/orkestrel.toml +3 -3
  26. package/dist/host/codex/agents/reviewer.toml +4 -2
  27. package/dist/host/configs/helpers.ts +311 -2
  28. package/dist/host/configs/policy.ts +1100 -51
  29. package/dist/host/dotfiles/oxlintrc.json +72 -1
  30. package/dist/host/guides/guide.md +749 -222
  31. package/dist/host/guides/scaffold.md +472 -378
  32. package/dist/host/manifest.json +34 -33
  33. package/dist/host/scripts/codex.sh +0 -0
  34. package/dist/host/scripts/cursor.sh +0 -0
  35. package/dist/host/scripts/deps.sh +0 -0
  36. package/dist/host/scripts/ollama.sh +0 -0
  37. package/dist/host/tests/config.test.ts +1200 -16
  38. package/dist/host/tests/policy.test.ts +157 -173
  39. package/dist/host/tests/setupPolicy.ts +522 -1007
  40. package/dist/src/core/index.cjs +371 -277
  41. package/dist/src/core/index.cjs.map +1 -1
  42. package/dist/src/core/index.d.cts +130 -120
  43. package/dist/src/core/index.d.ts +130 -120
  44. package/dist/src/core/index.js +371 -276
  45. package/dist/src/core/index.js.map +1 -1
  46. package/dist/src/server/index.cjs +28 -21
  47. package/dist/src/server/index.cjs.map +1 -1
  48. package/dist/src/server/index.d.cts +38 -33
  49. package/dist/src/server/index.d.ts +38 -33
  50. package/dist/src/server/index.js +28 -21
  51. package/dist/src/server/index.js.map +1 -1
  52. package/package.json +15 -16
@@ -1,10 +1,10 @@
1
1
  # The approval and the upload window
2
2
 
3
3
  A release needs the user at the keyboard to authenticate the session, and again to authorize each
4
- upload. An approval URL dies unclicked in under a minute, so mint one only in a
5
- moment the user can click, and relay it byte for byte. Where the account answers with a one-time
6
- code, take that code for the upload — it needs no browser authorization and opens no window to
7
- lose. Where the account has no code, the browser authorization opens a five-minute window, and the
4
+ upload. An approval URL dies unclicked in under a minute, so mint one only in a moment the user can
5
+ click, and relay it byte for byte. Where the account answers with a one-time code, take that code
6
+ for the upload — it needs no browser authorization, and § Authorize the upload states the code's own
7
+ life. Where the account has no code, the browser authorization opens a five-minute window, and the
8
8
  rest of the layer either fits inside it or takes another approval.
9
9
 
10
10
  ## Arm the terminal
@@ -25,8 +25,11 @@ rest of the layer either fits inside it or takes another approval.
25
25
  zero.
26
26
  - Re-probe `whoami` immediately before the first upload. A stored credential expires mid-session
27
27
  and an overnight gap expires it, so a session-start answer does not hold.
28
- - Read a login log that shows the spinner and then a legacy `Username:` prompt as an expired
29
- attempt rather than as a prompt to answer. Kill it by process id and mint a fresh flow.
28
+ - Read a login log that shows the spinner and then a legacy `Username:` prompt as a dead attempt
29
+ rather than as a prompt to answer: expired, or refused on its first poll per § Read a `403` on the
30
+ poll. Kill it by the process id recorded at its launch, per `.agents/orchestration.md` § Confirm
31
+ dead before relaunching. Every publish here runs under the same `script -qfc` form, so a pattern
32
+ over the process list reaches a live upload as readily as the dead login.
30
33
  - On a Windows host, Git Bash ships no `script` binary, so the upload step is operator-driven:
31
34
  prepare the layer, prove the gates, surface the exact `npm publish` command, and the operator
32
35
  runs it in a real terminal. Everything before and after the upload — bumps, re-pins, gates,
@@ -49,13 +52,17 @@ rest of the layer either fits inside it or takes another approval.
49
52
  against `registry.npmjs.org` with `npm` 10.9.7 and `node` 22.22.2: npm polls `GET /-/v1/done`
50
53
  every few seconds and takes `202` while the session waits, and the registry answers `403` at
51
54
  about 45 seconds. Whether the registry fixes that abandon by elapsed time or by poll count is
52
- unmeasured, so plan against the duration.
55
+ unmeasured, so plan against the duration. A `403` within seconds of the mint is not that abandon;
56
+ § Read a `403` on the poll names it.
53
57
  - Recognise the abandon on each side. The `npm login` command reads the `403` as web login being
54
58
  unsupported and drops to its legacy `Username:` prompt. The `npm publish` command exits `E403`
55
59
  naming `GET /-/v1/done?authId=`.
56
60
  - Never keep a link alive by re-minting on a loop. Each mint invalidates the URL before it, so a
57
61
  supervisor that re-mints on expiry makes the link a moving target and every relayed URL is dead
58
- on arrival. Mint once per human moment, and mint again only when the user asks.
62
+ on arrival. Mint once per human moment, and mint again only when the user asks. That ban covers
63
+ a URL already relayed. While no URL has been relayed, mint attempts until one survives its own
64
+ first poll, relay that one alone, and kill the rest by process id; § Read a `403` on the poll
65
+ names the first-poll refusal this answers.
59
66
  - Name the login approval and the upload authorization to the user before either arrives, or the
60
67
  authorization link reads as the login having failed.
61
68
  - Surface each approval URL the moment it appears in the log, and take the **last** one in log
@@ -75,17 +82,23 @@ rest of the layer either fits inside it or takes another approval.
75
82
 
76
83
  - Take the account's one-time code where the account has one. The
77
84
  `npm publish --ignore-scripts --otp=<code>` command uploads with no browser authorization and no
78
- poll, so it carries neither a window nor a race. In the `@orkestrel/scaffold` 0.0.56 run on
79
- 2026-08-27 the browser authorization failed on the 45-second abandon and the one-time code
80
- uploaded the package with no retry.
81
- - Ask for the code at the moment of the upload, and run the upload inside that code's own life. A
82
- code read minutes earlier is already spent.
85
+ poll. The code has its own life: measured on 2026-09-04 against `registry.npmjs.org`, one code
86
+ carried every upload of a layer started within about a minute of its first use, console at
87
+ 20:25:02 through router at 20:25:58, and the upload started at 20:25:58 was refused `EOTP` at
88
+ 20:26:00; a code read before its layer was ready was refused at its first upload.
89
+ - Prepare the whole layer, then ask for one code at the moment the layer's first upload starts, and
90
+ chase the layer's uploads back-to-back inside that code's life with no gate between them. A code
91
+ read minutes earlier is already spent.
92
+ - Read `EOTP` on this path as the code's life ending, never as the contention § Spend the window
93
+ describes for the browser path: the chain stops at the refused package, and the layer resumes
94
+ from that package on a fresh code. Never retry the refused upload on the same code.
83
95
  - Ask for the code and nothing else. Never ask for a password, an access token, or an auth file.
84
96
  `.agents/orchestration.md` § Publishing the fleet owns that law.
85
97
  - Arm a one-time-code upload the way § Arm the terminal arms every other publish.
86
98
  - Fall back to the browser authorization where the account answers with no code. That path mints
87
99
  the `auth/cli/<id>` URL, needs the click inside the session's life, and opens the five-minute
88
- window.
100
+ window. In the `@orkestrel/scaffold` 0.0.56 run on 2026-08-27 that authorization failed on the
101
+ 45-second abandon and the one-time code uploaded the package with no retry.
89
102
  - Tell the user that approving an `auth/cli/<id>` URL opens a five-minute window covering the rest
90
103
  of the layer.
91
104
 
@@ -95,7 +108,8 @@ rest of the layer either fits inside it or takes another approval.
95
108
  window, so none of it binds that path.
96
109
  - The window opens when the user approves, not when the first publish starts.
97
110
  - Open each layer with one package: publish it alone, surface its approval URL the moment the
98
- journal shows it, and confirm the upload from the registry before starting the rest.
111
+ journal shows it, and read its acceptance line in the journal before starting the rest; the
112
+ registry read confirms it.
99
113
  - Then chase the remaining uploads back-to-back in one process with no gap. An upload started
100
114
  within seconds of an approval frequently rides that approval, and each one that does not mints
101
115
  its own URL.
@@ -127,14 +141,25 @@ the same status. Rule from the evidence, never from which cause reads likelier.
127
141
  opened, and the registry closed the session.
128
142
  - The user clicked a superseded URL and poisoned the live attempt. The poll fails mid-flight while
129
143
  the user is looking at a page that reports success.
144
+ - The registry refused the attempt's first poll, seconds after the mint and before anyone could
145
+ click, because the poll left from an egress address other than the one that minted the session.
146
+ npm reads that `403` as web login unsupported and drops to the legacy `Username:` prompt within
147
+ seconds rather than at 45. Recover by minting attempts on a kept-alive connection until one
148
+ survives its first poll and relaying that one alone, per § Reach the approval.
130
149
  - Tell those causes apart from the log and the user, never from the status alone. A single minted
131
150
  URL that nobody opened in time is the abandon. A log carrying a URL the running attempt
132
- superseded, with the user reporting a click, is the poisoned attempt.
151
+ superseded, with the user reporting a click, is the poisoned attempt. A drop within seconds of
152
+ the mint, before any relay, is the egress refusal.
133
153
  - Recover the same way whichever it was: read the registry for the version, confirm no publish
134
154
  process is live, then mint exactly one fresh attempt with the user at the keyboard.
135
155
 
136
156
  ## Read the verdict from the registry
137
157
 
158
+ - Read `+ @orkestrel/<name>@<version>` in the upload's own journal as the accepted verdict, and
159
+ advance the chain on it. The registry's read lags its processing by minutes, which the
160
+ `Your package is being processed and may take a few minutes to become available.` notice beside
161
+ that line announces, so a chain keyed on the registry stops on an accepted upload. Read the
162
+ registry to confirm and to record the layer's close, never to gate the next upload.
138
163
  - Read the result from the registry, not from an exit code. A piped `npm publish` reports the exit
139
164
  status of the pipeline, and a CDN read straight after a publish can still serve the previous
140
165
  version.
@@ -43,58 +43,58 @@ so network-controlled descriptions never enter agent instruction context.
43
43
 
44
44
  <!-- orkestrel:catalog -->
45
45
 
46
- | Package | Version | Layer | Runtime dependencies |
47
- | ----------------------- | -------- | ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
48
- | `@orkestrel/abort` | `0.0.8` | L1 | `@orkestrel/contract` `^0.0.13` |
49
- | `@orkestrel/agent` | `0.0.19` | L5 | `@orkestrel/abort` `^0.0.8`, `@orkestrel/budget` `^0.0.8`, `@orkestrel/contract` `^0.0.13`, `@orkestrel/database` `^0.0.12`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/queue` `^0.0.11`, `@orkestrel/timeout` `^0.0.8`, `@orkestrel/tool` `^0.0.12`, `@orkestrel/workflow` `^0.0.16`, `@orkestrel/workspace` `^0.0.6` |
50
- | `@orkestrel/brief` | `0.0.6` | L4 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/interpret` `^0.0.11`, `@orkestrel/reason` `^0.0.8` |
51
- | `@orkestrel/browser` | `0.0.14` | L3 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/html` `^0.0.7`, `@orkestrel/websocket` `^0.0.10` |
52
- | `@orkestrel/budget` | `0.0.8` | L1 | `@orkestrel/contract` `^0.0.13` |
53
- | `@orkestrel/codec` | `0.0.1` | L0 | |
54
- | `@orkestrel/console` | `0.0.11` | L2 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8` |
55
- | `@orkestrel/contract` | `0.0.15` | L0 | |
56
- | `@orkestrel/csv` | `0.0.5` | L1 | `@orkestrel/contract` `^0.0.13` |
57
- | `@orkestrel/database` | `0.0.12` | L2 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/indexeddb` `^0.0.9`, `@orkestrel/sqlite` `^0.0.9` |
58
- | `@orkestrel/emitter` | `0.0.8` | L1 | `@orkestrel/contract` `^0.0.13` |
59
- | `@orkestrel/form` | `0.0.4` | L2 | `@orkestrel/contract` `^0.0.15`, `@orkestrel/emitter` `^0.0.8` |
60
- | `@orkestrel/guide` | `0.0.15` | L3 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/markdown` `^0.0.12` |
61
- | `@orkestrel/html` | `0.0.7` | L1 | `@orkestrel/contract` `^0.0.13` |
62
- | `@orkestrel/indexeddb` | `0.0.9` | L1 | `@orkestrel/contract` `^0.0.13` |
63
- | `@orkestrel/interpret` | `0.0.11` | L3 | `@orkestrel/reason` `^0.0.8`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/contract` `^0.0.13`, `@orkestrel/template` `^0.0.5` |
64
- | `@orkestrel/lsp` | `0.0.5` | L3 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/process` `^0.0.8` |
65
- | `@orkestrel/markdown` | `0.0.12` | L2 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/html` `^0.0.7` |
66
- | `@orkestrel/mcp` | `0.0.27` | L3 | `@orkestrel/codec` `^0.0.1`, `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/process` `^0.0.8`, `@orkestrel/sse` `^0.0.5`, `@orkestrel/tool` `^0.0.12`, `@orkestrel/websocket` `^0.0.10` |
67
- | `@orkestrel/middleware` | `0.0.18` | L2 | `@orkestrel/abort` `^0.0.8`, `@orkestrel/budget` `^0.0.8`, `@orkestrel/contract` `^0.0.13`, `@orkestrel/timeout` `^0.0.8` |
68
- | `@orkestrel/msg` | `0.0.8` | L0 | |
69
- | `@orkestrel/ndjson` | `0.0.8` | L1 | `@orkestrel/contract` `^0.0.13` |
70
- | `@orkestrel/ollama` | `0.0.13` | L6 | `@orkestrel/agent` `^0.0.19`, `@orkestrel/budget` `^0.0.8`, `@orkestrel/contract` `^0.0.13`, `@orkestrel/ndjson` `^0.0.8`, `@orkestrel/timeout` `^0.0.8`, `@orkestrel/tool` `^0.0.12` |
71
- | `@orkestrel/pool` | `0.0.9` | L2 | `@orkestrel/emitter` `^0.0.8` |
72
- | `@orkestrel/probe` | `0.0.11` | L4 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/lsp` `^0.0.5`, `@orkestrel/mcp` `^0.0.27`, `@orkestrel/queue` `^0.0.11`, `@orkestrel/timeout` `^0.0.8`, `@orkestrel/tool` `^0.0.12` |
73
- | `@orkestrel/process` | `0.0.9` | L2 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8` |
74
- | `@orkestrel/program` | `0.0.11` | L4 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/qualifier` `^0.0.12`, `@orkestrel/rater` `^0.0.12`, `@orkestrel/reason` `^0.0.8` |
75
- | `@orkestrel/qualifier` | `0.0.12` | L3 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/reason` `^0.0.8` |
76
- | `@orkestrel/queue` | `0.0.11` | L3 | `@orkestrel/abort` `^0.0.8`, `@orkestrel/contract` `^0.0.13`, `@orkestrel/database` `^0.0.12`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/timeout` `^0.0.8` |
77
- | `@orkestrel/rater` | `0.0.12` | L3 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/reason` `^0.0.8` |
78
- | `@orkestrel/reason` | `0.0.8` | L2 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8` |
79
- | `@orkestrel/relation` | `0.0.10` | L3 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/database` `^0.0.12`, `@orkestrel/emitter` `^0.0.8` |
80
- | `@orkestrel/router` | `0.0.12` | L2 | `@orkestrel/abort` `^0.0.8`, `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8` |
81
- | `@orkestrel/scaffold` | `0.0.60` | L3 | `@orkestrel/console` `^0.0.11`, `@orkestrel/contract` `^0.0.15`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/markdown` `^0.0.12`, `@orkestrel/process` `^0.0.9`, `@orkestrel/template` `^0.0.5` |
82
- | `@orkestrel/sea` | `0.0.13` | L3 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/process` `^0.0.8` |
83
- | `@orkestrel/server` | `0.0.17` | L3 | `@orkestrel/abort` `^0.0.8`, `@orkestrel/codec` `^0.0.1`, `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/router` `^0.0.12`, `@orkestrel/timeout` `^0.0.8` |
84
- | `@orkestrel/sqlite` | `0.0.9` | L1 | `@orkestrel/contract` `^0.0.13` |
85
- | `@orkestrel/sse` | `0.0.5` | L0 | |
86
- | `@orkestrel/supervisor` | `0.0.1` | L5 | `@orkestrel/contract` `^0.0.11`, `@orkestrel/database` `^0.0.9`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/workflow` `^0.0.12` |
87
- | `@orkestrel/table` | `0.0.3` | L2 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8` |
88
- | `@orkestrel/template` | `0.0.5` | L2 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/emitter` `^0.0.8` |
89
- | `@orkestrel/terminal` | `0.0.13` | L3 | `@orkestrel/console` `^0.0.11`, `@orkestrel/contract` `^0.0.13`, `@orkestrel/database` `^0.0.12`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/form` `^0.0.3`, `@orkestrel/sse` `^0.0.5` |
90
- | `@orkestrel/test` | `0.0.12` | L0 | |
91
- | `@orkestrel/timeout` | `0.0.8` | L1 | `@orkestrel/contract` `^0.0.13` |
92
- | `@orkestrel/tool` | `0.0.12` | L1 | `@orkestrel/contract` `^0.0.13` |
93
- | `@orkestrel/toolbox` | `0.0.11` | L6 | `@orkestrel/agent` `^0.0.19`, `@orkestrel/contract` `^0.0.13`, `@orkestrel/database` `^0.0.12`, `@orkestrel/form` `^0.0.3`, `@orkestrel/relation` `^0.0.10`, `@orkestrel/server` `^0.0.17`, `@orkestrel/terminal` `^0.0.13`, `@orkestrel/tool` `^0.0.12`, `@orkestrel/workflow` `^0.0.16`, `@orkestrel/workspace` `^0.0.6` |
94
- | `@orkestrel/websocket` | `0.0.10` | L2 | `@orkestrel/emitter` `^0.0.8` |
95
- | `@orkestrel/worker` | `0.0.10` | L4 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/database` `^0.0.12`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/pool` `^0.0.9`, `@orkestrel/queue` `^0.0.11` |
96
- | `@orkestrel/workflow` | `0.0.16` | L4 | `@orkestrel/abort` `^0.0.8`, `@orkestrel/budget` `^0.0.8`, `@orkestrel/contract` `^0.0.13`, `@orkestrel/database` `^0.0.12`, `@orkestrel/emitter` `^0.0.8`, `@orkestrel/queue` `^0.0.11`, `@orkestrel/timeout` `^0.0.8` |
97
- | `@orkestrel/workspace` | `0.0.6` | L3 | `@orkestrel/contract` `^0.0.13`, `@orkestrel/database` `^0.0.12`, `@orkestrel/emitter` `^0.0.8` |
46
+ | Package | Version | Layer | Runtime dependencies | Peer dependencies |
47
+ | ----------------------- | -------- | ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
48
+ | `@orkestrel/abort` | `0.0.10` | L1 | `@orkestrel/contract` `^0.0.17` | |
49
+ | `@orkestrel/agent` | `0.0.20` | L5 | `@orkestrel/abort` `^0.0.9`, `@orkestrel/budget` `^0.0.9`, `@orkestrel/contract` `^0.0.16`, `@orkestrel/database` `^0.0.13`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/queue` `^0.0.12`, `@orkestrel/timeout` `^0.0.9`, `@orkestrel/tool` `^0.0.13`, `@orkestrel/workflow` `^0.0.17`, `@orkestrel/workspace` `^0.0.7` | |
50
+ | `@orkestrel/brief` | `0.0.7` | L4 | `@orkestrel/contract` `^0.0.16`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/interpret` `^0.0.12`, `@orkestrel/reason` `^0.0.9` | |
51
+ | `@orkestrel/browser` | `0.0.15` | L3 | `@orkestrel/contract` `^0.0.16`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/html` `^0.0.8`, `@orkestrel/websocket` `^0.0.11` | |
52
+ | `@orkestrel/budget` | `0.0.10` | L1 | `@orkestrel/contract` `^0.0.17` | |
53
+ | `@orkestrel/codec` | `0.0.3` | L0 | | |
54
+ | `@orkestrel/console` | `0.0.13` | L2 | `@orkestrel/emitter` `^0.0.10`, `@orkestrel/contract` `^0.0.17` | |
55
+ | `@orkestrel/contract` | `0.0.17` | L0 | | |
56
+ | `@orkestrel/csv` | `0.0.7` | L1 | `@orkestrel/contract` `^0.0.17` | |
57
+ | `@orkestrel/database` | `0.0.14` | L2 | `@orkestrel/contract` `^0.0.17`, `@orkestrel/emitter` `^0.0.10`, `@orkestrel/indexeddb` `^0.0.11`, `@orkestrel/sqlite` `^0.0.11` | |
58
+ | `@orkestrel/emitter` | `0.0.10` | L1 | `@orkestrel/contract` `^0.0.17` | |
59
+ | `@orkestrel/form` | `0.0.6` | L2 | `@orkestrel/contract` `^0.0.17`, `@orkestrel/emitter` `^0.0.10` | |
60
+ | `@orkestrel/guide` | `0.0.17` | L3 | `@orkestrel/contract` `^0.0.16`, `@orkestrel/markdown` `^0.0.13` | |
61
+ | `@orkestrel/html` | `0.0.9` | L1 | `@orkestrel/contract` `^0.0.17` | |
62
+ | `@orkestrel/indexeddb` | `0.0.11` | L1 | `@orkestrel/contract` `^0.0.17` | |
63
+ | `@orkestrel/interpret` | `0.0.12` | L3 | `@orkestrel/reason` `^0.0.9`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/contract` `^0.0.16`, `@orkestrel/template` `^0.0.6` | |
64
+ | `@orkestrel/lsp` | `0.0.6` | L3 | `@orkestrel/emitter` `^0.0.9`, `@orkestrel/process` `^0.0.10`, `@orkestrel/contract` `^0.0.16` | |
65
+ | `@orkestrel/markdown` | `0.0.14` | L2 | `@orkestrel/contract` `^0.0.17`, `@orkestrel/html` `^0.0.9` | |
66
+ | `@orkestrel/mcp` | `0.0.28` | L4 | `@orkestrel/codec` `^0.0.2`, `@orkestrel/contract` `^0.0.16`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/process` `^0.0.10`, `@orkestrel/sse` `^0.0.6`, `@orkestrel/tool` `^0.0.13`, `@orkestrel/websocket` `^0.0.11` | `@orkestrel/router` `^0.0.13`, `@orkestrel/server` `^0.0.18` |
67
+ | `@orkestrel/middleware` | `0.0.19` | L4 | `@orkestrel/abort` `^0.0.9`, `@orkestrel/budget` `^0.0.9`, `@orkestrel/contract` `^0.0.16`, `@orkestrel/timeout` `^0.0.9` | `@orkestrel/database` `^0.0.13`, `@orkestrel/server` `^0.0.18` |
68
+ | `@orkestrel/msg` | `0.0.10` | L0 | | |
69
+ | `@orkestrel/ndjson` | `0.0.10` | L1 | `@orkestrel/contract` `^0.0.17` | |
70
+ | `@orkestrel/ollama` | `0.0.14` | L6 | `@orkestrel/agent` `^0.0.20`, `@orkestrel/budget` `^0.0.9`, `@orkestrel/contract` `^0.0.16`, `@orkestrel/ndjson` `^0.0.9`, `@orkestrel/timeout` `^0.0.9`, `@orkestrel/tool` `^0.0.13` | |
71
+ | `@orkestrel/pool` | `0.0.11` | L2 | `@orkestrel/emitter` `^0.0.10` | |
72
+ | `@orkestrel/probe` | `0.0.12` | L5 | `@orkestrel/contract` `^0.0.16`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/lsp` `^0.0.6`, `@orkestrel/mcp` `^0.0.28`, `@orkestrel/queue` `^0.0.12`, `@orkestrel/timeout` `^0.0.9`, `@orkestrel/tool` `^0.0.13` | `oxlint` `^1.80.0`, `typescript` `^6.0.3`, `vitest` `^4.1.11` |
73
+ | `@orkestrel/process` | `0.0.11` | L2 | `@orkestrel/contract` `^0.0.17`, `@orkestrel/emitter` `^0.0.10` | |
74
+ | `@orkestrel/program` | `0.0.12` | L4 | `@orkestrel/contract` `^0.0.16`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/qualifier` `^0.0.13`, `@orkestrel/rater` `^0.0.13`, `@orkestrel/reason` `^0.0.9` | |
75
+ | `@orkestrel/qualifier` | `0.0.13` | L3 | `@orkestrel/contract` `^0.0.16`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/reason` `^0.0.9` | |
76
+ | `@orkestrel/queue` | `0.0.12` | L3 | `@orkestrel/abort` `^0.0.9`, `@orkestrel/contract` `^0.0.16`, `@orkestrel/database` `^0.0.13`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/timeout` `^0.0.9` | |
77
+ | `@orkestrel/rater` | `0.0.13` | L3 | `@orkestrel/reason` `^0.0.9`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/contract` `^0.0.16` | |
78
+ | `@orkestrel/reason` | `0.0.10` | L2 | `@orkestrel/contract` `^0.0.17`, `@orkestrel/emitter` `^0.0.10` | |
79
+ | `@orkestrel/relation` | `0.0.11` | L3 | `@orkestrel/contract` `^0.0.16`, `@orkestrel/database` `^0.0.13`, `@orkestrel/emitter` `^0.0.9` | |
80
+ | `@orkestrel/router` | `0.0.14` | L2 | `@orkestrel/abort` `^0.0.10`, `@orkestrel/emitter` `^0.0.10`, `@orkestrel/contract` `^0.0.17` | |
81
+ | `@orkestrel/scaffold` | `0.0.63` | L3 | `@orkestrel/console` `^0.0.12`, `@orkestrel/contract` `^0.0.16`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/markdown` `^0.0.13`, `@orkestrel/process` `^0.0.10`, `@orkestrel/template` `^0.0.6` | |
82
+ | `@orkestrel/sea` | `0.0.14` | L3 | `@orkestrel/contract` `^0.0.16`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/process` `^0.0.10` | |
83
+ | `@orkestrel/server` | `0.0.18` | L3 | `@orkestrel/abort` `^0.0.9`, `@orkestrel/codec` `^0.0.2`, `@orkestrel/contract` `^0.0.16`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/router` `^0.0.13`, `@orkestrel/timeout` `^0.0.9` | |
84
+ | `@orkestrel/sqlite` | `0.0.11` | L1 | `@orkestrel/contract` `^0.0.17` | |
85
+ | `@orkestrel/sse` | `0.0.7` | L0 | | |
86
+ | `@orkestrel/supervisor` | `0.0.1` | L5 | `@orkestrel/contract` `^0.0.11`, `@orkestrel/database` `^0.0.9`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/workflow` `^0.0.12` | |
87
+ | `@orkestrel/table` | `0.0.5` | L2 | `@orkestrel/contract` `^0.0.17`, `@orkestrel/emitter` `^0.0.10` | |
88
+ | `@orkestrel/template` | `0.0.7` | L2 | `@orkestrel/emitter` `^0.0.10`, `@orkestrel/contract` `^0.0.17` | |
89
+ | `@orkestrel/terminal` | `0.0.14` | L3 | `@orkestrel/console` `^0.0.12`, `@orkestrel/contract` `^0.0.16`, `@orkestrel/database` `^0.0.13`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/form` `^0.0.5`, `@orkestrel/sse` `^0.0.6` | |
90
+ | `@orkestrel/test` | `0.0.14` | L0 | | `vitest` `^4.1.11` |
91
+ | `@orkestrel/timeout` | `0.0.10` | L1 | `@orkestrel/contract` `^0.0.17` | |
92
+ | `@orkestrel/tool` | `0.0.14` | L1 | `@orkestrel/contract` `^0.0.17` | |
93
+ | `@orkestrel/toolbox` | `0.0.12` | L6 | `@orkestrel/agent` `^0.0.20`, `@orkestrel/contract` `^0.0.16`, `@orkestrel/database` `^0.0.13`, `@orkestrel/form` `^0.0.5`, `@orkestrel/relation` `^0.0.11`, `@orkestrel/server` `^0.0.18`, `@orkestrel/terminal` `^0.0.14`, `@orkestrel/tool` `^0.0.13`, `@orkestrel/workflow` `^0.0.17`, `@orkestrel/workspace` `^0.0.7` | |
94
+ | `@orkestrel/websocket` | `0.0.12` | L2 | `@orkestrel/emitter` `^0.0.10` | |
95
+ | `@orkestrel/worker` | `0.0.11` | L4 | `@orkestrel/contract` `^0.0.16`, `@orkestrel/database` `^0.0.13`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/pool` `^0.0.10`, `@orkestrel/queue` `^0.0.12` | |
96
+ | `@orkestrel/workflow` | `0.0.17` | L4 | `@orkestrel/abort` `^0.0.9`, `@orkestrel/budget` `^0.0.9`, `@orkestrel/contract` `^0.0.16`, `@orkestrel/database` `^0.0.13`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/queue` `^0.0.12`, `@orkestrel/timeout` `^0.0.9` | |
97
+ | `@orkestrel/workspace` | `0.0.7` | L3 | `@orkestrel/contract` `^0.0.16`, `@orkestrel/database` `^0.0.13`, `@orkestrel/emitter` `^0.0.9` | |
98
98
 
99
99
  <!-- /orkestrel:catalog -->
100
100
 
@@ -121,10 +121,10 @@ forced `src`/`app` edit or a changed toolchain emit, and the package then bumps
121
121
  account rather than on the dependency's. A superfluous diff obliges nothing.
122
122
 
123
123
  The `Layer` column in the catalog table is the publish round, derived from the runtime
124
- edges in the same row. `L0` depends on nothing else in the fleet and publishes first; each
125
- later layer publishes only after every layer before it is on the registry. A row with no
126
- layer sits in a cycle and cannot be placed in a round at all. Packages in one layer are
127
- independent of each other and may publish in any order within it.
124
+ and peer edges in the same row. `L0` depends on nothing else in the fleet and publishes
125
+ first; each later layer publishes only after every layer before it is on the registry. A
126
+ row with no layer sits in a cycle and cannot be placed in a round at all. Packages in
127
+ one layer are independent of each other and may publish in any order within it.
128
128
 
129
129
  Report a disagreeing pin as a defect, never as drift to tidy later. When packages in
130
130
  one install graph pin different versions of a dependency, npm installs both copies, and the
@@ -42,6 +42,19 @@ subjective and creative lens:
42
42
  5. **Guide voice and product coherence** — documentation reads as the package's
43
43
  current, self-contained human guide and matches the experience the code presents.
44
44
 
45
+ While you hold the objective lane, audit the changed work through these lenses with
46
+ the same weight:
47
+
48
+ 1. **Correctness under adverse orderings** — the adverse conditions
49
+ `.claude/rules/quality.md` § Falsification names.
50
+ 2. **Constraints** — what the declared contracts, the installed declarations, and the
51
+ permission floor permit.
52
+ 3. **Dependency and range truth** — the installed `@orkestrel/*` capabilities and the
53
+ ranges that reach a consumer.
54
+ 4. **Test sufficiency** — the missing seam, the assertion that cannot fail, the probe
55
+ with no control.
56
+ 5. **Mechanical conformance** — the letter of `AGENTS.md` and the applicable rules.
57
+
45
58
  Test a design claim by asking whether the shipped artifact still matches it — a
46
59
  guide, charter, or name that described the work two revisions ago is drift, and
47
60
  that question is what finds it. Anything you cannot settle within your lane becomes
@@ -50,7 +50,7 @@ Use only the centralized files an environment needs.
50
50
  - Every declaration in a centralized file is exported. Fold away a trivial single-use declaration or export/test it; never leave it hidden.
51
51
  - The only permitted non-exported module-scope declarations are in a runtime entrypoint that must be self-contained and cannot import siblings, such as raw source loaded in a worker. Explain that necessity in a comment.
52
52
  - A runtime entry—`src/bin/main.ts`, `app/browser/main.ts`, `app/server/main.ts`—is a fixed name, not a centralized kind file. Both the data rule and the function rule reach it, so it declares no module-scope constant and no module-scope function: it imports what it needs and runs. The preceding self-contained exception covers only an entrypoint that cannot import siblings.
53
- - Perform a cleanup sweep after implementation: no stray implementation-file declarations, non-exported/wrong-kind centralized declarations, prohibited nested declarations, duplicate implementations, compatibility aliases, superfluous wrappers, stale imports/barrel rows, or untested extracted functions.
53
+ - Perform a cleanup pass after implementation: no stray implementation-file declarations, non-exported/wrong-kind centralized declarations, prohibited nested declarations, duplicate implementations, compatibility aliases, superfluous wrappers, stale imports/barrel rows, or untested extracted functions.
54
54
 
55
55
  ## Kind purity
56
56
 
@@ -96,55 +96,61 @@ Use only the centralized files an environment needs.
96
96
  and referenced by name, never to a function expression written in place. Neither file mixes kinds and the
97
97
  route set stays readable as data.
98
98
 
99
- ### What the policy sweep proves
99
+ ### What the policy instruments prove
100
100
 
101
- The fleet policy sweep (`tests/policy.test.ts`, `tests/setupPolicy.ts`) enforces syntactic
102
- placement: a declaration of a given syntactic kind appears only in a file permitted to hold that
103
- kind. It reads declaration syntax and file name, never meaning.
101
+ The `policy` Oxlint plugin (`configs/policy.ts`) enforces syntactic placement: a declaration of a
102
+ given syntactic kind appears only in a file permitted to hold that kind. The fleet policy sweep
103
+ (`tests/policy.test.ts`, `tests/setupPolicy.ts`) enforces what a path or a text reading decides.
104
+ The plugin reads declaration syntax and file name; the sweep reads paths and workspace text;
105
+ neither reads meaning.
104
106
 
105
- - It proves that a module function sits in a function-kind file, that module data sits in a
107
+ - The plugin proves that a module function sits in a function-kind file, that module data sits in a
106
108
  data-kind file, that every centralized declaration is exported, that a class sits in its matching
107
109
  implementation or errors file, and that `constants.ts` declares only UPPER_SNAKE_CASE consts with
108
110
  no bare collection literal.
109
- - It proves that no source, test, config, or script file carries an `eslint-disable` or
111
+ - The sweep proves that no source, test, config, or script file carries an `eslint-disable` or
110
112
  `oxlint-disable` directive.
111
- - It proves that every `.claude/rules/*.md` file has a rule-map row in `AGENTS.md` and that every
112
- row resolves to a file.
113
- - It proves the host portability rules that are path- or text-shaped over the populations
114
- `POLICY_PORTABILITY_GLOB` and `POLICY_PORTABILITY_SOURCE_GLOB` name: no path segment carries a
115
- Windows reserved device name, a character Windows refuses, a trailing dot or space, or a sibling
116
- differing only by case; no `package.json` script names a `.sh` file; and no source in the parsed
117
- population trims a payload before splitting it on `'\n'`, reads `os.EOL`, or imports `EOL` from
118
- `node:os`.
119
- - It cannot write a filename the host refuses. A Windows host rejects `<`, folds a case collision
120
- into one file, and turns `:` into an alternate data stream, so those boundaries are proven from a
121
- path population rather than from written files.
122
- - It does not prove a collection is frozen. It reads the declaration, never the value a call
123
- returns, so `Object.freeze([…])` and any other call initializer are one syntax to it. The freeze
124
- obligation in the earlier kind-purity rules binds regardless; only the bare literal is mechanical.
125
- - It does not tell one function kind from another. Every centralized file that permits functions
126
- reads the same to it apart from the `parse*` and `create*` name forms: `cloners.ts`, `combinators.ts`,
127
- `compilers.ts`, `errors.ts`, `factories.ts`, `handlers.ts`, `helpers.ts`, `inferers.ts`,
128
- `middlewares.ts`, `parsers.ts`, `relations.ts`, `schemas.ts`, `seeders.ts`, `shapers.ts`, and
129
- `validators.ts`. That list is exhaustive, a new function kind joins it, and no later version of
130
- the sweep claims more.
131
- - It reports no `data` violation in `helpers.ts`. The kind rules place a camelCase namespace of
132
- functions there, and a namespace of callables is not separable from a data table by declaration
133
- syntax, so `DATA_EXEMPT_FILES` in `tests/setupPolicy.ts` excludes the file. Ordinary module data
134
- there — `export const RETRIES = 3` — is unreported; the earlier constants rule binds regardless.
135
- - It inspects no ambient declaration file: `.d.ts`, `.d.mts`, and `.d.cts` are all outside its
136
- reach. An ambient declaration file is not a module in the kind table, so it sits outside the
137
- parsed population entirely rather than being exempted from the `type` rule.
138
- - It does not inspect class-expression members. A function assigned inside a class-expression
139
- method is unreported; the earlier functions rule still binds, and cleanup and review enforce it.
140
- - The cleanup sweep and independent review prove kind purity across those files. A helper misfiled
113
+ - The sweep proves that every `.claude/rules/*.md` file has a rule-map row in `AGENTS.md` and that
114
+ every row resolves to a file.
115
+ - The sweep proves the host portability rules that are path- or text-shaped over the population
116
+ `POLICY_PORTABILITY_GLOB` names: no path segment carries a Windows reserved device name, a
117
+ character Windows refuses, a trailing dot or space, or a sibling differing only by case; and no
118
+ `package.json` script names a `.sh` file.
119
+ - The plugin proves, over the population `POLICY_ENDING_GLOBS` names, that no source trims a
120
+ payload before splitting it on `'\n'`, reads `os.EOL`, or imports `EOL` from `node:os`.
121
+ - The sweep cannot write a filename the host refuses. A Windows host rejects `<`, folds a case
122
+ collision into one file, and turns `:` into an alternate data stream, so those boundaries are
123
+ proven from a path population rather than from written files.
124
+ - The plugin does not prove a collection is frozen. It reads the declaration, never the value a
125
+ call returns, so `Object.freeze([…])` and any other call initializer are one syntax to it. The
126
+ freeze obligation in the earlier kind-purity rules binds regardless; only the bare literal is
127
+ mechanical.
128
+ - The plugin does not tell one function kind from another. Every centralized file that permits
129
+ functions reads the same to it apart from the `parse*` and `create*` name forms: `cloners.ts`,
130
+ `combinators.ts`, `compilers.ts`, `errors.ts`, `factories.ts`, `handlers.ts`, `helpers.ts`,
131
+ `inferers.ts`, `middlewares.ts`, `parsers.ts`, `relations.ts`, `schemas.ts`, `seeders.ts`,
132
+ `shapers.ts`, and `validators.ts`. That list is exhaustive, a new function kind joins it, and no
133
+ later version of the plugin claims more.
134
+ - The plugin reports no `data` violation in `helpers.ts`. The kind rules place a camelCase
135
+ namespace of functions there, and a namespace of callables is not separable from a data table by
136
+ declaration syntax, so `DATA_EXEMPT_FILES` in `configs/policy.ts` excludes the file. Ordinary
137
+ module data there — `export const RETRIES = 3` — is unreported; the earlier constants rule binds
138
+ regardless.
139
+ - No placement or line-ending rule inspects an ambient declaration file: `.d.ts`, `.d.mts`, and
140
+ `.d.cts` are outside their reach. The lint population reaches those files, and each of those
141
+ rules refuses the file by its name, because an ambient declaration file is not a module in the
142
+ kind table.
143
+ - The plugin does not inspect class-expression members. A function assigned inside a
144
+ class-expression method is unreported; the earlier functions rule still binds, and cleanup and
145
+ review enforce it.
146
+ - The cleanup pass and independent review prove kind purity across those files. A helper misfiled
141
147
  as a parser, a coercer misfiled as a guard, a compiler misfiled as a factory, and a shaper
142
- misfiled as a cloner are review findings, not red tests.
143
- - It does not decide barrel membership. It parses each file alone and resolves no module, so it
144
- cannot tell whether a declaration is reachable from its barrel. That question belongs to each
145
- package's `tests/guides.test.ts`, which imports the barrel and gets real resolution. Do not add
146
- module resolution here to duplicate it.
147
- - The kind table is mandatory whether or not a test can see the violation.
148
+ misfiled as a cloner are review findings, not instrument diagnostics.
149
+ - The plugin does not decide barrel membership. It parses each file alone and resolves no module,
150
+ so it cannot tell whether a declaration is reachable from its barrel. That question belongs to
151
+ each package's `tests/guides.test.ts`, which imports the barrel and gets real resolution. Do not
152
+ add module resolution here to duplicate it.
153
+ - The kind table is mandatory whether or not an instrument can see the violation.
148
154
 
149
155
  ## Wrapper test
150
156
 
@@ -214,7 +220,7 @@ Store child managers in `#` fields and expose readonly getters typed as their in
214
220
 
215
221
  - A word is either a centralized kind or a domain folder, never both.
216
222
  - A folder named for a centralized kind—`helpers/`, `validators/`, `handlers/`—is that kind's file, not a folder.
217
- - `FUNCTION_DOMAIN_FOLDERS` in the fleet-canon register (`tests/setupPolicy.ts`) registers the
223
+ - `FUNCTION_DOMAIN_FOLDERS` in the fleet-canon register (`configs/policy.ts`) registers the
218
224
  folder paths whose direct modules are function modules. Registration makes a path eligible and
219
225
  fixes the declaration shape checked there; it judges nothing about what a module does.
220
226
  - Never infer a function domain from a folder's name; a camelCase module inside an unregistered
@@ -293,7 +299,7 @@ export * from './greeters/Greeter.js'
293
299
  - Keep everything generic/reusable and free of unrelated-project logic.
294
300
  - Do not expand the capability set without concrete need. Once that capability exists intentionally,
295
301
  its reusable top-level exports follow the earlier barrel rule without a second consumer gate.
296
- - Do not remove structural files because they are currently empty.
302
+ - Do not remove structural files because they are empty.
297
303
  - Prefer the smallest complete implementation that preserves architecture.
298
304
  - No deprecation aliases, compatibility shims, or backward-compatibility branches; update all consumers atomically.
299
305
  - No polling/busy loops or recursive microtasks as architecture. Park idle work on an event/abort wakeup and yield long work in cooperative quanta.
@@ -32,7 +32,24 @@ Documentation is an enforced contract, not explanatory decoration. The Writing r
32
32
  - Every public export is documented.
33
33
  - TypeScript, SCSS, Markdown, tests, and showcase remain aligned.
34
34
  - A parity failure identifies drift; never suppress or weaken the test.
35
- - The TSDoc voice rule governs a doc block; a guide tagline and a Surface-row description are noun phrases.
35
+ - A guide `Summary` cell equals its export's doc-block description paragraph, both read in the form
36
+ the `findDrift` function compares (a `{@link}` tag written as its target's code token, whitespace
37
+ collapsed, a code span's boundary whitespace trimmed); a titled `@example` equals the guide fence
38
+ under the heading of that title; and the README pitch equals the guide's tagline.
39
+ `tests/guides.test.ts` asserts each through the `findDrift` function and the `tagline` method
40
+ that `@orkestrel/guide` exports. Invoke the public `GuideCommand` class there with the package's
41
+ inventory policy and direct host ports, and register the package assertions in the anonymous
42
+ callback passed to its `execute` method. The command owns argument validation, explicit
43
+ rewriting, and remaining-drift reporting. Run `npm run test:guides` for read-only parity
44
+ assertions. Use
45
+ `npm run test:guides -- --to guide` or `npm run test:guides -- --to source` only when choosing
46
+ an explicit rewrite direction. Before adopting the generated command, implement its direct
47
+ entry and rewrite directions in the package-owned `tests/guides.test.ts`. Scaffold must not
48
+ synthesize or overwrite that authored proof. Update each fleet package's authored file before
49
+ its release. Never weaken the gate.
50
+ - The TSDoc voice rule governs a doc block, and a `Summary` cell carries that block's description
51
+ paragraph, so the same voice governs the cell. A guide tagline and a README pitch are noun
52
+ phrases, and each is the blockquote under its file's H1.
36
53
  - A vendored dependency guide is a mirror. Its relative links address the upstream tree and resolve to nothing here, so they are outside local-link parity. Refresh a mirror rather than rewriting it: a rewritten copy is a translation, and no comparison against the fetched bytes can check it.
37
54
  - Falsify a prose claim the way you falsify a code claim. The parity test proves a name exists, never that a sentence about behavior is true, so run the example and read what it returns. A `// false` beside a call that returns `true` is a defect of the same kind as a wrong return value, and it reaches every consumer who installs the package. That proof has a home: `tests/guides.test.ts` executes the flagship fences, per `.claude/rules/tests.md`. An ordered behaviour with no gate is not a gate.
38
55
  Asserting that the sentence appears is not asserting that it is true. `expect(text).toContain('a spawn fault reports null')` passes unchanged when the code starts returning something else, so it guards the documentation's presence and nothing about the behaviour. Where a prose claim about behaviour sits under no fence, add the executed assertion that would break if the claim went false, and keep the substring check only as a presence guard beside it. A row whose close condition names a behaviour does not close on a substring.
@@ -52,6 +52,8 @@ and the form of a conditional skip.
52
52
  host.
53
53
  - Read the temporary directory from `os.tmpdir()`. Never write a `/tmp` literal in source.
54
54
  - Build a `file:` URI with `pathToFileURL`. Never format one from a path string.
55
+ - Convert a `file:` URI to a host path with `fileURLToPath`. Never read `URL.pathname` as
56
+ a host path or strip the scheme, decode escapes, or rewrite a drive prefix by hand.
55
57
 
56
58
  ## Processes and executables
57
59
 
@@ -67,7 +67,7 @@ A review that reads a diff finds what the diff shows. A review that tries to bre
67
67
  - State an instrument's coverage beside its result. A conclusion inherits the instrument's scope, not the question's. An unstated coverage claim is read as complete, and it never is. A search proves something about the paths it walked, so name them.
68
68
  - Match the instrument to the question. A text search reports on text, so a claim about declarations, call sites, or structure needs the compiler or a parser instead. A pattern written for one spelling of a construct reports on that spelling alone. A path check answers relative to the directory it runs from, so resolve the inputs against their own base before reading a miss as a finding.
69
69
  - Name the rival reading the instrument must exclude, and show it reports differently under that reading. Give independent measurements independent state: one counter shared across members reports read order and per-member read count identically, so a result consistent with both measured neither.
70
- - Report a question unanswered rather than answering it with a weaker instrument. A fallback that measures something adjacent returns a confident wrong answer, and nothing downstream can tell that answer from the real one — searching commit messages for a release when the question is where a version changed will match some release, just not the one asked about. Name the substitute and what it actually measures, or say the question is open.
70
+ - Report a question unanswered rather than answering it with a weaker instrument. A fallback that measures something adjacent returns a confident wrong answer, and nothing downstream can tell that answer from the real one — searching commit messages for a release when the question is where a version changed will match some release, but not the one asked about. Name the substitute and what it actually measures, or say the question is open.
71
71
  - State what the controls established and what they did not. An instrument certified only from the inside is trusted exactly where it has never been tested.
72
72
  - Treat a gap between what an instrument says it checks and what it actually matches as a defect in the instrument, not as a documented limit. A recorded blind spot buys trust only when everything outside it is genuinely covered.
73
73
  - Measure the product, not the harness. A recorded baseline that counts something about its own fixture is not evidence about the shipped surface, however often a guide quotes it.
@@ -48,16 +48,16 @@ paths:
48
48
  A proof that covers the workspace instead of one module has a fixed location, so no package invents
49
49
  its own:
50
50
 
51
- | Path | Proves |
52
- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
53
- | `tests/policy.test.ts` | Every source file obeys the syntactic coding and placement law |
54
- | `tests/config.test.ts` | Root configuration resolves its aliases, projects, and outputs, and the `configs/` leaves behind them |
55
- | `tests/guides.test.ts` | Every documented API exists, every public API is documented, and every executable fence returns what the guide says it returns |
56
- | `tests/conformance.test.ts` | Where this package drifts from the official tooling it tracks |
57
- | `tests/distribution.test.ts` | The packed package installs and resolves through its public exports |
58
- | `tests/integration.test.ts` | The package's features work together end to end across environments |
59
- | `tests/setup*.test.ts` | Reusable behavior exported from sibling `tests/setup*.ts` modules works as the workspace's suites require |
60
- | `tests/service/**/*.test.ts` | The live external services this package drives, driven for real |
51
+ | Path | Proves |
52
+ | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
53
+ | `tests/policy.test.ts` | The path- and text-shaped policy laws: mirrors, suppressions, the rule map, filenames, manifest scripts, skills, and bridges |
54
+ | `tests/config.test.ts` | Root configuration resolves its aliases, projects, and outputs, and the `configs/` leaves behind them |
55
+ | `tests/guides.test.ts` | Every documented API exists, every public API is documented, every compared summary, example, and pitch equals its source, and every executable fence returns what the guide says it returns |
56
+ | `tests/conformance.test.ts` | Where this package drifts from the official tooling it tracks |
57
+ | `tests/distribution.test.ts` | The packed package installs and resolves through its public exports |
58
+ | `tests/integration.test.ts` | The package's features work together end to end across environments |
59
+ | `tests/setup*.test.ts` | Reusable behavior exported from sibling `tests/setup*.ts` modules works as the workspace's suites require |
60
+ | `tests/service/**/*.test.ts` | The live external services this package drives, driven for real |
61
61
 
62
62
  - Put each root `tests/setup*.test.ts` proof in the `setup` project. Keep its assertions on
63
63
  exported test-infrastructure behavior: do not duplicate production behavior there, and do not
@@ -105,7 +105,8 @@ The kinds split by which tool has to see the probe:
105
105
 
106
106
  - A **type probe** is read by `tsc`, whose scoped project includes only its own environment, so it
107
107
  lives in the source tree beside what it measures. Delete it before the unit returns; a leaked one
108
- fails the placement sweep, because a probe filename is not a centralized kind file.
108
+ fails the `policy` plugin's placement rules, because a probe filename is not a centralized kind
109
+ file.
109
110
  - A **runtime probe** is collected by a Vitest project, so it lives in `tmp/probe/` and runs through
110
111
  the `probe` project. `tmp/` is ignored by git, so no probe enters a commit by accident, and every
111
112
  test script names its project, so no gate runs the `probe` project.