baldart 4.92.0 → 4.93.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.
@@ -0,0 +1,144 @@
1
+ # Audit Teammate Contract (Step 6.6) — AGENT-FACING SPEC
2
+
3
+ > **Who reads this file: the audit TEAMMATES** spawned at audit-phase Step 6.6c, in
4
+ > their own context windows. The `/prd` orchestrator passes this file **BY PATH** in
5
+ > the spawn prompt and MUST NOT load it into its own context — in a teammate this
6
+ > spec costs one cheap Read; in the orchestrator it would ride every subsequent
7
+ > full-window API call for the rest of the run (context economy).
8
+
9
+ ## Identity
10
+
11
+ You are a SKEPTICAL auditor for a pre-development audit team ("check-audit").
12
+ Your default stance is that the card is NOT ready for implementation.
13
+ Do not rationalize away issues. Do not give benefit of the doubt.
14
+ If something COULD be a problem, flag it. The challenge pass (later) will filter false positives.
15
+ Your job is RECALL, not precision — catch everything, filter later.
16
+
17
+ ## Your Workflow
18
+
19
+ 1. Call `TaskList` to see your assigned tasks.
20
+ 2. For each task (in ID order):
21
+ a. Call `TaskGet` to read the full task description (card YAML + adjacent cards + file paths).
22
+ a2. **Claim the task**: if `claimed_by` is already set to a name other than yours, SKIP this task (another worker owns it). Otherwise set `claimed_by: <your name>` via `TaskUpdate`, then re-read via `TaskGet` to confirm your claim held; if a different name won the race, SKIP.
23
+ b. Mark task as `in_progress` via `TaskUpdate`.
24
+ c. Read any source files or PRDs referenced in the task (use Read tool).
25
+ d. Perform your audit (see § "Audit Instructions per role" — apply YOUR role's section).
26
+ e. Run the Challenge Pass on your findings (see below).
27
+ f. Run Severity Calibration on surviving findings (see below).
28
+ g. **Write findings into the task description** via `TaskUpdate` — append a `## FINDINGS` section.
29
+ h. Send a brief notification to orchestrator via `SendMessage` (task ID + one-line summary only).
30
+ i. Mark task as `completed` via `TaskUpdate`.
31
+ 3. After all tasks: send "all tasks complete" to orchestrator.
32
+
33
+ **IMPORTANT**: Always write findings to task description (step g) before notification (step h). Task description is durable; message is just a ping.
34
+
35
+ > **Codex runtime**: when the run is file-backed (no task spine), the same workflow
36
+ > applies over the task files at `…/sessions/<slug>-audit/tasks/` — claim by writing
37
+ > `claimed_by`, persist findings to `audit/findings/<agent>-<CARD-ID>.json`. Binding:
38
+ > `framework/agents/runtime-portability-protocol.md` § state-spine.
39
+
40
+ ## Audit Instructions per role
41
+
42
+ Apply the ONE section matching the role declared in your spawn prompt.
43
+
44
+ ### plan-auditor (card grounding)
45
+
46
+ This is the Claude-side, codebase-grounded counterpart to the cross-model Codex plan audit (6.6d) — it brings what Codex lacks: project memory (`.claude/agent-memory/plan-auditor/MEMORY.md`) and the card-native `[Target: <field>]` tagging this audit consumes. Apply the **full BACKLOG CARD ATTACK SURFACE** (INVEST, requirements-smell detection, persistence-specific checks per `stack.database`, card-structure checks) against each card's `.yml`, reading the existing files in `files_likely_touched` to ground claims about conflicts with existing patterns, missing reuse opportunities, and convention alignment (per `identity.design_philosophy`, project lint/type-check rules, `identity.language`). Check `## Adjacent Cards` for parallel file modifications and missing `depends_on` edges.
47
+
48
+ > **Mode (team-aware — read first)**: you are running inside the coordinating `check-audit` team, so you MUST behave as in QUICK mode for *spawning* while keeping FULL card-attack-surface *coverage*:
49
+ > - **Do NOT spawn `codebase-architect`** for grounding — `/prd` already primed codebase context at kickoff (context-primer) and you read `files_likely_touched` directly. A re-spawn here is wasteful and forbidden.
50
+ > - **Do NOT auto-spawn specialists** (`security-reviewer` / `api-perf-cost-auditor` / `ui-expert`) — they run as first-class teammates in this same team whenever their signal fires (6.6c). Consume their findings at merge time; never double-spawn.
51
+ > - **Output-format override** (symmetric with the security-reviewer / api-perf-cost-auditor overrides below): produce findings in the teammate contract format (`### [CARD-ID] — Plan Audit Findings` with `[Target: <field>]` tags written to a `## FINDINGS` section via `TaskUpdate`) — NOT plan-auditor's native 10-section OUTPUT FORMAT (Hardened Plan, Pre-Mortem, Risk Register, YAML schema dump). The `[Target: <field>]` tag system is native to plan-auditor, so the only change is the wrapping heading + `## FINDINGS` section.
52
+ > - Never suppress a `git_strategy: TBD`, missing-auth, `claimed_path_collision`, `adr_required_missing`, or prompt-injection finding (plan-auditor's never-suppress list still binds here).
53
+
54
+ ### doc-reviewer
55
+
56
+ Check documentation links, PRD references are valid and aligned, planned changes requiring doc updates not mentioned. Verify `files_likely_touched` includes doc files. Check `areas` completeness. Flag `git_strategy: TBD`. Include Obsidian trigger assessment (section H) in findings — evaluate whether the planned docs will require KB sync. If `.claude/skills/doc-reviewer-support/references/obsidian-integration.md` is present, use its criteria; otherwise apply a minimal check: does the card introduce new or significantly modified public documentation that should appear in the knowledge base? Proceed with a notice if the file is absent.
57
+
58
+ ### api-perf-cost-auditor (only when `perf_review_needed: true`)
59
+
60
+ Apply the 5-gate protocol from `api-perf-gate.md` (sibling file in this same `references/` dir — read it). Read referenced source files. Universal checks: unbounded reads, N+1 queries, fan-out writes, missing pagination, offset pagination, listener vs polling costs, payload size limits per `stack.deployment`, transaction hotspots. Stack-specific addenda apply per `stack.database` + `stack.framework` (same file). **Output format override**: produce findings in the teammate contract format (`### [CARD-ID] — Performance Findings` with `[Target: <field>]` tags and a `## FINDINGS` section written via `TaskUpdate`) rather than the native `PERF AUDIT DONE` verdict line + YAML schema, so findings merge correctly at Step 6.7 (symmetric with the `security-reviewer` override below).
61
+
62
+ ### security-reviewer (only when `security_review_needed: true`)
63
+
64
+ Apply the full security-reviewer methodology. Focus on: auth gaps, input validation, multi-tenant isolation, persistence-layer access rules alignment (Firestore rules / Supabase RLS / Mongo validator / DynamoDB IAM — per `stack.database`), sensitive data exposure, webhook validation, rate limiting, IDOR risks. **Output format override**: produce findings in the teammate contract format (`### [CARD-ID] — Security Findings` with `[Target: <field>]` tags and `## FINDINGS` section written via `TaskUpdate`) rather than the native `# Security Review Summary` format, so findings merge correctly at Step 6.7.
65
+
66
+ ## Output Format (mandatory evidence quotes)
67
+
68
+ For each card, return findings as:
69
+
70
+ ### [CARD-ID] — {Agent Role} Findings
71
+
72
+ - [ ] **Finding title** — Description of the issue, risk, or gap. (Severity: HIGH/MEDIUM/LOW) [Target: <field>]
73
+ > **Evidence:** "<exact quote from the card YAML, PRD, or source file>"
74
+ > **Source:** `<file path or field name>`
75
+
76
+ **MANDATORY**: Every finding MUST include an evidence quote — a direct excerpt that grounds it. Findings without quotable evidence MUST be discarded. State: "Considered but discarded — no quotable evidence found."
77
+
78
+ If no findings: "No issues found for [CARD-ID]."
79
+
80
+ ### `[Target: <field>]` tag reference (mandatory on every finding)
81
+
82
+ | Target tag | When to use |
83
+ |---|---|
84
+ | `[Target: requirements]` | Missing or wrong requirement text |
85
+ | `[Target: acceptance_criteria]` | Missing AC, vague AC needing rewrite |
86
+ | `[Target: definition_of_done]` | Missing DoD checkbox |
87
+ | `[Target: files_likely_touched]` | Missing file path |
88
+ | `[Target: depends_on]` | Missing dependency card ID |
89
+ | `[Target: areas]` | Missing area entry (api, docs, data, ui) |
90
+ | `[Target: git_strategy]` | `git_strategy: TBD` or wrong value |
91
+ | `[Target: unknowns]` | Unresolved unknown to surface |
92
+ | `[Target: existing_patterns]` | Missing or stale pattern reference |
93
+ | `[Target: validation_commands]` | Missing verification command |
94
+ | `[Target: anti_patterns]` | Missing DO NOT constraint |
95
+ | `[Target: scope_boundaries]` | Missing scope boundary item |
96
+ | `[Target: input_output_examples]` | Missing or incorrect I/O example |
97
+ | `[Target: error_handling]` | Missing failure mode spec |
98
+ | `[Target: reuse_analysis]` | Missing reuse opportunity or wrong path |
99
+ | `[Target: notes]` | LOW severity only — informational |
100
+
101
+ ## Challenge Pass (mandatory before reporting)
102
+
103
+ After generating initial findings, challenge EACH one:
104
+
105
+ "What is the strongest argument that this is a false positive?"
106
+
107
+ Consider:
108
+ - Is this already handled elsewhere in the codebase?
109
+ - Is this a convention in this project I'm unfamiliar with?
110
+ - Is the card intentionally deferring this to a later card?
111
+ - Am I applying a generic best practice that doesn't fit this context?
112
+
113
+ **Suppress the finding if the FP argument is convincing.** Record suppressed findings:
114
+
115
+ <details>
116
+ <summary>Suppressed findings (N items — challenge pass)</summary>
117
+ - **Finding title** — FP argument: <why suppressed>
118
+ </details>
119
+
120
+ ## Severity Calibration (after challenge pass)
121
+
122
+ Severity is **ABSOLUTE, per finding, anchored to its evidenced consequence** — never a relative ranking, never a quota. (Positional bands like "top 20% → HIGH" manufacture mandatory findings on clean plans and demote real defects on large batches; both are calibration failures.)
123
+
124
+ For each surviving finding ask: *"what happens if the card is implemented exactly as written?"*
125
+
126
+ - **HIGH** — implementing the card as written produces a defect: data loss, security bypass, breaking change, wrong behavior against a stated AC, an unimplementable AC (e.g. its seam file is outside the card's ownership), or a missing `depends_on` that creates a cross-card conflict. A HIGH MUST cite its evidence (card field/line + the code path that proves it); no evidence citation → cap at MEDIUM.
127
+ - **MEDIUM** — the card is implementable but a gap will plausibly cost a review cycle: missing doc/file in `files_likely_touched`, vague-but-inferable AC, missing validation command.
128
+ - **LOW** — informational, no behavioral consequence (naming, style, nice-to-have).
129
+
130
+ Zero HIGH — or zero findings at all — on a well-formed card is a legitimate, reportable outcome. Do NOT stretch severities to fill bands.
131
+
132
+ ### Severity Calibration Examples
133
+
134
+ **HIGH** (must fix before implementation):
135
+ - "acceptance_criteria says 'user can see <list>' but doesn't specify pagination → unbounded persistence-layer read"
136
+ > Evidence: "AC-2: <persona from identity.audience_segments[]> views <entity>" — no limit/pagination mentioned
137
+
138
+ **MEDIUM** (should fix, skip if ambiguous):
139
+ - "files_likely_touched missing the API route doc update"
140
+ > Evidence: files_likely_touched lists "src/app/api/v1/<domain>/route.ts" but not "${paths.references_dir}/api/<domain>.md"
141
+
142
+ **LOW** (note only):
143
+ - "Card title could be more descriptive"
144
+ > Evidence: title is "Booking API" — functional but generic
@@ -589,17 +589,34 @@ user's own folder outside the worktree — never modify or clean it; only read.
589
589
 
590
590
  Then continue to Step 1.6.5 with the selected subset.
591
591
 
592
- ### 1.6.5 — Mockup analysis (MANDATORY before Discovery)
593
-
594
- Analyze ALL provided mockups (visual analysis for chat images, Read for copied files
595
- that are textual like HTML/SVG). Populate `## UI Design` in the state file using the
596
- **Mockup analysis schema** (see appendix at the end of this file).
597
-
598
- When `features.has_design_system: true`, BLOCKING reads of `${paths.design_system}/INDEX.md`
599
- and `${paths.design_system}/tokens-reference.md` apply BEFORE running the alignment
600
- check same protocol as Step 3 (UI Design). Flag every hardcoded value (hex / shadow /
601
- radius / spacing) that conflicts with a known token as a violation in
602
- `mockup_analysis.design_system_alignment.violations[]`.
592
+ ### 1.6.5 — Mockup analysis (MANDATORY before Discovery — DELEGATED, mockup bytes never in the orchestrator)
593
+
594
+ **HARD RULE the orchestrator MUST NOT Read the mockup files** (images OR HTML/SVG
595
+ bundles). A decoded mockup read in the main context rides EVERY subsequent
596
+ full-window API call for the rest of the run (measured on a real session: ~208KB
597
+ dragged through the cache for ~300 events). The only exception is a mockup pasted
598
+ directly in chat as an image — that is already in context; analyze it inline.
599
+
600
+ For all on-disk mockups, **spawn ONE `ui-expert` subagent in read-only analysis
601
+ mode** with this brief:
602
+ - the mockup file paths (`mockups.canonical_paths[]`) — the agent Reads them in ITS
603
+ context (it is multimodal: images render, HTML is read as text);
604
+ - the schema to fill: instruct it to open THIS file
605
+ (`references/discovery-phase.md`) and read ONLY the section
606
+ `## Appendix — Mockup analysis schema` (Grep the heading, Read from that offset);
607
+ - when `features.has_design_system: true`: the paths of
608
+ `${paths.design_system}/INDEX.md` and `${paths.design_system}/tokens-reference.md`
609
+ (BLOCKING reads for the agent — same protocol as Step 3), with the instruction to
610
+ flag every hardcoded value (hex / shadow / radius / spacing) that conflicts with a
611
+ known token as a violation in `mockup_analysis.design_system_alignment.violations[]`;
612
+ - **ROLE BOUNDARY**: analysis only — no code, no file writes, no Task spawns. Its
613
+ final message is the filled `mockup_analysis` YAML and NOTHING else (COMPACT
614
+ return contract).
615
+
616
+ The orchestrator persists the returned YAML into `## UI Design` in the state file
617
+ verbatim. If the agent's YAML is malformed, re-spawn once with the error; on a
618
+ second failure, fall back to inline analysis (and say so — the fallback is visible,
619
+ never silent).
603
620
 
604
621
  ### 1.6.6 — Output and handoff to Discovery
605
622
 
@@ -6,15 +6,26 @@
6
6
 
7
7
  Mark task 5 as `in_progress`.
8
8
 
9
- 0. **Agent Specialization Audit (runs FIRST, blocks if any failure)** — for each generated child card (epics are skipped):
9
+ 0. **Agent Specialization Audit (runs FIRST, blocks if any failure)** — two layers:
10
10
 
11
- a. **`owner_agent` enum check (BLOCKING)** read the `owner_agent` field. It MUST equal one of:
12
- ```
13
- coder | ui-expert | plan | visual-designer | motion-expert
14
- ```
15
- Empty string, missing field, `claude`, or any other value = **BLOCKER**.
16
- Message to the user: `"Card <ID> has owner_agent: '<value>' — must be one of {coder, ui-expert, plan, visual-designer, motion-expert}. Fix the card YAML before commit."`
17
- Halt the audit. No subsequent checks run on a card that fails this gate.
11
+ **0-i. Deterministic gate EXECUTED, never recited (BLOCKING).** Run the framework validator in `/prd` mode on EVERY generated card (epics included — it is profile-aware):
12
+
13
+ ```bash
14
+ node .framework/framework/scripts/validate-card-baseline.js --prd \
15
+ "$WORKTREE_PATH/${paths.backlog_dir}"/FEAT-XXXX-*.yml
16
+ ```
17
+ (resolve the script path for your install; only if it is genuinely unreachable, fall back to reciting the checks below and say so explicitly in the audit log).
18
+
19
+ Exit 1 = **BLOCKER**: print the per-field errors verbatim, fix the card YAML, re-run until exit 0. No subsequent checks run on a card while it fails this gate. The script covers deterministically:
20
+ - **`owner_agent` enum** — must be one of `{coder, ui-expert, plan, visual-designer, motion-expert}` (empty/missing/`claude` = blocker);
21
+ - **`review_profile` enum + epic `skip`** — `skip|light|balanced|deep`; missing/empty = blocker (the card-writer must compute it via `prd-card-writer.md § Rule C`); epics MUST be `skip`;
22
+ - **conditional `requirements` presence (PO1)** — a child/standalone card with non-empty `acceptance_criteria` + `scope` MUST carry `requirements` (a fresh `/prd` card without them shifts a measured ~3–4M-token back-fill into `/new`'s orchestrator — see `card-schema.md` § "Authoring completeness invariants");
23
+ - **unresolved `[NEEDS CLARIFICATION: …]` markers** — a card carrying a planning ambiguity is NOT implementable. Resolve each marker (with the user via `AskUserQuestion` when it needs a human answer, or from Discovery/PRD material when it does not), edit the card, re-run;
24
+ - the universal field-state matrix + remaining enums (`card-schema.md`).
25
+
26
+ **Why executed and not recited:** the prose version of this gate has already failed silently on a real run (a whole card family shipped with zero audit trail). A recited check degrades under context pressure; an executed one cannot.
27
+
28
+ **0-ii. Semantic checks (the model reads the cards — not scriptable):**
18
29
 
19
30
  b. **UI-only scope heuristic (WARN — soft gate, not blocking)** — for each card with `owner_agent: ui-expert`:
20
31
  - Concatenate `scope.summary`, `requirements[]`, and `acceptance_criteria[]` into one lowercase string.
@@ -24,23 +35,7 @@ Mark task 5 as `in_progress`.
24
35
  - The check is intentionally a soft gate: substrings like "API contract per il mock" or "consumes the auth state from `useAuth()`" are legitimate UI references. The WARN exists to prompt human review, not to block.
25
36
  - Exception (no WARN): the marker appears INSIDE a quoted string that explicitly says "consume", "mock", "contract reference", or "exposed by sibling card" — those are UI cards reading an upstream contract, which is expected. **[SEMANTIC — not purely deterministic]** Determining whether a marker substring is (a) inside a YAML quoted string and (b) that string contains one of the escape phrases requires contextual interpretation across multiple YAML quoting styles (single-quote, double-quote, block scalar). FP posture: when in doubt, log the WARN and let the human decide — do not suppress a WARN based on uncertain quoting analysis.
26
37
 
27
- c. **`review_profile` enum check (BLOCKING)**read the `review_profile` field on each child card. It MUST equal one of:
28
- ```
29
- skip | light | balanced | deep
30
- ```
31
- - **Missing or empty** = **BLOCKER** — the card-writer must compute it via `prd-card-writer.md § Rule C` (the SSOT mapping table). Message: `"Card <ID> has no review_profile — compute it from Rule C (skip|light|balanced|deep) before commit."`
32
- - **Out-of-enum value** = **BLOCKER**. Message: `"Card <ID> has review_profile: '<value>' — must be one of {skip, light, balanced, deep}."`
33
- - **Consistency WARN (soft)**: a `feature`/`enhancement` card with `review_profile: light` may violate Rule C. Log `"[REVIEW-PROFILE-WARN] Card <ID> is feature/enhancement but review_profile: light — verify against Rule C in prd-card-writer.md."` **Do NOT auto-bump the field** — `review_profile` is computed by `prd-card-writer` (Rule C); see `prd-card-writer.md` for the criteria. This is a soft gate: log the warning and surface it for human review.
34
- - **Epics**: `review_profile` MUST be `skip` (trackers have no code work). Any other value = WARN, normalize to `skip`.
35
-
36
- d. **`requirements` presence (BLOCKING — PO1)** — for each CHILD/STANDALONE card whose
37
- `acceptance_criteria` AND `scope` are both non-empty, `requirements` MUST be present and
38
- non-empty. A freshly authored `/prd` card with derivable material but no `requirements` is an
39
- authoring miss that shifts a measured ~3–4M-token back-fill into `/new`'s orchestrator context
40
- (see `card-schema.md` § "Authoring completeness invariants"). `/prd` fixes it now for free.
41
- Message: `"Card <ID> has acceptance_criteria + scope but no requirements — emit requirements
42
- (faithful restatement of AC + scope) before commit."` (The `/new` conditional back-fill remains
43
- only as the safety net for legacy / externally-authored cards.)
38
+ c. **Rule C consistency (WARN — soft gate)**: a `feature`/`enhancement` card with `review_profile: light` may violate Rule C. Log `"[REVIEW-PROFILE-WARN] Card <ID> is feature/enhancement but review_profile: light verify against Rule C in prd-card-writer.md."` **Do NOT auto-bump the field** — `review_profile` is computed by `prd-card-writer` (Rule C); see `prd-card-writer.md` for the criteria. This is a soft gate: log the warning and surface it for human review. (The enum/presence/epic-`skip` checks are handled deterministically by 0-i.)
44
39
 
45
40
  e. **files_likely_touched coverage (BLOCKING — P1/P2 closure)** — two sub-checks:
46
41
  - **⊆ coverage (P2):** every file path named in a card's `requirements` /
@@ -58,9 +53,10 @@ Mark task 5 as `in_progress`.
58
53
  no card — assign it to a card's files_likely_touched."`
59
54
  Both are SEMANTIC (path extraction from prose / ISA needs interpretation) — bias toward flagging.
60
55
 
61
- **Gate**: every card has `owner_agent` AND `review_profile` in their enums, `requirements` present when
62
- derivable (d), and full files_likely_touched coverage (e). WARN entries from checks (b)/(c) are noted in
63
- the audit log; user may accept them after review. The (d)/(e) BLOCKERs must be fixed before commit.
56
+ **Gate**: the 0-i validator exits 0 on every card (enums, conditional `requirements`, zero
57
+ `[NEEDS CLARIFICATION]` markers) AND full files_likely_touched coverage (e). WARN entries from
58
+ checks (b)/(c) are noted in the audit log; user may accept them after review. The 0-i and (e)
59
+ BLOCKERs must be fixed before commit.
64
60
 
65
61
  1. **Field Name Audit (runs after item 0 — only when item 0 passes for a given card; skipped for cards blocked in item 0)** — for each generated card:
66
62
 
@@ -132,37 +128,37 @@ created by Step 1 (HARD RULE 17). `$WORKTREE_PATH` is set in the state file.
132
128
  (`paths.metrics` default: `docs/metrics`), which must be part of the same commit.
133
129
  Non-blocking: if it fails, log "Metrics Log: SKIPPED" in the progress bar and continue.
134
130
 
135
- 5b. **Holistic-audit provenance backstop (BLOCKING on the STAMP, never on the PRD).**
136
- The per-card stamp write in `audit-phase.md` Step 6.9.4 is model-driven and is the
137
- single most-omitted step under context pressure. When it is skipped, every card
138
- ships WITHOUT `metadata.holistic_audit` — and `/new` (`implement.md` Phase 1 step 4,
139
- P1) + `new2` then spawn the plan-auditor on **every** card even when the batch is
140
- implemented immediately after this audit (nothing changed). Step 6 is complete (its
141
- gate passed), so the audit genuinely ran: this is the authoritative, deterministic
142
- point to guarantee the stamp lands. **Do NOT re-derive the semantics** reuse the
143
- run-level values and the YAML block DEFINED in `audit-phase.md` § "Holistic-audit
144
- provenance stamp".
145
- - Recompute the three run-level values ONCE (same snippet as audit-phase.md), inside
146
- `$WORKTREE_PATH`:
147
- ```bash
148
- HOLISTIC_AUDITED_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
149
- HOLISTIC_AUDITED_COMMIT="$(git -C "$WORKTREE_PATH" merge-base HEAD "${git.trunk_branch}" 2>/dev/null || git -C "$WORKTREE_PATH" rev-parse "${git.trunk_branch}" 2>/dev/null || echo "")"
150
- # audited_set = the sorted Step-5 child card IDs (the joint cross-card scope).
151
- ```
152
- - For EACH **non-EPIC** card from Step 5: if it lacks `metadata.holistic_audit` with a
153
- non-empty `audited_commit`, WRITE the block (additive never overwrite other
154
- `metadata` keys). A card that already carries it (from Step 6.9.4) is untouched —
155
- **idempotent**. EPIC-parent cards are excluded (they carry `planning_session` only
156
- `card-schema.md`).
157
- - Re-read one backfilled card to verify the block landed. Log
158
- `holistic_audit provenance: N/N cards stamped (M backfilled here)`.
131
+ 5b. **Holistic-audit provenance backstop (BLOCKING on the STAMP, never on the PRD)
132
+ EXECUTED via script.** The per-card stamp write in `audit-phase.md` Step 6.9.4 is
133
+ model-driven and is the single most-omitted step under context pressure; when it is
134
+ skipped, every card ships WITHOUT `metadata.holistic_audit` — and `/new`
135
+ (`implement.md` Phase 1 step 4, P1) + `new2` then spawn the plan-auditor on **every**
136
+ card even when the batch is implemented immediately after this audit. Step 6 is
137
+ complete (its gate passed), so the audit genuinely ran: guarantee the stamp lands by
138
+ RUNNING the deterministic stamper (semantics defined in `audit-phase.md`
139
+ § "Holistic-audit provenance stamp" the script only mechanizes the write):
140
+ ```bash
141
+ HOLISTIC_AUDITED_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
142
+ HOLISTIC_AUDITED_COMMIT="$(git -C "$WORKTREE_PATH" merge-base HEAD "${git.trunk_branch}" 2>/dev/null || git -C "$WORKTREE_PATH" rev-parse "${git.trunk_branch}" 2>/dev/null || echo "")"
143
+ # --set = the sorted Step-5 child card IDs (the joint cross-card scope), comma-separated.
144
+ node .framework/framework/scripts/stamp-holistic-audit.js \
145
+ --at "$HOLISTIC_AUDITED_AT" --commit "$HOLISTIC_AUDITED_COMMIT" \
146
+ --set "<ID1,ID2,...>" "$WORKTREE_PATH/${paths.backlog_dir}"/FEAT-XXXX-*.yml
147
+ ```
148
+ The script is **idempotent** (a card already stamped by Step 6.9.4 is untouched),
149
+ **additive** (never overwrites other `metadata` keys), skips EPIC cards (they carry
150
+ `planning_session` only `card-schema.md`), and verifies its own writes. Log its
151
+ summary line (`holistic_audit provenance: N/N non-epic cards stamped (M written
152
+ here, …)`) in the audit log. Only if the script is genuinely unreachable in this
153
+ install, fall back to the manual per-card write per `audit-phase.md` § "Holistic-audit
154
+ provenance stamp" and say so explicitly.
159
155
  **Fail-safe (unchanged contract):** the stamp is an OPTIMIZATION hint, never a
160
156
  *correctness* gate — if `HOLISTIC_AUDITED_COMMIT` resolves to `""` (git genuinely
161
- unavailable), write it explicitly as `""` so `/new` treats it as drift and RUNS the
162
- plan-auditor. **NEVER block or abort the PRD over the stamp**; the only thing this gate
163
- blocks is shipping cards with the stamp SILENTLY absent. Runtime-neutral (bash + YAML
164
- edits) → identical on Claude and Codex. The backfilled YAMLs are staged by item 6's
165
- existing `FEAT-XXXX-*.yml` glob.
157
+ unavailable), the script writes it explicitly as `""` so `/new` treats it as drift and
158
+ RUNS the plan-auditor. **NEVER block or abort the PRD over the stamp**; the only thing
159
+ this gate blocks is shipping cards with the stamp SILENTLY absent. Runtime-neutral
160
+ (node + YAML edits) → identical on Claude and Codex. The backfilled YAMLs are staged
161
+ by item 6's existing `FEAT-XXXX-*.yml` glob.
166
162
 
167
163
  6. Stage ONLY PRD session artefacts by explicit file name (NEVER `git add .`
168
164
  or `git add -A`):
@@ -2,6 +2,11 @@
2
2
 
3
3
  Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
4
4
 
5
+ ## 1.1.1 — 2026-07-02
6
+
7
+ - Return Contract (SKILL.md): documentati i campi `bindings` + `lane_coverage`/`lanes`
8
+ del JSON programmatico (lo schema resta SSOT in references/integration.md).
9
+
5
10
  ## 1.1.0 — 2026-07-02
6
11
 
7
12
  - **Binding-compliance gate (Step 5b)**: nuovo script deterministico
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: ui-implement
3
3
  effort: medium
4
- version: 1.1.0
4
+ version: 1.1.1
5
5
  description: >
6
6
  Implement an ALREADY-APPROVED mockup into the codebase end-to-end and
7
7
  self-verify its fidelity. Takes a mockup in any channel — a Claude Design
@@ -233,7 +233,12 @@ older install), `/new` falls back to an inline **coarse** build brief + a direct
233
233
 
234
234
  **Mode:** COMPACT (default). Your final message is bounded — verdict
235
235
  (`passed | blocked | overridden | skipped | error`) + the chosen build workflow +
236
- key findings as `path:line` + `Report: <path>`. Persist the long form (the
236
+ key findings as `path:line` + `Report: <path>`. The programmatic JSON ALSO carries
237
+ (since 1.1.1): `bindings` (the Step 5b `check-bindings.mjs` one-line result, when
238
+ the card has `component_bindings`) and `lane_coverage` + `lanes` (forwarded from
239
+ the internal `/e2e-review` report) — full schema:
240
+ [references/integration.md](references/integration.md) § Return contract (the
241
+ SSOT; do not restate it here). Persist the long form (the
237
242
  `/e2e-review` report + tracker), return the short form. FULL narrative only when the
238
243
  user invoked you directly. Schema + persist-then-summarize rule:
239
244
  `framework/agents/return-contract-protocol.md`.
@@ -283,7 +283,7 @@ try {
283
283
  preflight = await agentSafe(
284
284
  `You are the deterministic PRE-FLIGHT for an autonomous /new batch (variant new2). Follow ${REF}/setup.md (Phase 0 + Pre-flight) and ${REF}/implement.md (Phase 1 depends-on gate) for the SEMANTICS, but replace EVERY AskUserQuestion with the deterministic policy below. You run all git/bash yourself (the workflow cannot).\n\n` +
285
285
  `${projectBrief}\n\nCards in batch (Read each YAML):\n${cardPaths.join('\n')}\nCard IDs: ${cardIds.join(' ')}\n\n` +
286
- `Create/maintain the recovery tracker at "$(git -C ${MAIN} rev-parse --git-common-dir)/baldart/run/batch-tracker-${firstCard}.md" (mkdir -p its dir first; per setup.md § Context Tracking — DURABLE location, v4.91.0: /tmp is volatile and a paused/overnight run lost it — B4).\n\n` +
286
+ `Create/maintain the recovery tracker at "$(git -C "${MAIN}" rev-parse --git-common-dir)/baldart/run/batch-tracker-${firstCard}.md" (mkdir -p its dir first; per setup.md § Context Tracking — DURABLE location, v4.91.0: /tmp is volatile and a paused/overnight run lost it — B4).\n\n` +
287
287
  `ROLE BOUNDARY (specialization integrity): you are the OPS/GIT agent. You NEVER edit source or doc files — any needed content change belongs to the coder specialist; report it instead.\n\n` +
288
288
  `DETERMINISTIC GATE POLICIES (NO user prompts):\n` +
289
289
  `• G1 dirty-tree (main repo ${MAIN}): partition framework-managed noise exactly as setup.md step 3 ($METRICS=${METRICS}, .baldart/generated|state.json|skill-conflicts.json — NOT overlays/). Genuine user work → auto-stash 'baldart-new2-${firstCard}' (main checkout) and record the label. Never commit/abort/prompt.\n` +
@@ -614,7 +614,7 @@ async function runCard(cardId, cardPath) {
614
614
  `Return: { epic, buildBlocked, blockedGate, unmetACs:[{n,text}], scopeFiles, mayEditPaths, revertedOutOfOwnership:[paths], fileDiffViolation, note }`,
615
615
  { label: `implement:${cardId}`, phase: 'Implement', agentType: ownerAgent,
616
616
  schema: { type: 'object', required: ['epic', 'buildBlocked', 'unmetACs', 'scopeFiles'], additionalProperties: true,
617
- properties: { epic: { type: 'boolean' }, buildBlocked: { type: 'boolean' }, blockedGate: { type: 'string' }, unmetACs: { type: 'array', items: { type: 'object', additionalProperties: true } }, scopeFiles: { type: 'array', items: { type: 'string' } }, mayEditPaths: { type: 'array', items: { type: 'string' } }, revertedOutOfOwnership: { type: 'array', items: { type: 'string' } }, fileDiffViolation: { type: 'boolean' }, note: { type: 'string' } } } }
617
+ properties: { epic: { type: 'boolean' }, buildBlocked: { type: 'boolean' }, blockedGate: { type: 'string' }, unmetACs: { type: 'array', items: { type: 'object', additionalProperties: true } }, scopeFiles: { type: 'array', items: { type: 'string' } }, mayEditPaths: { type: 'array', items: { type: 'string' } }, revertedOutOfOwnership: { type: 'array', items: { type: 'string' } }, fileDiffViolation: { type: 'boolean' }, note: { type: 'string' }, bindingCheck: { type: 'object', additionalProperties: true, description: 'check-bindings.mjs one-line JSON (mockup+bindings cards only)' } } } }
618
618
  )
619
619
  } catch (e) {
620
620
  if (e && e.transientExhausted) { noteDegraded('outage'); return { card: cardId, status: 'pending', gates } }
@@ -653,6 +653,23 @@ async function runCard(cardId, cardPath) {
653
653
  if (r.status !== 'resolved') { deferredOpen = true; deferredClasses.add(r.deferralClass || 'unresolved') }
654
654
  }
655
655
 
656
+ // --- R1-F2 (v4.92.1): the binding gate result is CONSUMED, not write-only. The agent
657
+ // was already told to run check-bindings.mjs and fix before returning; here the workflow
658
+ // verifies the report: violations survive the agent's own pass → resolve (ui domain);
659
+ // report absent on a bindings card → fail-open but LEDGERED (audit trail, never silent).
660
+ if (node.hasMockup && node.hasBindings) {
661
+ const bc = impl && impl.bindingCheck
662
+ const bcViol = (bc && Array.isArray(bc.violations)) ? bc.violations : []
663
+ if (!bc) g('binding-gate', 'NOT-REPORTED', 'agent returned no bindingCheck — fail-open (script may be absent on older installs), recorded for audit')
664
+ else if (bcViol.length) {
665
+ g('binding-gate', 'VIOLATIONS', bcViol.map((v) => v.component).join(', '))
666
+ for (const v of bcViol) {
667
+ const r = await resolve('blocker', cardId, `binding-not-honored: region "${v.region || ''}" is bound action:${v.action || 'reuse'} → ${v.component}, but the component appears in NO touched implementation file — the binding map is authoritative (card-schema.md § component_bindings)`, { mayEditPaths: mayEdit, scopeFiles, domain: 'ui' })
668
+ if (r.status !== 'resolved') { deferredOpen = true; deferredClasses.add(r.deferralClass || 'unresolved') }
669
+ }
670
+ } else g('binding-gate', 'OK', `${(bc && bc.honored) || 0}/${(bc && bc.checked) || 0} bindings honored`)
671
+ }
672
+
656
673
  // --- T1.A (v4.90.0): route-free structural fidelity lane — the new2 parallel of
657
674
  // /e2e-review Phase 2.7. new2 has NO browser lanes (visual/quality verify runs only on
658
675
  // the /new path), so the code-form structural check is its ONE per-card fidelity lane.
@@ -664,26 +681,42 @@ async function runCard(cardId, cardPath) {
664
681
  const structSrc = node.designSrcDir
665
682
  ? { level: 'design_src', design_src_dir: node.designSrcDir }
666
683
  : { level: 'html', mockup_path: node.designHtml }
684
+ const spawnMfv = () => agentSafe(
685
+ `Compare the implementation's code structure against the mockup's code structure and return JSON per your output schema (route-free — no browser, no dev server; cd into ${sharedCtx.worktreePath}).\n\nInput payload:\n${JSON.stringify({ card_id: cardId, route: '(card-level)', mockup_source: structSrc, implementation_files: scopeFiles, tolerance: 'strict' })}\n\nReturn STRICT JSON only: { status, findings:[{category,severity,description,expected,actual,fix_hint}] }`,
686
+ { label: `fidelity:${cardId}:structural`, phase: 'Implement', agentType: 'markup-fidelity-verifier',
687
+ schema: { type: 'object', required: ['findings'], additionalProperties: true, properties: { status: { type: 'string' }, findings: { type: 'array', items: { type: 'object', additionalProperties: true } } } } }
688
+ )
667
689
  let mfv = null
668
- try {
669
- mfv = await agentSafe(
670
- `Compare the implementation's code structure against the mockup's code structure and return JSON per your output schema (route-free — no browser, no dev server; cd into ${sharedCtx.worktreePath}).\n\nInput payload:\n${JSON.stringify({ card_id: cardId, route: '(card-level)', mockup_source: structSrc, implementation_files: scopeFiles, tolerance: 'strict' })}\n\nReturn STRICT JSON only: { findings:[{category,severity,description,expected,actual,fix_hint}] }`,
671
- { label: `fidelity:${cardId}:structural`, phase: 'Implement', agentType: 'markup-fidelity-verifier',
672
- schema: { type: 'object', required: ['findings'], additionalProperties: true, properties: { findings: { type: 'array', items: { type: 'object', additionalProperties: true } } } } }
673
- )
674
- } catch (e) {
675
- if (e && e.transientExhausted) noteDegraded('outage')
676
- }
677
- if (!mfv) {
678
- g('fidelity-structural', 'UNDERRUN', 'planned lane never executed (verifier unavailable) — verification-coverage-gap, card cannot auto-DONE')
690
+ try { mfv = await spawnMfv() } catch (e) { if (e && e.transientExhausted) noteDegraded('outage') }
691
+ // R1-F1 (v4.92.1): a verifier that RETURNS but self-reports `status:"skipped"`
692
+ // (no_structural_ground_truth unreadable mockup / no layout-bearing impl) is an
693
+ // under-run exactly like an unavailable verifier, NEVER a PASS.
694
+ const mfvSkipped = mfv && /skip/i.test(String(mfv.status || ''))
695
+ if (!mfv || mfvSkipped) {
696
+ g('fidelity-structural', 'UNDERRUN', `planned lane did not verify (${!mfv ? 'verifier unavailable' : 'verifier skipped: ' + String((mfv && mfv.reason) || 'no_structural_ground_truth')}) — verification-coverage-gap, card cannot auto-DONE`)
679
697
  deferredOpen = true; deferredClasses.add('unresolved')
680
- residuals.push({ card: cardId, kind: 'verification-coverage-gap', evidence: 'structural fidelity lane planned but not executed (markup-fidelity-verifier unavailable)', materialized: false })
698
+ residuals.push({ card: cardId, kind: 'verification-coverage-gap', evidence: `structural fidelity lane planned but did not verify (${!mfv ? 'markup-fidelity-verifier unavailable' : 'verifier skipped'})`, materialized: false })
681
699
  } else {
682
- const gating = (mfv.findings || []).filter((f) => /critical|major/i.test(String(f.severity || '')))
700
+ const gatingOf = (x) => ((x && x.findings) || []).filter((f) => /critical|major/i.test(String(f.severity || '')))
701
+ const gating = gatingOf(mfv)
683
702
  g('fidelity-structural', gating.length ? 'BLOCKED' : 'PASS', gating.length ? `${gating.length} gating structural finding(s)` : `ran — ${(mfv.findings || []).length} finding(s), none gating`)
703
+ let anyResolved = false
684
704
  for (const f of gating) {
685
705
  const r = await resolve('blocker', cardId, `structural fidelity (${f.category || 'layout'}): ${f.description || ''} — expected: ${f.expected || '(see mockup)'}`, { mayEditPaths: mayEdit, scopeFiles, domain: 'ui' })
686
706
  if (r.status !== 'resolved') { deferredOpen = true; deferredClasses.add(r.deferralClass || 'unresolved') }
707
+ else anyResolved = true
708
+ }
709
+ // R1-F3 (v4.92.1): a resolved structural fix is RE-VERIFIED by the deterministic
710
+ // verifier once (mirror of /e2e-review Phase 5b step 2 — a fixer's self-report is
711
+ // not the verifier's confirmation). Single re-check, no loop: still-gating after
712
+ // the re-run → deferral (the follow-up carries it), never a second fix pass here.
713
+ if (anyResolved) {
714
+ let mfvRe = null
715
+ try { mfvRe = await spawnMfv() } catch (e) { if (e && e.transientExhausted) noteDegraded('outage') }
716
+ const reGating = mfvRe && !/skip/i.test(String(mfvRe.status || '')) ? gatingOf(mfvRe) : null
717
+ if (reGating === null) { g('fidelity-structural-reverify', 'UNDERRUN', 're-verify did not run — treated as unresolved'); deferredOpen = true; deferredClasses.add('unresolved') }
718
+ else if (reGating.length) { g('fidelity-structural-reverify', 'STILL-BLOCKED', `${reGating.length} gating finding(s) survive the fix — deferred`); deferredOpen = true; deferredClasses.add('unresolved'); residuals.push({ card: cardId, kind: 'blocker', domain: 'ui', evidence: `structural fidelity still failing after fix: ${reGating.map((f) => f.category).join(', ')}`, materialized: false }) }
719
+ else g('fidelity-structural-reverify', 'PASS', 'fix confirmed by the verifier')
687
720
  }
688
721
  }
689
722
  }
@@ -1152,7 +1185,7 @@ if (!committed.length) {
1152
1185
  } else {
1153
1186
  try {
1154
1187
  mergeResult = await agentSafe(
1155
- `Auto-merge the batch worktree to ${TRUNK} per ${REF}/merge-cleanup.md: Phase 6 via the DETERMINISTIC script merge-worktree.sh, then Phase 6b status reconciliation + Phase 6c hygiene. Run git yourself.\n\n${projectBrief}\nWorktree: ${sharedCtx.worktreePath}\nBranch: ${sharedCtx.branch}\nmerge_strategy: ${mergeStrategy}\nCommitted cards: ${committed.map((r) => r.card).join(' ')}\nPhase-0 stash to restore (if any): see "$(git -C ${MAIN} rev-parse --git-common-dir)/baldart/run/batch-tracker-${firstCard}.md".\n\n` +
1188
+ `Auto-merge the batch worktree to ${TRUNK} per ${REF}/merge-cleanup.md: Phase 6 via the DETERMINISTIC script merge-worktree.sh, then Phase 6b status reconciliation + Phase 6c hygiene. Run git yourself.\n\n${projectBrief}\nWorktree: ${sharedCtx.worktreePath}\nBranch: ${sharedCtx.branch}\nmerge_strategy: ${mergeStrategy}\nCommitted cards: ${committed.map((r) => r.card).join(' ')}\nPhase-0 stash to restore (if any): see "$(git -C "${MAIN}" rev-parse --git-common-dir)/baldart/run/batch-tracker-${firstCard}.md".\n\n` +
1156
1189
  `PHASE 6 — run the SSOT script (the merge analogue of setup-worktree.sh; NOT a hand-rolled merge, NOT /mw inline):\n` +
1157
1190
  ` MERGE_SH="$(ls .framework/framework/.claude/skills/worktree-manager/scripts/merge-worktree.sh .claude/skills/worktree-manager/scripts/merge-worktree.sh 2>/dev/null | head -1)"\n` +
1158
1191
  ` bash "$MERGE_SH" --worktree ${sharedCtx.worktreePath} --manifest /tmp/new2-merge-status-${firstCard}.txt --skip-checks\n` +