baldart 4.89.2 → 4.92.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/VERSION +1 -1
  3. package/framework/.claude/agents/api-perf-cost-auditor.md +9 -0
  4. package/framework/.claude/agents/code-reviewer.md +9 -0
  5. package/framework/.claude/agents/codebase-architect.md +6 -0
  6. package/framework/.claude/agents/coder.md +1 -1
  7. package/framework/.claude/agents/markup-fidelity-verifier.md +24 -1
  8. package/framework/.claude/agents/qa-sentinel.md +9 -0
  9. package/framework/.claude/agents/security-reviewer.md +9 -0
  10. package/framework/.claude/agents/ui-expert.md +49 -0
  11. package/framework/.claude/agents/visual-fidelity-verifier.md +16 -12
  12. package/framework/.claude/skills/e2e-review/CHANGELOG.md +49 -0
  13. package/framework/.claude/skills/e2e-review/SKILL.md +178 -24
  14. package/framework/.claude/skills/new/CHANGELOG.md +29 -0
  15. package/framework/.claude/skills/new/SKILL.md +44 -2
  16. package/framework/.claude/skills/new/references/completeness.md +1 -0
  17. package/framework/.claude/skills/new/references/implement.md +19 -3
  18. package/framework/.claude/skills/new/references/review-cycle.md +23 -1
  19. package/framework/.claude/skills/new/references/team-mode.md +4 -0
  20. package/framework/.claude/skills/new/scripts/verify-item.sh +54 -0
  21. package/framework/.claude/skills/new2/CHANGELOG.md +30 -0
  22. package/framework/.claude/skills/new2/SKILL.md +35 -8
  23. package/framework/.claude/skills/ui-implement/CHANGELOG.md +15 -0
  24. package/framework/.claude/skills/ui-implement/SKILL.md +31 -6
  25. package/framework/.claude/skills/ui-implement/references/integration.md +12 -3
  26. package/framework/.claude/skills/ui-implement/scripts/check-bindings.mjs +103 -0
  27. package/framework/.claude/workflows/new-card-review.js +19 -5
  28. package/framework/.claude/workflows/new-final-review.js +24 -9
  29. package/framework/.claude/workflows/new2.js +50 -4
  30. package/framework/agents/code-search-protocol.md +24 -0
  31. package/framework/agents/design-system-protocol.md +27 -0
  32. package/framework/docs/WORKFLOWS.md +9 -4
  33. package/framework/scripts/structural-compare.mjs +178 -0
  34. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: e2e-review
3
3
  effort: medium
4
- version: 1.0.0
4
+ version: 1.2.0
5
5
  description: >
6
6
  Deterministic, BLOCKING end-to-end review run after a feature is implemented.
7
7
  Combines functional E2E (Playwright spec written by `coder`, executed via
@@ -181,10 +181,18 @@ system constraints into a machine-readable verification plan.
181
181
  }
182
182
  ],
183
183
  "non_functional_checks": ["loads in < 2s on slow 3G"],
184
- "mockup_refs": [{"route": "/dashboard", "level": "local", "path": "mockups/dashboard.png"}]
184
+ "mockup_refs": [{"route": "/dashboard", "level": "local", "path": "mockups/dashboard.png"}],
185
+ "tablet_in_scope": false
185
186
  }
186
187
  ```
187
188
 
189
+ 5. **Tablet-in-scope detection (deterministic).** Set `tablet_in_scope: true` when
190
+ the card's title, `acceptance_criteria[]`, `scope`, or `requirements[]` mention
191
+ `tablet` (case-insensitive) or an explicit breakpoint in the 768–1024px band.
192
+ This drives the optional third viewport capture (Phase 3) and the tablet
193
+ responsive pass (Phase 4 step 2c). Default `false` — the tablet lane is
194
+ scope-driven, not unconditional (cost discipline).
195
+
188
196
  ---
189
197
 
190
198
  ## Phase 2 — Mockup Source Cascade (4 levels)
@@ -227,8 +235,17 @@ directory alongside its YAML:
227
235
 
228
236
  **Structural source (route-free ground truth).** Independently of the image, record a
229
237
  `structural_source` for the route when the mockup is **code**:
230
- - `links.design` is an `.html` file → `structural_source = { level: "html", mockup_path:
231
- "<abs html path>" }`.
238
+ - `links.design` is an `.html` file → FIRST probe it for an encoded Claude Design
239
+ offline bundle: run `node .framework/framework/scripts/extract-mockup-design.mjs
240
+ --input <abs html path> --out .baldart/e2e-review/<CARD-ID>/mockups/_src/` (always
241
+ exits 0, one JSON line on stdout). On `status: "ok"` the decoded JSX/CSS is the
242
+ richer ground truth → `structural_source = { level: "design_src", design_src_dir:
243
+ ".baldart/e2e-review/<CARD-ID>/mockups/_src/" }`. On `status: "no-code"` /
244
+ `"skipped"` the raw markup is still diffable → `structural_source = { level:
245
+ "html", mockup_path: "<abs html path>" }`. **Never leave an `.html` mockup with no
246
+ `structural_source`** — a decode failure downgrades the source, it does not drop
247
+ the structural lane (that was the handoff gap that let an HTML mockup skip Phase
248
+ 2.7 entirely).
232
249
  - `links.design_src` is set / `mockups/_src/` exists (finished Claude Design CODE, v4.75)
233
250
  → `structural_source = { level: "design_src", design_src_dir: "<abs dir>" }`.
234
251
  This feeds Phase 2.7 (`markup-fidelity-verifier`) and is what makes per-card fidelity
@@ -258,6 +275,35 @@ If none of (a)–(c) apply (no mockup, no design system):
258
275
 
259
276
  The cascade output is appended to the plan under `mockup_refs[]`.
260
277
 
278
+ ### Phase 2 closing step — Lane Matrix (verification-coverage ledger)
279
+
280
+ SSOT: `design-system-protocol.md` § "Route-Independent Fidelity & Coverage
281
+ Obligation" → 6. Verification-Lane Ledger. After the cascade, derive the
282
+ **planned lanes** per route and persist them into `plan.json` under `lanes`:
283
+
284
+ ```json
285
+ "lanes": {
286
+ "/dashboard": {
287
+ "structural": "planned", // iff a structural_source was recorded
288
+ "visual_desktop": "planned", // iff mockup_source.level ∈ {figma, local, compliance-only}
289
+ "visual_mobile": "planned", // iff the route is renderable-in-plan and not marked desktop-only
290
+ "visual_tablet": "planned", // iff plan.tablet_in_scope == true (same renderability condition)
291
+ "quality": "planned", // iff a quality oracle exists (has_design_system OR ui_guidelines)
292
+ "functional": "planned" // iff ≥1 scenario touches the route
293
+ }
294
+ }
295
+ ```
296
+
297
+ A lane that does not meet its condition is recorded `"not_planned"` (it will never
298
+ be asserted). As each later phase runs, update the route's lane in the state
299
+ directory to `"ran"` or `"skipped:<authorized reason>"` — this ledger is what
300
+ Phase 5 step 0 reconciles. The authorized skip reasons are ONLY:
301
+ `pixel_diff_below_threshold` (visual pre-filter passed — the comparison DID run),
302
+ `render_unavailable_structural_covered` (Phase 3.8 outcome on a code-form mockup),
303
+ `no_quality_oracle`, `harness_render_fidelity_guard`, `desktop_only_route`
304
+ (responsive passes), `quality_prefilter_skip` (Phase 4b gate). Anything else left
305
+ un-run is an under-run and blocks (Phase 5 step 0).
306
+
261
307
  ---
262
308
 
263
309
  ## Phase 2.7 — Structural Fidelity (route-free, runs BEFORE the browser)
@@ -278,6 +324,20 @@ For each such route:
278
324
  1. **Resolve the implementation files** for the route from the diff / the card's File
279
325
  Ownership Map (the `*.tsx` / `*.css` / `*.module.css` / `*.scss` / `*.svelte` / `*.vue`
280
326
  the card touched under `${paths.app_dir}` / `${paths.components_root}`).
327
+ 1b. **Deterministic pre-gate (structural-compare.mjs — signals, not verdicts).** Run:
328
+
329
+ ```bash
330
+ node .framework/framework/scripts/structural-compare.mjs \
331
+ --mockup <structural_source dir|file> --impl <implementation files> \
332
+ > .baldart/e2e-review/<CARD-ID>/structural-signals-<route-slug>.json
333
+ ```
334
+
335
+ One JSON line (`match | divergence | skipped` + graded-confidence findings). The
336
+ signals do NOT replace the verifier (a regex signature cannot see grids/landmarks
337
+ owned by composed primitives — necessary, not sufficient) and a `match` never
338
+ authorizes skipping step 2. They are **focus hints**: pass them to the verifier
339
+ below so it confirms or dismisses each deterministic signal explicitly instead of
340
+ rediscovering the obvious. Script errors fail open (`skipped`).
281
341
  2. **Invoke `markup-fidelity-verifier`** (input contract in
282
342
  [`markup-fidelity-verifier.md`](../../agents/markup-fidelity-verifier.md)):
283
343
 
@@ -295,7 +355,8 @@ For each such route:
295
355
  "mockup_source": { ...the route's structural_source (html | design_src)... },
296
356
  "implementation_files": [ ...abs paths the card touched for this route... ],
297
357
  "tolerance": "<from config>",
298
- "resolved_orphans": [ ...from the card's UI Element Inventory, as in Phase 4... ]
358
+ "resolved_orphans": [ ...from the card's UI Element Inventory, as in Phase 4... ],
359
+ "deterministic_signals": { ...the structural-compare.mjs output from step 1b... }
299
360
  }
300
361
  ```
301
362
  3. **Parse the JSON strictly** (same discipline as Phase 4). On malformed output log
@@ -360,9 +421,19 @@ capture the result + screenshots per route.
360
421
  path: ".baldart/e2e-review/<CARD-ID>/screenshots/<route-slug>@mobile.png",
361
422
  fullPage: true
362
423
  });
424
+ // Tablet — ONLY when plan.tablet_in_scope == true (Phase 1 step 5).
425
+ await page.setViewportSize({ width: 768, height: 1024 });
426
+ await page.goto(route);
427
+ await page.waitForLoadState("networkidle");
428
+ await page.screenshot({
429
+ path: ".baldart/e2e-review/<CARD-ID>/screenshots/<route-slug>@tablet.png",
430
+ fullPage: true
431
+ });
363
432
  Both screenshot paths are consumed by the visual-fidelity-verifier in
364
433
  Phase 4 — do not change them. 375px is the canonical small-screen audit
365
- width (design-system-protocol Reference Tables); it is fixed, not a config key.
434
+ width and 768px the canonical tablet audit width (design-system-protocol
435
+ Reference Tables); they are fixed, not config keys. Omit the tablet block
436
+ entirely when `tablet_in_scope` is false.
366
437
  - For OTP-driven personas (test_plan.test_credentials.auth_method == "otp"),
367
438
  use `await page.pause()` between phone-entry and code-entry. Document
368
439
  the manual step in a test.step block.
@@ -470,10 +541,18 @@ still wrong).
470
541
  For each `route` in `plan.routes[]`:
471
542
 
472
543
  1. **Pre-filter with pixel-diff** (optimization to control Vision cost):
473
- - When `mockup_source.level in {figma, local}`, run a pixel-level diff
474
- (any cheap library: `pixelmatch`, `looks-same`, or even ImageMagick's
475
- `compare -metric AE`) between the implementation screenshot and the
476
- mockup. Pass `pixel_diff_threshold` from config (default `0.02` = 2%).
544
+ - **1a-pre. Viewport-dimension validation (BEFORE any diff).** Compare the
545
+ implementation screenshot's pixel WIDTH with the mockup image's (±5px for
546
+ rounding). On mismatch, the pixel-diff is meaningless a diff computed
547
+ across different widths can land under the threshold while hiding a real
548
+ misalignment (the false-negative channel), or blow to 100% noise. Do NOT
549
+ run the pre-filter: log `viewport_dimension_mismatch (<impl>w vs <mock>w)`
550
+ in `gaps[]` and go DIRECTLY to step 2 (the Vision comparison judges
551
+ content, not pixel alignment). Never resize either image to force the diff.
552
+ - When `mockup_source.level in {figma, local}` (and dimensions validated), run
553
+ a pixel-level diff (any cheap library: `pixelmatch`, `looks-same`, or even
554
+ ImageMagick's `compare -metric AE`) between the implementation screenshot
555
+ and the mockup. Pass `pixel_diff_threshold` from config (default `0.02` = 2%).
477
556
  - If the diff is **below** the threshold, skip the Vision call for this
478
557
  route and record an empty findings array with
479
558
  `pre_filter: "pixel_diff_below_threshold"`. This is the primary
@@ -561,6 +640,14 @@ For each `route` in `plan.routes[]`:
561
640
  desktop-only in the plan. Cost is ~one extra verifier call per route — measured
562
641
  marginal (~0.3% of a card run) because the 375px screenshot is lighter than the
563
642
  1440px one and only renderable routes incur it.
643
+ 2c. **Tablet responsive-integrity pass** *(only when `plan.tablet_in_scope ==
644
+ true`)*. Same contract as step 2b with the `<route-slug>@tablet.png`
645
+ screenshot, `"viewport": { "width": 768, "height": 1024 }` and
646
+ `viewport_role: "tablet"`. This closes the "responsive middle" gap: a card
647
+ whose ACs promise tablet behaviour (the 768–1024px band) was previously
648
+ verified only at 1440px + 375px, so a grid that collapses at the wrong
649
+ breakpoint hid between the two audited widths. Findings are tagged
650
+ `viewport_role: "tablet"` and deduplicate alongside the other passes.
564
651
  3. **Parse the agent's JSON response** strictly. If the response is malformed
565
652
  or contains prose outside the JSON, log
566
653
  `verifier_protocol_violation` in `gaps[]` and treat the route's findings
@@ -596,7 +683,24 @@ mockup) **and `mockup_source.level == "harness-render"` per-primitive PNGs from
596
683
  arrive here, NEVER at Phase 4 fidelity). Skip a route only when no screenshot was
597
684
  captured, or when BOTH `features.has_design_system: false` AND
598
685
  `${paths.ui_guidelines}` is unset (no quality oracle at all — log
599
- `no_quality_oracle` in `gaps[]`). **i18n note:** a harness PNG flagged
686
+ `no_quality_oracle` in `gaps[]`), or on the **quality pre-filter** below.
687
+
688
+ **Quality pre-filter skip (authorized lane skip).** When ALL of these hold for a
689
+ route, skip the critic and mark the route's quality lane
690
+ `skipped:quality_prefilter_skip` in the Lane Matrix:
691
+ - `features.has_design_system: true` (the surface is built from a governed
692
+ registry), AND
693
+ - the route's structural lane (Phase 2.7) ran with ZERO gating findings, AND
694
+ - the route's desktop visual pass was pre-filtered clean
695
+ (`pixel_diff_below_threshold` — the render is pixel-faithful to an
696
+ APPROVED mockup).
697
+ Rationale: quality scoring is advisory-leaning by calibration
698
+ (`design-system-protocol.md` § Gating philosophy), and a render that is both
699
+ structure- and pixel-faithful to a user-approved mockup composed of governed
700
+ primitives has no room for a *gating* quality finding — the critic spawn is pure
701
+ cost there. **Never apply this skip to `harness-render` PNGs** (isolated quality
702
+ IS their purpose) or when the route has no mockup (`level: skip` — the critic is
703
+ then the only design oracle). **i18n note:** a harness PNG flagged
600
704
  `i18n-incomplete` (raw `t()` keys rendered — no provider) is passed with that
601
705
  disclaimer so the critic does not raise false `typography`/`hierarchy` findings on
602
706
  placeholder text.
@@ -645,6 +749,23 @@ For each such `route`:
645
749
  **Goal**: combine functional + visual findings into a single verdict using
646
750
  the canonical severity taxonomy from `visual-fidelity-verifier.md`.
647
751
 
752
+ 0. **Lane-coverage assertion (runs FIRST — before any verdict math).** SSOT:
753
+ `design-system-protocol.md` § "...→ 6. Verification-Lane Ledger". Reconcile
754
+ the Lane Matrix from Phase 2 against what actually executed: every
755
+ route×lane marked `"planned"` must now be `"ran"` or
756
+ `"skipped:<authorized reason>"` (the closed reason set from the Phase 2
757
+ closing step). For every planned lane still `not_run`, synthesize a
758
+ **`verification-coverage-gap`** finding (Critical, `source: "coverage"`,
759
+ `route`, `lane`, `description: "planned lane never executed"`). Like
760
+ `coverage-gap`, it is NOT fixable by an agent — it bypasses the Phase 5b
761
+ self-heal loop and maps to `blocked`. This is the global guard against the
762
+ FEAT-0064 under-run (a "fidelity review" in which the visual + quality
763
+ verifiers were never spawned and the verdict recorded nothing): a lane
764
+ under-run can never again surface as a pass. There is NO self-served
765
+ "accepted (harness limit)" verdict — accepting an un-run lane requires the
766
+ explicit override path (manual mode, human reason) or the caller's
767
+ escalation (programmatic mode returns `blocked`).
768
+
648
769
  1. **Compose** the `findings[]` array:
649
770
  - **Structural findings** from Phase 2.7 (`source: "structural"`) — route-free
650
771
  code-structure divergence (`markup-fidelity-verifier`).
@@ -654,12 +775,15 @@ the canonical severity taxonomy from `visual-fidelity-verifier.md`.
654
775
  orchestrator-synthesized `coverage-gap` (Phase 4 step 1c — a required check that
655
776
  could not run at all).
656
777
  - Design-quality findings from Phase 4b (`source: "design-quality"`).
778
+ - Coverage findings from step 0 (`source: "coverage"` —
779
+ `verification-coverage-gap`).
657
780
  - Each finding carries: `severity`, `category` (or `dimension` for
658
781
  design-quality), `source`, `route`, `description`, `expected`, `actual`,
659
782
  `fix_hint`, `evidence`, `ds_drift_code`, `confidence`. All sources
660
783
  share the **same canonical severity taxonomy**, so the tolerance filter in
661
- step 2 applies to them uniformly — no per-source special-casing. (`coverage-gap`
662
- and `data-state-mismatch` are Critical and gate in every tolerance.)
784
+ step 2 applies to them uniformly — no per-source special-casing. (`coverage-gap`,
785
+ `verification-coverage-gap` and `data-state-mismatch` are Critical and gate in
786
+ every tolerance.)
663
787
  2. **Apply tolerance filter** based on
664
788
  `features.e2e_review.fidelity_tolerance`:
665
789
  - `strict` (DEFAULT): Critical + Major + Minor all gate. Additionally,
@@ -680,12 +804,13 @@ applies only when `verdict == "blocked"` AND `iteration < max`.
680
804
 
681
805
  For each iteration:
682
806
 
683
- 0. **`coverage-gap` findings bypass the loop.** A `coverage-gap` is not fixable by any
684
- agent (the route still will not render — it is a harness/data limitation, not a code
685
- defect). Do NOT spawn a fixer for it. It keeps the verdict `blocked` and flows straight
686
- to the override path below (programmatic mode returns `blocked`; `/new` escalates it to
687
- a follow-up card). If `coverage-gap` is the ONLY gating finding, skip the rest of this
688
- loop entirely.
807
+ 0. **`coverage-gap` and `verification-coverage-gap` findings bypass the loop.** Neither is
808
+ fixable by any agent (`coverage-gap`: the route still will not render — a harness/data
809
+ limitation; `verification-coverage-gap`: a planned lane never executed an
810
+ orchestration failure, not a code defect). Do NOT spawn a fixer for either. They keep
811
+ the verdict `blocked` and flow straight to the override path below (programmatic mode
812
+ returns `blocked`; `/new` escalates to a follow-up card). If coverage findings are the
813
+ ONLY gating findings, skip the rest of this loop entirely.
689
814
 
690
815
  1. **Route the remaining gating findings to the right fixer by `source`**, then spawn the
691
816
  fixer(s). **Fidelity is UI-scoped**: `source == "structural"` (layout/grid/order —
@@ -713,7 +838,8 @@ For each iteration:
713
838
 
714
839
  ## Rules
715
840
  - File ownership: stay within the card's allowed file set (see
716
- /tmp/batch-tracker-<FIRST-CARD-ID>.md ## File Ownership Map).
841
+ "$(git rev-parse --git-common-dir)/baldart/run/batch-tracker-<FIRST-CARD-ID>.md"
842
+ ## File Ownership Map — the durable tracker location, new/SKILL.md § Context Tracking).
717
843
  - One fix per finding. Reuse existing tokens / primitives before inventing
718
844
  new ones (cite ${paths.design_system}/INDEX.md + tokens-reference.md).
719
845
  - For a `structural` `layout-break`, match the mockup's layout exactly (e.g.
@@ -740,7 +866,8 @@ For each iteration:
740
866
 
741
867
  ## Rules
742
868
  - File ownership: stay within the card's allowed file set (see
743
- /tmp/batch-tracker-<FIRST-CARD-ID>.md ## File Ownership Map).
869
+ "$(git rev-parse --git-common-dir)/baldart/run/batch-tracker-<FIRST-CARD-ID>.md"
870
+ ## File Ownership Map — the durable tracker location, new/SKILL.md § Context Tracking).
744
871
  - One fix per finding when possible. Reuse existing tokens / primitives
745
872
  before inventing new ones (cite ${paths.design_system}/INDEX.md +
746
873
  tokens-reference.md).
@@ -753,12 +880,27 @@ For each iteration:
753
880
 
754
881
  Both fixers run in `bypassPermissions` mode when called by `/new`.
755
882
 
883
+ **Spawn ONLY the non-empty bucket(s)** — when every gating finding is UI-scoped,
884
+ no `coder` is spawned (and vice versa); a fixer with an empty findings list is a
885
+ wasted spawn.
886
+
756
887
  2. **Re-run the affected verifiers**: Phase 2.7 (structural, route-free — re-run when a
757
888
  `structural` fix was applied), Phase 3 (functional), Phase 3.8 (re-render / state),
758
889
  Phase 4 (visual), and Phase 4b (quality) with fresh screenshots. Increment `iteration`.
759
- (A `coverage-gap`-only block does not enter this loop — see step 0.)
760
- 3. **Re-aggregate and re-decide** (back to Phase 5 step 1).
761
- 4. **STOP** when `verdict == "passed"` OR `iteration == max`.
890
+ (A `coverage-gap`-only block does not enter this loop — see step 0.) Deliberately NOT
891
+ narrowed to "only the lane that failed": a fix can regress a lane that previously
892
+ passed, and a selectively-skipped lane would be exactly the under-run the Phase 5
893
+ step 0 Lane-Ledger assertion exists to block. The Phase 4 pixel-diff pre-filter and
894
+ the Phase 4b quality pre-filter already make a clean re-run cheap.
895
+ 3. **Re-aggregate and re-decide** (back to Phase 5 step 1), with **convergence
896
+ detection (v4.92.0)**: compare the new gating-finding set with the pre-fix one by
897
+ `(route, source, category, region)`. If the fix produced **NEW gating findings that
898
+ were not present before** (the fixer is regressing while fixing — the documented
899
+ non-convergent failure mode), do NOT burn another iteration: STOP the loop now and
900
+ transition to the override path with both sets in the report (`convergence:
901
+ "diverging"`). Shrinking-or-equal sets keep iterating normally.
902
+ 4. **STOP** when `verdict == "passed"` OR `iteration == max` OR convergence detection
903
+ fired (step 3).
762
904
 
763
905
  When the loop exhausts iterations and findings remain, transition to the
764
906
  **override path** below.
@@ -796,6 +938,17 @@ Write the final report to `.baldart/e2e-review/<CARD-ID>/report.json`:
796
938
  "compliance-only": 1,
797
939
  "skip": 0
798
940
  },
941
+ "lane_coverage": "complete" | "incomplete",
942
+ "lanes": {
943
+ "/dashboard": {
944
+ "structural": "ran",
945
+ "visual_desktop": "skipped:pixel_diff_below_threshold",
946
+ "visual_mobile": "ran",
947
+ "visual_tablet": "not_planned",
948
+ "quality": "ran",
949
+ "functional": "ran"
950
+ }
951
+ },
799
952
  "functional": {
800
953
  "scenarios_total": 5,
801
954
  "passed": 5,
@@ -908,6 +1061,7 @@ and starts from Phase 3 (skipping plan extraction and mockup cascade).
908
1061
  | `quality_critic_protocol_violation` in gaps | `ui-quality-critic` returned prose mixed with JSON | Re-spawn the critic once; if persistent, surface to user (likely a model regression). Treat that route's quality findings as empty meanwhile |
909
1062
  | `verifier_protocol_violation` in gaps | `visual-fidelity-verifier` returned prose mixed with JSON | Re-spawn the verifier once; if persistent, surface to user (likely a model regression) |
910
1063
  | `coverage-gap` (Critical) on a route | An image-only mockup route could not be rendered in any lane and its state could not be reached | Correct by design — the gate BLOCKS instead of silently passing. Add a reach-state recipe to `.baldart/overlays/e2e-review.md` (seed / select populated context), or provide an HTML/`design_src` mockup so the route-free structural pass covers it. Never override without fixing the lane |
1064
+ | `verification-coverage-gap` (Critical) on a route×lane | A lane the Phase 2 Lane Matrix planned was never executed (verifier not spawned / phase silently skipped) — the FEAT-0064 under-run | Correct by design — an under-run blocks, never passes. Re-run the skill; if the lane keeps under-running, that is an orchestration bug to report, not a finding to override |
911
1065
  | `data-state-mismatch` (Critical) on a route | Render opened an empty / wrong-tenant / error state; mockup depicts a populated state | The reach-state loop (Phase 3.8) should seed/select a populated context. If it has no recipe and cannot auto-seek, add `select_populated_context` / `seed_data` to the e2e-review overlay |
912
1066
  | `markup_verifier_protocol_violation` in gaps | `markup-fidelity-verifier` returned prose mixed with JSON | Re-spawn once; if persistent, surface to user. Treat that route's structural findings as empty meanwhile |
913
1067
 
@@ -2,6 +2,35 @@
2
2
 
3
3
  Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
4
4
 
5
+ ## 1.3.0 — 2026-07-02
6
+
7
+ - **Hot-file economy (T2.A)**: il briefing "Codebase Context" passa il baseline
8
+ per PATH (`/tmp/arch-baseline-<CARD-ID>.md`, guardia `[BASELINE-READ-FAIL]`) e
9
+ la disciplina range-read sui file > 800 righe è SSOT in
10
+ `code-search-protocol.md` § "Large-file read discipline" (Hot-File Map emesso
11
+ dal contratto terse di `codebase-architect`).
12
+ - **Empty-scope guard (T2.B)**: Phase 2.5x + team-mode D.1.6 non invocano più
13
+ `new-card-review` a scope vuoto (cluster no-op misurati agents=0).
14
+ - **verify-item.sh (T2.C)**: la Grep-verification discipline del completeness è
15
+ ora CODE (`scripts/verify-item.sh` — exit 2 = UNVERIFIED mai Missing);
16
+ composizioni shell hand-rolled per quei check = protocol violation.
17
+
18
+ ## 1.2.0 — 2026-07-02
19
+
20
+ - **AUTONOMOUS OFF-CONTEXT DELEGATION GATE** (kickoff, pre-Phase-0): un batch
21
+ `-auto` viene delegato di default al motore workflow `new2` (pipeline per-card
22
+ off-context) quando Workflow tool + `new2.js` + skill `new2` sono presenti, il
23
+ batch è migration-free e non è `-auto-ship`. Driver misurato su 3 run reali:
24
+ turn-tax orchestrator = 53–59% dei token fatturati (contesto accumulato
25
+ ri-servito a ogni turno). Delega MANDATORY a condizioni verdi (no discretion,
26
+ come i gate Phase 2.5x/F.1.5); escape hatch `-inline`; il path classico resta
27
+ SSOT per interattivo / Codex / `-auto-ship` / migrazioni dichiarate.
28
+ - Nuovo token `-inline` / `--inline` nel parser (`FORCE_INLINE`).
29
+ - Phase 2.6: consumo additivo `lane_coverage`/`lanes` dal report `/e2e-review`
30
+ (`[E2E-LANE-UNDERRUN]`, niente chiusura fidelity-AC su under-run — T1.A).
31
+ - Coarse fallback UI: binding-compliance verificata con `check-bindings.mjs`
32
+ (T1.A) + obbligo build-with-eyes nel briefing [UI-ONLY] (T1.B).
33
+
5
34
  ## 1.1.2 — 2026-07-01
6
35
 
7
36
  - Context economy: trimmed the top-level **RUNTIME PORTABILITY** citation to a one-liner (cite, don't restate — the detail lives in `runtime-portability-protocol.md`). ~470 chars off the SKILL.md core, which sits in context every turn. No behavior change.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: new
3
3
  effort: medium
4
- version: 1.1.2
4
+ version: 1.3.0
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
@@ -45,7 +45,49 @@ If no card IDs are provided, ask the user which cards to implement.
45
45
 
46
46
  **Opt-in session telemetry (`-stats` / `--stats`)**: strip `-stats` / `--stats` from the args list exactly like `-full` (it is NOT a card ID), and set an internal flag `STATS=true` for this run. When present, Phase 8 additionally measures REAL token + wall-clock cost per agent role (coder, code-reviewer, …) by post-processing the session transcripts — zero model-token overhead, runs in Bash after the work is done. When absent, Phase 8 behaves exactly as before. The flag is batch-scoped and composes with `-full` (`/new FEAT-005 -full -stats`).
47
47
 
48
- **Autonomous run (`-auto` / `--auto`, `-auto-ship` / `--auto-ship`)**: strip these tokens from the args list exactly like `-full`/`-stats` (they are NOT card IDs). `-auto` / `--auto` sets `AUTONOMOUS=true`; `-auto-ship` / `--auto-ship` sets `AUTO_SHIP=true` AND implies `AUTONOMOUS=true`. When `AUTONOMOUS` is set the orchestrator runs the WHOLE batch without ever calling `AskUserQuestion` — every decision gate is resolved deterministically per § "AUTONOMOUS RESOLUTION RULE" below. The flags are **batch-scoped** and compose with `-full`/`-stats` (`/new FEAT-005 -full -auto`). `AUTONOMOUS` is ALSO set when the environment already signals an unattended run — env `BALDART_AUTONOMOUS`, `CI`, or `GITHUB_ACTIONS` (unify with that existing codepath: treat the env signal and the flag as the same `AUTONOMOUS` state; `AUTO_SHIP` is flag-only). **Record `AUTONOMOUS` / `AUTO_SHIP` in the Phase-0 tracker** (the recovery SSOT — write them once into `## Issues & Flags` or a dedicated tracker field; never re-derive them per turn, since a compaction would otherwise lose the run mode).
48
+ **Autonomous run (`-auto` / `--auto`, `-auto-ship` / `--auto-ship`)**: strip these tokens from the args list exactly like `-full`/`-stats` (they are NOT card IDs). `-auto` / `--auto` sets `AUTONOMOUS=true`; `-auto-ship` / `--auto-ship` sets `AUTO_SHIP=true` AND implies `AUTONOMOUS=true`. When `AUTONOMOUS` is set the orchestrator runs the WHOLE batch without ever calling `AskUserQuestion` — every decision gate is resolved deterministically per § "AUTONOMOUS RESOLUTION RULE" below. The flags are **batch-scoped** and compose with `-full`/`-stats` (`/new FEAT-005 -full -auto`). `AUTONOMOUS` is ALSO set when the environment already signals an unattended run — env `BALDART_AUTONOMOUS`, `CI`, or `GITHUB_ACTIONS` (unify with that existing codepath: treat the env signal and the flag as the same `AUTONOMOUS` state; `AUTO_SHIP` is flag-only). **Record `AUTONOMOUS` / `AUTO_SHIP` in the Phase-0 tracker** (the recovery SSOT — write them once into `## Issues & Flags` or a dedicated tracker field; never re-derive them per turn, since a compaction would otherwise lose the run mode). Also strip `-inline` / `--inline` (sets `FORCE_INLINE=true` — the escape hatch of the delegation gate below; NOT a card ID).
49
+
50
+ ## AUTONOMOUS OFF-CONTEXT DELEGATION GATE (v4.90.0 — fires at kickoff, BEFORE Phase 0)
51
+
52
+ Forensics on three real `-auto` batches showed the **orchestrator's own turn-tax is
53
+ 53–59% of the whole run's billed tokens** (an ever-growing accumulated context,
54
+ replayed via `cache_read` on every one of 400–700 turns) — while the genuinely new
55
+ tokens are a rounding error. An `-auto` run has, by definition, **no human gates**:
56
+ exactly the case the workflow-hosted `new2` engine was built for (the entire per-card
57
+ pipeline runs in an isolated background runtime whose output never enters this
58
+ context). So an `-auto` batch is delegated off-context **by default**:
59
+
60
+ **Delegate — invoke the `new2` skill — when ALL of these hold** (checked once, at
61
+ kickoff, right after arg parsing):
62
+
63
+ 1. `AUTONOMOUS == true` AND `AUTO_SHIP == false` (the `AUTO_SHIP` deploy boundary —
64
+ § "AUTONOMOUS SAFETY BOUNDARY" — is implemented by THIS skill's
65
+ production/merge phases only; an `-auto-ship` run stays inline).
66
+ 2. `FORCE_INLINE == false` (the user did not pass `-inline`).
67
+ 3. The `Workflow` tool is available in this session AND `.claude/workflows/new2.js`
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.
73
+
74
+ **Action**: log ONE tracker line `[DELEGATED] -auto batch → new2 engine
75
+ (off-context): <residual args>` and invoke the `new2` skill with the SAME residual
76
+ invocation — card IDs + `-full` + `-stats` + `effort=<level>` (its grammar is
77
+ identical; do NOT forward `-auto`/`-auto-ship`/`-inline`, which new2 does not
78
+ parse — new2 IS autonomous by contract). `new2` owns the run end-to-end
79
+ (pre-flight → per-card pipeline → final review → merge → follow-up
80
+ materialization → truthful telemetry) and this orchestrator does NOT re-run any
81
+ phase: relay new2's final report verbatim and stop. **This delegation is
82
+ MANDATORY when the conditions hold — no discretion** (the same no-discretion
83
+ contract as the Phase 2.5x / F.1.5 workflow gates; running inline with the
84
+ conditions green is a protocol violation that costs the measured 53–59%).
85
+
86
+ **Else** — any condition fails (interactive run, Codex / no Workflow tool,
87
+ `-auto-ship`, declared migration, `-inline`, missing new2) → run the classic
88
+ inline path below exactly as written. The classic prose remains the SSOT for all
89
+ semantics; the delegation only changes WHERE the batch executes, never what it
90
+ does.
49
91
 
50
92
  ## Effort
51
93
 
@@ -10,6 +10,7 @@ Before triggering any review, you MUST verify that the coder agent implemented *
10
10
  > 1. **Quote-broken pattern.** Code-shaped tokens contain shell/regex metacharacters — a TS union literal `mode:'catalog'|'picker'`, an object key, anything with `'` `"` `|` `[]` `?` `(`. Embedding such a pattern in an `echo "=== … ===" && grep …` block or an unquoted `-E` arg breaks the shell/regex parse → the grep matches **nothing even though the code is there**. **Search literals with `rg -F '<exact string>'` (fixed-string, no regex), single-quote the WHOLE pattern, and run ONE pattern per `rg` call** — never fold the search token into a quoted header/echo composition.
11
11
  > 2. **`0 matches` ≠ absent.** A zero-match result is ambiguous (true miss vs. broken pattern / wrong file / wrong escaping). Do NOT classify an item `Missing`/`Partial` on a 0-match alone. **If the 0-match CONTRADICTS the coder completion report's claim that the item is implemented, treat it as a likely broken grep — re-confirm by reading the report's cited `file:line` (or a corrected `rg -F`) BEFORE flagging or spawning a gap-fix agent.** Never re-spawn a coder on a 0-match alone. When unsure, a targeted `Read` at the report's `file:line` is more reliable than a hand-rolled grep.
12
12
  > 3. **Binary-heuristic false-empty.** Plain `grep` decides a file is *binary* when it sees a byte it reads as non-text (a non-UTF-8 byte, an accented char under some locales) or very long lines, and then prints `Binary file … matches` / **nothing** instead of the line — so a perfectly valid source file (e.g. an 8000-line `data.ts` with accented strings) silently returns 0 matches for tokens that ARE present. The mandated `rg` does NOT have this failure mode (it searches UTF-8 and long lines fine). **Always reach for `rg -F` first; if you must use `grep`, force text mode with `grep -a`.** A 0-match from plain `grep` on a file you KNOW was just modified is almost always this artifact — switch to `rg`/`grep -a` and re-check before concluding the code is missing.
13
+ > 4. **Canonical runner (v4.91.0 — use the SCRIPT, not hand-rolled shell).** All three guards above are CODE in `scripts/verify-item.sh` — run every per-item check through it: `bash .claude/skills/new/scripts/verify-item.sh '<exact literal>' <changed files...>` (one pattern per call; the pattern is a single argv, so metacharacters cannot break the parse). Exit `0` = FOUND (with `file:line`); exit `2` = **UNVERIFIED — not Missing**: confirm via a targeted `Read` at the completion report's cited `file:line` before classifying or spawning a gap-fixer; exit `3` = the FILES were wrong (fix the paths first). Hand-rolled `echo "===" && grep …` compositions for these checks are now a protocol violation.
13
14
 
14
15
  **Step-by-step**:
15
16
 
@@ -135,7 +135,15 @@
135
135
  [if field is empty/missing, read PRD Section 1b from card's links.prd path]
136
136
 
137
137
  ### Codebase Context
138
- [paste codebase-architect key findings: file paths, exact line numbers, type signatures, existing patterns to follow]
138
+ Architecture baseline: Read /tmp/arch-baseline-<CARD-ID>.md BEFORE writing any
139
+ code — it holds the file paths, exact line numbers, type signatures, existing
140
+ patterns to follow, high-risk paths, and the ## Hot-File Map (per-symbol line
141
+ ranges for files > 800 lines: Read those RANGES, never the whole file —
142
+ `code-search-protocol.md` § "Large-file read discipline"). If the baseline file
143
+ is missing/unreadable, report `[BASELINE-READ-FAIL] <path>` in your completion
144
+ report and derive the context yourself per Phase 1 — do NOT code blind.
145
+ [do NOT paste the baseline content here — pass the path only (§ "Context
146
+ economy" rule 4); inline ONLY the plan-auditor corrections/anti-patterns below]
139
147
  [include any corrections or anti-patterns flagged by plan-auditor grounding check]
140
148
 
141
149
  ### Design Reference (UI cards only — include if card has links.design)
@@ -184,7 +192,12 @@
184
192
  Only `action: new` entries (confirmed at `/prd` time) may introduce a component.
185
193
  UI scope + `features.has_design_system: true` but NO `component_bindings` → run
186
194
  the cascade below as your own reconciliation before writing code (do not assume
187
- a mockup affordance is a new component).
195
+ a mockup affordance is a new component). **After the build, compliance is
196
+ VERIFIED deterministically** — run
197
+ `node .claude/skills/ui-implement/scripts/check-bindings.mjs --bindings-json
198
+ '<bindings as JSON>' --files <touched files>` (exit 3 = `binding-not-honored`,
199
+ Critical → one fix retry, then blocked; `/ui-implement` Step 5b is the SSOT of
200
+ this gate — the coarse fallback runs the same script).
188
201
 
189
202
  ### i18n constraint (include when `features.has_i18n: true`)
190
203
  No user-facing string may be hardcoded. Externalize every label through the
@@ -196,7 +209,10 @@
196
209
  curates the registry afterwards; the anti-hardcoded linter is the gate.
197
210
 
198
211
  ### [UI-ONLY] Registry-first BLOCKING pre-work (include ONLY when briefing = "ui-expert")
199
- You are spawned as `ui-expert`. Before writing any UI code, you MUST complete
212
+ You are spawned as `ui-expert`. When the card has a mockup, your own
213
+ `ui-expert.md` § "Build-with-eyes" self-check contract applies (bounded
214
+ structural + visual self-check BEFORE returning — report its one-line
215
+ outcome). Before writing any UI code, you MUST complete
200
216
  the registry-first cascade defined in
201
217
  `framework/.claude/agents/ui-expert.md` § "Authoritative Style Reference —
202
218
  Registry-First Protocol" (the SSOT lives in
@@ -58,6 +58,16 @@ so it surfaces in telemetry.
58
58
  ```
59
59
  `codexScriptPath` — resolve the Codex companion path ONCE per `/new` run (glob in `references/team-mode.md` § Step D delegation) and reuse it here + at the Final, so the workflow skips its internal Haiku resolver; omit when stdout is empty (the workflow self-resolves as fallback).
60
60
 
61
+ **Empty-scope pre-invocation guard (v4.91.0).** If `scopeFiles` resolves EMPTY (the card's
62
+ committed diff has no files — e.g. its work was absorbed by a prior in-batch commit), do
63
+ NOT invoke the workflow: it would only schedule an empty cluster that immediately no-ops
64
+ (measured: two `new-card-review` runs with `agents=0, dur=0` in one real batch — a wasted
65
+ invocation + wake each). Log `review-cluster: SKIP (empty diff — nothing to review)` and
66
+ proceed to Phase 2.6. This mirrors the workflow's own internal no-op guard; the guard
67
+ belongs HERE too so the invocation itself is saved. (An empty diff on a card that was
68
+ supposed to change code is suspicious — cross-check `alreadyCommitted` / the tracker
69
+ before treating it as fine.)
70
+
61
71
  The workflow runs Simplify + Codex (agent-launched, code-reviewer fallback) + qa-sentinel + security,
62
72
  FP-checks each specialist's own findings, then the **domain writer applies its VERIFIED findings**
63
73
  (canonical writer map v4.26.1: `security` → `security-reviewer`; `code`/`perf`/`migration`/`test`/
@@ -228,10 +238,22 @@ When the gate above passes, the orchestrator invokes `/e2e-review` in
228
238
  "findings": [ ... ],
229
239
  "ds_drift_codes": [ "DS_TOKENS_DRIFT" | "DS_COMPONENT_STALE" | "DS_INDEX_DRIFT" ],
230
240
  "gaps": [ ... ],
241
+ "lane_coverage": "complete" | "incomplete",
242
+ "lanes": { "<route>": { "<lane>": "ran|skipped:<reason>|not_planned" } },
231
243
  "transcript_paths": [ ... ]
232
244
  }
233
245
  ```
234
246
 
247
+ `lane_coverage` / `lanes` (additive, e2e-review ≥1.1.0): the skill's own
248
+ Lane Matrix reconciliation (`design-system-protocol.md` § Verification-Lane
249
+ Ledger). An unauthorized lane under-run already surfaces as a
250
+ `verification-coverage-gap` finding → `status: "blocked"`, so no NEW gating
251
+ logic is needed here — but log the field in the tracker line, and treat a
252
+ `"passed"` accompanied by `lane_coverage: "incomplete"` (possible only from
253
+ an older skill version or a protocol violation) like the suspicious-skip
254
+ case: record `[E2E-LANE-UNDERRUN]` in `## Issues & Flags` and do NOT close
255
+ fidelity ACs on it.
256
+
235
257
  #### Gating logic (BLOCKING)
236
258
 
237
259
  | `status` from `/e2e-review` | Orchestrator action |
@@ -257,7 +279,7 @@ of the skill (skipping plan extraction and mockup cascade).
257
279
  Append to `## Current Card` and (on completion) carry into `## Completed Cards`:
258
280
 
259
281
  ```
260
- e2e-review: <status> | iterations: <N> | findings: <X critical, Y major, Z minor> | drift: [<codes>] | tolerance: <strict|balanced|lenient>
282
+ e2e-review: <status> | iterations: <N> | findings: <X critical, Y major, Z minor> | drift: [<codes>] | lanes: <complete|incomplete> | tolerance: <strict|balanced|lenient>
261
283
  ```
262
284
 
263
285
  #### Out of scope for this phase
@@ -209,6 +209,10 @@ After ALL agents in the group complete successfully:
209
209
  Map slice), `qaTier` (`full` iff the GROUP max is `deep`/risk-escalated per D.1.5 — mirrors D.4's
210
210
  group predicate; else `light`), `hasSecurityFiles` (scope ∩ `paths.high_risk_modules`),
211
211
  `runSimplify:true`, `archBaselinePath` (`/tmp/arch-baseline-group-<FIRST-CARD-ID>.md`).
212
+ **Empty-scope pre-invocation guard (v4.91.0, mirrors Phase 2.5x):** drop from `cards[]` any card
213
+ whose `scopeFiles` resolves EMPTY (absorbed by a prior in-batch commit — cross-check the tracker),
214
+ and if the WHOLE wave's `cards[]` ends up empty, do NOT invoke the workflow at all — log
215
+ `D.1.6: SKIP (empty wave scope — nothing to review)` instead of scheduling a no-op cluster.
212
216
  ```
213
217
  Workflow({ name: 'new-card-review', args: {
214
218
  cards: [ …one entry per non-trivial card in the wave… ],
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env bash
2
+ # verify-item.sh — deterministic single-item grep verification for /new Phase 2.5
3
+ # (T2.C, framework v4.91.0). Encodes the "Grep-verification discipline" of
4
+ # completeness.md as CODE, so the three prose-documented failure modes cannot
5
+ # recur by hand-rolled shell composition:
6
+ # 1. quote-broken pattern → the pattern is ONE argv, searched fixed-string (-F),
7
+ # never interpolated into an echo/regex composition.
8
+ # 2. `0 matches` ≠ absent → exit 2 prints UNVERIFIED, never "Missing" — the
9
+ # caller MUST confirm via a targeted Read before classifying/spawning a fixer.
10
+ # 3. binary false-empty → rg searches NUL/UTF-8 files as text; the grep
11
+ # fallback forces text mode with -a.
12
+ #
13
+ # Usage: verify-item.sh '<exact literal pattern>' <file> [<file> ...]
14
+ # Output: one line — "FOUND <n> match(es); first: <file>:<line>" or
15
+ # "UNVERIFIED: 0 matches — NOT proof of Missing; Read the completion
16
+ # report's cited file:line before classifying"
17
+ # Exit: 0 = found · 2 = unverified (0 matches) · 3 = usage/no readable files
18
+ # (never 1, so a caller cannot confuse it with grep's no-match code)
19
+
20
+ set -u
21
+
22
+ if [ "$#" -lt 2 ]; then
23
+ echo "usage: verify-item.sh '<exact literal pattern>' <file> [<file> ...]" >&2
24
+ exit 3
25
+ fi
26
+
27
+ pattern="$1"; shift
28
+
29
+ files=()
30
+ for f in "$@"; do
31
+ [ -f "$f" ] && [ -r "$f" ] && files+=("$f")
32
+ done
33
+ if [ "${#files[@]}" -eq 0 ]; then
34
+ echo "UNVERIFIED: no readable files among the $# provided — verify the paths before classifying anything"
35
+ exit 3
36
+ fi
37
+
38
+ if command -v rg >/dev/null 2>&1; then
39
+ # rg: fixed-string, line numbers, filename always (-H), NUL-safe text search.
40
+ out="$(rg -F -n -H --no-messages -- "$pattern" "${files[@]}" 2>/dev/null)"
41
+ else
42
+ # grep fallback: -a forces text mode (binary-heuristic guard), -F fixed-string.
43
+ out="$(grep -a -F -n -H -- "$pattern" "${files[@]}" 2>/dev/null)"
44
+ fi
45
+
46
+ if [ -n "$out" ]; then
47
+ n="$(printf '%s\n' "$out" | wc -l | tr -d ' ')"
48
+ first="$(printf '%s\n' "$out" | head -1 | cut -d: -f1,2)"
49
+ echo "FOUND ${n} match(es); first: ${first}"
50
+ exit 0
51
+ fi
52
+
53
+ echo "UNVERIFIED: 0 matches for the pattern in ${#files[@]} file(s) — NOT proof of Missing (completeness.md discipline #2): Read the completion report's cited file:line (or re-check the pattern) BEFORE classifying or spawning a gap-fixer"
54
+ exit 2