baldart 5.4.0 → 5.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/CHANGELOG.md CHANGED
@@ -5,6 +5,53 @@ All notable changes to BALDART will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [5.5.0] - 2026-07-03
9
+
10
+ **Migration deploy chain hardened (FEAT-0067 post-mortem — mayo).** The batch's DB
11
+ migration was fully declared in the cards (`db_migration` + `db_migrations_summary`,
12
+ per the consumer's prd-card-writer overlay) but the remote `db:push` was never
13
+ executed nor surfaced: new2's Migration Gate probed ONLY `migration_plan` (structural
14
+ vocabulary mismatch → silent no-op) and the end-of-batch `db-migration-deploy`
15
+ residual was lost by the mid-run DONE-flip, closing the epic against an unmet DoD.
16
+
17
+ - **Vocabulary aliases** (`/new` 2.2.1, `new2` 1.7.0): the `-auto` delegation gate,
18
+ `/new` Phase 0 1b and new2 Step 3.5 now detect a declared migration under
19
+ `migration_plan.required` (epic) OR `db_migrations_summary` (epic) OR
20
+ `db_migration.required: true` (per-card overlay convention).
21
+ - **new2 Step 5.1b Migration Sync Tail (NEW)**: after the autonomous workflow returns
22
+ (post-merge, on trunk), the SKILL runs the project's post-merge sync check
23
+ (overlay "branch B"); on red it asks Push now / Defer / Override interactively —
24
+ never pushes autonomously. Triggered by the Step-3.5 DECLARATION, not only by the
25
+ residual ledger (belt-and-braces vs a dropped residual). Telemetry
26
+ `migration_sync_tail`.
27
+ - Codex parity: portable (prose-only skill/reference edits; `new2` is Claude-only by
28
+ design — `/new` classic remains the Codex path and gains the same alias probe).
29
+
30
+ ## [5.4.1] - 2026-07-03
31
+
32
+ **Minimal light lane — the roster for a simple card is now literally: grounding (inline),
33
+ coder, code review, doc review, commit, merge. Nothing else.** Completes v5.4.0 per the
34
+ maintainer's directive ("per cose così semplici voglio due passaggi in croce"): the compact
35
+ lane cut the CARD COUNT; this cuts the per-run AGENT COUNT. FEAT-0067-class run: ~33
36
+ agents → ~7.
37
+
38
+ - **Per-card doc review at light (single-card batch)**: the light review matrix adds the
39
+ scoped `doc-reviewer` per-card (review-cycle.md deferral-gate exception) because the
40
+ Final pass it used to defer to is now skippable.
41
+ - **Final Review skip gate (`final-review.md` § F.0 + `new2.js` `skipFinalLight`)**: ONE
42
+ light card + zero actionable per-card findings (→ zero fixes applied) → skip F.2–F.5
43
+ entirely. Honors the v4.56.1 refutation (per-card PRE-fix + final POST-fix are
44
+ non-redundant) by scoping the skip to runs where there IS no post-fix state; any
45
+ finding, fix, second card, or degraded reviewer → Final runs as always.
46
+ - **i18n fill spawn cut at light**: the owner briefing instructs the coder to fill every
47
+ target locale itself; the anti-hardcoded lint + locale-parity gates still enforce.
48
+ - **Production-readiness spawn cut for all-light batches**: no deployable surface exists
49
+ by Rule C (no migrations/indexes/cron/schema) — the agent only ever reported zero items.
50
+ - `check-new-parity.mjs` rule 18 locks the prose↔workflow lockstep for every cut.
51
+ - Skills: `new` 2.2.0 · `new2` 1.6.0.
52
+
53
+ Codex parity: portable (prose modules are the cross-tool SSOT; `new2.js` Claude-only by design).
54
+
8
55
  ## [5.4.0] - 2026-07-03
9
56
 
10
57
  **Elastic pipeline — `/prd`+`/new` compress to the minimum for trivial features and
package/VERSION CHANGED
@@ -1 +1 @@
1
- 5.4.0
1
+ 5.5.0
@@ -2,6 +2,27 @@
2
2
 
3
3
  Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
4
4
 
5
+ ## 2.2.1 — 2026-07-03 (FEAT-0067 post-mortem: migration vocabulary aliases)
6
+
7
+ - Il gate di delega `-auto` (condizione 4) e il Migration Gate Phase 0 1b (setup.md
8
+ step 2) ora rilevano una migrazione dichiarata sotto QUALSIASI vocabolario:
9
+ `migration_plan.required` (epic), `db_migrations_summary` (epic),
10
+ `db_migration.required: true` (per-card, convenzione overlay consumer). Una
11
+ migrazione dichiarata solo via alias con artifact non ancora su disco degrada come
12
+ oggi (missing-artifact) ma la dichiarazione viene SEMPRE registrata nel tracker —
13
+ il sync post-merge e il ledger dei follow-up devono conoscerla.
14
+
15
+ ## 2.2.0 — 2026-07-03 (framework v5.4.1)
16
+
17
+ - **Minimal light lane — roster cut to the core four.** A single light card now runs:
18
+ grounding-inline (step 2d) → owner → code review (Codex-light/code-reviewer) → scoped
19
+ doc-reviewer per-card (review-cycle deferral exception) → commit → merge. Cut for the
20
+ lane: i18n fill spawn (7c — owner fills locales inline, lint gate enforces), Final
21
+ Review when the per-card review is clean (final-review.md § F.0 Minimal-light skip
22
+ gate — POST-fix verification with zero fixes is vacuous; any finding/fix/second card →
23
+ Final runs), production-readiness spawn for all-light batches (no deployable surface
24
+ by Rule C). Guarded by check-new-parity rule 18.
25
+
5
26
  ## 2.1.0 — 2026-07-03 (framework v5.4.0)
6
27
 
7
28
  - **Light-lane inline grounding (implement.md step 2d).** A `review_profile: light` card
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: new
3
3
  effort: medium
4
- version: 2.1.0
4
+ version: 2.2.1
5
5
  description: >
6
6
  Orchestrate a team of specialized agents to implement one or more backlog cards
7
7
  end-to-end inside a dedicated worktree, with code review, doc review, QA, and
@@ -66,10 +66,16 @@ kickoff, right after arg parsing):
66
66
  2. `FORCE_INLINE == false` (the user did not pass `-inline`).
67
67
  3. The `Workflow` tool is available in this session AND `.claude/workflows/new2.js`
68
68
  is linked AND the `new2` skill is available.
69
- 4. No epic card in the batch declares `migration_plan.required: true` (cheap YAML
70
- probe on the batch's `group.parent` epics). A declared migration keeps the
71
- classic path its Phase 0 step 1b owns the owner-gated apply under
72
- § "AUTONOMOUS RESOLUTION RULE"; `new2`'s own migration gate is interactive.
69
+ 4. No card in the batch declares a DB migration, under ANY of the recognized
70
+ vocabularies (cheap YAML probe): epic `migration_plan.required: true`, epic
71
+ `db_migrations_summary` (non-empty), or a batch card with
72
+ `db_migration.required: true` (the consumer-overlay convention e.g. mayo's
73
+ prd-card-writer overlay). A declared migration keeps the classic path — its
74
+ Phase 0 step 1b owns the owner-gated apply under § "AUTONOMOUS RESOLUTION
75
+ RULE"; `new2`'s own migration gate is interactive. *Why the aliases are
76
+ load-bearing*: probing only `migration_plan` silently classified a
77
+ `db_migration`-declaring batch as migration-free (FEAT-0067 post-mortem — the
78
+ remote `db:push` was never surfaced).
73
79
 
74
80
  **Action**: log ONE tracker line `[DELEGATED] -auto batch → new2 engine
75
81
  (off-context): <residual args>` and invoke the `new2` skill with the SAME residual
@@ -42,6 +42,19 @@ Once ALL cards are committed in the worktree:
42
42
  > A single card has no cross-card surface, so a finder's per-card pass and its final pass are genuinely
43
43
  > the same review.
44
44
 
45
+ ### Step F.0 — Minimal-light skip gate (v5.4.1)
46
+
47
+ When the batch has exactly ONE committed card AND it is `review_profile: light` AND its
48
+ per-card review surfaced ZERO actionable findings (no blocks, no scope-expansion → no fix
49
+ pass was applied), **SKIP the entire Final Review** (F.2–F.5) and proceed to merge. The
50
+ Final pass exists for POST-fix verification and cross-card surface (v4.56.1 — per-card
51
+ PRE-fix + final POST-fix are non-redundant); with zero fixes and zero cross-card surface,
52
+ both reasons are absent — and doc coverage already ran per-card (the single-card light
53
+ review matrix includes the scoped doc-reviewer instead of deferring here, see
54
+ review-cycle.md § Deferral gate). Any finding on the card, any fix applied, any second
55
+ card, or any degraded reviewer → run the Final Review as written below. Log
56
+ `final-review: SKIPPED (single light card, clean per-card review — nothing post-fix to verify)`.
57
+
45
58
  ### Step F.1 — Resolve scope
46
59
 
47
60
  (All cards are committed; the batch now enters Final Review. No visibility emission — the internal tracker is the only state surface, see SKILL.md § "State surface — the tracker only". Record the Final-Review start/finish in the tracker only.)
@@ -500,7 +500,7 @@
500
500
  - **Grounding (cheap `grep`, like the Phase-1 plan-auditor grounding):** every cited source symbol/class MUST appear in `mockups/_src/` (`grep -rF "<symbol>" <prd_dir>/<slug>/mockups/_src/`), and every cited registry primitive MUST exist in the registry (`grep` `${paths.design_system}/INDEX.md` / `components/`). A symbol that is not in the source, or a primitive that is not in the registry, means a fabricated declaration → reject + re-spawn (do NOT pass it to the gates). Log `design-source: grounded` or `design-source: rejected (<reason>)` in the tracker.
501
501
  - This does NOT re-verify pixels — that stays with `visual-fidelity-verifier` (Phase 2.6). It only proves the source was read and the reuse-mapping targets are real. When the card has no design source, skip this step entirely.
502
502
 
503
- 7c. **i18n fill pass (explicit, deterministic — v5.0.0, promoted from the step-9 sub-bullet).** When `features.has_i18n: true` AND this card's diff added new source-locale keys — detect deterministically, never by intuition:
503
+ 7c. **i18n fill pass (explicit, deterministic — v5.0.0, promoted from the step-9 sub-bullet).** **Light lane (v5.4.1): SKIP this spawn** — on the light lane (step 2d) the owner briefing already instructed the coder to fill EVERY target locale itself (context-aware from the registry); the anti-hardcoded lint + locale-parity gates at step 8 enforce it. Log `i18n-fill: SKIP (light lane — owner fills inline)`. Otherwise: when `features.has_i18n: true` AND this card's diff added new source-locale keys — detect deterministically, never by intuition:
504
504
  ```bash
505
505
  cd <worktree-path>
506
506
  # $SRC_LOCALE = the source-language locale file path (from i18n config / the project's locales root)
@@ -6,6 +6,12 @@
6
6
 
7
7
  After Phase 6 completes (or after the final summary report if Phase 6 is deferred), present a **Production Readiness Checklist** — a clear list of all manual or infrastructure actions required to launch the implemented changes in production.
8
8
 
9
+ > **Minimal light lane (v5.4.1): SKIP the spawn for an all-`light` batch** — a light card
10
+ > carries no deployable surface by Rule C (no migrations, indexes, cron, schema, env); the
11
+ > checklist agent would only report zero items. Log
12
+ > `phase7-production: SKIPPED (all-light batch — no deployable surface)`. Any
13
+ > `balanced`/`deep` card in the batch → run as written.
14
+
9
15
  ### How to detect items
10
16
 
11
17
  Scan ALL files changed across the batch (use the tracker's completed cards + `git diff` against the base branch) and check for the items below.
@@ -305,7 +305,7 @@ skill's Phase 1 falls back to deriving Gherkin scenarios from
305
305
 
306
306
  > **Note**: Code review is NOT performed in this phase — it is handled by the **mandatory unconditional `/codexreview` gate in Phase 3.7**, which runs per-card BEFORE the Phase 4 commit. The post-batch `/codexreview` in the Final review remains as a final FULL-diff sweep over the entire batch (since v3.37.0 — the guaranteed full-depth merge gate). This Phase 3 focuses exclusively on documentation sync, which must happen per-card (tied to the specific commit).
307
307
 
308
- > **Deferral gate (since v4.7.0 — enumerated, `review_profile`-driven)**: SKIP per-card doc-review and defer it to the **Final Review F.3 doc-reviewer** (which runs over the entire batch) when the card is **`review_profile == light` AND its committed diff touches NO documentation file** (no `.md`, no path under `${paths.references_dir}`, no data-model/ssot/api doc). These are small code changes with no doc surface of their own; their only doc concern is drift, which the batch-wide Final doc-reviewer catches. Log `doc-review: DEFERRED to Final FULL gate (light, no doc files in diff)` and proceed to Phase 3.5. **KEEP per-card doc-review** for: `deep` cards, `balanced` cards (they may still PASS-fast via the deterministic gate below — that gate, not this deferral, is their only skip lane), ANY card whose diff touches a doc file (the change IS doc-relevant), and trivial cards (doc-review is their primary gate — § "Trivial-card fast-lane", unchanged). Never defer "for time" — the only enumerated reasons are `light + non-doc diff` and the deterministic PASS-fast below.
308
+ > **Deferral gate (since v4.7.0 — enumerated, `review_profile`-driven)**: SKIP per-card doc-review and defer it to the **Final Review F.3 doc-reviewer** (which runs over the entire batch) when the card is **`review_profile == light` AND its committed diff touches NO documentation file** (no `.md`, no path under `${paths.references_dir}`, no data-model/ssot/api doc). These are small code changes with no doc surface of their own; their only doc concern is drift, which the batch-wide Final doc-reviewer catches. Log `doc-review: DEFERRED to Final FULL gate (light, no doc files in diff)` and proceed to Phase 3.5. **KEEP per-card doc-review** for: `deep` cards, `balanced` cards (they may still PASS-fast via the deterministic gate below — that gate, not this deferral, is their only skip lane), ANY card whose diff touches a doc file (the change IS doc-relevant), and trivial cards (doc-review is their primary gate — § "Trivial-card fast-lane", unchanged). Never defer "for time" — the only enumerated reasons are `light + non-doc diff` and the deterministic PASS-fast below. **Single-card light batch exception (v5.4.1 minimal light lane): do NOT defer** — when the batch has exactly ONE card and it is `light`, the Final Review is itself SKIPPED if the per-card review is clean (see final-review.md § Minimal-light skip gate), so the deferral target would never run: run the scoped per-card doc-reviewer here instead.
309
309
  >
310
310
  > **Deterministic PASS-fast (v5.0.0 — measured: 84/127 per-card doc reviews returned PASS)**: when implement.md step 11c produced `/tmp/docinv-<CARD-ID>.json` AND it says `pass_fast_eligible: true` (zero non-advisory invariant rows triggered + no doc file in the diff + the card declares no `documentation_impact`/`canonical_docs`) AND the card is NOT `deep` → log `doc-review: PASS-fast (doc-invariants: 0 blocking rows) — Final doc-reviewer backstop stands` and **skip the per-card spawn** (proceed to Phase 3.5). No gate is removed: the batch-Final F.3 doc-reviewer still sees this card's diff. Own-Doc Closure: NEW-file obligations are definitionally empty (no invariant row fired, no docs declared); obligations on MODIFIED primitives/routes are covered by the automatic spec resync (`DS_COMPONENT_STALE` serializer gate, v4.70.0) plus the Final F.3 backstop — the invariant table's R1/R3 fire on new files only, by design. `deep` cards always spawn. Missing docinv JSON → no PASS-fast (fall through to the normal path).
311
311
 
@@ -22,7 +22,7 @@
22
22
  1b. **Migration Gate (BLOCKING only when a migration is *declared* — else a silent no-op)** — resolve DB migrations interactively **before** the worktree exists, so the schema is live before any card builds against it. *Why this exists*: a migration applied to a shared/remote DB is owner-gated, so without this gate it is deferred to the END of the batch — and every downstream card in the batch is then built and verified against a schema that is not yet live (`validation_commands` / QA / E2E / DB-generated `tsc` types fail falsely → those cards cascade into deferral/blocked). Front-loading the migration removes that root cause. **The declaration lives in the EPIC card** (`migration_plan` block — project-specific, authored by the user, typically via the `.baldart/overlays/new.md` overlay). Steps:
23
23
 
24
24
  1. **Find the batch's epic card(s).** Collect the distinct `group.parent` of the batch cards (or, for a `-full` invocation, the parent already known). For each, locate the epic YAML in `${paths.backlog_dir}` (filename `<PARENT>-*-epic.yml`, or the card whose `id == <PARENT>` / `group.is_epic: true`) and Read it.
25
- 2. **Look for `migration_plan` with `required: true`.** If **no** epic in the batch declares one → write `## Migration\nnone (no migration_plan declared)` to the tracker and **proceed to step 2** (behaviour is identical to today — zero extra prompts). This is the common case; do not surface anything.
25
+ 2. **Look for a declared migration — under ANY recognized vocabulary.** Primary: epic `migration_plan` with `required: true`. **Compat aliases (consumer-overlay conventions — same semantics, different field names)**: epic `db_migrations_summary` (non-empty), or any batch card carrying `db_migration.required: true` (map: `artifacts` ← each `db_migration.expected_file`; `summary` ← the slug; apply/push modalities ← `db_migration.remote_push` + the overlay's post-merge section; no `verify` unless declared). A migration declared ONLY via alias whose `expected_file` does not yet exist on disk (it will be authored mid-run by the coder) follows the step-3 missing-artifact degradation — but the declaration itself MUST still be recorded in the tracker (`## Migration\ndeclared (via db_migration) — deferred to post-merge sync`) so the post-merge Migration Sync step and the follow-up ledger know about it. If **nothing** declares a migration → write `## Migration\nnone (no migration declared)` to the tracker and **proceed to step 2** (behaviour is identical to today — zero extra prompts). This is the common case; do not surface anything.
26
26
  3. **(declared) Verify the artifacts exist on disk.** For each path in `migration_plan.artifacts`, check it exists under `$MAIN`. If **any is missing** → do **NOT** apply or prompt; write `## Migration\ndeclared but artifact(s) missing: <paths> — degraded to deferred (author the migration first)` to the tracker and **proceed to step 2** (the migration falls back to the current end-of-batch owner-gated deferral — no regression). *(Auto-generating a missing artifact is out of scope; the migration must already be authored to be applied up-front.)*
27
27
  4. **(declared + artifacts present) Assemble the apply modalities**, in this source order, de-duplicating by `id`: (a) `migration_plan.apply_modalities` from the epic block; (b) a `## Migration modalities` section in `.baldart/overlays/new.md` if present; (c) any project-memory note on how this project applies migrations; (d) the built-in tail `["Già applicata — prosegui", "Abort"]`. Each modality is `{ id, label, command? }`.
28
28
  5. **`AskUserQuestion`** — `"La epic dichiara una migrazione DB (<summary>). Va applicata PRIMA del batch così le card downstream verificano contro lo schema reale. Come procedo?"` with up to 4 of the assembled modalities (always include "Già applicata — prosegui" and "Abort" as the last options). This is a legitimate Phase 0 question (Auto Mode does not override it).
@@ -2,6 +2,28 @@
2
2
 
3
3
  Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
4
4
 
5
+ ## 1.7.0 — 2026-07-03 (FEAT-0067 post-mortem: migration mai deployata)
6
+
7
+ - **Step 3.5 vocabulary aliases**: il Migration Gate ora riconosce, oltre a
8
+ `migration_plan.required` (epic), anche `db_migrations_summary` (epic) e
9
+ `db_migration.required: true` (per-card — convenzione overlay consumer, es. mayo).
10
+ Root cause FEAT-0067: il probe solo-`migration_plan` classificava la batch come
11
+ migration-free → gate in silent no-op, `db:push` mai emerso.
12
+ - **Step 5.1b Migration Sync Tail (NEW)**: post-merge, il ramo B interattivo del sync
13
+ gate eseguito dalla SKILL (check-sync read-only → su rosso `AskUserQuestion`
14
+ Push now / Defer / Override; mai push in autonomo). Trigger belt-and-braces sulla
15
+ DICHIARAZIONE Step 3.5, non solo sul residual ledger (che il DONE-flip di FEAT-0067
16
+ aveva perso). Telemetria `migration_sync_tail`.
17
+ - Step 1.5: nota di delega aggiornata (il probe di `/new -auto` copre tutti i vocabolari).
18
+
19
+ ## (workflow new2.js, framework v5.4.1) — 2026-07-03
20
+
21
+ - Minimal light lane mirror: single-card light adds per-card doc-reviewer to the review
22
+ matrix, skips the i18n fill spawn (owner briefing carries the fill instruction),
23
+ skips new-final-review when `lightLane && cleanReview` (zero actionable findings),
24
+ skips production-readiness for all-light batches. Result objects carry
25
+ `lightLane`/`cleanReview` for the Final gate.
26
+
5
27
  ## (workflow new2.js, framework v5.4.0) — 2026-07-03
6
28
 
7
29
  - Light lane: `review_profile: light|skip` cards no longer spawn the Phase-1 retriever —
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: new2
3
3
  effort: high
4
- version: 1.5.0
4
+ version: 1.7.0
5
5
  description: >
6
6
  Workflow-hosted batch engine for /new. Implements one or
7
7
  more backlog cards end-to-end by delegating the WHOLE batch to a background
@@ -85,8 +85,9 @@ everywhere this skill branches on it — Step 3.5 resolves deterministically (it
85
85
  AUTONOMOUS branch) and **Step 3b's escape hatch is SKIPPED** (the user asked for
86
86
  `-auto`; a post-batch question — and a possible `/new` re-invocation at full
87
87
  inline cost — would break that promise even with a TTY present). `/new` only
88
- delegates **migration-free** batches (it probes `migration_plan.required`
89
- itself), so Step 3.5's one interactive question cannot fire on a delegated run —
88
+ delegates **migration-free** batches (it probes ALL migration vocabularies
89
+ itself `migration_plan.required`, epic `db_migrations_summary`, per-card
90
+ `db_migration.required`), so Step 3.5's one interactive question cannot fire on a delegated run —
90
91
  if you nonetheless find a declared, unapplied migration in a delegated batch,
91
92
  HALT and hand back to `/new` classic (`"batch con migrazione dichiarata — riesegui
92
93
  /new <cards> -auto -inline"`) instead of asking. Everything else is identical to a
@@ -129,8 +130,16 @@ untouched), so the schema is live **before** the workflow starts. It mirrors `/n
129
130
  1. **Find the batch's epic card(s)** — distinct `group.parent` of the resolved cards (or the `-full`
130
131
  parent). Read each epic YAML in `${paths.backlog_dir}` (`<PARENT>-*-epic.yml`, or the card whose
131
132
  `id == <PARENT>` / `group.is_epic: true`).
132
- 2. **Look for `migration_plan` with `required: true`.** None set `migration = { status: 'none' }`
133
- and go to Step 4 (identical to today zero extra prompts; do not surface anything).
133
+ 2. **Look for a declared migration under ANY recognized vocabulary.** Primary: epic
134
+ `migration_plan` with `required: true`. **Compat aliases (consumer-overlay conventions
135
+ FEAT-0067 root cause: probing only `migration_plan` made this gate structurally blind to
136
+ `db_migration`-declaring cards)**: epic `db_migrations_summary` (non-empty), or any batch card
137
+ with `db_migration.required: true` (map: `artifacts` ← `db_migration.expected_file`; `summary`
138
+ ← the slug; modalities ← `db_migration.remote_push` + overlay). Nothing declared → set
139
+ `migration = { status: 'none' }` and go to Step 4 (identical to today — zero extra prompts; do
140
+ not surface anything). Declared via ANY vocabulary → record `migrationDeclared: true` +
141
+ `declaredVia` in your own working state (Step 5.1b keys off it — it must NOT depend on the
142
+ workflow's residual ledger surviving) and continue below.
134
143
  3. **(declared) Verify `migration_plan.artifacts` exist on disk under `$MAIN`.** Any missing → do NOT
135
144
  apply or prompt; set `migration = { status: 'degraded', reason: 'artifact missing', artifacts }`
136
145
  and go to Step 4 (the migration falls back to the current end-of-batch owner-gated deferral — no
@@ -245,6 +254,40 @@ returns when the batch is done. It returns:
245
254
  step-1b HALT fields — the `/new` pre-flight back-fill (step 1b-ii) then normalizes its
246
255
  `review_profile`/`owner_agent` on first ingestion, so even the crisis stub self-heals. This
247
256
  main-repo, **disk-verified** write is the SSOT — nothing is dropped even on a non-merged batch.
257
+ 1b. **Migration Sync Tail (post-merge "branch B" — the interactive owner-gated deploy the
258
+ autonomous batch could not run).** The workflow has returned and merged; you are the skill
259
+ main loop, in `$MAIN`, typically on `$TRUNK` — exactly the precondition of the project's
260
+ post-merge sync gate (e.g. the overlay's "on trunk, post-merge" branch). **Trigger — EITHER
261
+ condition, independently** (belt-and-braces: the FEAT-0067 failure dropped the residual
262
+ mid-run, so this tail keys on the Step-3.5 DECLARATION, never only on the ledger):
263
+ (a) `migrationDeclared` from Step 3.5 (any vocabulary) with `migration.status ≠ 'applied'`;
264
+ (b) any `residuals[]` entry whose kind/owner-gated action maps to `db-migration-deploy`.
265
+ Neither → skip silently. Steps:
266
+ - **Resolve the project's post-merge sync contract**, in source order: (i) the batch cards'
267
+ `db_migration.remote_push` / `migration_plan` (check command + push command); (ii) the
268
+ post-merge / trunk branch of the Migration Sync Gate section in `.baldart/overlays/new.md`;
269
+ (iii) `migration_plan.verify` alone (check-only). Nothing resolvable → skip the tail (the
270
+ step-1 follow-up card remains the ledger) and say so in the presentation.
271
+ - **Run the read-only sync CHECK first** (e.g. the overlay's check-sync command), from `$MAIN`
272
+ on `$TRUNK` (if `$MAIN` is not on `$TRUNK`, do not switch branches yourself — report and
273
+ leave the follow-up as ledger). **Green** → the migration is live: the `db-migration-deploy`
274
+ residual is SATISFIED (no follow-up needed — if step 1 already created one, delete it in the
275
+ same reconciliation commit and note why); record it and let step 2 DONE-flip the migration
276
+ card with its schema AC satisfied.
277
+ - **Red + INTERACTIVE** → ONE `AskUserQuestion` (post-batch skill-layer interaction, same
278
+ class as Step 3b — the zero-ask-during-batch invariant is untouched): *"La migration del
279
+ batch non risulta applicata al DB remoto (check-sync rosso). Come procedo?"* Options:
280
+ **[Push now]** (run the declared owner-gated push command from `$TRUNK` — honour
281
+ `stack.schema_deploy_from_trunk_only`; output to `/tmp/migration-push-<firstCard>.log`,
282
+ surface the exit code, then RE-RUN the check and require green) / **[Defer]** (keep the
283
+ step-1 follow-up card as the ledger; the card stays DONE-with-follow-up per its
284
+ `owner-gated` class) / **[Override]** (record the user's verbatim reason). Never push
285
+ without the user picking it.
286
+ - **Red + AUTONOMOUS** (env / delegated `/new -auto` origin — Step 1.5) → NEVER push
287
+ (outward, owner-gated). Ensure the step-1 follow-up exists, and surface the pending deploy
288
+ PROMINENTLY in the presentation (step 4) — a truthful "schema NOT live" line, not a footnote.
289
+ - Record `migration_sync_tail: { ran, trigger: 'declared'|'residual'|'both', check:
290
+ 'green'|'red'|'unavailable', pushed: <bool>, outcome }` for telemetry (step 5).
248
291
  2. **Mark deferred cards DONE — only after their follow-up exists AND every deferral class allows
249
292
  it (F-040/H + A3).** Some committed cards were intentionally left **NON-DONE** because they carry
250
293
  an open deferred AC: they are `perCardResults[]` entries with `deferred:true` plus a
@@ -358,7 +401,8 @@ returns when the batch is done. It returns:
358
401
  (partial, output-only, excludes nested workflows). Keep `degraded`/`degradation_reasons` +
359
402
  `cards_deferred_done_pending` in the record so
360
403
  the A/B comparison stays honest. Also record `migration_gate: <migration.status>`
361
- (`none`|`applied`|`skipped`|`degraded`) the Step-3.5 gate is a pre-launch interaction, NOT a
404
+ (`none`|`applied`|`skipped`|`degraded`) and `migration_sync_tail` (Step 5.1b whether the
405
+ post-merge check ran, its outcome, and whether an owner-gated push was user-approved) — the Step-3.5 gate is a pre-launch interaction, NOT a
362
406
  mid-batch question, so it does not break the zero-ask-during-batch invariant; logging it keeps the
363
407
  A/B honest about when a migration was front-loaded. Keep `deferral_breakdown` (per-class counts
364
408
  of WHY residuals became follow-ups instead of in-batch fixes) in the record — a class dominating
@@ -617,7 +617,7 @@ async function runCard(cardId, cardPath) {
617
617
  const phase1Brief = architectOk
618
618
  ? `Phase 1 already ran as specialist agents. READ the architecture baseline at ${baselinePath} BEFORE writing any code — it is your codebase context; do NOT redo the exploration.${auditCorrections.length ? `\nPlan-audit corrections (apply them as amendments to this briefing — do NOT modify the backlog YAML):\n${auditCorrections.map((c, i) => ` ${i + 1}. ${c}`).join('\n')}` : ''}`
619
619
  : phase1.architect === 'inline-light'
620
- ? `Phase 1 light lane (v5.4.0): this card is review_profile '${reviewProfile}' (≤3-file scale) — no dedicated retriever ran. Ground INLINE per AGENTS.md § Change Tiers (orchestrated-pipeline exception): BEFORE writing code, read the files in scope + their immediate integration points (imports/consumers). Scoped exploration of the touched area only — not a repo sweep; the understanding obligation is unchanged, only the delegation was waived.`
620
+ ? `Phase 1 light lane (v5.4.0): this card is review_profile '${reviewProfile}' (≤3-file scale) — no dedicated retriever ran. Ground INLINE per AGENTS.md § Change Tiers (orchestrated-pipeline exception): BEFORE writing code, read the files in scope + their immediate integration points (imports/consumers). Scoped exploration of the touched area only — not a repo sweep; the understanding obligation is unchanged, only the delegation was waived.${features.has_i18n ? ' I18N (light lane): NO separate fill pass runs for this card — fill EVERY target locale yourself for any new key (context-aware from the registry); the anti-hardcoded lint gate must pass.' : ''}`
621
621
  : `Phase 1 fallback: the architect specialist was unavailable — do the Phase 1 claim+architect exploration yourself per ${REF}/implement.md and persist the baseline to ${baselinePath} before coding.`
622
622
  // T1.A (v4.90.0) — mockup cards: binding-compliance is verified deterministically by the
623
623
  // check-bindings.mjs script (ui-implement Step 5b is the SSOT; this is the new2 parallel).
@@ -670,7 +670,10 @@ async function runCard(cardId, cardPath) {
670
670
  // v5.0.0 — explicit i18n fill pass (implement.md step 7c mirror; closes the measured leakage
671
671
  // where locale fills were spawned as general-purpose). The translator is named BY TYPE; it
672
672
  // self-detects new source-locale keys from the diff and no-ops cheaply when there are none.
673
- if (features.has_i18n) {
673
+ // v5.4.1 minimal light lane — no separate fill pass: the owner filled every locale itself
674
+ // (its briefing carries the instruction) and the Phase-2 anti-hardcoded lint gate enforces it.
675
+ if (features.has_i18n && lightLane) g('i18n-fill', 'SKIP', 'light lane — owner fills locales inline; lint gate enforces')
676
+ else if (features.has_i18n) {
674
677
  try {
675
678
  const fill = await agentSafe(
676
679
  `You are i18n-translator (fill pass for card ${cardId} — agents/i18n-protocol.md per-card cascade). cd into the card worktree. FIRST detect deterministically whether this card's diff (/tmp/diff-${cardId}.txt) added NEW source-locale keys; if none → return { ran:false, keys:0 } immediately (no-op). If keys exist: fill the missing TARGET locales for exactly those keys, context-aware from the registry at ${paths.i18n_registry || 'the i18n registry'} — the source locale stays untouched (the coder is source-only by design). Then re-run the project's locale-parity check if one exists. Return: { ran, keys, note }`,
@@ -834,7 +837,14 @@ async function runCard(cardId, cardPath) {
834
837
  }
835
838
  let reviewers
836
839
  if (reviewProfile === 'skip') reviewers = []
837
- else if (reviewProfile === 'light') reviewers = codexAvail ? ['codex'] : ['code-reviewer']
840
+ else if (reviewProfile === 'light') {
841
+ reviewers = codexAvail ? ['codex'] : ['code-reviewer']
842
+ // v5.4.1 minimal light lane — a single-card light batch skips the Final workflow when its
843
+ // per-card review is clean (nothing post-fix to verify), so doc coverage cannot defer
844
+ // there: run the scoped doc-reviewer per-card instead. Multi-card light keeps the
845
+ // deferral (the Final batch pass still runs and covers it).
846
+ if (cardIds.length === 1) reviewers.push('doc-reviewer')
847
+ }
838
848
  else if (noEvidence) reviewers = FULL_FANOUT // balanced/deep, no surface evidence → conservative full
839
849
  else reviewers = relevanceGated() // balanced AND deep — surface-gated breadth
840
850
  if (reviewProfile !== 'skip') g('review-matrix', 'PLANNED', `[${reviewProfile}${noEvidence ? '→conservative-full (no surface evidence)' : ''}] ${reviewers.join('+') || '(none)'} · docs:${touchesDocs} code:${touchesCode} api/data:${touchesApiData} sec:${securityRelevant}`)
@@ -974,8 +984,13 @@ async function runCard(cardId, cardPath) {
974
984
  // B6 — which reviewers ACTUALLY ran (the gated matrix); Phase Final keys its slim/skip
975
985
  // decision on this, and per_card telemetry records it for the A/B spawn accounting.
976
986
  reviewersRun: reviewers,
977
- // B7 — Phase-1 specialist record (architect: done|inline-fallback; audit: pass|fixes|skipped-*).
987
+ // B7 — Phase-1 specialist record (architect: done|inline-fallback|inline-light; audit: pass|fixes|skipped-*).
978
988
  phase1,
989
+ // v5.4.1 minimal light lane — Phase Final keys its skip on these: a single light card whose
990
+ // per-card review surfaced NOTHING actionable has no post-fix state for the Final pass to
991
+ // verify (the Final review's purpose — v4.56.1 — is POST-fix verification, not re-review).
992
+ lightLane,
993
+ cleanReview: blocks.length === 0 && scopeExp.length === 0,
979
994
  scopeFiles, archBaselinePath: `/tmp/arch-baseline-${cardId}.md`, gates,
980
995
  }
981
996
  }
@@ -1151,7 +1166,13 @@ const committed = perCardResults.filter((r) => r.status === 'committed')
1151
1166
  phase('Final')
1152
1167
  let finalSummary = null
1153
1168
  let mergeBlocked = degraded
1154
- if (committed.length && !degraded) {
1169
+ // v5.4.1 minimal light lane — ONE light card, clean per-card review (zero actionable
1170
+ // findings → zero fixes applied): the Final pass has nothing post-fix to verify and its
1171
+ // doc coverage already ran per-card (the light single-card matrix includes doc-reviewer).
1172
+ // Any finding/fix on the card, any second card, or any degradation → Final runs as always.
1173
+ const skipFinalLight = committed.length === 1 && committed[0].lightLane && committed[0].cleanReview && !degraded
1174
+ if (skipFinalLight) ledger(firstCard, 'final-review', 'SKIPPED', 'single light card, clean per-card review — nothing post-fix to verify (doc ran per-card)')
1175
+ if (committed.length && !degraded && !skipFinalLight) {
1155
1176
  const reviewScopeFiles = dedupe(committed.flatMap((r) => r.scopeFiles || []))
1156
1177
  const archPaths = committed.map((r) => r.archBaselinePath).filter(Boolean)
1157
1178
  const allArch = archPaths.length === committed.length ? archPaths : null
@@ -1295,7 +1316,11 @@ if (!committed.length) {
1295
1316
  // Phase Production (Phase 7) — non-blocking; report-not-execute (F-011 infra-deferred).
1296
1317
  // ───────────────────────────────────────────────────────────────────────────
1297
1318
  phase('Production')
1298
- if (mergeResult && mergeResult.merged) {
1319
+ if (mergeResult && mergeResult.merged && committed.length && committed.every((r) => r.lightLane)) {
1320
+ // v5.4.1 minimal light lane — light cards carry no deployable surface by Rule C (no
1321
+ // migrations/indexes/cron/schema); the checklist would only burn a spawn to report zero items.
1322
+ ledger(firstCard, 'phase7-production', 'SKIPPED', 'all-light batch — no deployable surface (Rule C)')
1323
+ } else if (mergeResult && mergeResult.merged) {
1299
1324
  try {
1300
1325
  // Schema-deploy-from-trunk-only invariant (v4.33.0): a remote schema/RLS/index/
1301
1326
  // migration deploy desyncs a shared remote datastore when run off-trunk. When the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "baldart",
3
- "version": "5.4.0",
3
+ "version": "5.5.0",
4
4
  "description": "Claude Agent Framework - Reusable framework for coordinating AI agents and humans in software projects",
5
5
  "bin": {
6
6
  "baldart": "./bin/baldart.js"