@wemuda/launchrail 1.8.0 → 1.9.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.
@@ -3,21 +3,23 @@
3
3
  //
4
4
  // The Ralph loop as a deterministic workflow: the plan, the frontier bookkeeping,
5
5
  // and every intermediate report live in script variables — not in any context window —
6
- // so long or wide runs cannot compact away their own state. The watchable, checkpointed
7
- // variant of the same loop is the launch-ralph skill; the two share one policy block,
8
- // and a policy change belongs in both places (ADR-0005, field-revised by ADR-0010).
6
+ // so long or wide runs cannot compact away their own state. This is the engine for
7
+ // every multi-ticket run (ADR-0022); the launch-ralph skill carries the same policy
8
+ // block as the supervisor's contract and the declared-exception watchable mode, and
9
+ // a policy change belongs in both places (ADR-0005, field-revised by ADR-0010, ADR-0022).
9
10
  export const meta = {
10
11
  name: 'ralph',
11
12
  description: 'Autonomous Ralph loop: implement ready tickets with fresh-context subagents, verification-gated',
12
13
  whenToUse:
13
- 'Run the Ralph implementation loop over the ticket backlog when the dependency graph is wide or the run is long. Scope a run via args: { only: [9, 10], width: 2 }, just [9, 10], or { max: 5 } to stop after 5 verified merges ("the next five" — the frontier picks which, in dependency order). Args must be JSON — resolve any natural-language scope to ticket numbers and a cap before launching. For a watchable, checkpointed run (or when something is already going wrong), use the launch-ralph skill instead.',
14
+ 'The engine for any multi-ticket Ralph run. Scope a run via args: { only: [9, 10], width: 2 }, just [9, 10], or { max: 5 } to stop after 5 verified merges ("the next five" — the frontier picks which, in dependency order). Declare the integration target with { target: "spec/44-mvp" } to consolidate the campaign onto that branch (default branch untouched; release later with one PR) — omit it to merge each ticket into the default branch. { canary: true } holds width at 1 until the first verified merge. Args must be JSON — resolve any natural-language scope to ticket numbers, a cap, and a target before launching. For a watchable run (an explicit user ask, or a targeted intervention), use the launch-ralph skill instead — and say why.',
14
15
  phases: [
15
- { title: 'Preflight', detail: 'read project config, sync the base, run the verification gate' },
16
+ { title: 'Preflight', detail: 'read project config, resolve the integration target, run the verification gate' },
16
17
  { title: 'Graph', detail: 'list ready tickets and their blocking edges, verbatim' },
17
- { title: 'Build', detail: 'one fresh-context implementer per ticket, merge included' },
18
+ { title: 'Build', detail: 'one fresh-context implementer per ticket, handing off at PR-open' },
19
+ { title: 'Gate', detail: 'per-ticket merge gate: CI wait, squash-merge, explicit close' },
18
20
  { title: 'Verify', detail: 'remote ground truth for every claimed merge' },
19
21
  { title: 'Park', detail: 'comment failure history, label needs-info' },
20
- { title: 'Release', detail: 'final verification gate and evidence summary' },
22
+ { title: 'Release', detail: 'final verification gate and the where-it-lives recap' },
21
23
  ],
22
24
  }
23
25
 
@@ -52,8 +54,17 @@ const POLICY = {
52
54
  max: A.max ?? 0,
53
55
  // Parallel implementers. Width also caps local build concurrency — several implementers
54
56
  // share one machine, and fanning out test runs buys backpressure, not speed. Use 1 until
55
- // a run has landed tickets cleanly on this project.
57
+ // a run has landed tickets cleanly on this project — or pass canary: true, which does it
58
+ // for you. Tickets that add DB migrations collide on the next migration number when run
59
+ // in parallel; the pre-PR sync renumbers, but serializing them is cheaper.
56
60
  width: A.width ?? 3,
61
+ // Integration target: '' (trunk) merges each ticket PR into the default branch; a branch
62
+ // name consolidates the whole campaign onto that branch and never touches the default
63
+ // branch — the run ends by offering ONE release PR target -> default (ADR-0022).
64
+ target: A.target ?? '',
65
+ // Canary: hold width at 1 until the run's first verified merge proves the plumbing
66
+ // end to end (branch, PR, CI, merge gate, close). For a project's first campaign.
67
+ canary: A.canary ?? false,
57
68
  // Tries per ticket: 1 attempt + 1 retry with a fresh context, then park. Deferrals
58
69
  // (a declared blocker had not landed yet) hand their attempt back, capped separately.
59
70
  attempts: A.attempts ?? 2,
@@ -84,12 +95,14 @@ where it left off — do not start over. Never open a second PR for the same tic
84
95
  const PREFLIGHT_SCHEMA = {
85
96
  type: 'object',
86
97
  additionalProperties: false,
87
- required: ['green', 'base', 'trackerAccess', 'verifyCommand', 'localCommands', 'failures'],
98
+ required: ['green', 'base', 'defaultBranch', 'trackerAccess', 'verifyCommand', 'localCommands', 'failures'],
88
99
  properties: {
89
100
  green: { type: 'boolean', description: 'base is synced and the verification gate passed' },
90
101
  headSha: { type: 'string', description: 'commit sha the gate ran against' },
91
102
  repo: { type: 'string', description: 'owner/name from the git remote, or empty' },
92
- base: { type: 'string', description: 'default branch name' },
103
+ base: { type: 'string', description: "the run's integration base: the declared target branch when one is set, else the default branch" },
104
+ defaultBranch: { type: 'string', description: 'the repository default branch name' },
105
+ targetCreated: { type: 'boolean', description: 'true when a declared target branch was missing from the remote and was created from the default branch tip' },
93
106
  issueTracker: { type: 'string', description: 'issueTracker from .launchrail.yml (github | linear | none)' },
94
107
  trackerAccess: {
95
108
  type: 'string',
@@ -145,7 +158,9 @@ const BUILD_SCHEMA = {
145
158
  properties: {
146
159
  status: {
147
160
  type: 'string',
148
- enum: ['merged', 'already-done', 'blocked', 'ci-red', 'ci-timeout', 'conflict', 'verify-failed', 'failed'],
161
+ enum: ['pr-open', 'merged', 'already-done', 'blocked', 'conflict', 'verify-failed', 'failed'],
162
+ description:
163
+ '"pr-open" is the normal hand-off (the loop owns CI and merge); "merged" only when an adopted PR turned out to be merged already (idempotency)',
149
164
  },
150
165
  pr: { type: 'integer', description: 'PR number, when one was opened or adopted' },
151
166
  mergeCommit: { type: 'string' },
@@ -162,6 +177,24 @@ const BUILD_SCHEMA = {
162
177
  },
163
178
  }
164
179
 
180
+ const GATE_SCHEMA = {
181
+ type: 'object',
182
+ additionalProperties: false,
183
+ required: ['status', 'summary'],
184
+ properties: {
185
+ status: {
186
+ type: 'string',
187
+ enum: ['merged', 'ci-failed', 'ci-timeout', 'not-mergeable', 'failed'],
188
+ },
189
+ mergeCommit: { type: 'string' },
190
+ issueClosed: { type: 'boolean' },
191
+ summary: {
192
+ type: 'string',
193
+ description: 'on merged: the API facts; on failure: the failing check or conflicting files, enough for a fresh implementer to act on',
194
+ },
195
+ },
196
+ }
197
+
165
198
  const VERIFY_SCHEMA = {
166
199
  type: 'object',
167
200
  additionalProperties: false,
@@ -211,24 +244,32 @@ Start clean: delete the failed ralph/${ticket.number}-* branch first, re-sync th
211
244
  : ''
212
245
  return `${preamble(pre)}
213
246
 
214
- Implement ticket #${ticket.number} ("${ticket.title}") end to end — merge included. You own it alone; assume no knowledge of any other session. Other implementers are working on other tickets against the same base right now, so ${pre.base} will move under you. That is expected.
247
+ Implement ticket #${ticket.number} ("${ticket.title}") through to an open PR. You own the build alone; assume no knowledge of any other session. Other implementers are working on other tickets against the same base right now, so ${pre.base} will move under you. That is expected.
215
248
  ${retry}
216
249
  Steps, in order:
217
250
  1. Dependency gate: before anything else, confirm every ticket on this ticket's "Blocked by" line is CLOSED with its work merged into ${pre.base}. If any blocker is still open, do NOT build on a missing dependency — report status "blocked", name the open blocker in "failure", and stop. That is a deferral, not a failure; the loop retries you after the blocker lands.
218
- 2. Read the ticket and everything it links (spec sections, ADRs, journeys). Report status "already-done" if it is already closed.
251
+ 2. Read the ticket and everything it links (spec sections, ADRs, journeys). If the tracker tool truncates the body (long code spans are a known trigger), fetch the full text by another route — the tracker's search API, the spec file in the repo — and never implement from a truncated ticket. Report status "already-done" if the ticket is already closed.
219
252
  3. Label the ticket ralph:building so a lost session leaves a trace.
220
253
  4. Branch from a fresh sync of ${pre.base}: ralph/${ticket.number}-<short-slug>.
221
254
  5. Implement by invoking the launch-ralph-implement skill — it owns the per-ticket contract: TDD, the verification gate, browser smoke for user-facing changes, self-review via /code-review, commit conventions.
222
- 6. Pre-PR sync: merge the latest ${pre.base} into your branch. Conflicts are ordinary work — resolve them with the launch-resolving-merge-conflicts skill and re-run the verification gate if anything changed.
223
- 7. Open a PR titled from the ticket, with "Closes #${ticket.number}" in the body. Never open a second PR if one already exists — adopt it. Opening against an up-to-date base means CI tests the state that will actually land.
224
- 8. Wait for CI if the repository has it, spacing polls with the Monitor tool or a background sleep — never a foreground sleep, never a busy loop; treat ~20 minutes as the budget and report status "ci-timeout" beyond it. Fix what your branch broke and push. If a failure reproduces on ${pre.base} itself, report "ci-red" and stop — that is systemic, not this ticket's problem.
225
- 9. Immediately before merging, re-sync with ${pre.base} once more (retry up to 3 times if the base keeps moving), then squash-merge. Squash-merge does not reliably fire "Closes" — read the issue back, close it explicitly if it is still open, and remove the ralph:building label. Never push to ${pre.base} directly; the PR is the only door.
255
+ 6. Pre-PR sync: merge the latest ${pre.base} into your branch. Conflicts are ordinary work — resolve them with the launch-resolving-merge-conflicts skill. If ${pre.base} gained DB migrations since you branched, regenerate yours to follow them with the project's migration tool — never hand-edit the migration journal. Re-run the verification gate if anything changed.
256
+ 7. Open a PR against ${pre.base}, titled from the ticket, with "Closes #${ticket.number}" in the body. Never open a second PR if one already exists — adopt it. Opening against an up-to-date base means CI tests the state that will actually land. Then report status "pr-open" with the PR number and STOP: the CI wait, the merge, and the issue close belong to the loop's merge gate, not to you — a subagent cannot wait on CI (a background sleep will not resume you). Never push to ${pre.base} directly; the PR is the only door.
226
257
 
227
258
  ${INTEGRITY}
228
259
 
229
260
  ${IDEMPOTENCY}
230
261
 
231
- Report honestly via the schema: "merged" only after the squash-merge API call succeeded; "blocked" when a declared blocker had not landed; "verify-failed" when the verification gate would not go green; "conflict" when a conflict was too ambiguous to resolve without losing behavior (say which files and why); "ci-red" / "ci-timeout" / "failed" otherwise, with a summary a fresh retry can act on. List deliberately-out-of-scope discoveries in "punted".`
262
+ Report honestly via the schema: "pr-open" once the PR exists against ${pre.base}; "merged" only when an adopted PR turned out to be already merged; "blocked" when a declared blocker had not landed; "verify-failed" when the verification gate would not go green; "conflict" when a conflict was too ambiguous to resolve without losing behavior (say which files and why); "failed" otherwise, with a summary a fresh retry can act on. List deliberately-out-of-scope discoveries in "punted".`
263
+ }
264
+
265
+ function gatePrompt(pre, ticket, build) {
266
+ return `You are the merge gate for ticket #${ticket.number}: PR #${build.pr} is open against ${pre.base}.
267
+ Tracker access from this environment: ${pre.trackerAccess}
268
+ You own the CI wait, the squash-merge, and the tracker bookkeeping — and nothing else. You never write code, never push commits, never repair a failing branch; a failing PR is reported, not fixed here.
269
+ 1. Wait for CI on the PR, if the repository has it. Space checks with the Monitor tool — NEVER a bare background sleep (it will not resume you) and never a busy loop. Treat ~20 minutes as the budget; beyond it report status "ci-timeout".
270
+ 2. CI green (or absent): check mergeability against ${pre.base} — the base may have moved since CI started. Mergeable: squash-merge via the tracker API; if the base moves between check and merge, re-check and retry up to 3 times. A real conflict is status "not-mergeable" — name the conflicting files if the API reports them.
271
+ 3. Merged: read issue #${ticket.number} back and close it explicitly if it is still open — "Closes #n" only auto-fires from the default branch${POLICY.target ? ', and this run does not merge there' : ', and squash-merge does not reliably fire it even there'} — then remove the ralph:building label. Report status "merged" with the merge commit sha.
272
+ 4. CI failed on the PR: report status "ci-failed" with the failing check and a summary a fresh implementer can act on. Fix nothing.`
232
273
  }
233
274
 
234
275
  function verifyPrompt(pre, ticket, build) {
@@ -308,17 +349,39 @@ async function drive(pre, ticket) {
308
349
  s.failures.push(`still blocked after ${s.defers} deferrals: ${build.failure ?? build.summary}`)
309
350
  return { ticket, ok: false }
310
351
  }
311
- if (build.status !== 'merged') {
352
+ if (build.status !== 'pr-open' && build.status !== 'merged') {
312
353
  s.failures.push(`[attempt ${s.attempts}] ${build.status}: ${build.failure ?? build.summary}`)
313
354
  return { ticket, ok: false }
314
355
  }
315
356
  if (!build.pr) {
316
- s.failures.push(`[attempt ${s.attempts}] reported merged but returned no PR number`)
357
+ s.failures.push(`[attempt ${s.attempts}] reported ${build.status} but returned no PR number`)
317
358
  return { ticket, ok: false }
318
359
  }
360
+ let mergeCommit = build.mergeCommit
361
+ if (build.status === 'pr-open') {
362
+ // The loop owns the merge gate (ADR-0022): an implementer cannot wait on CI (a
363
+ // subagent's background sleep never resumes it), and a single gate owner keeps
364
+ // merge ordering sane. A failing gate hands the ticket back as a failed attempt;
365
+ // the fresh retry adopts the PR via the idempotency clause, repairs, hands off again.
366
+ const gate = await agent(gatePrompt(pre, ticket, build), {
367
+ label: `gate:#${ticket.number}`,
368
+ phase: 'Gate',
369
+ schema: GATE_SCHEMA,
370
+ effort: 'low',
371
+ })
372
+ if (!gate) {
373
+ s.failures.push('gate agent died (infrastructure)')
374
+ return { ticket, ok: false, dead: true }
375
+ }
376
+ if (gate.status !== 'merged') {
377
+ s.failures.push(`[attempt ${s.attempts}] PR #${build.pr} ${gate.status}: ${gate.summary}`)
378
+ return { ticket, ok: false }
379
+ }
380
+ mergeCommit = gate.mergeCommit || mergeCommit
381
+ }
319
382
  // Nothing is trusted from a report — a claimed merge is checked against the remote
320
383
  // by a separate, cheap agent with tracker access only.
321
- const verdict = await agent(verifyPrompt(pre, ticket, build), {
384
+ const verdict = await agent(verifyPrompt(pre, ticket, { pr: build.pr, mergeCommit }), {
322
385
  label: `verify:#${ticket.number}`,
323
386
  phase: 'Verify',
324
387
  schema: VERIFY_SCHEMA,
@@ -328,7 +391,7 @@ async function drive(pre, ticket) {
328
391
  if (verdict?.merged && verdict.issueClosed) {
329
392
  s.status = 'merged'
330
393
  s.pr = build.pr
331
- s.mergeCommit = verdict.mergeCommit || build.mergeCommit
394
+ s.mergeCommit = verdict.mergeCommit || mergeCommit
332
395
  return { ticket, ok: true }
333
396
  }
334
397
  // Merged-but-issue-open fails verification too: the retry adopts the merged PR (the
@@ -363,9 +426,13 @@ function frontier(tickets, closedBefore) {
363
426
  // ---------------------------------------------------------------------------
364
427
  phase('Preflight')
365
428
  const pre = await agent(
366
- `Preflight for a Ralph loop run in this repository. Fix nothing; report actual state.
429
+ `Preflight for a Ralph loop run in this repository. Report actual state; fix nothing — the one permitted mutation is creating the declared integration branch in step 2.
367
430
  1. Read .launchrail.yml (issueTracker, testing commands, modules) and AGENTS.md (verbatim commands).
368
- 2. Identify the repo (git remote) and the default/base branch; sync it fresh (clean tree). If the base branch does not exist on the remote, report not green and say the base is missing — do not guess another branch.
431
+ 2. Identify the repo (git remote) and its default branch; report the default branch name as defaultBranch. ${
432
+ POLICY.target
433
+ ? `This run consolidates onto the integration branch "${POLICY.target}" — that branch is the base. If it does not exist on the remote, create it from the default branch's tip (no force; the default branch itself is never touched) and report targetCreated: true. A missing DEFAULT branch is still not green — do not guess.`
434
+ : `This run merges into the default branch (trunk) — that branch is the base. If it does not exist on the remote, report not green and say the base is missing — do not guess another branch.`
435
+ } Sync the base fresh (clean tree) and report its name as base.
369
436
  3. Determine how the tracker is reachable from THIS environment: check whether the CLI the project docs assume (e.g. gh) is installed; if not, name the concrete substitute available here (e.g. GitHub MCP tools) as an instruction future agents can follow.
370
437
  4. Run the project's install command, then the verification gate: npx @wemuda/launchrail verify. Report the actual exit codes, not the reassuring summary line. An empty verification contract failing the gate is a refusal condition, not something to work around.
371
438
  green means: base synced AND the verification gate exited 0.`,
@@ -384,11 +451,14 @@ if ((pre.issueTracker ?? 'none') === 'none') {
384
451
  phase('Graph')
385
452
  log(
386
453
  `Base green at ${pre.headSha ?? pre.base} on ${pre.base}. ` +
454
+ (POLICY.target
455
+ ? `Consolidating onto ${pre.base}${pre.targetCreated ? ' (created from the default branch tip)' : ''}; ${pre.defaultBranch || 'the default branch'} stays untouched. `
456
+ : `Trunk mode — each ticket merges into ${pre.base}. `) +
387
457
  (POLICY.only.length > 0
388
458
  ? `Scoped to ${POLICY.only.map((n) => `#${n}`).join(', ')}.`
389
459
  : 'No scope — building the whole ready frontier.') +
390
460
  (POLICY.max > 0 ? ` Stopping after ${POLICY.max} verified merge(s).` : '') +
391
- ` Width ${POLICY.width}, ${POLICY.attempts} attempts per ticket.`,
461
+ ` Width ${POLICY.width}${POLICY.canary ? ' (canary: width 1 until the first verified merge)' : ''}, ${POLICY.attempts} attempts per ticket.`,
392
462
  )
393
463
  let graph = await agent(graphPrompt(pre), { label: 'read-graph', phase: 'Graph', schema: GRAPH_SCHEMA, model: 'haiku', effort: 'low' })
394
464
  if (!graph) throw new Error('graph agent died — refusing to start')
@@ -424,7 +494,10 @@ while (rounds < POLICY.maxRounds) {
424
494
  const ready = frontier(tickets, closedBefore)
425
495
  if (ready.length === 0) break
426
496
  rounds += 1
427
- const batch = ready.slice(0, Math.min(POLICY.width, capLeft))
497
+ // Canary: the first verified merge proves the plumbing end to end (branch, PR, CI,
498
+ // merge gate, explicit close); until it lands, dispatch one ticket at a time.
499
+ const width = POLICY.canary && mergedCount() === 0 ? 1 : POLICY.width
500
+ const batch = ready.slice(0, Math.min(width, capLeft))
428
501
  log(`round ${rounds}: dispatching ${batch.map((t) => `#${t.number}`).join(', ')} (${ready.length} unblocked)`)
429
502
  const results = await parallel(batch.map((t) => () => drive(pre, t)))
430
503
  const landed = results.filter((r) => r?.ok)
@@ -495,10 +568,18 @@ verified means: the verification gate exited 0${pre.browserTesting && merged.len
495
568
  { label: 'release-verification', phase: 'Release', schema: RELEASE_SCHEMA },
496
569
  )
497
570
 
571
+ // The recap is part of the contract (ADR-0022): where the work lives and the one
572
+ // next step, as data — the supervisor relays it, never reconstructs it.
573
+ const mode = POLICY.target ? 'consolidation' : 'trunk'
498
574
  return {
499
575
  rounds,
500
576
  verified: release?.verified ?? false,
501
577
  maxReached,
578
+ target: { mode, base: pre.base, defaultBranch: pre.defaultBranch ?? '', headSha: release?.headSha ?? '' },
579
+ nextStep:
580
+ mode === 'consolidation'
581
+ ? `All campaign work is on ${pre.base}; ${pre.defaultBranch || 'the default branch'} is untouched. Release it with one PR ${pre.base} -> ${pre.defaultBranch || 'the default branch'} — offer it, and open it only when the user says so.`
582
+ : `Every merged ticket is live on ${pre.base}; nothing is left to integrate.`,
502
583
  release,
503
584
  merged: merged.map((s) => ({ ticket: s.ticket.number, title: s.ticket.title, pr: s.pr, mergeCommit: s.mergeCommit })),
504
585
  parked: parked.map((s) => ({ ticket: s.ticket.number, title: s.ticket.title, failures: s.failures })),
@@ -18,7 +18,9 @@ From `.launchrail.yml`: `issueTracker` and the `testing` commands. From the argu
18
18
  - **a count** — "the next 5", "max 5" → the loop with a merge cap. Don't hand-pick which five: the cap is a stop condition, and the frontier decides the order — the loop stops after that many *verified merges* and leaves the rest ready;
19
19
  - **a spec, slice, or epic reference** — "spec #2's tickets", "the rest of slice 1" → resolve it to explicit numbers against the live tracker: the open tickets that belong to it (a "Part of: #n" line, the spec issue's ticket list, or a label), plus any open in-set blockers so the scope stays dependency-closed. Combinations compose: "the next 5 of spec #2" → that spec's tickets *and* a cap of 5.
20
20
 
21
- **Resolve prose to data before anything launches.** The loop's inputs are ticket numbers and policy values (`only`, `max`, `width`) — the workflow form takes them as JSON args and refuses a natural-language string by design. Translating the user's words into that scope, against live tracker state, is *your* job, and it ends with an echo before any dispatch: "Scope: #14, #15, #19 — the remaining slice-1 tickets; #19 builds after #14. Cap: none." A misread scope corrected here costs a sentence; corrected after launch it costs a run.
21
+ **Resolve the integration target with the scope.** Every loop run merges its per-ticket PRs into exactly one base (ADR-0022): **trunk** — the default branch, each ticket live the moment it merges — unless the user names a consolidation branch ("collect spec #44 on `spec/44-mvp`", "don't touch master yet") or the environment forbids pushing to the default branch (a harness-designated branch: use it, and say that constraint is why). Consolidation means the default branch stays untouched and the run ends by offering one release PR `<target> → <default>`; it is a choice the user should recognize, never a silent fallback.
22
+
23
+ **Resolve prose to data before anything launches.** The loop's inputs are ticket numbers and policy values (`only`, `max`, `width`, `target`) — the workflow takes them as JSON args and refuses a natural-language string by design. Translating the user's words into that scope, against live tracker state, is *your* job, and it ends with an echo before any dispatch: "Scope: #14, #15, #19 — the remaining slice-1 tickets; #19 builds after #14. Cap: none. Target: trunk (`master`). Engine: the `ralph` workflow." A misread scope corrected here costs a sentence; corrected after launch it costs a run.
22
24
 
23
25
  ## Step 2 — Repair setup, don't gatekeep
24
26
 
@@ -26,13 +28,13 @@ If the loop's materials are missing — `modules.ralph` off in the manifest, or
26
28
 
27
29
  ## Step 3 — Route by scope
28
30
 
29
- **The frontier (or a resolved scope):** run the loop under the `launch-ralph` skill — it owns the policies (width, attempts, cap, deferrals, remote-verified merges) and the orchestrator's contract. For a wide dependency graph or a long run, prefer its workflow form (`.claude/workflows/ralph.js`) — the skill explains when — passing the resolved scope as JSON args, e.g. `{ only: [14, 15, 19], max: 5, width: 1 }`.
31
+ **The frontier (or any multi-ticket scope):** the engine is the `ralph` workflow (`.claude/workflows/ralph.js`) — launch it with the resolved scope and target as JSON args, e.g. `{ only: [14, 15, 19], max: 5, target: 'spec/2-checkout' }` (`canary: true` on a project's first run), then supervise it per the `launch-ralph` skill, which owns the policies (width, attempts, cap, deferrals, the merge gate, remote-verified merges) and the supervisor's contract. Orchestrating dispatches by hand under that skill instead is the exception, chosen out loud in the echo: the user asked to watch each dispatch, the Workflow tool is unavailable here, or the run is a targeted intervention (one parked ticket). One engine, one shape — a session that invents its own fan-out is not running the loop.
30
32
 
31
- **One ticket:** build it here, watchable, under the same contract a Ralph dispatch carries (kept textually parallel with `launch-ralph` — change one, change both):
33
+ **One ticket:** build it here, watchable, under the same contract a Ralph dispatch carries (kept textually parallel with `launch-ralph` — change one, change both). One deliberate divergence: you are the session, not a subagent, so you also run the merge gate yourself — waiting on CI here is fine:
32
34
 
33
35
  1. **Dependency gate:** every ticket on the `Blocked by:` line is closed with its work merged. An open blocker stops you before any code — name it and offer to build it first.
34
36
  2. Read the ticket and everything it links (spec sections, ADRs, journeys), plus `AGENTS.md`/`CLAUDE.md`.
35
- 3. Label the ticket `ralph:building`; branch `ralph/<n>-<short-slug>` from a fresh sync of the base.
37
+ 3. Label the ticket `ralph:building`; branch `ralph/<n>-<short-slug>` from a fresh sync of the base (the resolved integration target).
36
38
  4. Implement by the **`launch-ralph-implement`** contract — TDD, the `verify` gate, browser smoke for user-facing changes, self-review, commit conventions. Name the skill; don't paraphrase it.
37
39
  5. Pre-PR sync: merge the latest base; resolve conflicts with `launch-resolving-merge-conflicts`; re-run the gate if anything changed.
38
40
  6. Open a PR titled from the ticket with `Closes #<n>`; adopt an existing `ralph/<n>-*` branch or PR rather than opening a second.
@@ -44,3 +46,4 @@ If the loop's materials are missing — `modules.ralph` off in the manifest, or
44
46
  - **Only the user starts this.** Conductors and other skills hand over the command (`/launch-implement`); they never invoke it. The engines behind it inherit the same rule — reaching them through this door *is* the explicit user start.
45
47
  - **Nothing is done until `npx @wemuda/launchrail verify` is green** — per ticket, and once more on the final base when a loop run ends. Where `modules.browser-testing` is enabled and the change is user-facing, a `launch-browser-smoke` journey is part of done.
46
48
  - **Report evidence, not assertions:** PR numbers, merge commits, issues closed, the verify outcome — and what was parked or punted, with why.
49
+ - **Every loop run ends with the campaign recap** (the `launch-ralph` close-out): where the work lives — target branch and head SHA — the ticket → PR → merge-commit table, parked and stuck tickets, punted follow-ups in one list, and the single next step; in consolidation mode that step is *offering* the one release PR to the default branch, opened only when the user says so.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: launch-ralph
3
- description: Orchestrate the bounded Ralph implementation loop — dispatch fresh-context implementer subagents over the ready ticket frontier, verify every claimed merge against the remote, and gate completion on the project's verification contract. Also the supervisor's contract when the loop runs as the ralph workflow. The engine behind /launch-implement (the user-typed front door) — never invoke it on your own initiative; reach it through that door or an explicit user request to run the loop.
3
+ description: The Ralph implementation loop's contract — policies, dispatch steps, the loop-owned merge gate, and the supervisor's duties when the loop runs as the ralph workflow (the default engine for any multi-ticket run). Skill-mode orchestration of fresh-context implementers lives here too, as the declared exception. Behind /launch-implement (the user-typed front door) — never invoke it on your own initiative; reach it through that door or an explicit user request to run the loop.
4
4
  ---
5
5
 
6
6
  # Ralph — the autonomous implementation loop
@@ -9,13 +9,14 @@ The user starts this loop through `/launch-implement` (or by asking for it in so
9
9
 
10
10
  You are the orchestrator. **You do not write code. You do not read diffs. You do not fix failing branches yourself.** You compute what's ready, dispatch, verify, and keep a running log. Tracker state and subagent reports in, decisions out.
11
11
 
12
- One agent implementing a whole backlog in a single session degrades — context fills with diffs and half-remembered state, and quality drops with every ticket. This loop inverts that: every ticket gets a fresh-context implementer subagent that owns it end to end, merge included, and nothing an implementer reports is trusted until the remote confirms it.
12
+ One agent implementing a whole backlog in a single session degrades — context fills with diffs and half-remembered state, and quality drops with every ticket. This loop inverts that: every ticket gets a fresh-context implementer subagent that owns the build through an open PR, the loop's merge gate lands it, and nothing anyone reports is trusted until the remote confirms it.
13
13
 
14
- This skill is the watchable, checkpointed frontend of the loop. The same loop exists as a deterministic workflow (`.claude/workflows/ralph.js`, installed by init; `launchrail sync` restores it) — prefer the workflow when the dependency graph is wide or the run is long (script state cannot be compacted away); prefer this skill when you want to watch each dispatch, the graph is a chain, or something is already going wrong. The two share one policy block: change a policy here, change it in the workflow too (ADR-0005, field-revised by ADR-0010).
14
+ This skill is the loop's contract and its supervisor. The loop itself runs as the deterministic `ralph` workflow (`.claude/workflows/ralph.js`, installed by init; `launchrail sync` restores it) — **the workflow is the engine for every multi-ticket run**, launched with the resolved scope and integration target as JSON args and then supervised per this skill; its script state cannot be compacted away. Orchestrating dispatches from this session instead is the exception, and it is chosen out loud — name the engine and why before anything dispatches: the user asked to watch each dispatch, the Workflow tool is unavailable in this environment, or this is a targeted intervention (one parked ticket, re-run watchably). A hand-rolled fan-out that is neither is not the loop. The two forms share one policy block: change a policy here, change it in the workflow too (ADR-0005, field-revised by ADR-0010 and ADR-0022).
15
15
 
16
16
  ## Policies
17
17
 
18
- - **Width: 3** implementers at once. Width multiplies conflict rate and shared-machine load, not just throughput — use 1 until a run has landed tickets cleanly on this project. Cut a batch below width when its tickets would obviously collide (same module, same files); when in doubt, narrow.
18
+ - **Integration target: declared, singular, restated.** Every run merges its per-ticket PRs into exactly one base, named before anything dispatches and again in the close-out. **Trunk** (the default): the repository's default branch — each verified merge is immediately on mainline, and when the run ends there is nothing left to integrate. **Consolidation**: one named integration branch (e.g. `spec/44-mvp`) collects the whole campaign and the default branch is never touched; the run ends by *offering* one release PR `<target> → <default>` — opened only when the user says so. Consolidation is chosen, never fallen into: the user names a branch or asks for it, or the environment forbids pushing to the default branch — announce that constraint as the reason. A named target missing from the remote is created from the default branch's tip before preflight verifies it; a missing *default* branch stays a refusal. In consolidation mode `Closes #n` never auto-fires (auto-close only triggers from the default branch), so the explicit post-merge close is load-bearing, not belt-and-suspenders.
19
+ - **Width: 3** implementers at once. Width multiplies conflict rate and shared-machine load, not just throughput — use 1 until a run has landed tickets cleanly on this project (the workflow's `canary: true` encodes exactly that). Cut a batch below width when its tickets would obviously collide (same module, same files); when in doubt, narrow. Tickets that add DB migrations are a known collision: two parallel implementers both claim the next migration number — serialize them, or expect the second to renumber at pre-PR sync.
19
20
  - **Cap: none** by default. The user may bound a run ("the next 5"): stop once that many merges have been *verified*, keeping every batch within the remainder so the run cannot overshoot. Failed and deferred dispatches never consume the cap — their slots go to other tickets. Hitting the cap ends the run cleanly: the rest of the frontier stays ready (reported, never parked), and close-out runs as usual.
20
21
  - **Attempts: 2** — retry a failed ticket once with a fresh context, then park it.
21
22
  - **Deferrals are not attempts.** An implementer that stops at its dependency gate (a declared blocker had not actually landed) hands the attempt back and is retried after the blocker lands — capped at 2 deferrals, then it counts as a real failure.
@@ -23,14 +24,14 @@ This skill is the watchable, checkpointed frontend of the loop. The same loop ex
23
24
  - **Checkpoints: none** by default — run to completion, report once. The user may ask for a pause after each round instead.
24
25
  - **Review gate:** the implementer's own self-review via `/launch-code-review`, inside `launch-ralph-implement`.
25
26
  - **Verification gate:** `npx @wemuda/launchrail verify` — per ticket before the PR, and once more on the final base before the loop may report success.
26
- - **Merge ordering: optimistic, arbitrated by the remote.** Implementers re-sync immediately before merging and retry up to 3 times if the base moved. No merge locks.
27
+ - **Merge ownership: the loop, not the implementer.** Implementers build, open the PR, and hand off at PR-open; the merge gate — CI wait, mergeability re-check, squash-merge, explicit issue close, `ralph:building` removal — belongs to the loop (you in skill mode, a per-ticket gate agent in the workflow). An implementer subagent must never sit in a CI wait: it cannot foreground-sleep, and a background sleep surfaces to its parent without resuming it — tokens burn, nothing advances. Merge ordering stays optimistic and remote-arbitrated (re-check mergeability immediately before merging, up to 3 retries if the base moves; no merge locks), and the single gate owner serializes where it matters — critical-path first, schema-touching tickets one at a time.
27
28
  - **Labels:** tickets enter as `ready-for-agent`, are marked `ralph:building` while owned, and leave as closed or `needs-info` (parked).
28
29
 
29
30
  ## Preconditions — refuse to start if any fails
30
31
 
31
32
  1. `.launchrail.yml` exists with `issueTracker` not `none`, and the tracker is reachable **from this environment**: check whether the CLI the project docs assume (e.g. `gh`) is installed here; if not, identify the substitute (e.g. GitHub MCP tools) and name it in every dispatch.
32
33
  2. Open tickets labeled `ready-for-agent` exist and carry explicit `Blocked by: #n` edges (or the tracker's native blocking relations). No tickets with edges → nothing to orchestrate; point the user at `launch-tickets`. If anything wearing `ready-for-agent` is plainly not an implementable ticket — a published spec, research notes, an epic — stop and have it relabeled (e.g. `spec`) before starting: the frontier is computed from the label alone and cannot tell prose from work.
33
- 3. The base branch exists on the remote and is green on a fresh checkout: sync it, run the install command, then `npx @wemuda/launchrail verify` — report actual exit codes, not the reassuring summary line; a broken base poisons every implementer after it. A missing base branch is a refusal, not a cue to guess another. An **empty verification contract fails `verify` and is a refusal condition**: a run whose completion nothing can verify must not start. Tell the user to configure `testing` commands in `.launchrail.yml` first.
34
+ 3. The integration target is resolved (trunk or a named consolidation branch — see Policies) and its branch is green on a fresh checkout: sync it (creating a named consolidation branch from the default branch's tip if the remote lacks it), run the install command, then `npx @wemuda/launchrail verify` — report actual exit codes, not the reassuring summary line; a broken base poisons every implementer after it. A missing *default* branch is a refusal, not a cue to guess another. An **empty verification contract fails `verify` and is a refusal condition**: a run whose completion nothing can verify must not start. Tell the user to configure `testing` commands in `.launchrail.yml` first.
34
35
  4. The verbatim local commands are known (from `AGENTS.md` / `.launchrail.yml`), including which checks belong to CI rather than the shared local machine.
35
36
 
36
37
  ## The loop
@@ -43,16 +44,24 @@ Sync → compute frontier → dispatch batch → verify → handle outcomes →
43
44
 
44
45
  ## The dispatch prompt
45
46
 
46
- Each implementer prompt is self-contained — assume it knows nothing about this session or the other implementers. It carries: the ticket number and title, the verbatim commands, how to reach the tracker from this environment, and these eight steps:
47
+ Each implementer prompt is self-contained — assume it knows nothing about this session or the other implementers. It carries: the ticket number and title, the verbatim commands, how to reach the tracker from this environment, which branch is the base (the integration target), and these seven steps:
47
48
 
48
49
  1. **Dependency gate:** before anything else, confirm every ticket on the `Blocked by` line is closed with its work merged into the base. If any blocker is still open, do not build on a missing dependency — report "blocked" naming the open blocker, and stop. A deferral, not a failure; the loop retries after the blocker lands.
49
- 2. Read the ticket and everything it links (spec sections, ADRs, journeys), plus `AGENTS.md`/`CLAUDE.md`. If the ticket is already closed, report "already-done" and stop.
50
+ 2. Read the ticket and everything it links (spec sections, ADRs, journeys), plus `AGENTS.md`/`CLAUDE.md`. If the tracker tool truncates the body (long code spans are a known trigger), fetch the full text by another route — the tracker's search API, the spec file in the repo — and never implement from a truncated ticket. If the ticket is already closed, report "already-done" and stop.
50
51
  3. Label the ticket `ralph:building` so a lost session leaves a trace.
51
52
  4. Branch from a fresh sync of the base: `ralph/<n>-<short-slug>`.
52
53
  5. Implement by invoking the **`launch-ralph-implement`** skill — it owns TDD, the verification gate, browser smoke for user-facing changes, self-review via `/launch-code-review`, and commit conventions. Name the skill; do not paraphrase it.
53
- 6. Pre-PR sync: merge the latest base into the branch; resolve conflicts with the **`launch-resolving-merge-conflicts`** skill; re-run the verification gate if anything changed.
54
- 7. Open a PR titled from the ticket with `Closes #<n>` in the body. Never open a second PR for a ticket — adopt an existing one. Opening against an up-to-date base means CI tests the state that will actually land.
55
- 8. Wait for CI if the repository has it — space polls with the Monitor tool or a background sleep, never a foreground sleep or busy loop; ~20 minutes is the budget. Fix what the branch broke and push; a failure that reproduces on the base itself is "ci-red" — systemic, not this ticket's problem. Re-sync immediately before merging (up to 3 retries if the base moves), then squash-merge. Squash-merge does not reliably fire `Closes` — read the issue back, close it explicitly if still open, and remove `ralph:building`.
54
+ 6. Pre-PR sync: merge the latest base into the branch; resolve conflicts with the **`launch-resolving-merge-conflicts`** skill; if the base gained DB migrations since branching, regenerate yours to follow them with the project's migration tool — never hand-edit the journal; re-run the verification gate if anything changed.
55
+ 7. Open a PR against the base, titled from the ticket, with `Closes #<n>` in the body. Never open a second PR for a ticket — adopt an existing one. Opening against an up-to-date base means CI tests the state that will actually land. Then **report PR-open and stop**: the CI wait, the merge, and the issue close belong to the loop's merge gate, not to you. Never push to the base directly.
56
+
57
+ ## The merge gate — owned by the loop
58
+
59
+ In skill mode, you run the gate for every PR the implementers hand off (the workflow runs it as a per-ticket gate agent). Order merges yourself — critical-path first, schema-touching PRs one at a time:
60
+
61
+ 1. Wait for the PR's CI from *this* session, spacing checks with your own timers (a background sleep here wakes you — the orchestrator can wait; implementer subagents cannot). ~20 minutes is the budget.
62
+ 2. Green → re-check mergeability (the base may have moved since CI started), then squash-merge; if the base moves between check and merge, re-check and retry up to 3 times.
63
+ 3. Merged → read the issue back and close it explicitly if still open — in consolidation mode auto-close never fires — and remove `ralph:building`. Then verify as always: the remote's word, not yours.
64
+ 4. CI failed on the PR, or a real conflict → the ticket becomes a failed attempt with the failing check or conflicting files as its summary; the fresh retry adopts the PR (idempotency clause), repairs, and hands off again. A failure that reproduces on the base itself is systemic — stop the run, not the ticket.
56
65
 
57
66
  Every dispatch — retries included — also carries these two clauses verbatim:
58
67
 
@@ -77,7 +86,7 @@ When the Ralph loop runs as the `ralph` workflow instead of through this skill,
77
86
  1. **Read the resolved scope back, immediately.** The first `log()` lines state it ("Scoped to #11, #12", "Stopping after 5 verified merge(s)", or "No scope — building the whole ready frontier"). An unscoped run when the user asked for three tickets is the cheapest failure to catch and the most expensive to miss — stop and relaunch if it is wrong. Scan the listed numbers for anything that is not an implementable ticket: a spec or research issue wearing `ready-for-agent` will be built as if it were work (the workflow excludes and logs obvious cases, but the label is the fix — have it corrected).
78
87
  2. **Establish ground truth from the remote, never from the run's own reports.** On every check-in read the workflow journal (`journal.jsonl`) *and* the tracker/PRs. A merge is real only when the commit is on the base branch and the issue is closed.
79
88
  3. **Arm check-ins across the long waits.** If the session can schedule a self-message, arm one a few minutes out (confirm scope and the first dispatches) and a longer fallback (catch completion or a stall). The workflow's completion notification is the primary signal; the check-ins are the backstop so the run survives an interruption.
80
- 4. **Know the healthy shapes so you don't cry wolf.** A ticket can appear twice in Build — that is the retry policy, or a *deferral* because its dependency had not landed yet (not a failure). A ticket only truly fails after two real attempts, then it parks.
89
+ 4. **Know the healthy shapes so you don't cry wolf.** A ticket can appear twice in Build — that is the retry policy, or a *deferral* because its dependency had not landed yet (not a failure). Build ending at PR-open with a separate Gate agent doing the merge is the design, not a stall. A ticket only truly fails after two real attempts, then it parks.
81
90
  5. **Intervene by exception, not by reflex.** Parked ticket → dispatch a fresh scoped run for just that one. Stall (an agent stops writing, CI never returns) → diagnose from the journal. Wrong scope or wrong base → stop, fix, relaunch. Otherwise stay out of the way; the loop is built to self-correct.
82
91
  6. **Report once at the end, concretely** — PR numbers, merge commits, issues closed, the verification outcome, anything punted — then disarm the check-ins.
83
92
 
@@ -87,12 +96,18 @@ When the frontier drains (or max rounds / a stop condition hits):
87
96
 
88
97
  1. Sync a fresh base and run `npx @wemuda/launchrail verify`. **The loop may not report success while this fails** — report "unverified" with the failures instead.
89
98
  2. If `.launchrail.yml` has `modules.browser-testing: true` and any merged ticket changed user-facing behavior, dispatch one smoke run per the `launch-browser-smoke` skill and reference its evidence bundle (`artifacts/verification/<run-id>/`).
90
- 3. Report the release evidence summary: merged tickets (PR and merge commit each), parked tickets with their failure histories, stuck tickets and what blocks them, follow-ups implementers punted, and the verification outcome with its evidence. Evidence over assertion — link what was run, never summarize what wasn't.
99
+ 3. Report the campaign recap — it must let the user act without scrolling back:
100
+ - **Where the work lives:** the integration target and its head SHA; in consolidation mode, say explicitly that the default branch is untouched.
101
+ - The ticket → PR → merge-commit table; parked tickets with their failure histories; stuck tickets and what blocks them.
102
+ - Follow-ups and operator steps implementers punted, gathered into one list.
103
+ - The verification outcome with its evidence. Evidence over assertion — link what was run, never summarize what wasn't.
104
+ - **The single next step:** trunk — nothing; every merged ticket is live on the default branch. Consolidation — offer the one release PR `<target> → <default>` with this recap as its body, and open it only when the user says so.
91
105
 
92
106
  ## Rules
93
107
 
94
108
  - Fresh context per dispatch, per retry. No exceptions.
95
- - Never implement, review, or repair code in the orchestrator session — dispatch instead.
109
+ - One integration target and one engine per run, both declared before the first dispatch and restated in the recap.
110
+ - Never implement, review, or repair code in the orchestrator session — dispatch instead. Running the merge gate is bookkeeping, not repair.
96
111
  - Name the skills (`launch-ralph-implement`, `launch-resolving-merge-conflicts`, `launch-browser-smoke`); never paraphrase their contents into a prompt.
97
112
  - Blocking edges are parsed from the verbatim `Blocked by` line, by you — never resolved by a model in between.
98
113
  - Nothing counts as merged until the remote says so; nothing counts as done until `verify` is green.
package/dist/lib/seeds.js CHANGED
@@ -69,7 +69,8 @@ function claudeGeneratedMd(ctx) {
69
69
  ? `
70
70
  ## The Ralph loop
71
71
 
72
- - Implementation starts with \`/launch-implement\` — all ready tickets, or one with \`/launch-implement <ticket>\`. It drives the Ralph loop: the \`launch-ralph\` skill (watchable, checkpointed) or the \`ralph\` workflow in \`.claude/workflows/ralph.js\` (wide or long runs). Only ever started explicitly by the user.
72
+ - Implementation starts with \`/launch-implement\` — all ready tickets, or one with \`/launch-implement <ticket>\`. Multi-ticket runs execute as the \`ralph\` workflow (\`.claude/workflows/ralph.js\`), supervised per the \`launch-ralph\` skill; single tickets build in-session. Only ever started explicitly by the user.
73
+ - Every run declares one integration target: the default branch (trunk — the default) or a named consolidation branch via the \`target\` workflow arg, which collects the campaign and ends by offering one release PR to the default branch. The run's recap states where the work lives and the single next step.
73
74
  - Tickets enter the loop with the \`ready-for-agent\` label and explicit \`Blocked by: #n\` edges; parked tickets carry \`needs-info\` plus their failure history.
74
75
  - A ticket counts done only when its PR is merged on the remote, the issue is closed, and \`npx @wemuda/launchrail verify\` is green — agent reports are claims, not evidence.
75
76
  - \`.claude/workflows/ralph.js\` is managed by Launchrail: override policy per run via workflow args (e.g. \`{ width: 1 }\`), never by editing the file.
@@ -1 +1 @@
1
- {"version":3,"file":"seeds.js","sourceRoot":"","sources":["../../src/lib/seeds.ts"],"names":[],"mappings":"AASA,SAAS,QAAQ,CAAC,GAAgB;IAChC,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC;IACzB,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,WAAW;QAC3C,CAAC,CAAC,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,WAAW,GAAG,OAAO;QACtD,CAAC,CAAC,qEAAqE,CAAC;IAE1E,MAAM,aAAa,GAAG,QAAQ,CAAC,WAAW,CAAC,mBAAmB;QAC5D,CAAC,CAAC;;;;CAIL;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO,gCAAgC,GAAG,CAAC,WAAW;;;;;;;;;;;;;;;;EAgBtD,QAAQ;EACR,aAAa;;;;;;;;;;;6BAWc,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,QAAQ,CAAC,OAAO,CAAC,WAAW,KAAK,CAAC,CAAC,CAAC,EAAE;;;;CAIxG,CAAC;AACF,CAAC;AAED,SAAS,QAAQ;IACf,OAAO;;;;;;CAMR,CAAC;AACF,CAAC;AAED,SAAS,iBAAiB,CAAC,GAAgB;IACzC,MAAM,cAAc,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,iBAAiB,CAAC;QAC5D,CAAC,CAAC;;;;;;;;CAQL;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK;QACtC,CAAC,CAAC;;;;;;;;CAQL;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO,+BAA+B,GAAG,CAAC,iBAAiB;;;;;;;;;;EAU3D,cAAc,GAAG,KAAK,EAAE,CAAC;AAC3B,CAAC;AAED,SAAS,WAAW;IAClB,OAAO;;;;;;;;;;;;;;;;;;;CAmBR,CAAC;AACF,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,mBAAmB,CAAC,GAAgB;IAClD,OAAO,EAAE,OAAO,EAAE,iCAAiC,EAAE,OAAO,EAAE,iBAAiB,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;AAC/G,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,SAAS,CAAC,GAAgB;IACxC,OAAO;QACL,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE;QACrE,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE;QAClE,EAAE,OAAO,EAAE,2BAA2B,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE;QACrF,mBAAmB,CAAC,GAAG,CAAC;KACzB,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"seeds.js","sourceRoot":"","sources":["../../src/lib/seeds.ts"],"names":[],"mappings":"AASA,SAAS,QAAQ,CAAC,GAAgB;IAChC,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC;IACzB,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,WAAW;QAC3C,CAAC,CAAC,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,WAAW,GAAG,OAAO;QACtD,CAAC,CAAC,qEAAqE,CAAC;IAE1E,MAAM,aAAa,GAAG,QAAQ,CAAC,WAAW,CAAC,mBAAmB;QAC5D,CAAC,CAAC;;;;CAIL;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO,gCAAgC,GAAG,CAAC,WAAW;;;;;;;;;;;;;;;;EAgBtD,QAAQ;EACR,aAAa;;;;;;;;;;;6BAWc,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,QAAQ,CAAC,OAAO,CAAC,WAAW,KAAK,CAAC,CAAC,CAAC,EAAE;;;;CAIxG,CAAC;AACF,CAAC;AAED,SAAS,QAAQ;IACf,OAAO;;;;;;CAMR,CAAC;AACF,CAAC;AAED,SAAS,iBAAiB,CAAC,GAAgB;IACzC,MAAM,cAAc,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,iBAAiB,CAAC;QAC5D,CAAC,CAAC;;;;;;;;CAQL;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK;QACtC,CAAC,CAAC;;;;;;;;;CASL;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO,+BAA+B,GAAG,CAAC,iBAAiB;;;;;;;;;;EAU3D,cAAc,GAAG,KAAK,EAAE,CAAC;AAC3B,CAAC;AAED,SAAS,WAAW;IAClB,OAAO;;;;;;;;;;;;;;;;;;;CAmBR,CAAC;AACF,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,mBAAmB,CAAC,GAAgB;IAClD,OAAO,EAAE,OAAO,EAAE,iCAAiC,EAAE,OAAO,EAAE,iBAAiB,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;AAC/G,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,SAAS,CAAC,GAAgB;IACxC,OAAO;QACL,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE;QACrE,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE;QAClE,EAAE,OAAO,EAAE,2BAA2B,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE;QACrF,mBAAmB,CAAC,GAAG,CAAC;KACzB,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wemuda/launchrail",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "Launchrail — initialize, inspect, update, and validate repositories using the Launchrail development system.",
5
5
  "license": "MIT",
6
6
  "type": "module",