@iceinvein/agent-skills 0.1.39 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/README.md +18 -2
  2. package/dist/cli/index.js +105 -28
  3. package/package.json +1 -1
  4. package/skills/index.json +14 -2
  5. package/skills/magpie/README.md +2 -1
  6. package/skills/magpie/SKILL.md +138 -45
  7. package/skills/magpie/bin/magpie.ts +43 -0
  8. package/skills/magpie/fixtures/example-pr/brief.json +18 -0
  9. package/skills/magpie/fixtures/fake-gh-nodiff.sh +38 -0
  10. package/skills/magpie/fixtures/fake-gh.sh +14 -0
  11. package/skills/magpie/package.json +1 -1
  12. package/skills/magpie/references/peer-review.md +7 -2
  13. package/skills/magpie/references/scout.md +90 -0
  14. package/skills/magpie/references/specialists.md +117 -6
  15. package/skills/magpie/scripts/__tests__/cli.test.ts +101 -1
  16. package/skills/magpie/scripts/__tests__/dedupe-cmd.test.ts +187 -0
  17. package/skills/magpie/scripts/__tests__/diff-chunks.test.ts +51 -0
  18. package/skills/magpie/scripts/__tests__/filter-diff-preservation.test.ts +54 -0
  19. package/skills/magpie/scripts/__tests__/findings-files.test.ts +35 -0
  20. package/skills/magpie/scripts/__tests__/gh.test.ts +90 -0
  21. package/skills/magpie/scripts/__tests__/git-diff.test.ts +83 -0
  22. package/skills/magpie/scripts/__tests__/helpers/git-fixture.ts +47 -0
  23. package/skills/magpie/scripts/__tests__/path-filter.test.ts +27 -0
  24. package/skills/magpie/scripts/__tests__/preview-cmd.test.ts +16 -0
  25. package/skills/magpie/scripts/__tests__/refresh.test.ts +45 -0
  26. package/skills/magpie/scripts/__tests__/render-cmd.test.ts +173 -1
  27. package/skills/magpie/scripts/__tests__/render-findings.test.ts +151 -1
  28. package/skills/magpie/scripts/__tests__/render-progress.test.ts +42 -0
  29. package/skills/magpie/scripts/__tests__/setup-cmd.test.ts +83 -1
  30. package/skills/magpie/scripts/__tests__/shard.test.ts +165 -0
  31. package/skills/magpie/scripts/__tests__/skill-lint.test.ts +207 -7
  32. package/skills/magpie/scripts/__tests__/types.test.ts +47 -0
  33. package/skills/magpie/scripts/dedupe-cmd.ts +58 -3
  34. package/skills/magpie/scripts/diff-chunks.ts +28 -0
  35. package/skills/magpie/scripts/findings-files.ts +32 -0
  36. package/skills/magpie/scripts/gh.ts +68 -14
  37. package/skills/magpie/scripts/git-diff.ts +111 -0
  38. package/skills/magpie/scripts/path-filter.ts +9 -5
  39. package/skills/magpie/scripts/preview-cmd.ts +11 -1
  40. package/skills/magpie/scripts/refresh.ts +32 -3
  41. package/skills/magpie/scripts/render-cmd.ts +35 -11
  42. package/skills/magpie/scripts/render-findings.ts +78 -2
  43. package/skills/magpie/scripts/render-progress.ts +6 -1
  44. package/skills/magpie/scripts/setup-cmd.ts +38 -1
  45. package/skills/magpie/scripts/shard.ts +171 -0
  46. package/skills/magpie/scripts/status-cmd.ts +7 -4
  47. package/skills/magpie/scripts/types.ts +50 -0
  48. package/skills/magpie/skill.json +2 -2
  49. package/skills/magpie/templates/styles.css +66 -0
  50. package/skills/migrate/README.md +194 -0
  51. package/skills/migrate/SKILL.md +197 -0
  52. package/skills/migrate/bin/migrate +15 -0
  53. package/skills/migrate/bin/migrate.ts +309 -0
  54. package/skills/migrate/biome.json +35 -0
  55. package/skills/migrate/bun.lock +24 -0
  56. package/skills/migrate/docs/architecture.md +294 -0
  57. package/skills/migrate/docs/reference.md +590 -0
  58. package/skills/migrate/fixtures/tiny-express/GROUND-TRUTH.md +39 -0
  59. package/skills/migrate/fixtures/tiny-express/app.js +29 -0
  60. package/skills/migrate/fixtures/tiny-express/cron.js +6 -0
  61. package/skills/migrate/fixtures/tiny-express/reports/daily-users.json +6 -0
  62. package/skills/migrate/fixtures/tiny-express/schema.sql +12 -0
  63. package/skills/migrate/fixtures/tiny-express/settings.json +4 -0
  64. package/skills/migrate/fixtures/tiny-express/views/users.html +9 -0
  65. package/skills/migrate/fixtures/tiny-webforms/Controllers/UsersController.cs +68 -0
  66. package/skills/migrate/fixtures/tiny-webforms/Default.aspx +7 -0
  67. package/skills/migrate/fixtures/tiny-webforms/Default.aspx.cs +14 -0
  68. package/skills/migrate/fixtures/tiny-webforms/GROUND-TRUTH.md +50 -0
  69. package/skills/migrate/fixtures/tiny-webforms/Integrations/BillingClient.cs +16 -0
  70. package/skills/migrate/fixtures/tiny-webforms/Jobs/NightlyDigestJob.cs +33 -0
  71. package/skills/migrate/fixtures/tiny-webforms/Reports/DailyUsers.rdl +11 -0
  72. package/skills/migrate/fixtures/tiny-webforms/Schema.sql +12 -0
  73. package/skills/migrate/fixtures/tiny-webforms/Site.master +16 -0
  74. package/skills/migrate/fixtures/tiny-webforms/Users.aspx +8 -0
  75. package/skills/migrate/fixtures/tiny-webforms/Users.aspx.cs +14 -0
  76. package/skills/migrate/fixtures/tiny-webforms/web.config +10 -0
  77. package/skills/migrate/install.sh +68 -0
  78. package/skills/migrate/package.json +17 -0
  79. package/skills/migrate/references/phases/enumerate.md +291 -0
  80. package/skills/migrate/references/phases/extract.md +652 -0
  81. package/skills/migrate/references/phases/parity.md +275 -0
  82. package/skills/migrate/references/phases/probe.md +135 -0
  83. package/skills/migrate/references/phases/queue.md +242 -0
  84. package/skills/migrate/references/phases/seam.md +416 -0
  85. package/skills/migrate/references/recipes/README.md +116 -0
  86. package/skills/migrate/references/recipes/aspnet.md +287 -0
  87. package/skills/migrate/references/run-ops.md +280 -0
  88. package/skills/migrate/scripts/__tests__/census.test.ts +775 -0
  89. package/skills/migrate/scripts/__tests__/check.test.ts +458 -0
  90. package/skills/migrate/scripts/__tests__/citations.test.ts +156 -0
  91. package/skills/migrate/scripts/__tests__/cli.test.ts +183 -0
  92. package/skills/migrate/scripts/__tests__/concurrency.test.ts +164 -0
  93. package/skills/migrate/scripts/__tests__/config.test.ts +112 -0
  94. package/skills/migrate/scripts/__tests__/e2e-express.test.ts +1093 -0
  95. package/skills/migrate/scripts/__tests__/e2e-webforms.test.ts +1276 -0
  96. package/skills/migrate/scripts/__tests__/e2e.test.ts +320 -0
  97. package/skills/migrate/scripts/__tests__/ids.test.ts +38 -0
  98. package/skills/migrate/scripts/__tests__/import.test.ts +155 -0
  99. package/skills/migrate/scripts/__tests__/init.test.ts +192 -0
  100. package/skills/migrate/scripts/__tests__/leaks.test.ts +176 -0
  101. package/skills/migrate/scripts/__tests__/lock.test.ts +183 -0
  102. package/skills/migrate/scripts/__tests__/paths.test.ts +129 -0
  103. package/skills/migrate/scripts/__tests__/phase-cmd.test.ts +151 -0
  104. package/skills/migrate/scripts/__tests__/phases.test.ts +70 -0
  105. package/skills/migrate/scripts/__tests__/queue.test.ts +475 -0
  106. package/skills/migrate/scripts/__tests__/report.test.ts +150 -0
  107. package/skills/migrate/scripts/__tests__/run-state.test.ts +136 -0
  108. package/skills/migrate/scripts/__tests__/status-reset.test.ts +318 -0
  109. package/skills/migrate/scripts/__tests__/store.test.ts +132 -0
  110. package/skills/migrate/scripts/__tests__/validate.test.ts +54 -0
  111. package/skills/migrate/scripts/census-cmd.ts +109 -0
  112. package/skills/migrate/scripts/census.ts +342 -0
  113. package/skills/migrate/scripts/check-cmd.ts +24 -0
  114. package/skills/migrate/scripts/check.ts +376 -0
  115. package/skills/migrate/scripts/citations.ts +92 -0
  116. package/skills/migrate/scripts/config.ts +237 -0
  117. package/skills/migrate/scripts/ids.ts +31 -0
  118. package/skills/migrate/scripts/import-cmd.ts +141 -0
  119. package/skills/migrate/scripts/init-cmd.ts +118 -0
  120. package/skills/migrate/scripts/leaks.ts +184 -0
  121. package/skills/migrate/scripts/lock.ts +188 -0
  122. package/skills/migrate/scripts/paths.ts +103 -0
  123. package/skills/migrate/scripts/phase-cmd.ts +63 -0
  124. package/skills/migrate/scripts/phases.ts +113 -0
  125. package/skills/migrate/scripts/queue-cmd.ts +98 -0
  126. package/skills/migrate/scripts/queue.ts +258 -0
  127. package/skills/migrate/scripts/report-cmd.ts +47 -0
  128. package/skills/migrate/scripts/report.ts +131 -0
  129. package/skills/migrate/scripts/reset-cmd.ts +120 -0
  130. package/skills/migrate/scripts/status-cmd.ts +52 -0
  131. package/skills/migrate/scripts/store.ts +159 -0
  132. package/skills/migrate/scripts/types.ts +137 -0
  133. package/skills/migrate/scripts/validate.ts +221 -0
  134. package/skills/migrate/skill.json +33 -0
  135. package/skills/migrate/templates/config.toml +27 -0
  136. package/skills/migrate/templates/queue-item.md +17 -0
  137. package/skills/migrate/tsconfig.json +18 -0
  138. package/skills/migrate/uninstall.sh +31 -0
  139. package/skills/sluice/SKILL.md +82 -0
  140. package/skills/sluice/references/deep-channel.md +94 -0
  141. package/skills/sluice/references/finish.md +35 -0
  142. package/skills/sluice/references/intent.md +29 -0
  143. package/skills/sluice/references/review.md +42 -0
  144. package/skills/sluice/references/root-cause.md +38 -0
  145. package/skills/sluice/references/show-or-say.md +36 -0
  146. package/skills/sluice/references/test-first.md +35 -0
  147. package/skills/sluice/references/verify.md +26 -0
  148. package/skills/sluice/skill.json +32 -0
@@ -0,0 +1,294 @@
1
+ # migrate architecture
2
+
3
+ How the skill is built, why it is shaped this way, and how to extend it. For the
4
+ file formats and gate behaviour, see [reference.md](reference.md).
5
+
6
+ ## The boundary rule
7
+
8
+ Three parts, one rule that decides which part gets a given piece of work.
9
+
10
+ - **The skill** (`SKILL.md`, plus the six phase manuals under
11
+ `references/phases/` and the cross-cutting `references/run-ops.md`, all
12
+ landed in Milestone 2) holds judgment: what to look for in a legacy codebase,
13
+ how to decide a requirement is confirmed rather than inferred, when to
14
+ escalate to the queue.
15
+ - **The CLI** (`bin/`, `scripts/`) holds anything that must not be
16
+ self-reported: the store, the arithmetic, the gates.
17
+ - **The references** are loaded just in time, so a run pays only for the phase
18
+ it is in.
19
+
20
+ The rule is the point of the whole design. The method this generalises asked
21
+ agents to hand-write lines like `table census: 45 tables in source (43 ddl + 40
22
+ orm, deduped), 44 in ledger, 1 added` and to hand-verify the sum. Those numbers
23
+ were wrong twice on a real campaign. A number the tool can compute should never
24
+ be a discipline an agent must maintain, so anything countable moved into the
25
+ CLI, and anything requiring judgment stayed in the prompt.
26
+
27
+ When adding something, ask which side it belongs on. If an agent could get it
28
+ wrong and nobody would notice, it belongs in the CLI.
29
+
30
+ ## Module map
31
+
32
+ ```
33
+ bin/migrate bash wrapper, resolves symlinks, execs bun
34
+ bin/migrate.ts subcommand table, flag parsing, exit codes, central error guard
35
+
36
+ scripts/
37
+ types.ts every row and record type; discriminated unions
38
+ ids.ts element id derivation and slug validation
39
+ paths.ts store paths, store-root lookup, containment guard
40
+ config.ts config.toml load and write, TOML escaping
41
+ store.ts JSONL read, atomic write, id upsert, file readers
42
+ lock.ts store lock: serialises the read-modify-write in import,
43
+ census, phase --status and reset
44
+ phases.ts phases.json state and committed batches
45
+ validate.ts per-row shape validation shared by import and check
46
+ census.ts census kinds, balance and bounds invariants, subject identity
47
+ citations.ts resolves src refs against the source tree
48
+ leaks.ts scans artifacts and git history for env values
49
+ queue.ts queue item parsing and grammar
50
+ check.ts composes the ten gates into a violation list
51
+ report.ts markdown rendering
52
+ *-cmd.ts one per subcommand; argument handling and orchestration
53
+ ```
54
+
55
+ The `-cmd.ts` split exists so the logic is testable without spawning a process.
56
+ `check.ts` returns a violation list; `check-cmd.ts` decides how to print it and
57
+ what to exit with. Tests exercise both, and the end-to-end test drives the real
58
+ binary so argument parsing and exit codes are covered too.
59
+
60
+ ## Why the store is split
61
+
62
+ Tabular artifacts are JSONL rows the CLI owns. Prose artifacts stay markdown.
63
+
64
+ Rows, because counts should be derived rather than authored. The moment an
65
+ element ledger is a hand-maintained markdown table, its totals are a claim
66
+ rather than a fact, and pipe characters in free text start breaking the table.
67
+
68
+ Markdown, because queue items, seam evidence and parity-basis notes are prose an
69
+ owner reads and reviews in a diff. A queue item is written to be adjudicated by
70
+ a human in about a minute; a JSON blob is worse at that job.
71
+
72
+ `migrate report` renders the rows into markdown views on demand, so humans get a
73
+ readable artifact without anyone hand-maintaining one.
74
+
75
+ ## Invariants worth knowing before you change anything
76
+
77
+ **Nothing writes into the source tree.** `assertNotUnderSource` in `paths.ts`
78
+ guards every writer. It resolves real paths, so a symlinked `.migrate` cannot
79
+ sneak a write in, and it handles case-insensitive volumes. `citations.ts` shares
80
+ the same `isContained` predicate for the read side.
81
+
82
+ If you add a writer, it has to reach that guard, and there are exactly two ways
83
+ it can. Almost every writer gets there for free by going through `writeRows` or
84
+ `writeAtomically`, which call `assertNotUnderSource` themselves; prefer that,
85
+ since it also buys temp-plus-rename. Prefer it especially because the second
86
+ way is a command checking its own targets up front, and exactly one command
87
+ does: `init-cmd.ts` calls `assertNotUnderSource` on `config.toml`, `queue/` and
88
+ `.gitignore` before it creates anything, which covers all three of `init`'s
89
+ writes at once. None of those three calls the guard for itself:
90
+
91
+ - `config.ts`'s `writeConfig` is a plain `writeFile`. Deliberate: whether the
92
+ config it renders is usable is `init`'s decision, not this function's, and
93
+ routing it through the guard would make the hand-edited config those
94
+ downstream guards exist for unconstructible through that API.
95
+ - The `.gitignore` **append** is `appendFile`. It could not be `writeAtomically`
96
+ in any case: an append is not a whole-file write, so there is nothing to
97
+ temp-and-rename.
98
+ - The `.gitignore` **create** is a plain `writeFile`. This one could have gone
99
+ through `writeAtomically` and does not; it is a small, brand-new file in a
100
+ fresh target, and the up-front check already covers containment. Worth
101
+ revisiting if that file ever grows.
102
+
103
+ So the guard holds for every writer, but only the helpers give atomicity, and
104
+ `init`'s three writes have none of it. What is not negotiable is the guard: a
105
+ raw `writeFile` reached by neither route is the bug this rule exists to stop,
106
+ and `init` shipped exactly that bug in Milestone 2.
107
+
108
+ **Writes are atomic.** `writeAtomically` writes to a randomly-named sibling then
109
+ renames, and cleans up the temp file on failure without masking the original
110
+ error. A fixed temp name was tried first and lost data under concurrent writes.
111
+
112
+ **The read-modify-write around a store file is lock-serialised.** Four commands
113
+ do one. `import` and `census` each read a whole store file, upsert or replace
114
+ rows, and rewrite the whole file; both also commit a batch into `phases.json`,
115
+ via `recordBatch`, inside the same lock. `phase --status` does its own
116
+ read-modify-write on `phases.json` alone. `reset` does the widest one of the
117
+ four, and which files it touches depends on the phase named: `elements.jsonl`
118
+ and `census.jsonl` for `enumerate`, `capabilities.jsonl` plus removal of
119
+ `seam.json` and `seam.md` for `seam`, `requirements.jsonl` and `census.jsonl`
120
+ and `elements.jsonl` again for `extract`, `deltas.jsonl` and
121
+ `requirements.jsonl` for `parity`, and `phases.json` for every phase including
122
+ the four that clear nothing else. Atomic writes alone do not make any of that
123
+ safe under a concurrent caller: two callers can still read the same base and
124
+ one rename can still discard the other's rows.
125
+
126
+ `withStoreLock` is **not reentrant**, so a helper called from inside a critical
127
+ section must not take it. That is why `savePhases` and `recordBatch` are
128
+ lock-free while `setPhaseStatus` is not, and why `reset-cmd.ts` calls
129
+ `savePhases` rather than `setPhaseStatus` to move its phase back to `pending`.
130
+ Check this before adding a call inside any of the four.
131
+
132
+ `census-cmd.ts` orders its two writes deliberately: it commits the batch into
133
+ `phases.json` first, and writes `census.jsonl` second. If the process is
134
+ interrupted between them, the result is an orphan batch entry, never an orphan
135
+ census row, and that is the safer half-write of the two. Nothing treats
136
+ `phases.json`'s batch list as meaningful except as corroboration for a census
137
+ row that is also expected to exist, so an orphan batch entry is inert. An
138
+ orphan census row would be worse: a record naming a batch that was never
139
+ actually committed, which is exactly the mismatch the run-state gate exists to
140
+ catch.
141
+
142
+ `lock.ts`'s `withStoreLock` wraps each of these four write paths in one lock
143
+ file for the whole store (`.migrate/.lock`, `O_EXCL` create, bounded retry with
144
+ backoff). It distinguishes a lock file that is merely absent or momentarily
145
+ empty (never counted against a corruption budget) from one that is genuinely
146
+ corrupt (five consecutive unreadable reads), re-reads before declaring a
147
+ holder's pid stale (the holder may have released between reads), and checks
148
+ its deadline unconditionally rather than only while a live holder is in view.
149
+ A lock failure raises `LockError`, which every caller maps to exit 3, not the
150
+ generic exit-2 path in `bin/migrate.ts`'s guard; `--force-unlock` removes a
151
+ lock believed stale before retrying.
152
+
153
+ **No timestamps in the store.** Git supplies chronology. An injected clock makes
154
+ tests flake, and there is no field a resume path needs it for.
155
+
156
+ **Rows upsert by id, preserving position.** `upsertRows` counts net changes
157
+ against a snapshot, so an intra-batch duplicate cannot inflate the count.
158
+ Comparison uses a key-order-insensitive serialization, since two equivalent rows
159
+ should not read as a change.
160
+
161
+ **Every command exits deliberately.** `bin/migrate.ts` wraps handler invocation
162
+ in a guard that turns any thrown `Error` into a clean one-line diagnostic at
163
+ exit 2. Do not add a local `try/catch` that duplicates it, and do not let a
164
+ handler print a stack trace. This guard was added late, after three separate
165
+ tasks each shipped the same crash class, and it covers every future command.
166
+
167
+ ## How to extend
168
+
169
+ ### Add a subcommand
170
+
171
+ Add an entry to `HANDLERS` in `bin/migrate.ts` using the existing lazy
172
+ `await import('../scripts/<name>-cmd.ts')` style, which keeps startup cheap. Use
173
+ the shared `readFlag` helper so a missing flag value is a usage error at 2 like
174
+ everywhere else. Add the verb to the `USAGE` string; `cli.test.ts` asserts the
175
+ help text lists every subcommand.
176
+
177
+ Most commands resolve their root with `findStoreRoot(process.cwd())` and exit 2
178
+ when there is none. `init` is the deliberate exception, because it creates the
179
+ store.
180
+
181
+ ### Add a gate
182
+
183
+ Gates live in `check.ts` and push `{ gate, message }` onto one list.
184
+
185
+ 1. Add the gate name to `GATE_ORDER`, which fixes its position in the report.
186
+ 2. Push violations that name the specific offending row, path or id. An
187
+ aggregate "check failed" is never acceptable; the message is what an agent
188
+ acts on without a human.
189
+ 3. If the gate is expensive, make it opt-in behind a flag like `--leaks`, and
190
+ have `check-cmd.ts` pass it through. If it is cheap enough to want on by
191
+ default instead, follow citations: on unless the caller passes
192
+ `--no-citations`, so an orchestrator does not have to remember to ask for
193
+ it.
194
+ 4. Add tests for both directions. A gate that produces false failures is worse
195
+ than no gate, because it makes `check` ignorable.
196
+
197
+ ### Add a census kind
198
+
199
+ 1. Add the variant to the `Census` union in `types.ts`.
200
+ 2. Add its balance rule to `balanceOf` in `census.ts`. The message must state the
201
+ arithmetic so a reviewer can check it without re-deriving anything.
202
+ 3. Add its subject identity to `censusKey`, so re-recording replaces rather than
203
+ stacks.
204
+ 4. Extend `validateCensus` for the new fields.
205
+
206
+ ### Add a surface type
207
+
208
+ Surface types are configuration, not code. Declare them in `[surfaces].types`.
209
+ Element id prefixes derive from the surface name by stripping a trailing `s`, so
210
+ a surface that is already singular but ends in `s` needs a
211
+ `[surfaces.singular]` override.
212
+
213
+ This is the main source-genericity lever: a COBOL source declares `programs`,
214
+ `copybooks`, `jcl-jobs` and `bms-maps` and every downstream gate follows.
215
+
216
+ ## Testing conventions
217
+
218
+ `bun test` from `skills/migrate`. Also `bun run lint` (biome) and
219
+ `bun run typecheck` (tsc). All three must be clean.
220
+
221
+ **A test that passes against a broken implementation is treated as a defect**,
222
+ not a minor style issue. Two shipped during this milestone and both were caught
223
+ only by mutation. When you add a regression test, verify it fails against the
224
+ unfixed code: revert the source file with
225
+ `git checkout <sha> -- <path>`, run the test, observe the failure, then restore
226
+ with `git checkout HEAD -- <path>`.
227
+
228
+ **Never use `git stash` for that.** The stash stack is shared with other
229
+ worktrees and other sessions, and popping it can destroy someone else's work.
230
+
231
+ **Attack the code, do not just read it.** Nearly every real defect found in this
232
+ milestone came from constructing hostile input and running it: padding a census
233
+ with cosmetic duplicates, defeating containment with a symlink, breaking a
234
+ markdown table with a pipe, injecting a TOML key through a scope string. None
235
+ was visible in a diff.
236
+
237
+ Fixtures live in `fixtures/`, two of them, each with its ground truth committed
238
+ beside it in `GROUND-TRUTH.md`.
239
+
240
+ `tiny-express` is a small Express/Node app: twelve elements across all eight
241
+ default surfaces. Its stack (`express`) matches no file in
242
+ `references/recipes/`, so it is the contract-only path, where the enumerating
243
+ agent derives its own two directions per surface. `tiny-webforms` is a small
244
+ ASP.NET Web Forms app, sixteen elements across the same eight surfaces, and its
245
+ stack matches `references/recipes/aspnet.md`, so it is that recipe's run
246
+ against committed code rather than against the throwaway trees it was written
247
+ against.
248
+
249
+ Three tests drive them, all copying the fixture to a temp directory and running
250
+ the real binary as a subprocess so argument parsing and exit codes are covered
251
+ too:
252
+
253
+ - `e2e.test.ts` takes a deliberately narrow slice of `tiny-express` (two
254
+ surfaces, three elements) and shows the gate failing on unaccounted elements
255
+ before it passes. That arc is the point: a test that only demonstrates the
256
+ passing state would be worth much less.
257
+ - `e2e-express.test.ts` and `e2e-webforms.test.ts` drive the whole of each
258
+ fixture probe through queue: `init`, `import`, `census`, `phase`, `queue
259
+ add`, `queue list`, and `check`, ending green at `migrate check --phase
260
+ queue` and then asserting plain `migrate check` fails on exactly `adjudicate`
261
+ and `handoff`. Both parse `GROUND-TRUTH.md` for their element rows rather
262
+ than hand-copying them, so fixture and test cannot drift, and both close on a
263
+ mutation showing the terminus assertion is load-bearing.
264
+
265
+ When you change a fixture, the tests that read it will tell you; when you
266
+ change a manual the tests follow (`seam.md`'s clustering procedure,
267
+ `extract.md`'s citation rules, `parity.md`'s test-path template), they will
268
+ not, so re-run them deliberately.
269
+
270
+ ## Known limits
271
+
272
+ **Free-text uniqueness.** Census `skipped` element names are compared after
273
+ trimming and case folding, and no further. Element names name real things in a
274
+ legacy source, so `orders.` and `orders` can legitimately differ, and nothing
275
+ can distinguish `orders` from `order`. This is documented in `census.ts` beside
276
+ the check. The padding route that mattered more, an inflated `in_ledger`, is
277
+ closed by reconciliation against the real element count.
278
+
279
+ **Concurrency is closed, not open.** This used to say `recordBatch`'s
280
+ read-modify-write on `phases.json` could lose committed-batch history under
281
+ concurrent importers, and that the preferred fix was an append-only batch log
282
+ matching how `elements.jsonl` already worked. Both halves of that were wrong
283
+ by the time a concurrent caller actually existed: `elements.jsonl` was never
284
+ append-only (`import` reads, upserts, and rewrites the whole file, the same
285
+ shape as `phases.json`), so an append-only log would have been the odd
286
+ mechanism out rather than a pattern already proven in the store. Milestone 2
287
+ closed this with a store lock instead; see the invariant above.
288
+
289
+ **`capabilities.jsonl` has no import path.** It is hand-written, which is why the
290
+ `refs` gate checks for duplicate slugs explicitly.
291
+
292
+ **Bun 1.3.14 TOML quirk.** `Bun.TOML.parse` swaps the named `\t` and `\f`
293
+ escapes: parsing `a = "x\ty"` yields codepoint 12. `config.ts` works around it
294
+ with explicit unicode escapes and a comment; re-verify on a Bun upgrade.