@iceinvein/agent-skills 0.3.0 → 0.5.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 +1 -1
- package/package.json +1 -1
- package/skills/index.json +3 -3
- package/skills/migrate/README.md +35 -23
- package/skills/migrate/SKILL.md +75 -15
- package/skills/migrate/bin/migrate.ts +90 -0
- package/skills/migrate/docs/architecture.md +61 -26
- package/skills/migrate/docs/reference.md +53 -8
- package/skills/migrate/fixtures/fake-gh.ts +113 -0
- package/skills/migrate/fixtures/flow-target/docs/WORK.md +12 -0
- package/skills/migrate/fixtures/flow-target/docs/modernisation/capability-map/.gitkeep +0 -0
- package/skills/migrate/fixtures/flow-target/tools/flow/src/cli.ts +156 -0
- package/skills/migrate/package.json +1 -1
- package/skills/migrate/references/phases/adjudicate.md +161 -0
- package/skills/migrate/references/phases/handoff.md +220 -0
- package/skills/migrate/references/phases/probe.md +2 -2
- package/skills/migrate/references/phases/queue.md +21 -14
- package/skills/migrate/references/run-ops.md +17 -13
- package/skills/migrate/scripts/__tests__/adapter-flow.test.ts +290 -0
- package/skills/migrate/scripts/__tests__/adapter-github.test.ts +232 -0
- package/skills/migrate/scripts/__tests__/adapter-markdown.test.ts +183 -0
- package/skills/migrate/scripts/__tests__/adjudicate.test.ts +332 -0
- package/skills/migrate/scripts/__tests__/assumptions.test.ts +179 -0
- package/skills/migrate/scripts/__tests__/coverage.test.ts +192 -0
- package/skills/migrate/scripts/__tests__/e2e-express.test.ts +167 -7
- package/skills/migrate/scripts/__tests__/e2e-webforms.test.ts +9 -4
- package/skills/migrate/scripts/__tests__/forecast.test.ts +280 -0
- package/skills/migrate/scripts/__tests__/gates-handoff.test.ts +309 -0
- package/skills/migrate/scripts/__tests__/handoff-cmd.test.ts +308 -0
- package/skills/migrate/scripts/__tests__/handoff-order.test.ts +156 -0
- package/skills/migrate/scripts/adapters/flow.ts +280 -0
- package/skills/migrate/scripts/adapters/github.ts +260 -0
- package/skills/migrate/scripts/adapters/markdown.ts +175 -0
- package/skills/migrate/scripts/adjudicate-cmd.ts +243 -0
- package/skills/migrate/scripts/assumptions.ts +188 -0
- package/skills/migrate/scripts/check.ts +119 -320
- package/skills/migrate/scripts/coverage-cmd.ts +86 -0
- package/skills/migrate/scripts/coverage.ts +151 -0
- package/skills/migrate/scripts/dates.ts +17 -0
- package/skills/migrate/scripts/forecast-cmd.ts +124 -0
- package/skills/migrate/scripts/forecast.ts +264 -0
- package/skills/migrate/scripts/gates/adjudication.ts +30 -0
- package/skills/migrate/scripts/gates/census.ts +107 -0
- package/skills/migrate/scripts/gates/citations.ts +11 -0
- package/skills/migrate/scripts/gates/context.ts +76 -0
- package/skills/migrate/scripts/gates/coverage.ts +22 -0
- package/skills/migrate/scripts/gates/deltas.ts +15 -0
- package/skills/migrate/scripts/gates/handoff.ts +145 -0
- package/skills/migrate/scripts/gates/leaks.ts +11 -0
- package/skills/migrate/scripts/gates/parity.ts +15 -0
- package/skills/migrate/scripts/gates/queue.ts +9 -0
- package/skills/migrate/scripts/gates/refs.ts +97 -0
- package/skills/migrate/scripts/gates/run-state.ts +67 -0
- package/skills/migrate/scripts/gates/source.ts +28 -0
- package/skills/migrate/scripts/handoff-cmd.ts +186 -0
- package/skills/migrate/scripts/handoff.ts +330 -0
- package/skills/migrate/scripts/paths.ts +4 -0
- package/skills/migrate/scripts/types.ts +43 -0
- package/skills/migrate/scripts/validate.ts +12 -0
- package/skills/migrate/skill.json +2 -2
- package/skills/migrate/templates/forecast-assumptions.md +59 -0
- package/skills/sluice/SKILL.md +24 -7
- package/skills/sluice/references/deep-channel.md +197 -43
- package/skills/sluice/references/meter.md +7 -0
- package/skills/sluice/references/review.md +15 -7
- package/skills/sluice/scripts/run-stats.sh +22 -3
- package/skills/sluice/skill.json +1 -1
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ Auto-detects Claude Code, Cursor, Codex, or Gemini CLI. Use `--tool claude` to t
|
|
|
28
28
|
|
|
29
29
|
| Skill | What it does |
|
|
30
30
|
|-------|--------------|
|
|
31
|
-
| **migrate** | Walks a legacy codebase through probe, enumerate, seam, extract, parity, and
|
|
31
|
+
| **migrate** | Walks a legacy codebase through probe, enumerate, seam, extract, parity, queue, adjudicate and handoff: measured surface coverage, an empirically derived capability seam, cited requirements, a parity plan, a batch decision queue, and work items emitted to markdown, GitHub or a flow target with coverage and forecast read back. Bundles a Bun CLI that enforces the coverage arithmetic and phase ordering instead of trusting it. |
|
|
32
32
|
|
|
33
33
|
### Code Architecture
|
|
34
34
|
|
package/package.json
CHANGED
package/skills/index.json
CHANGED
|
@@ -225,9 +225,9 @@
|
|
|
225
225
|
},
|
|
226
226
|
{
|
|
227
227
|
"name": "migrate",
|
|
228
|
-
"description": "Source-agnostic legacy migration mapping. Walks a legacy codebase through probe, enumerate, seam, extract, parity, and
|
|
228
|
+
"description": "Source-agnostic legacy migration mapping. Walks a legacy codebase through probe, enumerate, seam, extract, parity, queue, adjudicate and handoff, building an auditable requirements ledger with mandatory citations and a `migrate check` gate in place of self-reported completeness. Use when the user asks to migrate, re-specify, replatform, or map a legacy system onto a new stack, to hand mapped requirements to a delivery team, or to resume, check, report, or forecast a mapping run already under way.",
|
|
229
229
|
"type": "prompt",
|
|
230
|
-
"version": "0.
|
|
230
|
+
"version": "0.3.0"
|
|
231
231
|
},
|
|
232
232
|
{
|
|
233
233
|
"name": "module-secret-auditor",
|
|
@@ -283,7 +283,7 @@
|
|
|
283
283
|
"name": "sluice",
|
|
284
284
|
"description": "Routes work by change shape into four channels (bypass, fast, main, deep) and applies only the rules each channel needs, so a one-line fix does not pay the cost of a multi-subsystem build. Carries seven rules as one-liners in the router and the full treatment in references read only on friction, and closes each run with a ledger read out of the session transcript: elapsed, tools, tokens, and what every dispatched agent cost. Claude Code only; conflicts with the superpowers plugin.",
|
|
285
285
|
"type": "prompt",
|
|
286
|
-
"version": "0.
|
|
286
|
+
"version": "0.2.0"
|
|
287
287
|
},
|
|
288
288
|
{
|
|
289
289
|
"name": "temporal-coupling-detector",
|
package/skills/migrate/README.md
CHANGED
|
@@ -8,9 +8,9 @@ directions per lens, derives a capability seam empirically rather than by
|
|
|
8
8
|
guesswork, extracts cited functional requirements, plans parity against the
|
|
9
9
|
source, and routes every ambiguity to a batch decision queue for a human to
|
|
10
10
|
adjudicate. The coverage arithmetic, citation resolution, and phase ordering
|
|
11
|
-
are enforced by a bundled Bun CLI instead of being self-reported.
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
are enforced by a bundled Bun CLI instead of being self-reported. Adjudicate
|
|
12
|
+
and handoff complete the walkthrough, and `migrate coverage` and `migrate
|
|
13
|
+
forecast` read delivery back through the medium handoff emitted into.
|
|
14
14
|
|
|
15
15
|
## Using it
|
|
16
16
|
|
|
@@ -31,14 +31,16 @@ that phase is current so a run never pays for prose it does not need yet.
|
|
|
31
31
|
| 3 | Extract | `references/phases/extract.md` | `requirements.jsonl`, attribute/rule-sweep/closer census records, terminal element dispositions |
|
|
32
32
|
| 4 | Parity | `references/phases/parity.md` | `deltas.jsonl` and a parity plan on every non-queued requirement |
|
|
33
33
|
| 5 | Queue | `references/phases/queue.md` | Queue items carrying evidence, options and a recommendation for anything ambiguous |
|
|
34
|
-
| 6 | Adjudicate |
|
|
35
|
-
| 7 | Handoff |
|
|
36
|
-
|
|
37
|
-
A run
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
34
|
+
| 6 | Adjudicate | `references/phases/adjudicate.md` | A ruling on every queue item, recorded in its own frontmatter |
|
|
35
|
+
| 7 | Handoff | `references/phases/handoff.md` | Work items in a delivery medium, plus `handoff.json` recording what was emitted |
|
|
36
|
+
|
|
37
|
+
A run goes probe through handoff, and plain `migrate check`, with no
|
|
38
|
+
`--phase`, gates every one of them: its exit 0 is what "the migration is
|
|
39
|
+
mapped" means. `migrate check --phase <p>` is the mid-run form, and the two
|
|
40
|
+
gates that describe phases 6 and 7 are phase-scoped so they stay silent
|
|
41
|
+
until the checked terminus reaches them. Once handoff has run, `migrate
|
|
42
|
+
coverage` and `migrate forecast` read delivery back through the same adapter
|
|
43
|
+
that emitted the work. `references/run-ops.md` covers what applies across every phase
|
|
42
44
|
rather than any one of them: subagent dispatch, the batch-checkpoint
|
|
43
45
|
discipline, and what happens when two agents contend for the store lock.
|
|
44
46
|
|
|
@@ -69,10 +71,11 @@ directory. See `references/recipes/README.md` for the exact file shape.
|
|
|
69
71
|
migrate check --phase <current-phase>
|
|
70
72
|
```
|
|
71
73
|
|
|
72
|
-
bounds the run-state gate at that phase
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
bounds the run-state gate at that phase, and skips the two gates that
|
|
75
|
+
describe phases the terminus has not reached (`adjudication` and `handoff`).
|
|
76
|
+
The remaining nine always read the whole store, so a coverage or census gap
|
|
77
|
+
past your current phase still fails on its own gate regardless of `--phase`.
|
|
78
|
+
Citations are checked by default; pass `--no-citations` to skip that gate.
|
|
76
79
|
|
|
77
80
|
## Fixtures
|
|
78
81
|
|
|
@@ -92,12 +95,14 @@ end:
|
|
|
92
95
|
|
|
93
96
|
`scripts/__tests__/e2e-express.test.ts` and
|
|
94
97
|
`scripts/__tests__/e2e-webforms.test.ts` copy the respective fixture to a
|
|
95
|
-
temp directory and drive the real CLI as a subprocess
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
`
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
temp directory and drive the real CLI as a subprocess through `init`,
|
|
99
|
+
`import`, `census`, `phase`, `queue add`, `queue list`, and `check`,
|
|
100
|
+
reconciling every row against the fixture's ground truth.
|
|
101
|
+
`e2e-webforms.test.ts` runs probe through queue against the `aspnet` recipe.
|
|
102
|
+
`e2e-express.test.ts` carries on past it, through `adjudicate`, `handoff`,
|
|
103
|
+
`coverage` and `forecast`, and closes on plain `migrate check` at exit 0:
|
|
104
|
+
the whole point of the unbounded gate is that some real run has to be able
|
|
105
|
+
to reach it.
|
|
101
106
|
|
|
102
107
|
Both show gates in both directions. Failing before they pass: the mid-run check
|
|
103
108
|
after enumerate names the three closer records extract has not written yet, and
|
|
@@ -110,7 +115,7 @@ asserts the gate that should catch it does.
|
|
|
110
115
|
|
|
111
116
|
- **[docs/reference.md](docs/reference.md)** is what you need to drive the CLI:
|
|
112
117
|
the batch-file and census formats with worked examples, the row schemas and
|
|
113
|
-
their grammars, what each of the
|
|
118
|
+
their grammars, what each of the twelve gates enforces, and the exit-code
|
|
114
119
|
convention. Ships with the installed skill.
|
|
115
120
|
- **[docs/architecture.md](docs/architecture.md)** is for working on the skill
|
|
116
121
|
itself: the module map, the rule that decides what belongs in the CLI rather
|
|
@@ -133,6 +138,8 @@ The store lives at `.migrate/` in the target repo and is committed.
|
|
|
133
138
|
| `.migrate/phases.json` | object | per-phase status, batches, resume pointers |
|
|
134
139
|
| `.migrate/seam.md` | prose | validator scripts and their raw output |
|
|
135
140
|
| `.migrate/parity-basis.md` | prose | runnable-versus-source-only detection evidence |
|
|
141
|
+
| `.migrate/handoff.json` | What handoff emitted, the refs that make a re-run idempotent, and the forecast basis |
|
|
142
|
+
| `.migrate/forecast-assumptions.md` | Owner-attested forecast inputs; `migrate forecast` refuses without it |
|
|
136
143
|
| `.migrate/queue/q-<slug>.md` | prose | evidence, options, recommendation |
|
|
137
144
|
| `.migrate/.env` | secrets | runtime-lens credentials, gitignored |
|
|
138
145
|
| `docs/migrate/*.md` | generated | human-readable views, written by `migrate report` |
|
|
@@ -151,6 +158,10 @@ The store lives at `.migrate/` in the target repo and is committed.
|
|
|
151
158
|
| `migrate check [--phase <p>] [--no-citations] [--leaks]` | Runs the gates |
|
|
152
159
|
| `migrate status` | Phase state, counts, resume pointer |
|
|
153
160
|
| `migrate reset --phase <phase>` | Clears one phase's derived rows and returns it to `pending` |
|
|
161
|
+
| `migrate adjudicate [<id>] [--ruling <text>] [--force]` | Prints the review sheet, or records one ruling |
|
|
162
|
+
| `migrate handoff [--adapter <name>] [--dry-run]` | Emits the requirements as work items |
|
|
163
|
+
| `migrate coverage [--adapter <name>]` | Built versus confirmed, read back through the adapter |
|
|
164
|
+
| `migrate forecast [--adapter <name>]` | Projects remaining work from measured throughput |
|
|
154
165
|
| `migrate report [--out <dir>]` | Renders markdown views |
|
|
155
166
|
|
|
156
167
|
Run `migrate --help` for the same list from the CLI itself.
|
|
@@ -163,7 +174,8 @@ list at the same time. `import` and `census` touch `phases.json` incidentally,
|
|
|
163
174
|
each moving a phase to `running` (unless it is already `done`) when they record
|
|
164
175
|
a batch. Nothing else writes it at all.
|
|
165
176
|
|
|
166
|
-
A lock failure on `import`, `census`, `phase --status`,
|
|
177
|
+
A lock failure on `import`, `census`, `phase --status`, `reset`, `adjudicate`
|
|
178
|
+
or `handoff` exits `3`;
|
|
167
179
|
pass `--force-unlock` once you have confirmed no other agent is actually
|
|
168
180
|
writing.
|
|
169
181
|
|
package/skills/migrate/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: migrate
|
|
3
|
-
description: Source-agnostic legacy migration mapping. Walks a legacy codebase through probe, enumerate, seam, extract, parity, and
|
|
3
|
+
description: Source-agnostic legacy migration mapping. Walks a legacy codebase through probe, enumerate, seam, extract, parity, queue, adjudicate and handoff, building an auditable requirements ledger with mandatory citations and a `migrate check` gate in place of self-reported completeness. Use when the user asks to migrate, re-specify, replatform, or map a legacy system onto a new stack, to hand mapped requirements to a delivery team, or to resume, check, report, or forecast a mapping run already under way.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# migrate
|
|
@@ -129,29 +129,89 @@ migrate phase queue --status done
|
|
|
129
129
|
|
|
130
130
|
### 6. Adjudicate
|
|
131
131
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
132
|
+
Produces a ruling on every queue item: `status: adjudicated` plus a `ruling`
|
|
133
|
+
line, written into the item's own frontmatter.
|
|
134
|
+
|
|
135
|
+
Read `references/phases/adjudicate.md` before dispatching anything.
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
migrate adjudicate
|
|
139
|
+
migrate adjudicate <id> --ruling "<text>"
|
|
140
|
+
migrate import <elements|reqs|deltas> <batch.json>
|
|
141
|
+
migrate phase adjudicate --status done
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Run `migrate adjudicate` with no arguments first: it prints the review sheet,
|
|
145
|
+
severity first, each item carrying the first line of its recommendation, which
|
|
146
|
+
is what makes one pass over the whole queue possible.
|
|
147
|
+
|
|
148
|
+
The verb writes queue frontmatter and nothing else. A ruling's consequence in
|
|
149
|
+
the store (an element's disposition, a requirement's confidence or parity) goes
|
|
150
|
+
through `migrate import`, exactly as in phases 3 and 4, so the row files keep
|
|
151
|
+
one writer and one validation path. The command prints that next step on every
|
|
152
|
+
success because it is easy to believe the ruling did it for you.
|
|
136
153
|
|
|
137
154
|
### 7. Handoff
|
|
138
155
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
156
|
+
Produces the requirements as work items in whatever the delivery team uses,
|
|
157
|
+
one work item per capability in dependency order, plus `.migrate/handoff.json`
|
|
158
|
+
recording what was emitted and the forecast basis.
|
|
159
|
+
|
|
160
|
+
Read `references/phases/handoff.md` before dispatching anything.
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
migrate handoff --dry-run
|
|
164
|
+
migrate handoff [--adapter <markdown|github|flow>]
|
|
165
|
+
migrate phase handoff --status done
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Three adapters: `markdown` (the default, a roadmap plus a file per capability),
|
|
169
|
+
`github` (a milestone per capability, an issue per requirement), and `flow`
|
|
170
|
+
(a Nexus `stack` target's capability map, and `docs/WORK.md` where it exists).
|
|
171
|
+
|
|
172
|
+
Handoff refuses while anything is unresolved and names every blocker at once:
|
|
173
|
+
the gate with citations and leaks both on, any queue item still open, and any
|
|
174
|
+
requirement blocked by one. Blocked means a `queued` confidence or a sub-high
|
|
175
|
+
`rubric` parity whose queue item is **still open**; once that item is
|
|
176
|
+
adjudicated the requirement stops blocking, even though its confidence field
|
|
177
|
+
still reads `queued`.
|
|
178
|
+
|
|
179
|
+
`--dry-run` writes nothing at all, `handoff.json` included, so it is safe to
|
|
180
|
+
run against a store you are still working on.
|
|
181
|
+
|
|
182
|
+
## Reading progress back
|
|
183
|
+
|
|
184
|
+
Once handoff has run, these two re-read delivery through the same adapter that
|
|
185
|
+
emitted the work. They are the only part of this tool meant to be run
|
|
186
|
+
repeatedly after the mapping run ends.
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
migrate coverage
|
|
190
|
+
migrate forecast
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
`coverage` divides built by **confirmed** requirements, reports the
|
|
194
|
+
non-confirmed exclusions separately, and names the evidence it read. `forecast`
|
|
195
|
+
needs an owner-attested `.migrate/forecast-assumptions.md`, copied from
|
|
196
|
+
`templates/forecast-assumptions.md`, and refuses without one; it labels every
|
|
197
|
+
scenario as measured or as an owner target so an aspiration never reads as a
|
|
198
|
+
fact.
|
|
142
199
|
|
|
143
200
|
## Checking as you go
|
|
144
201
|
|
|
145
202
|
Run `migrate check --phase <current>` after every batch. It bounds the
|
|
146
|
-
run-state gate at that phase; the
|
|
147
|
-
store, so a coverage or census gap past
|
|
148
|
-
own gate regardless of `--phase`.
|
|
203
|
+
run-state gate at that phase; the nine gates that are neither run-state nor
|
|
204
|
+
phase-scoped always read the whole store, so a coverage or census gap past
|
|
205
|
+
your current phase still fails on its own gate regardless of `--phase`.
|
|
149
206
|
|
|
150
207
|
Run plain `migrate check` only when claiming the whole migration is complete:
|
|
151
|
-
with no `--phase`, it gates every phase through `handoff
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
208
|
+
with no `--phase`, it gates every phase through `handoff`, and its exit 0 is
|
|
209
|
+
what "the migration is mapped" means.
|
|
210
|
+
|
|
211
|
+
Two of the twelve gates are phase-scoped. `adjudication` and `handoff` describe
|
|
212
|
+
phases 6 and 7, so they stay silent until the checked terminus reaches them;
|
|
213
|
+
that is what keeps `migrate check --phase queue` usable for a whole mid-run
|
|
214
|
+
campaign instead of red from the first batch.
|
|
155
215
|
|
|
156
216
|
```
|
|
157
217
|
migrate check --phase queue
|
|
@@ -18,12 +18,20 @@ Subcommands:
|
|
|
18
18
|
queue add <file.md> Add a queue item
|
|
19
19
|
queue list [--open] List queue items, severity first
|
|
20
20
|
queue show <id> Print one queue item
|
|
21
|
+
adjudicate [<id>] [--ruling <text>] [--force] [--force-unlock]
|
|
22
|
+
Print the review sheet, or record one ruling
|
|
21
23
|
check [--phase <p>] [--no-citations] [--leaks]
|
|
22
24
|
Run the gates; without --phase, exit 0 means
|
|
23
25
|
the whole migration is complete
|
|
24
26
|
status Phase state, counts, resume pointer
|
|
25
27
|
reset --phase <phase> [--force-unlock]
|
|
26
28
|
Clear one phase's derived rows
|
|
29
|
+
handoff [--adapter <name>] [--dry-run] [--force-unlock]
|
|
30
|
+
Emit the ratified requirements as work items
|
|
31
|
+
coverage [--adapter <name>] Built versus confirmed, read back through
|
|
32
|
+
the adapter that emitted the work
|
|
33
|
+
forecast [--adapter <name>] Project remaining work from measured
|
|
34
|
+
throughput and attested assumptions
|
|
27
35
|
report [--out <dir>] Render markdown views
|
|
28
36
|
--version Print the migrate version
|
|
29
37
|
--help Show this message`
|
|
@@ -166,6 +174,38 @@ const HANDLERS: Record<string, Handler> = {
|
|
|
166
174
|
const { runQueue } = await import('../scripts/queue-cmd.ts')
|
|
167
175
|
return runQueue({ root, args })
|
|
168
176
|
},
|
|
177
|
+
adjudicate: async (args) => {
|
|
178
|
+
const ruling = readFlag(args, '--ruling')
|
|
179
|
+
if (ruling.error) {
|
|
180
|
+
process.stderr.write(`adjudicate: ${ruling.error}\n`)
|
|
181
|
+
return 2
|
|
182
|
+
}
|
|
183
|
+
// Same rule as `phase`: a flag sitting in the first positional slot leaves
|
|
184
|
+
// the id undefined, which would silently downgrade a write to a listing
|
|
185
|
+
// nobody asked for. Any write-intent flag with no id to apply it to is
|
|
186
|
+
// that mistake, so it is refused rather than serviced.
|
|
187
|
+
const id = args[0]?.startsWith('--') ? undefined : args[0]
|
|
188
|
+
if (!id && (ruling.value !== undefined || args.includes('--force'))) {
|
|
189
|
+
process.stderr.write(
|
|
190
|
+
'adjudicate: want <id> before --ruling/--force, as in `adjudicate q-x --ruling "..."`\n',
|
|
191
|
+
)
|
|
192
|
+
return 2
|
|
193
|
+
}
|
|
194
|
+
const { findStoreRoot } = await import('../scripts/paths.ts')
|
|
195
|
+
const root = await findStoreRoot(process.cwd())
|
|
196
|
+
if (!root) {
|
|
197
|
+
process.stderr.write('adjudicate: no .migrate store found above the cwd\n')
|
|
198
|
+
return 2
|
|
199
|
+
}
|
|
200
|
+
const { runAdjudicate } = await import('../scripts/adjudicate-cmd.ts')
|
|
201
|
+
return runAdjudicate({
|
|
202
|
+
root,
|
|
203
|
+
...(id ? { id } : {}),
|
|
204
|
+
...(ruling.value !== undefined ? { ruling: ruling.value } : {}),
|
|
205
|
+
...(args.includes('--force') ? { force: true } : {}),
|
|
206
|
+
...(args.includes('--force-unlock') ? { forceUnlock: true } : {}),
|
|
207
|
+
})
|
|
208
|
+
},
|
|
169
209
|
check: async (args) => {
|
|
170
210
|
const phase = readFlag(args, '--phase')
|
|
171
211
|
if (phase.error) {
|
|
@@ -227,6 +267,56 @@ const HANDLERS: Record<string, Handler> = {
|
|
|
227
267
|
const { runReset } = await import('../scripts/reset-cmd.ts')
|
|
228
268
|
return runReset({ root, phase, ...(forceUnlock ? { forceUnlock: true } : {}) })
|
|
229
269
|
},
|
|
270
|
+
handoff: async (args) => {
|
|
271
|
+
const adapter = readFlag(args, '--adapter')
|
|
272
|
+
if (adapter.error) {
|
|
273
|
+
process.stderr.write(`handoff: ${adapter.error}\n`)
|
|
274
|
+
return 2
|
|
275
|
+
}
|
|
276
|
+
const { findStoreRoot } = await import('../scripts/paths.ts')
|
|
277
|
+
const root = await findStoreRoot(process.cwd())
|
|
278
|
+
if (!root) {
|
|
279
|
+
process.stderr.write('handoff: no .migrate store found above the cwd\n')
|
|
280
|
+
return 2
|
|
281
|
+
}
|
|
282
|
+
const { runHandoff } = await import('../scripts/handoff-cmd.ts')
|
|
283
|
+
return runHandoff({
|
|
284
|
+
root,
|
|
285
|
+
...(adapter.value ? { adapter: adapter.value } : {}),
|
|
286
|
+
...(args.includes('--dry-run') ? { dryRun: true } : {}),
|
|
287
|
+
...(args.includes('--force-unlock') ? { forceUnlock: true } : {}),
|
|
288
|
+
})
|
|
289
|
+
},
|
|
290
|
+
coverage: async (args) => {
|
|
291
|
+
const adapter = readFlag(args, '--adapter')
|
|
292
|
+
if (adapter.error) {
|
|
293
|
+
process.stderr.write(`coverage: ${adapter.error}\n`)
|
|
294
|
+
return 2
|
|
295
|
+
}
|
|
296
|
+
const { findStoreRoot } = await import('../scripts/paths.ts')
|
|
297
|
+
const root = await findStoreRoot(process.cwd())
|
|
298
|
+
if (!root) {
|
|
299
|
+
process.stderr.write('coverage: no .migrate store found above the cwd\n')
|
|
300
|
+
return 2
|
|
301
|
+
}
|
|
302
|
+
const { runCoverage } = await import('../scripts/coverage-cmd.ts')
|
|
303
|
+
return runCoverage({ root, ...(adapter.value ? { adapter: adapter.value } : {}) })
|
|
304
|
+
},
|
|
305
|
+
forecast: async (args) => {
|
|
306
|
+
const adapter = readFlag(args, '--adapter')
|
|
307
|
+
if (adapter.error) {
|
|
308
|
+
process.stderr.write(`forecast: ${adapter.error}\n`)
|
|
309
|
+
return 2
|
|
310
|
+
}
|
|
311
|
+
const { findStoreRoot } = await import('../scripts/paths.ts')
|
|
312
|
+
const root = await findStoreRoot(process.cwd())
|
|
313
|
+
if (!root) {
|
|
314
|
+
process.stderr.write('forecast: no .migrate store found above the cwd\n')
|
|
315
|
+
return 2
|
|
316
|
+
}
|
|
317
|
+
const { runForecast } = await import('../scripts/forecast-cmd.ts')
|
|
318
|
+
return runForecast({ root, ...(adapter.value ? { adapter: adapter.value } : {}) })
|
|
319
|
+
},
|
|
230
320
|
report: async (args) => {
|
|
231
321
|
const result = readFlag(args, '--out')
|
|
232
322
|
if (result.error) {
|
|
@@ -7,9 +7,9 @@ file formats and gate behaviour, see [reference.md](reference.md).
|
|
|
7
7
|
|
|
8
8
|
Three parts, one rule that decides which part gets a given piece of work.
|
|
9
9
|
|
|
10
|
-
- **The skill** (`SKILL.md`, plus the
|
|
11
|
-
`references/phases/` and the cross-cutting `references/run-ops.md
|
|
12
|
-
|
|
10
|
+
- **The skill** (`SKILL.md`, plus the eight phase manuals under
|
|
11
|
+
`references/phases/` and the cross-cutting `references/run-ops.md`) holds
|
|
12
|
+
judgment: what to look for in a legacy codebase,
|
|
13
13
|
how to decide a requirement is confirmed rather than inferred, when to
|
|
14
14
|
escalate to the queue.
|
|
15
15
|
- **The CLI** (`bin/`, `scripts/`) holds anything that must not be
|
|
@@ -40,14 +40,22 @@ scripts/
|
|
|
40
40
|
config.ts config.toml load and write, TOML escaping
|
|
41
41
|
store.ts JSONL read, atomic write, id upsert, file readers
|
|
42
42
|
lock.ts store lock: serialises the read-modify-write in import,
|
|
43
|
-
census, phase --status and
|
|
43
|
+
census, phase --status, reset, adjudicate and handoff
|
|
44
44
|
phases.ts phases.json state and committed batches
|
|
45
45
|
validate.ts per-row shape validation shared by import and check
|
|
46
46
|
census.ts census kinds, balance and bounds invariants, subject identity
|
|
47
47
|
citations.ts resolves src refs against the source tree
|
|
48
48
|
leaks.ts scans artifacts and git history for env values
|
|
49
49
|
queue.ts queue item parsing and grammar
|
|
50
|
-
check.ts
|
|
50
|
+
check.ts loads the store once, runs GATE_ORDER, applies the
|
|
51
|
+
phase-scope rule; holds no gate logic itself
|
|
52
|
+
gates/context.ts GateContext: everything a gate may read
|
|
53
|
+
gates/<gate>.ts one module per gate, twelve of them
|
|
54
|
+
handoff.ts adapter contract, dependency order, preflight, handoff.json
|
|
55
|
+
adapters/*.ts markdown, github, flow: one per delivery medium
|
|
56
|
+
coverage.ts built over confirmed, per capability
|
|
57
|
+
assumptions.ts parses and validates forecast-assumptions.md
|
|
58
|
+
forecast.ts measured velocities, demand, scenario projection
|
|
51
59
|
report.ts markdown rendering
|
|
52
60
|
*-cmd.ts one per subcommand; argument handling and orchestration
|
|
53
61
|
```
|
|
@@ -109,7 +117,7 @@ and `init` shipped exactly that bug in Milestone 2.
|
|
|
109
117
|
renames, and cleans up the temp file on failure without masking the original
|
|
110
118
|
error. A fixed temp name was tried first and lost data under concurrent writes.
|
|
111
119
|
|
|
112
|
-
**The read-modify-write around a store file is lock-serialised.**
|
|
120
|
+
**The read-modify-write around a store file is lock-serialised.** Six commands
|
|
113
121
|
do one. `import` and `census` each read a whole store file, upsert or replace
|
|
114
122
|
rows, and rewrite the whole file; both also commit a batch into `phases.json`,
|
|
115
123
|
via `recordBatch`, inside the same lock. `phase --status` does its own
|
|
@@ -127,7 +135,7 @@ one rename can still discard the other's rows.
|
|
|
127
135
|
section must not take it. That is why `savePhases` and `recordBatch` are
|
|
128
136
|
lock-free while `setPhaseStatus` is not, and why `reset-cmd.ts` calls
|
|
129
137
|
`savePhases` rather than `setPhaseStatus` to move its phase back to `pending`.
|
|
130
|
-
Check this before adding a call inside any of
|
|
138
|
+
Check this before adding a call inside any of them.
|
|
131
139
|
|
|
132
140
|
`census-cmd.ts` orders its two writes deliberately: it commits the batch into
|
|
133
141
|
`phases.json` first, and writes `census.jsonl` second. If the process is
|
|
@@ -139,7 +147,7 @@ orphan census row would be worse: a record naming a batch that was never
|
|
|
139
147
|
actually committed, which is exactly the mismatch the run-state gate exists to
|
|
140
148
|
catch.
|
|
141
149
|
|
|
142
|
-
`lock.ts`'s `withStoreLock` wraps each of these
|
|
150
|
+
`lock.ts`'s `withStoreLock` wraps each of these write paths in one lock
|
|
143
151
|
file for the whole store (`.migrate/.lock`, `O_EXCL` create, bounded retry with
|
|
144
152
|
backoff). It distinguishes a lock file that is merely absent or momentarily
|
|
145
153
|
empty (never counted against a corruption budget) from one that is genuinely
|
|
@@ -180,20 +188,43 @@ store.
|
|
|
180
188
|
|
|
181
189
|
### Add a gate
|
|
182
190
|
|
|
183
|
-
Gates live
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
191
|
+
Gates live one per module under `scripts/gates/`, each exporting
|
|
192
|
+
`export const gate: Gate` over the shared `GateContext` that `check.ts` builds
|
|
193
|
+
once per run. A gate is a pure function of that record: it may not read the
|
|
194
|
+
store itself, which is what keeps the store parsed exactly once no matter how
|
|
195
|
+
many gates consult it, and what stops a gate depending on another having run
|
|
196
|
+
first. Add the name to `GATE_ORDER`, the module to `GATES`, and, only if the
|
|
197
|
+
gate describes work a mid-run terminus has not reached, an entry to
|
|
198
|
+
`GATE_PHASE`.
|
|
199
|
+
|
|
200
|
+
1. Write `scripts/gates/<name>.ts` exporting `export const gate: Gate`, and
|
|
201
|
+
add the name to `GATE_ORDER`, which fixes its position in the report, and
|
|
202
|
+
the module to `GATES`.
|
|
203
|
+
2. Return violations that name the specific offending row, path or id. An
|
|
187
204
|
aggregate "check failed" is never acceptable; the message is what an agent
|
|
188
205
|
acts on without a human.
|
|
189
|
-
3. If the gate is expensive, make it opt-in behind a flag like `--leaks`,
|
|
190
|
-
have `check-cmd.ts` pass it through. If it is cheap
|
|
191
|
-
default instead, follow citations: on unless the caller
|
|
192
|
-
`--no-citations
|
|
193
|
-
|
|
194
|
-
|
|
206
|
+
3. If the gate is expensive, make it opt-in behind a flag like `--leaks`, read
|
|
207
|
+
off `GateContext`, and have `check-cmd.ts` pass it through. If it is cheap
|
|
208
|
+
enough to want on by default instead, follow citations: on unless the caller
|
|
209
|
+
passes `--no-citations`.
|
|
210
|
+
4. Only if the gate describes work a mid-run terminus has not reached, add an
|
|
211
|
+
entry to `GATE_PHASE`. This is a narrow escape hatch, not the norm: nine of
|
|
212
|
+
the twelve gates read the whole store regardless of `--phase`, and a tenth,
|
|
213
|
+
run-state, is bounded by the terminus rather than skipped. A defect is a
|
|
214
|
+
defect whenever it is found.
|
|
215
|
+
5. Add tests for both directions. A gate that produces false failures is worse
|
|
195
216
|
than no gate, because it makes `check` ignorable.
|
|
196
217
|
|
|
218
|
+
### Add a handoff adapter
|
|
219
|
+
|
|
220
|
+
`scripts/adapters/<name>.ts` exporting an `Adapter`, registered in the
|
|
221
|
+
`ADAPTERS` map in `handoff-cmd.ts`, `coverage-cmd.ts` and `forecast-cmd.ts`.
|
|
222
|
+
An adapter reaches the world only through `HandoffInput`'s `root`, `gitBin` and
|
|
223
|
+
`ghBin`, which is what makes `github` testable against `fixtures/fake-gh.ts`
|
|
224
|
+
and `flow` against `fixtures/flow-target/`. `apply()` must be idempotent, and
|
|
225
|
+
must not destroy progress the medium is carrying: the markdown adapter reads
|
|
226
|
+
its own roadmap before regenerating it for exactly that reason.
|
|
227
|
+
|
|
197
228
|
### Add a census kind
|
|
198
229
|
|
|
199
230
|
1. Add the variant to the `Census` union in `types.ts`.
|
|
@@ -219,7 +250,7 @@ This is the main source-genericity lever: a COBOL source declares `programs`,
|
|
|
219
250
|
`bun run typecheck` (tsc). All three must be clean.
|
|
220
251
|
|
|
221
252
|
**A test that passes against a broken implementation is treated as a defect**,
|
|
222
|
-
not a minor style issue. Two shipped during
|
|
253
|
+
not a minor style issue. Two shipped during Milestone 2 and both were caught
|
|
223
254
|
only by mutation. When you add a regression test, verify it fails against the
|
|
224
255
|
unfixed code: revert the source file with
|
|
225
256
|
`git checkout <sha> -- <path>`, run the test, observe the failure, then restore
|
|
@@ -254,13 +285,17 @@ too:
|
|
|
254
285
|
surfaces, three elements) and shows the gate failing on unaccounted elements
|
|
255
286
|
before it passes. That arc is the point: a test that only demonstrates the
|
|
256
287
|
passing state would be worth much less.
|
|
257
|
-
- `e2e-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
288
|
+
- `e2e-webforms.test.ts` drives the whole of `tiny-webforms` probe through
|
|
289
|
+
queue: `init`, `import`, `census`, `phase`, `queue add`, `queue list`, and
|
|
290
|
+
`check`, ending green at `migrate check --phase queue`.
|
|
291
|
+
- `e2e-express.test.ts` carries on past that terminus through `adjudicate`,
|
|
292
|
+
`handoff`, `coverage` and `forecast`, and closes on plain `migrate check` at
|
|
293
|
+
exit 0. Some real run has to be able to reach the unbounded gate, or it
|
|
294
|
+
asserts nothing.
|
|
295
|
+
|
|
296
|
+
Both parse `GROUND-TRUTH.md` for their element rows rather than hand-copying
|
|
297
|
+
them, so fixture and test cannot drift, and both close on a mutation showing the
|
|
298
|
+
terminus assertion is load-bearing.
|
|
264
299
|
|
|
265
300
|
When you change a fixture, the tests that read it will tell you; when you
|
|
266
301
|
change a manual the tests follow (`seam.md`'s clustering procedure,
|