@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.
- package/README.md +18 -2
- package/dist/cli/index.js +105 -28
- package/package.json +1 -1
- package/skills/index.json +14 -2
- package/skills/magpie/README.md +2 -1
- package/skills/magpie/SKILL.md +138 -45
- package/skills/magpie/bin/magpie.ts +43 -0
- package/skills/magpie/fixtures/example-pr/brief.json +18 -0
- package/skills/magpie/fixtures/fake-gh-nodiff.sh +38 -0
- package/skills/magpie/fixtures/fake-gh.sh +14 -0
- package/skills/magpie/package.json +1 -1
- package/skills/magpie/references/peer-review.md +7 -2
- package/skills/magpie/references/scout.md +90 -0
- package/skills/magpie/references/specialists.md +117 -6
- package/skills/magpie/scripts/__tests__/cli.test.ts +101 -1
- package/skills/magpie/scripts/__tests__/dedupe-cmd.test.ts +187 -0
- package/skills/magpie/scripts/__tests__/diff-chunks.test.ts +51 -0
- package/skills/magpie/scripts/__tests__/filter-diff-preservation.test.ts +54 -0
- package/skills/magpie/scripts/__tests__/findings-files.test.ts +35 -0
- package/skills/magpie/scripts/__tests__/gh.test.ts +90 -0
- package/skills/magpie/scripts/__tests__/git-diff.test.ts +83 -0
- package/skills/magpie/scripts/__tests__/helpers/git-fixture.ts +47 -0
- package/skills/magpie/scripts/__tests__/path-filter.test.ts +27 -0
- package/skills/magpie/scripts/__tests__/preview-cmd.test.ts +16 -0
- package/skills/magpie/scripts/__tests__/refresh.test.ts +45 -0
- package/skills/magpie/scripts/__tests__/render-cmd.test.ts +173 -1
- package/skills/magpie/scripts/__tests__/render-findings.test.ts +151 -1
- package/skills/magpie/scripts/__tests__/render-progress.test.ts +42 -0
- package/skills/magpie/scripts/__tests__/setup-cmd.test.ts +83 -1
- package/skills/magpie/scripts/__tests__/shard.test.ts +165 -0
- package/skills/magpie/scripts/__tests__/skill-lint.test.ts +207 -7
- package/skills/magpie/scripts/__tests__/types.test.ts +47 -0
- package/skills/magpie/scripts/dedupe-cmd.ts +58 -3
- package/skills/magpie/scripts/diff-chunks.ts +28 -0
- package/skills/magpie/scripts/findings-files.ts +32 -0
- package/skills/magpie/scripts/gh.ts +68 -14
- package/skills/magpie/scripts/git-diff.ts +111 -0
- package/skills/magpie/scripts/path-filter.ts +9 -5
- package/skills/magpie/scripts/preview-cmd.ts +11 -1
- package/skills/magpie/scripts/refresh.ts +32 -3
- package/skills/magpie/scripts/render-cmd.ts +35 -11
- package/skills/magpie/scripts/render-findings.ts +78 -2
- package/skills/magpie/scripts/render-progress.ts +6 -1
- package/skills/magpie/scripts/setup-cmd.ts +38 -1
- package/skills/magpie/scripts/shard.ts +171 -0
- package/skills/magpie/scripts/status-cmd.ts +7 -4
- package/skills/magpie/scripts/types.ts +50 -0
- package/skills/magpie/skill.json +2 -2
- package/skills/magpie/templates/styles.css +66 -0
- package/skills/migrate/README.md +194 -0
- package/skills/migrate/SKILL.md +197 -0
- package/skills/migrate/bin/migrate +15 -0
- package/skills/migrate/bin/migrate.ts +309 -0
- package/skills/migrate/biome.json +35 -0
- package/skills/migrate/bun.lock +24 -0
- package/skills/migrate/docs/architecture.md +294 -0
- package/skills/migrate/docs/reference.md +590 -0
- package/skills/migrate/fixtures/tiny-express/GROUND-TRUTH.md +39 -0
- package/skills/migrate/fixtures/tiny-express/app.js +29 -0
- package/skills/migrate/fixtures/tiny-express/cron.js +6 -0
- package/skills/migrate/fixtures/tiny-express/reports/daily-users.json +6 -0
- package/skills/migrate/fixtures/tiny-express/schema.sql +12 -0
- package/skills/migrate/fixtures/tiny-express/settings.json +4 -0
- package/skills/migrate/fixtures/tiny-express/views/users.html +9 -0
- package/skills/migrate/fixtures/tiny-webforms/Controllers/UsersController.cs +68 -0
- package/skills/migrate/fixtures/tiny-webforms/Default.aspx +7 -0
- package/skills/migrate/fixtures/tiny-webforms/Default.aspx.cs +14 -0
- package/skills/migrate/fixtures/tiny-webforms/GROUND-TRUTH.md +50 -0
- package/skills/migrate/fixtures/tiny-webforms/Integrations/BillingClient.cs +16 -0
- package/skills/migrate/fixtures/tiny-webforms/Jobs/NightlyDigestJob.cs +33 -0
- package/skills/migrate/fixtures/tiny-webforms/Reports/DailyUsers.rdl +11 -0
- package/skills/migrate/fixtures/tiny-webforms/Schema.sql +12 -0
- package/skills/migrate/fixtures/tiny-webforms/Site.master +16 -0
- package/skills/migrate/fixtures/tiny-webforms/Users.aspx +8 -0
- package/skills/migrate/fixtures/tiny-webforms/Users.aspx.cs +14 -0
- package/skills/migrate/fixtures/tiny-webforms/web.config +10 -0
- package/skills/migrate/install.sh +68 -0
- package/skills/migrate/package.json +17 -0
- package/skills/migrate/references/phases/enumerate.md +291 -0
- package/skills/migrate/references/phases/extract.md +652 -0
- package/skills/migrate/references/phases/parity.md +275 -0
- package/skills/migrate/references/phases/probe.md +135 -0
- package/skills/migrate/references/phases/queue.md +242 -0
- package/skills/migrate/references/phases/seam.md +416 -0
- package/skills/migrate/references/recipes/README.md +116 -0
- package/skills/migrate/references/recipes/aspnet.md +287 -0
- package/skills/migrate/references/run-ops.md +280 -0
- package/skills/migrate/scripts/__tests__/census.test.ts +775 -0
- package/skills/migrate/scripts/__tests__/check.test.ts +458 -0
- package/skills/migrate/scripts/__tests__/citations.test.ts +156 -0
- package/skills/migrate/scripts/__tests__/cli.test.ts +183 -0
- package/skills/migrate/scripts/__tests__/concurrency.test.ts +164 -0
- package/skills/migrate/scripts/__tests__/config.test.ts +112 -0
- package/skills/migrate/scripts/__tests__/e2e-express.test.ts +1093 -0
- package/skills/migrate/scripts/__tests__/e2e-webforms.test.ts +1276 -0
- package/skills/migrate/scripts/__tests__/e2e.test.ts +320 -0
- package/skills/migrate/scripts/__tests__/ids.test.ts +38 -0
- package/skills/migrate/scripts/__tests__/import.test.ts +155 -0
- package/skills/migrate/scripts/__tests__/init.test.ts +192 -0
- package/skills/migrate/scripts/__tests__/leaks.test.ts +176 -0
- package/skills/migrate/scripts/__tests__/lock.test.ts +183 -0
- package/skills/migrate/scripts/__tests__/paths.test.ts +129 -0
- package/skills/migrate/scripts/__tests__/phase-cmd.test.ts +151 -0
- package/skills/migrate/scripts/__tests__/phases.test.ts +70 -0
- package/skills/migrate/scripts/__tests__/queue.test.ts +475 -0
- package/skills/migrate/scripts/__tests__/report.test.ts +150 -0
- package/skills/migrate/scripts/__tests__/run-state.test.ts +136 -0
- package/skills/migrate/scripts/__tests__/status-reset.test.ts +318 -0
- package/skills/migrate/scripts/__tests__/store.test.ts +132 -0
- package/skills/migrate/scripts/__tests__/validate.test.ts +54 -0
- package/skills/migrate/scripts/census-cmd.ts +109 -0
- package/skills/migrate/scripts/census.ts +342 -0
- package/skills/migrate/scripts/check-cmd.ts +24 -0
- package/skills/migrate/scripts/check.ts +376 -0
- package/skills/migrate/scripts/citations.ts +92 -0
- package/skills/migrate/scripts/config.ts +237 -0
- package/skills/migrate/scripts/ids.ts +31 -0
- package/skills/migrate/scripts/import-cmd.ts +141 -0
- package/skills/migrate/scripts/init-cmd.ts +118 -0
- package/skills/migrate/scripts/leaks.ts +184 -0
- package/skills/migrate/scripts/lock.ts +188 -0
- package/skills/migrate/scripts/paths.ts +103 -0
- package/skills/migrate/scripts/phase-cmd.ts +63 -0
- package/skills/migrate/scripts/phases.ts +113 -0
- package/skills/migrate/scripts/queue-cmd.ts +98 -0
- package/skills/migrate/scripts/queue.ts +258 -0
- package/skills/migrate/scripts/report-cmd.ts +47 -0
- package/skills/migrate/scripts/report.ts +131 -0
- package/skills/migrate/scripts/reset-cmd.ts +120 -0
- package/skills/migrate/scripts/status-cmd.ts +52 -0
- package/skills/migrate/scripts/store.ts +159 -0
- package/skills/migrate/scripts/types.ts +137 -0
- package/skills/migrate/scripts/validate.ts +221 -0
- package/skills/migrate/skill.json +33 -0
- package/skills/migrate/templates/config.toml +27 -0
- package/skills/migrate/templates/queue-item.md +17 -0
- package/skills/migrate/tsconfig.json +18 -0
- package/skills/migrate/uninstall.sh +31 -0
- package/skills/sluice/SKILL.md +82 -0
- package/skills/sluice/references/deep-channel.md +94 -0
- package/skills/sluice/references/finish.md +35 -0
- package/skills/sluice/references/intent.md +29 -0
- package/skills/sluice/references/review.md +42 -0
- package/skills/sluice/references/root-cause.md +38 -0
- package/skills/sluice/references/show-or-say.md +36 -0
- package/skills/sluice/references/test-first.md +35 -0
- package/skills/sluice/references/verify.md +26 -0
- 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.
|