baldart 3.39.0 → 3.41.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,41 @@ 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
+ ## [3.41.0] - 2026-06-01
9
+
10
+ **`qa-sentinel` is now profile-driven: `balanced` cards run SCOPED tests (only the tests related to the touched files), and the FULL suite is reserved for `deep` cards (or a `balanced` change whose diff reveals undeclared risk).** This is a **behavioral change to QA depth** — the default for an ordinary feature/refactor changes from "run the whole suite" to "run the related tests" — not an additive feature. **No new `baldart.config.yml` keys** — it reuses the existing `review_profile` field; scoped/smoke test selection is auto-probed at runtime from the project's test runner.
11
+
12
+ > **Why.** The `review_profile` (skip/light/balanced/deep) is computed deterministically upstream by `prd-card-writer` (Rule C) and passed to `qa-sentinel` by `/new` — but the agent **ignored it**. Its prompt literally said `Run FULL VALIDATION MODE` while the line below passed `QA profile: balanced`, and the agent re-decided QUICK-vs-FULL from a crude `>5 files` heuristic. Net effect: a 6-file UI-only `balanced` card triggered the entire regression suite. The fix makes the profile **authoritative**: it selects the tier; file *count* no longer escalates (the framework already treats file count as advisory-only — `prd-card-writer.md`). The only upgrade path is **risk drift** — the diff itself touching auth/permission/payment/schema/migration/API-contract — which catches a card the author under-classified. An adversarial review of the first design rejected three over-reaches kept out of this release: putting the executable tier table in `agents/testing.md` (the agent never reads it), a ghost `SMOKE` tier mapped to `light` (which never invokes the agent), and a `>15 files` escalation (re-introducing the very full-too-often problem).
13
+
14
+ ### Changed — `qa-sentinel` honors `review_profile` (scoped-by-default)
15
+
16
+ - **[framework/.claude/agents/qa-sentinel.md](framework/.claude/agents/qa-sentinel.md)** — Step 0 + `Operating Modes`: tier is now selected from the `QA profile` in the prompt, not file count. MODE 1 is **SCOPED VALIDATION** (profile `balanced`): a stack-aware test-impact cascade (`jest --findRelatedTests` → `vitest related` → `pytest <paths>` → `--testPathPattern`, monorepo-workspace-aware) that runs only related tests and **never** falls through to the full suite. MODE 2 is **FULL VALIDATION** (profile `deep`, or `balanced` escalated by risk drift). Confidence rule added: a SCOPED run that finds no related tests for a *logic* change records `Tests: SKIP — no related tests` and caps confidence at ≤70% instead of reporting PASS 95%. The H2 headings are unchanged (preserves consumer agent overlays); only the section bodies were rewritten.
17
+ - **[framework/.claude/skills/new/SKILL.md](framework/.claude/skills/new/SKILL.md)** — Phase 3.5 qa-sentinel prompt no longer says "FULL VALIDATION MODE"; it passes the tier contract (`balanced → SCOPED`, `deep → FULL`, risk-drift-only escalation) and derives the E2E depth from the profile (`deep` full+blocking, `balanced` `--grep` advisory). Duplicate step "22" renumbered. Team-mode **D.4** documents that a mixed group with any `deep` card runs FULL (max-profile intentionally overrides the per-card SCOPED tier).
18
+ - **[framework/.claude/commands/qa.md](framework/.claude/commands/qa.md)** — `/qa` now passes `QA profile` to qa-sentinel (so the agent never defaults to absent), and the LIGHT path no longer falls back to the full suite when the test pattern is unclear.
19
+
20
+ ### Changed — documentation aligned
21
+
22
+ - **[framework/agents/testing.md](framework/agents/testing.md)** — new stack-neutral **Scope-aware Test Selection** section documenting the principle (balanced→scoped / deep→full / risk-drift→escalate), explicitly pointing at `qa-sentinel` as the executable SSOT and fencing TIA as heuristic (no coverage maps / AST / ML). Runner-specific commands deliberately kept out (Codex / non-Node consumers read this module).
23
+ - **[framework/agents/index.md](framework/agents/index.md)** + **[README.md](README.md)** — routing pointer and the qa-sentinel one-liner updated (`Quick/Full/Deep profiles` → `profile-driven: scoped-by-default, full on deep`).
24
+
25
+ ## [3.40.0] - 2026-06-01
26
+
27
+ **Doc fixes surfaced by `/new`'s doc-review are now applied by the `doc-reviewer` itself, not delegated to `coder`.** The `doc` fix-domain owner changes from `coder` to `doc-reviewer` (write mode) across every `/new` site that previously spawned a coder for documentation: sequential Phase 3, team-mode D.4a, and the post-batch Final review. **No new `baldart.config.yml` keys** — this is a fix-routing change internal to `/new`.
28
+
29
+ > **Why.** When `/new`'s post-implementation doc-review found stale/missing docs, the remediation was routed to `coder` — the wrong agent on two counts. (1) It's *overkill*: the `doc-reviewer` already ran the audit and has the full doc context in-context; a coder spawn re-derives it from scratch. (2) It's *wrong*: the doc invariants the orchestrator must not break (freshness markers, linking protocol, frontmatter standard, tabular formatting, SSOT/registry coverage, dependency-topological order, SCIP/code refs) are encoded in the **`doc-reviewer`** system prompt, NOT the coder's — so doc fixes were going to the agent *least* equipped for them. The `doc-reviewer.md` Constraints already mandate "WRITE missing docs directly … do not defer to other agents"; `/new` Phase 3 contradicted that by invoking it read-only and handing the fix to `coder`. The v3.28.3 Domain-Override decision was framed as "orchestrator-inline **vs** coder" and simply never put "doc-reviewer writes" on the menu. This release closes that gap. (`/prd` is unaffected — it *produces* docs directly via `prd-card-writer`/the writing phases and has no read-only-audit→remediation loop.)
30
+
31
+ ### Changed — `doc` fix-domain owner is now `doc-reviewer` (not `coder`)
32
+
33
+ - **[framework/.claude/skills/new/SKILL.md](framework/.claude/skills/new/SKILL.md)** — Domain-Override Domains table: the `doc` domain now carries an explicit **Owning agent = `doc-reviewer` (write mode)** column, with a rationale block ("Why `doc` is owned by `doc-reviewer`, not `coder`"). `security` and `migration` stay with `coder`. The mechanical `CHANGELOG.md`/`ssot-registry.md` append edge case now goes through `doc-reviewer`, never `coder`.
34
+ - **Sequential Phase 3** collapses from two spawns (doc-reviewer read-only audit + coder apply) to **one** — the doc-reviewer audits AND applies in a single invocation (it runs alone; code-review moved to Phase 3.7, so the old read-only/parallel-safety constraint no longer applies). The only output it does NOT fix itself is a doc-drift→bug finding rooted in CODE, which follows the code fix path.
35
+ - **Team-mode D.2/D.4a**: D.2 keeps the doc-reviewer **read-only** (it runs in parallel with `code-reviewer` — parallel-safety preserved); D.4a now re-invokes the **doc-reviewer in write mode** over the group to apply the per-card-attributed findings, instead of spawning a fix-coder.
36
+ - **Final review F.5**: verified `>= MEDIUM` findings are now partitioned by domain — `doc`-domain findings → `doc-reviewer`, all other findings → `coder`.
37
+
38
+ ### Changed — Fix Application telemetry recognises the new owner
39
+
40
+ - **[framework/.claude/skills/new/SKILL.md](framework/.claude/skills/new/SKILL.md)** Fix Application Log Schema: `decision` and `applied_by` gain the `doc-reviewer` value; `phase` gains `D.4a`. (The analyzer regex in `framework/scripts/analyze-fix-application.js` already accepts arbitrary `[\w-]+` tokens — no code change needed.)
41
+ - **[framework/docs/FIX-APPLICATION-TELEMETRY.md](framework/docs/FIX-APPLICATION-TELEMETRY.md)**: violation/healthy pattern tables updated — `applied_by=coder` on a Phase 3 `doc` row is now itself a violation; the healthy doc pattern is `decision=doc-reviewer | applied_by=doc-reviewer`.
42
+
8
43
  ## [3.39.0] - 2026-06-01
9
44
 
10
45
  **A `/prd` or `/new` run now CONCLUDES clean — it never ends by handing the user a list of "azioni tue, non bloccanti" (an uncommitted file blocking the local `develop` fast-forward, a merged remote branch left undeleted).** Every workspace-hygiene leftover is either auto-resolved by the finalizer or put behind ONE explicit `AskUserQuestion` gate. No passive manual TODO ever survives into the final summary. **No new `baldart.config.yml` keys.**
package/README.md CHANGED
@@ -99,7 +99,7 @@ No additional activation steps needed — once installed, Claude Code (and Codex
99
99
  9. **plan-auditor**: MANDATORY after planning — 4-persona plan review + High-Risk Path triggers + Specialist Auto-Spawn
100
100
  10. **senior-researcher**: Evidence-based research with AI-readable retrieval-optimized reports
101
101
  11. **api-perf-cost-auditor**: API performance and cost analysis with Findings Schema emission
102
- 12. **qa-sentinel**: SDET-grade gate runner (Quick/Full/Deep profiles) — lint, tsc, test, build, audit
102
+ 12. **qa-sentinel**: SDET-grade gate runner (profile-driven: scoped-by-default, full on deep) — lint, tsc, test, build, audit
103
103
 
104
104
  **Design & UX**
105
105
  13. **ui-expert**: UI/UX design and review
package/VERSION CHANGED
@@ -1 +1 @@
1
- 3.39.0
1
+ 3.41.0
@@ -97,21 +97,23 @@ Weekly skill/agent improvement? --> skill-improver
97
97
 
98
98
  ### Profile selection
99
99
 
100
- | Profile | Mode | Use when |
100
+ | Profile | Tier | Use when |
101
101
  |---------|------|----------|
102
- | `light` | QUICK VALIDATION | Small bugfix, ≤5 files, no core modules, style/docs only |
103
- | `balanced` | FULL VALIDATION | Normal feature card, medium scope (default) |
104
- | `deep` | FULL VALIDATION + Playwright | Auth/payments/DB/permissions, >15 files, Feature Complete cards |
102
+ | `light` | SCOPED (related tests) | Small bugfix, no core modules, style/docs only |
103
+ | `balanced` | SCOPED (related tests) | Normal feature card, medium scope (default) |
104
+ | `deep` | FULL (whole suite) + Playwright | Auth/payments/DB/permissions, broad blast radius, Feature Complete cards |
105
105
  | `skip` | — | Docs-only or cosmetic cards with zero logic changes |
106
106
 
107
+ Tier is selected by the **profile**, not file count. A `balanced` change is escalated to FULL only when the diff reveals undeclared risk (auth/permission/payment/schema/migration/API contract); file count never escalates.
108
+
107
109
  ### Invocation pattern
108
110
 
109
111
  ```
110
112
  subagent_type: "qa-sentinel"
111
113
  prompt: |
112
- Run [QUICK | FULL] VALIDATION MODE on card <CARD-ID>.
114
+ Run QA on card <CARD-ID> at the tier dictated by the profile (do NOT default to FULL).
113
115
  Worktree: <path> Branch: <branch> Changed files: <list>
114
- Profile: [light | balanced | deep]
116
+ QA profile: [light | balanced | deep] (light/balanced → SCOPED, deep → FULL)
115
117
  Run gates ONLY. No AC verification, no code analysis, no recommendations.
116
118
  Write gate results + verdict to: /qa/<CARD-ID>.md (under 40 lines)
117
119
  ```
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: qa-sentinel
3
- description: "Use this agent when code changes need quality assurance validation before merging, after implementing a feature or bugfix, when running pre-commit or pre-merge checks, when a backlog card is marked Feature Complete, or when you need structured QA reports with risk assessments. Also use proactively after significant code changes to core modules like authentication, payments, DB schema, or API contracts.\\n\\n<example>\\nContext: The user has just implemented a new feature touching the authentication middleware and multiple API routes.\\nuser: \"I've finished implementing the merchant session refresh logic across 8 files. Can you check it's ready?\"\\nassistant: \"This touches authentication and multiple API routes — I'll invoke QA-Sentinel to run a full validation.\"\\n<commentary>\\nThe change set affects core auth logic (>threshold files, critical path), so use the Task tool to launch qa-sentinel in FULL VALIDATION MODE.\\n</commentary>\\nassistant: \"Let me launch the QA-Sentinel agent to validate this change set.\"\\n</example>\\n\\n<example>\\nContext: Developer refactored a utility function used only in non-critical UI components.\\nuser: \"Just cleaned up the formatCurrency helper and updated the 3 components that use it.\"\\nassistant: \"Small refactor on a non-core module — I'll have QA-Sentinel do a quick validation pass.\"\\n<commentary>\\nThis is a small, non-core change, so use the Task tool to launch qa-sentinel in QUICK VALIDATION MODE.\\n</commentary>\\nassistant: \"Launching QA-Sentinel for a quick validation of the refactor.\"\\n</example>\\n\\n<example>\\nContext: A backlog card has just been marked as Feature Complete by the implementing agent.\\nuser: \"FEAT-0096 is done, all acceptance criteria checked.\"\\nassistant: \"Feature Complete status reached — I'll invoke QA-Sentinel to run release-level validation before we open the PR.\"\\n<commentary>\\nFeature Complete cards require FULL VALIDATION MODE. Use the Task tool to launch qa-sentinel.\\n</commentary>\\nassistant: \"Invoking QA-Sentinel for release-level confidence on FEAT-0096.\"\\n</example>\\n\\n<example>\\nContext: A bug was fixed and the developer wants to ensure it won't regress.\\nuser: \"Fixed the table control race condition in FEAT-0177.\"\\nassistant: \"Bug fix complete — I'll run QA-Sentinel to validate the fix and ensure a regression test is in place.\"\\n<commentary>\\nEvery bug fix should be validated and covered by a regression test. Use the Task tool to launch qa-sentinel.\\n</commentary>\\nassistant: \"Launching QA-Sentinel to validate the fix and check regression coverage.\"\\n</example>"
3
+ description: "Use this agent when code changes need quality assurance validation before merging, after implementing a feature or bugfix, when running pre-commit or pre-merge checks, when a backlog card is marked Feature Complete, or when you need structured QA reports with risk assessments. Also use proactively after significant code changes to core modules like authentication, payments, DB schema, or API contracts.\\n\\n<example>\\nContext: The user has just implemented a new feature touching the authentication middleware and multiple API routes.\\nuser: \"I've finished implementing the merchant session refresh logic across 8 files. Can you check it's ready?\"\\nassistant: \"This touches authentication and multiple API routes — I'll invoke QA-Sentinel to run a full validation.\"\\n<commentary>\\nThe change set affects core auth logic (>threshold files, critical path), so use the Task tool to launch qa-sentinel in FULL VALIDATION MODE.\\n</commentary>\\nassistant: \"Let me launch the QA-Sentinel agent to validate this change set.\"\\n</example>\\n\\n<example>\\nContext: Developer refactored a utility function used only in non-critical UI components.\\nuser: \"Just cleaned up the formatCurrency helper and updated the 3 components that use it.\"\\nassistant: \"Small refactor on a non-core module — I'll have QA-Sentinel do a quick validation pass.\"\\n<commentary>\\nThis is a small, non-core change (profile balanced), so use the Task tool to launch qa-sentinel in SCOPED VALIDATION.\\n</commentary>\\nassistant: \"Launching QA-Sentinel for a quick validation of the refactor.\"\\n</example>\\n\\n<example>\\nContext: A backlog card has just been marked as Feature Complete by the implementing agent.\\nuser: \"FEAT-0096 is done, all acceptance criteria checked.\"\\nassistant: \"Feature Complete status reached — I'll invoke QA-Sentinel to run release-level validation before we open the PR.\"\\n<commentary>\\nFeature Complete cards require FULL VALIDATION MODE. Use the Task tool to launch qa-sentinel.\\n</commentary>\\nassistant: \"Invoking QA-Sentinel for release-level confidence on FEAT-0096.\"\\n</example>\\n\\n<example>\\nContext: A bug was fixed and the developer wants to ensure it won't regress.\\nuser: \"Fixed the table control race condition in FEAT-0177.\"\\nassistant: \"Bug fix complete — I'll run QA-Sentinel to validate the fix and ensure a regression test is in place.\"\\n<commentary>\\nEvery bug fix should be validated and covered by a regression test. Use the Task tool to launch qa-sentinel.\\n</commentary>\\nassistant: \"Launching QA-Sentinel to validate the fix and check regression coverage.\"\\n</example>"
4
4
  model: sonnet
5
5
  color: pink
6
6
  memory: project
@@ -35,7 +35,11 @@ Default pre-commit gates (MUST pass before any commit verdict):
35
35
 
36
36
  Before running any checks:
37
37
  1. Run `git diff --name-only HEAD` (or against base branch) to get the actual change set.
38
- 2. Select the appropriate operating mode based on file count and paths.
38
+ 2. Read the authoritative `QA profile` from your invocation prompt (`balanced` | `deep`) and map it
39
+ to a test tier via the table in **Operating Modes**. The profile — NOT file count — selects the
40
+ tier. The only allowed adjustment is `balanced → FULL` when the diff itself reveals risk the card
41
+ did not declare (see the risk-drift rule below). If the prompt carries no profile, default to
42
+ SCOPED and note it — never invent your own classification.
39
43
 
40
44
  Never fabricate test results. Never assume what changed — always read the diff.
41
45
  Do NOT read the changed files to "understand scope" — that's the code-reviewer's job. Just run the gates.
@@ -73,7 +77,12 @@ Long-running commands (especially `npm run build`) produce thousands of lines th
73
77
  # Build — only capture exit code and last 20 lines
74
78
  npm run build 2>&1 | tail -20; echo "EXIT:$?"
75
79
 
76
- # Tests — capture summary only
80
+ # Tests (SCOPED default for balanced) — related tests only, summary
81
+ npx jest --findRelatedTests <changed files> 2>&1 | tail -30; echo "EXIT:$?"
82
+ npm test -- --testPathPattern="<touched-module-regex>" 2>&1 | tail -30; echo "EXIT:$?"
83
+ pytest <related test paths> 2>&1 | tail -30; echo "EXIT:$?"
84
+
85
+ # Tests (FULL — only for deep / risk-drift) — whole suite, summary
77
86
  npm test 2>&1 | tail -30; echo "EXIT:$?"
78
87
 
79
88
  # ESLint — full output is OK (usually short), but cap at 50 lines
@@ -89,38 +98,54 @@ If a gate FAILS, you may re-run WITHOUT `tail` to get the full error — but onl
89
98
 
90
99
  ## Operating Modes
91
100
 
92
- ### MODE 1: QUICK VALIDATION
101
+ The `QA profile` from your prompt selects the tier. The `skip` profile never reaches you (handled
102
+ upstream). Two execution tiers — **SCOPED** (default for `light`/`balanced`) and **FULL** (`deep`):
103
+
104
+ | QA profile | Tier | Tests | Audit | E2E | Target |
105
+ |------------|------|-------|-------|-----|--------|
106
+ | `light` | SCOPED | related tests only (TIA cascade below); skip if upstream gates already ran | skip | none | <3 min |
107
+ | `balanced` | SCOPED | related tests only (TIA cascade below) | skip unless new deps | advisory | <5 min |
108
+ | `deep` | FULL | whole suite | `npm audit --audit-level=high` | blocking | <15 min |
109
+
110
+ > Some orchestrator entry points run the upstream gates themselves and skip invoking you for `light`;
111
+ > others invoke you with `light` — in that case run SCOPED. Either way `light` never means FULL.
93
112
 
94
- Triggers:
95
- - ≤5 files changed
96
- - Non-core modules (UI components, utility helpers, config)
97
- - Pure refactoring (no logic change)
98
- - UI-only adjustments
99
- - Documentation changes only
113
+ **Risk-drift escalation (the ONLY upgrade path):** run `balanced` as FULL when the diff itself
114
+ reveals risk the card did not declare — paths touching `auth`/`permission`/`payment`/`billing`,
115
+ DB schema / migration / index changes, or an API contract change. File **count** never escalates
116
+ (it is advisory only). When you escalate, log: `escalated balanced→FULL (risk drift: <reason>)`.
117
+ Never de-escalate `deep` to SCOPED.
118
+
119
+ ### MODE 1: SCOPED VALIDATION (profile `balanced`)
100
120
 
101
121
  Execution:
102
122
  1. `npx eslint --max-warnings=0 <changed files>`
103
123
  2. `npx tsc --noEmit`
104
- 3. `npm test -- --testPathPattern=<impacted modules>` (scoped, not full suite)
124
+ 3. **Scoped tests** run only the tests related to the changed files, via the first cascade rung
125
+ that applies to the repo (graceful degradation, never fall through to the full suite):
126
+ - Jest (`jest.config*` / `jest` in package.json): `npx jest --findRelatedTests <changed files>`
127
+ - Vitest (`vitest.config*` / `vitest`): `npx vitest related --run <changed files>`
128
+ - Pytest (`pytest.ini` / `pyproject` with pytest): `pytest <test paths matching touched modules>`
129
+ - Generic fallback: `npm test -- --testPathPattern="<regex of touched module dirs>"`
130
+ - Monorepo: resolve the workspace from the changed-file paths before invoking the runner.
131
+ - If no runner is determinable, or the runner reports "no tests found" for a **logic** change:
132
+ do NOT FAIL and do NOT run the full suite — record `Tests: SKIP — no related tests found` and
133
+ apply the confidence cap (see QA Report Format). For a docs/config-only change, a test SKIP is
134
+ legitimate at full confidence.
105
135
  4. `npm run build`
106
- 5. Minimal smoke check if smoke tests exist for changed area
136
+ 5. Smoke check if the project exposes one (`test:smoke` script or `tests/smoke/` opt-in convention,
137
+ absent is fine).
107
138
 
108
- Goal: Fast confidence without blocking flow. Target: <3 minutes.
139
+ Goal: Fast confidence on the touched surface without blocking flow. Target: <5 minutes.
109
140
 
110
141
  ---
111
142
 
112
- ### MODE 2: FULL VALIDATION
113
-
114
- Triggers (any one is sufficient):
115
- - >5 files changed
116
- - Core logic touched (auth, payments, Firestore schema, API contracts)
117
- - Backlog card marked "Feature Complete"
118
- - Bug fix, DB queries/transactions/indexes changed, new external dependency, API route changes, permission logic
143
+ ### MODE 2: FULL VALIDATION (profile `deep`, or `balanced` escalated by risk drift)
119
144
 
120
145
  Execution:
121
146
  1. `npx eslint --max-warnings=0 <changed .ts/.tsx files>`
122
147
  2. `npx tsc --noEmit`
123
- 3. `npm run test` (full suite)
148
+ 3. `npm run test` (whole suite — or the project's full-suite equivalent, e.g. `pytest`)
124
149
  4. `npm run build`
125
150
  5. `npm audit --audit-level=high` (security baseline — flag HIGH/CRITICAL only)
126
151
  6. `npx markdownlint-cli2 <changed .md files>` (if any)
@@ -169,7 +194,7 @@ Keep reports **under 40 lines**. No prose, no analysis, no recommendations. Just
169
194
  ```
170
195
  ## QA REPORT — [FEAT-XXXX] — [DATE]
171
196
 
172
- Mode: QUICK / FULL | Files changed: N
197
+ Tier: SCOPED / FULL | Profile: balanced / deep | Files changed: N
173
198
 
174
199
  | Gate | Status | Notes |
175
200
  |------|--------|-------|
@@ -193,6 +218,9 @@ Rules:
193
218
  - Pre-existing test failures: exclude from count, note as "N pre-existing excluded"
194
219
  - Security audit: only report NEW vulnerabilities vs baseline (24 vulns as of 2026-03-06). If no new packages added, write "no delta"
195
220
  - If all gates PASS → verdict is PASS, confidence 95-100%
221
+ - **Scoped coverage gap**: if a SCOPED run found no related tests for a **logic** change, write
222
+ `Tests: SKIP — no related tests found` and cap confidence at **≤70%** with a one-line coverage-gap
223
+ note. Do not report PASS at 95% when no test actually ran on a logic change.
196
224
  - If any gate FAIL → verdict is FAIL, list only the failing gates
197
225
 
198
226
  ---
@@ -146,7 +146,7 @@ For each card, execute these phases in order:
146
146
  21. **If profile is LIGHT, BALANCED, or DEEP**: invoke the **`qa-sentinel`** agent (subagent_type: `qa-sentinel`) via Task tool with the following context:
147
147
 
148
148
  ```
149
- Run [QUICK | FULL] VALIDATION MODE on card <CARD-ID>.
149
+ Run QA on card <CARD-ID> at the tier dictated by the QA profile (do NOT default to FULL).
150
150
 
151
151
  Context:
152
152
  - Worktree path: <worktree-path>
@@ -156,10 +156,12 @@ For each card, execute these phases in order:
156
156
  - Risk level: [Low | Medium | High]
157
157
  - QA profile selected: [light | balanced | deep]
158
158
 
159
- Profile → mode mapping:
160
- - light QUICK VALIDATION MODE
161
- - balanced → FULL VALIDATION MODE
162
- - deep → FULL VALIDATION MODE (plus: run e2e smoke suite if configured)
159
+ Profile → tier mapping:
160
+ - light SCOPED (related tests only)
161
+ - balanced → SCOPED (related tests only; risk drift in the diff may escalate to FULL)
162
+ - deep → FULL (whole suite + audit + e2e smoke suite if configured)
163
+ Only path-based risk drift (auth/permission/payment/schema/migration/API contract) escalates;
164
+ file count never does.
163
165
 
164
166
  After running all gates, write the complete QA Report to:
165
167
  /qa/<CARD-ID>.md
@@ -105,7 +105,7 @@ Run directly in this session (no sub-agents):
105
105
  1. Extract changed source files from the diff.
106
106
  2. Lint check on changed files — if no changed files, skip with note.
107
107
  3. Type check (e.g., `npx tsc --noEmit`)
108
- 4. Scoped tests — if test pattern unclear, run full suite.
108
+ 4. Scoped tests — run only tests related to the changed files (related-tests / testPathPattern on touched modules). If no related tests are found, record `Tests: SKIP — no related tests` and cap confidence; do NOT fall through to the full suite.
109
109
  5. Build check.
110
110
 
111
111
  For each step: report PASS / FAIL. On FAIL, show the exact error output.
@@ -127,7 +127,7 @@ No findings file required. Present final verdict immediately.
127
127
  Invoke `qa-sentinel` via Task tool:
128
128
 
129
129
  ```
130
- Run FULL VALIDATION MODE on the current changeset.
130
+ Run QA on the current changeset at the tier dictated by the QA profile below (do NOT default to FULL).
131
131
 
132
132
  Context:
133
133
  - Branch: <branch>
@@ -135,6 +135,11 @@ Context:
135
135
  - Changed files: <list from git diff>
136
136
  - Risk level: <Low/Medium/High>
137
137
  - Card: <CARD-ID or "none">
138
+ - QA profile: <the profile this /qa was invoked with: balanced | deep>
139
+
140
+ Tier contract: balanced → SCOPED (related tests on touched modules); deep → FULL suite. Only
141
+ path-based risk drift (auth/permission/payment/schema/migration/API contract in the diff) may
142
+ escalate balanced→FULL.
138
143
 
139
144
  After completing validation gates (lint, tsc, tests, build, audit):
140
145
  1. Start the dev server if appropriate for the change, exercise key flows, and check for server/browser console errors and unexpected 4xx/5xx responses. Focus on flows directly touched by the diff.
@@ -962,16 +962,16 @@ Every fix-application decision in Phases 2.55 / 3 / 3.5 / 3.7 appends one row to
962
962
  **Row format** (one row per finding processed, including skipped):
963
963
 
964
964
  ```
965
- <phase> | <domain> | est_lines=<n> | decision=<inline|coder|coder-batch|skipped> | applied_by=<orchestrator|coder|orchestrator-fallback|-> | <key=val>...
965
+ <phase> | <domain> | est_lines=<n> | decision=<inline|coder|coder-batch|doc-reviewer|skipped> | applied_by=<orchestrator|coder|doc-reviewer|orchestrator-fallback|-> | <key=val>...
966
966
  ```
967
967
 
968
968
  | Field | Values |
969
969
  |---|---|
970
- | `phase` | `2.55`, `3`, `3.5`, `3.7` |
970
+ | `phase` | `2.55`, `3`, `3.5`, `3.7`, `D.4a` (team-mode doc gate) |
971
971
  | `domain` | `simplify-reuse`, `simplify-quality`, `simplify-efficiency`, `doc`, `qa-blocker`, `qa-major`, `qa-minor`, `qa-none`, `codex-security`, `codex-correctness`, `codex-other` |
972
972
  | `est_lines` | Orchestrator's at-a-glance estimate of the diff size suggested by the finding. Bucketed `1-5` / `6-20` / `21+` is sufficient — exact precision not required. Use `0` if no patch (e.g. skipped). |
973
- | `decision` | `inline` (orchestrator applied), `coder` (single coder spawn for this finding), `coder-batch` (folded into a batched coder spawn covering ≥2 findings), `skipped` (false positive / not worth addressing / no findings) |
974
- | `applied_by` | `orchestrator`, `coder`, `orchestrator-fallback` (agent crash with non-domain-override fallback — see Sub-agent failure protocol), `-` (skipped) |
973
+ | `decision` | `inline` (orchestrator applied), `coder` (single coder spawn for this finding), `coder-batch` (folded into a batched coder spawn covering ≥2 findings), `doc-reviewer` (doc-domain fix applied by the doc-reviewer in write mode — since v3.40.0), `skipped` (false positive / not worth addressing / no findings) |
974
+ | `applied_by` | `orchestrator`, `coder`, `doc-reviewer` (doc-domain owner — since v3.40.0), `orchestrator-fallback` (agent crash with non-domain-override fallback — see Sub-agent failure protocol), `-` (skipped) |
975
975
  | Trailing `key=val` | Phase-specific extras: `finding=<1-line summary>`, `severity=<BLOCKER\|HIGH\|MEDIUM\|FALSE-POSITIVE>`, `retry=<n>`, `reason=<...>` |
976
976
 
977
977
  **Write target**: the same tracker file the orchestrator already writes per-card status to (path resolved from `${paths.references_dir}/trackers/` or the project's tracker convention). Section header `## Fix Application Log` is created on first row of the card. Append-only — never rewrite previous rows.
@@ -980,17 +980,19 @@ Every fix-application decision in Phases 2.55 / 3 / 3.5 / 3.7 appends one row to
980
980
 
981
981
  #### Domain-Override Domains (since v3.28.3)
982
982
 
983
- Some fix domains are **never** safe for inline orchestrator apply, regardless of size. The coder agent's system prompt + project overlay enforces invariants (doc freshness, tabular formatting, RLS policy structure, migration ordering) that orchestrator inline edits routinely break. The orchestrator MUST delegate every fix in these domains to `coder`, even when the patch is a one-liner.
983
+ Some fix domains are **never** safe for inline orchestrator apply, regardless of size the orchestrator MUST delegate every fix in these domains to the **domain-owning agent**, even when the patch is a one-liner. The owning agent is the one whose system prompt + project overlay encodes that domain's invariants; an orchestrator inline edit (or a fix routed to the wrong agent) routinely breaks them.
984
984
 
985
985
  Enumerated tassativamente:
986
986
 
987
- | Domain | Match rule |
988
- |---|---|
989
- | `doc` | File path matching `*.md` under `${paths.references_dir}`, `${paths.prd_dir}`, project root `CHANGELOG.md`, or any `ssot-registry.md`. |
990
- | `security` | File path matching the Phase 3.7 detector Triggers #2 (auth/permissions: `src/lib/auth/middleware.ts`, `src/lib/permissions.ts`, anything matching `withAuth`) or #3 (payments: `^src/lib/payments/`, `^src/app/api/v1/billing/`). Also any SQL migration whose content matches `CREATE POLICY|ALTER POLICY|DROP POLICY` (RLS policy mutations). |
991
- | `migration` | File path matching `supabase/migrations/*.sql` (or `${paths.migrations_dir}/*.sql` if defined in `baldart.config.yml`). |
987
+ | Domain | Owning agent | Match rule |
988
+ |---|---|---|
989
+ | `doc` | **`doc-reviewer`** (write mode) | File path matching `*.md` under `${paths.references_dir}`, `${paths.prd_dir}`, project root `CHANGELOG.md`, or any `ssot-registry.md`. |
990
+ | `security` | `coder` | File path matching the Phase 3.7 detector Triggers #2 (auth/permissions: `src/lib/auth/middleware.ts`, `src/lib/permissions.ts`, anything matching `withAuth`) or #3 (payments: `^src/lib/payments/`, `^src/app/api/v1/billing/`). Also any SQL migration whose content matches `CREATE POLICY|ALTER POLICY|DROP POLICY` (RLS policy mutations). |
991
+ | `migration` | `coder` | File path matching `supabase/migrations/*.sql` (or `${paths.migrations_dir}/*.sql` if defined in `baldart.config.yml`). |
992
+
993
+ **Why `doc` is owned by `doc-reviewer`, not `coder` (since v3.40.0)** — the doc invariants the orchestrator must not break (freshness markers, linking protocol, frontmatter standard, tabular formatting, SSOT/registry coverage, dependency-topological order, SCIP/code refs) are encoded in the **`doc-reviewer`** system prompt, NOT the coder's. The coder is a code-oriented agent that lacks the doc-invariant contract — routing doc fixes to it is the wrong agent doing work the auditing agent already has full context for. The agent that *audits* the docs is also the agent that *fixes* them (`doc-reviewer.md` § Constraints: "WRITE missing docs directly. You are fully responsible — do not defer to other agents"). NEVER route a `doc`-domain fix to `coder`.
992
994
 
993
- **Edge case explicit** — a mechanical append-a-row update to `CHANGELOG.md` or `ssot-registry.md` is still classified `doc` and still goes through coder. The uniformity of the rule matters more than the cost of the individual spawn. If telemetry from Release A shows that doc-mechanical-append is a large fraction of doc coder spawns, a sub-domain `doc-mechanical-append` may be carved out in a later release.
995
+ **Edge case explicit** — a mechanical append-a-row update to `CHANGELOG.md` or `ssot-registry.md` is still classified `doc` and still goes through `doc-reviewer`, never inline and never `coder`. The uniformity of the rule matters more than the cost of the individual spawn.
994
996
 
995
997
  Domains NOT listed here remain governed by the per-phase rules of the corresponding phase (e.g. `simplify-*` follows Phase 2.55 inline rule).
996
998
 
@@ -1158,30 +1160,31 @@ skill's Phase 1 falls back to deriving Gherkin scenarios from
1158
1160
  - [derive from FRESHNESS_MAP: src/app/api/** → ${paths.references_dir}/api/; src/lib/booking/** → booking.md etc.]
1159
1161
  ```
1160
1162
 
1161
- Invoke the **doc-reviewer** agent as a **read-only audit**, passing the Doc Sync Context:
1163
+ Invoke the **doc-reviewer** agent in **audit-and-apply mode** (single invocation — it both finds the gaps AND fixes them, per `doc-reviewer.md` § Constraints), passing the Doc Sync Context:
1162
1164
  ```
1163
1165
  Doc Sync Context:
1164
1166
  [paste the Doc Sync Context block built above]
1165
1167
 
1166
1168
  Instructions for doc-reviewer:
1169
+ - You OWN the `doc` domain end-to-end: audit AND apply the fixes in THIS invocation.
1170
+ Do NOT defer doc writes to another agent — you are the agent with the doc-invariant
1171
+ contract and the full context. Write the missing/stale docs directly.
1167
1172
  - Set freshness_status: fresh and last_verified_from_code: <today's date> on any doc you touch
1168
- - Check each invariant in the checklist above and flag any that are unmet
1169
- - Flag any doc in "Related docs to check" that should be stale but isn't marked yet
1170
- - Run your standard doc audit on all changed files
1173
+ - Check each invariant in the checklist above and fix any that are unmet
1174
+ - Update any doc in "Related docs to check" that should be marked stale/fresh
1175
+ - Run your standard doc audit on all changed files and apply the corrections
1171
1176
  - **Spec/docs-drift → bug lens (MANDATORY since v3.35.0)**: flag any place where the
1172
1177
  implementation contradicts a documented contract/spec in a way that can cause incorrect
1173
1178
  behavior (response shape diverging from `api/<module>.md`, field semantics diverging from
1174
1179
  `data-model.md`, etc.). This lens was previously exclusive to `/codexreview` agent #4; the
1175
1180
  per-card Phase 3.7 gate now skips that duplicate (lean mode), so THIS pass MUST carry it.
1176
- Report as a doc finding with the conflicting code location + the doc it violates.
1181
+ A doc-drift→bug finding whose root cause is in CODE (not the doc) is the ONE thing
1182
+ doc-reviewer does NOT fix itself — report it with the conflicting code location + the doc
1183
+ it violates, and the orchestrator routes it to the `security`/code fix path as appropriate.
1177
1184
  ```
1178
- Doc-reviewer collects findings WITHOUT making changes.
1179
- 14. **Obsidian Corpus Dispatch**: Parse section H from doc-reviewer findings. If `Trigger: YES`, dispatch the `obsidian-sync` agent (`.claude/agents/obsidian-sync.md`) with the listed paths after all fixes are applied (step 16). If `Trigger: NO`, skip. This is non-blocking -- do not wait for obsidian-sync to complete before proceeding.
1180
- 15. If doc findings exist, invoke the **coder** agent once to apply **ALL doc fixes in one pass**.
1181
-
1182
- **Doc fixes are NEVER applied inline by the orchestrator**, regardless of size or perceived triviality (since v3.28.3). `doc` is a domain-override domain — see "Domain-Override Domains" sub-section. A one-line `CHANGELOG.md` append and a 50-line `data-model.md` migration row update both go through coder. The freshness invariants and tabular formatting that the coder's system prompt enforces are routinely broken by orchestrator inline edits; the cost of a coder spawn is the cost of correctness.
1183
-
1184
- **Telemetry** — after the coder returns, append one row per doc finding to `## Fix Application Log`: `3 | doc | est_lines=<n> | decision=coder-batch | applied_by=coder | finding=<1-line>`. If 0 findings, append one row: `3 | doc | est_lines=0 | decision=skipped | applied_by=- | reason=no-findings`.
1185
+ Doc-reviewer applies all doc-domain fixes itself. The orchestrator does NOT spawn a coder for doc fixes (since v3.40.0 — `doc` is owned by `doc-reviewer`, see "Domain-Override Domains"). The only doc-reviewer output that leaves this phase unfixed is a doc-drift→bug finding rooted in code, which follows the normal code fix path.
1186
+ 14. **Obsidian Corpus Dispatch**: Parse section H from doc-reviewer findings. If `Trigger: YES`, dispatch the `obsidian-sync` agent (`.claude/agents/obsidian-sync.md`) with the listed paths after the doc fixes are applied. If `Trigger: NO`, skip. This is non-blocking -- do not wait for obsidian-sync to complete before proceeding.
1187
+ 15. **Telemetry** — after doc-reviewer returns, append one row per doc finding to `## Fix Application Log`: `3 | doc | est_lines=<n> | decision=doc-reviewer | applied_by=doc-reviewer | finding=<1-line>`. If 0 findings, append one row: `3 | doc | est_lines=0 | decision=skipped | applied_by=- | reason=no-findings`.
1185
1188
  16. Run `npm run lint` and `npx tsc --noEmit` to verify nothing broke. If any check fails, apply the self-healing retry loop (up to 3 times, no user prompt).
1186
1189
  17. **Update tracker**: phase = "3-doc-review DONE", log doc findings count, fixes applied.
1187
1190
  If doc-reviewer found a recurring gap, append 1-line to `## Lessons Learned`:
@@ -1196,7 +1199,7 @@ skill's Phase 1 falls back to deriving Gherkin scenarios from
1196
1199
  22. **If profile is BALANCED or DEEP**: invoke the **`qa-sentinel`** agent (subagent_type: `qa-sentinel`) via Task tool with the following context:
1197
1200
 
1198
1201
  ```
1199
- Run FULL VALIDATION MODE on card <CARD-ID>.
1202
+ Run QA on card <CARD-ID> at the tier dictated by the QA profile below (do NOT default to FULL).
1200
1203
 
1201
1204
  Context:
1202
1205
  - Worktree path: <worktree-path>
@@ -1204,9 +1207,13 @@ skill's Phase 1 falls back to deriving Gherkin scenarios from
1204
1207
  - Changed files: <list from implementation phase>
1205
1208
  - QA profile: [balanced | deep]
1206
1209
 
1207
- Run E2E tests: `npx playwright test --reporter=list`
1208
- E2E failures are BLOCKING for deep profile, ADVISORY for balanced profile.
1209
- Also run gates (lint, tsc, test, build, markdownlint) as sanity check.
1210
+ Tier contract: balanced → SCOPED (related tests on touched modules); deep → FULL suite.
1211
+ Only path-based risk drift (auth/permission/payment/schema/migration/API contract in the diff)
1212
+ may escalate balanced→FULL. File count never escalates.
1213
+
1214
+ E2E: deep → `npx playwright test --reporter=list` (BLOCKING);
1215
+ balanced → `npx playwright test --grep "<changed area>" --reporter=list` (ADVISORY).
1216
+ Also run gates (lint, tsc, build, markdownlint) as sanity check.
1210
1217
  Do NOT verify acceptance criteria (Phase 2.5 already did this).
1211
1218
  Do NOT analyze code for bugs/patterns (deferred to /codexreview post-batch).
1212
1219
  Do NOT write recommendations or follow-up actions.
@@ -1216,14 +1223,14 @@ skill's Phase 1 falls back to deriving Gherkin scenarios from
1216
1223
  Return verdict: PASS or FAIL.
1217
1224
  ```
1218
1225
 
1219
- 22. **Read qa-sentinel's output.** Verify the findings file was written to `/qa/<CARD-ID>.md`.
1220
- 23. **If QA verdict is FAIL**:
1226
+ 23. **Read qa-sentinel's output.** Verify the findings file was written to `/qa/<CARD-ID>.md`.
1227
+ 24. **If QA verdict is FAIL**:
1221
1228
  - Spawn the **coder** agent to fix all BLOCKER findings (pass it the findings file path + list of blockers). Do NOT ask the user.
1222
1229
  - After coder fixes, re-invoke `qa-sentinel` in the same mode to re-validate. Repeat up to **2 times**.
1223
1230
  - If still FAIL after 2 retries: log in `## Issues & Flags` and **ask the user** whether to proceed or stop.
1224
1231
  - The commit in Phase 4 MUST NOT happen until QA verdict is PASS (or user explicitly overrides).
1225
1232
  - **Telemetry** — after each coder spawn, append one row per blocker to `## Fix Application Log`: `3.5 | qa-blocker | est_lines=<n> | decision=coder | applied_by=coder | retry=<0|1|2>`. After PASS without any spawn (initial verdict PASS), append one row: `3.5 | qa-none | est_lines=0 | decision=skipped | applied_by=- | reason=qa-pass`.
1226
- 24. **Update tracker**: phase = "3.5-qa DONE", log: profile used, verdict (PASS/FAIL/SKIP), confidence %, findings count (blockers/majors/minors), findings file path.
1233
+ 25. **Update tracker**: phase = "3.5-qa DONE", log: profile used, tier run (SCOPED/FULL) + any risk-drift escalation, verdict (PASS/FAIL/SKIP), confidence %, findings count (blockers/majors/minors), findings file path.
1227
1234
 
1228
1235
  ### Phase 3.7 — Pre-Merge Codex Review Gate (MANDATORY — UNCONDITIONAL)
1229
1236
 
@@ -1557,8 +1564,10 @@ Once ALL cards are committed in the worktree:
1557
1564
  ### Step F.5 — Apply fixes and final build
1558
1565
 
1559
1566
  10. **Persist verified findings** to `/tmp/batch-final-review-<FIRST-CARD-ID>.md`.
1560
- 11. If VERIFIED findings with severity >= MEDIUM exist, invoke the **coder** agent once to
1561
- apply ALL fixes in a single pass. Pass only the verified findings, not false positives.
1567
+ 11. If VERIFIED findings with severity >= MEDIUM exist, apply them by **domain owner** (since v3.40.0 — same Domain-Override routing as per-card phases). Partition the verified findings:
1568
+ - **`doc`-domain findings** (file path matching the `doc` match rule in "Domain-Override Domains" — `*.md` under `${paths.references_dir}`/`${paths.prd_dir}`, `CHANGELOG.md`, `ssot-registry.md`) → invoke the **doc-reviewer** agent once in write mode to apply them. NEVER route doc fixes to coder.
1569
+ - **All other findings** (code, perf, security, migration, test) → invoke the **coder** agent once to apply them in a single pass.
1570
+ Run both in the order doc-reviewer → coder (or skip either if its partition is empty). Pass only the verified findings, not false positives.
1562
1571
  12. Run final build: `npm run lint && npx tsc --noEmit && npm run build`.
1563
1572
  If any check fails, apply self-healing retry loop (up to 3 times).
1564
1573
  13. **Update tracker** with final review results:
@@ -2057,7 +2066,7 @@ If at ANY point you are unsure where you are in the batch:
2057
2066
  ### Sequential mode (default for small batches)
2058
2067
 
2059
2068
  - Cards execute one at a time through the full per-card pipeline (Phases 1-5).
2060
- - Code review and doc review for the same card run as **parallel read-only audits**, then fixes are applied in a single sequential pass.
2069
+ - Code review and doc review for the same card run as **parallel read-only audits**, then fixes are applied by domain owner: **doc findings → `doc-reviewer` (write mode)**, code/security/migration findings → `coder`. (Sequential Phase 3 is even simpler — doc-reviewer runs alone, so it audits AND applies in one invocation.)
2061
2070
  - This mode is unchanged from the original behavior.
2062
2071
 
2063
2072
  ### Team mode (for complex batches)
@@ -2222,7 +2231,7 @@ After ALL agents in the group complete successfully:
2222
2231
 
2223
2232
  1. **D.1 — Build verification (group)** — Run `npm run build` in the worktree to verify combined changes compile. If build fails, identify which card's changes broke it (from `git diff` per card), spawn a targeted fix-coder for those files only.
2224
2233
 
2225
- 2. **D.2 — Combined static review (group)** — Invoke **code-reviewer** + **doc-reviewer** IN PARALLEL (read-only) across ALL changes in the group. ONE combined pass — catches cross-card issues. The doc-reviewer MUST **attribute every finding to a specific card** (by file → ownership map) and apply the full Phase 3 mandate INCLUDING the spec/docs-drift→bug lens (since v3.35.0), so this single pass satisfies each card's doc gate. D.4a consumes these per-card findings (no second doc-reviewer spawn).
2234
+ 2. **D.2 — Combined static review (group)** — Invoke **code-reviewer** + **doc-reviewer** IN PARALLEL (read-only) across ALL changes in the group. ONE combined pass — catches cross-card issues. The doc-reviewer runs **read-only here** (it cannot write while code-reviewer reads the same diff in parallel — parallel-safety), MUST **attribute every doc finding to a specific card** (by file → ownership map), and applies the full Phase 3 mandate INCLUDING the spec/docs-drift→bug lens (since v3.35.0). D.4a consumes these per-card findings and dispatches the doc-reviewer (now alone, in write mode) to apply them — no second AUDIT spawn, but the FIXES are still owned by doc-reviewer.
2226
2235
 
2227
2236
  3. **D.3 — Apply fixes (group)** — If D.2 findings exist, spawn ONE fix-coder to apply all fixes in a single pass. Run build + lint after.
2228
2237
 
@@ -2232,9 +2241,9 @@ After ALL agents in the group complete successfully:
2232
2241
 
2233
2242
  3c. **D.3c — Phase 2.6 E2E-Review (per-card)** — For EACH card in the group, honor the existing Gate table (skip when `features.has_e2e_review: false`, backend-only diff per the diff predicate documented in Phase 2.6, or card type `docs`/`chore`/`config`). For cards that pass the gate, invoke `/e2e-review` in programmatic mode with that card's payload. BLOCKING per-card: if `/e2e-review` returns `"blocked"` or `"error"`, surface to the user via the same `AskUserQuestion` documented in Phase 2.6 — do NOT proceed to D.4 with an unresolved card. Skips are logged with the documented gate reason, never with `"time budget"` or similar.
2234
2243
 
2235
- 4. **D.4 — QA gate (group)** — Read each card's `review_profile` field (fallback: compute via the QA Profile Selector table for legacy cards). Select the HIGHEST profile across all cards in the group (e.g., if one card is BALANCED and another is DEEP, use DEEP). Invoke **qa-sentinel** once for the group.
2244
+ 4. **D.4 — QA gate (group)** — Read each card's `review_profile` field (fallback: compute via the QA Profile Selector table for legacy cards). Select the HIGHEST profile across all cards in the group (e.g., if one card is BALANCED and another is DEEP, use DEEP) and pass it as the `QA profile` to **qa-sentinel** (invoked once for the group) using the same prompt contract as Phase 3.5 step 22 (`do NOT default to FULL`; balanced → SCOPED, deep → FULL). Note: a mixed group containing any DEEP card runs the group at FULL — the max-profile intentionally overrides the SCOPED tier the BALANCED cards would get individually, because the combined diff must be validated together.
2236
2245
 
2237
- 4a. **D.4a — Per-card doc gate (consumes D.2, since v3.35.0)** — Do NOT re-spawn `doc-reviewer`. For EACH card in the group, take that card's doc findings from D.2's per-card-attributed output; if any exist, spawn a targeted fix-coder for that card only. (Previously D.4a re-ran doc-reviewer per card a duplicate of D.2; collapsed to a single attributed D.2 pass. D.4b's per-card `/codexreview` also skips its doc-reviewer via the lean contract, so doc-review now runs once per group instead of ~3× per card.)
2246
+ 4a. **D.4a — Per-card doc gate (consumes D.2, since v3.35.0; doc-reviewer-applied since v3.40.0)** — Do NOT re-run the doc AUDIT (D.2 already produced per-card-attributed findings). If any card in the group has doc findings, invoke the **doc-reviewer once in write mode** over the group, passing the per-card-attributed findings from D.2, to APPLY all doc fixes in a single pass (it is now alone — code-reviewer is done — so the D.2 parallel-safety constraint no longer applies). Do **NOT** spawn a fix-coder for doc findings: `doc` is owned by `doc-reviewer` (see "Domain-Override Domains"); a code-oriented coder lacks the doc-invariant contract. The only exception is a doc-drift→bug finding rooted in CODE — that follows the D.4b code fix path. (Previously D.4a spawned a fix-coder per card; the audit was already collapsed to a single attributed D.2 pass. D.4b's per-card `/codexreview` also skips its doc-reviewer via the lean contract, so the doc AUDIT runs once per group while the doc FIXES run once per group via doc-reviewer.) **Telemetry** — append one row per applied doc finding to `## Fix Application Log`: `D.4a | doc | est_lines=<n> | decision=doc-reviewer | applied_by=doc-reviewer | card=<ID> | finding=<1-line>`.
2238
2247
 
2239
2248
  4b. **D.4b — Pre-Merge Codex Review Gate (per-card, MANDATORY — UNCONDITIONAL)** — Invoke `/codexreview` for EACH card in the group, one at a time, BEFORE any commit. This mirrors Phase 3.7 of the sequential path and is non-skippable regardless of file paths or perceived risk. Apply the same fix sub-loop: if the consolidated report shows verified BLOCKER/HIGH findings, spawn a fix-coder and re-run `/codexreview <CARD-ID>` (max 2 retries per card). If still BLOCKER/HIGH after retries, ask the user before proceeding to D.5. The D.5 commits MUST NOT happen until every card in the group has a PASS verdict (or explicit user override via `AskUserQuestion`). Log results in the tracker under `## Pre-Merge Codex Review` per card. **Lean + profile (since v3.35.0)**: before each card's `/codexreview`, apply the same Review Profile Selector and write the same `/tmp/codexreview-lean-<CARD-ID>.json` contract as sequential Phase 3.7 Step C — `arch_baseline_path` pointing at `/tmp/arch-baseline-group-<FIRST-CARD-ID>.md`, `skip_doc_reviewer: true`, and `profile: light|full` derived from the card's `review_profile` (`skip`/`light` → `light`, `balanced`/`deep` → `full`; since v3.38.0 read from the card, not recomputed) — with the same **escalation-only** rule: a Step A high-risk trigger on the actual diff promotes `light` → `full`, never the reverse. LIGHT runs `code-reviewer` + FP-gate only — the per-card **Codex adversarial pass is dropped at `light` since v3.38.0** (prerogative of `full` + the final cross-check). The Review Profile Selector is **ACTIVE since v3.37.0** — same status as sequential Phase 3.7 Step C above: `light` is per-card early feedback, and the merge-gate safety is the post-batch **Final-review FULL gate** (a single FULL `/codexreview` over the entire batch diff, which team mode reaches via "Post-batch — same as sequential mode"). To make per-card always-full again, restore `profile = full`; the final full gate stays regardless.
2240
2249
 
@@ -22,7 +22,8 @@ Route agents to the right module with minimal reading.
22
22
  - If touching design-review workflows or UI guidelines -> read `agents/design-review.md` and project-specific UI guidelines.
23
23
  - If touching architecture, auth, or tech stack -> read `agents/architecture.md`.
24
24
  - If touching workflow/process/commits/backlog -> read `agents/workflows.md`.
25
- - If touching testing or QA issues -> read `agents/testing.md`.
25
+ - If touching testing or QA issues -> read `agents/testing.md` (also documents the scope-aware,
26
+ profile-driven test-selection strategy consumed by `qa-sentinel`).
26
27
  - If touching GitHub issues or issue workflow -> read `agents/github-issue-subagent.md`.
27
28
  - If touching terminology or naming -> read `agents/coding-standards.md`.
28
29
  - If touching env vars or scripts -> read `agents/runbook.md` and `agents/env-reference.md`.
@@ -9,6 +9,32 @@ Define required manual testing and QA issue workflow, including browser automati
9
9
  **In**: Manual testing steps, QA issue creation and handling, browser automation.
10
10
  **Out**: Performance testing strategy (see agents/performance.md).
11
11
 
12
+ ## Scope-aware Test Selection (Automated QA)
13
+
14
+ The automated QA gate (`qa-sentinel`) does **not** run the full test suite on every change. It runs
15
+ the depth dictated by the card's `review_profile` (the deterministic SSOT computed at authoring
16
+ time), so test effort matches change risk:
17
+
18
+ - **balanced → SCOPED**: run only the tests *related to the changed files* (test-impact selection),
19
+ not the whole suite. This is the default for ordinary features and refactors.
20
+ - **deep → FULL**: run the whole suite + security audit + blocking E2E. Reserved for high-risk
21
+ changes (data model, security, broad blast radius).
22
+ - **skip / light**: the gate is not invoked — these are handled upstream (docs/chore, or gates
23
+ already run during implementation).
24
+ - **Risk-drift escalation**: a `balanced` change is promoted to FULL only when the diff itself
25
+ reveals undeclared risk — paths touching auth, permissions, payment/billing, DB schema/migration,
26
+ or an API contract. Change *count* never escalates (it is advisory only).
27
+
28
+ Test selection is **heuristic**, not instrumented: it relies on file paths, naming, and the test
29
+ runner's native related-tests capability — no coverage maps, AST analysis, or ML. When no related
30
+ tests can be resolved for a logic change, the gate records a coverage-gap and lowers its confidence
31
+ rather than silently passing or falling back to the full suite. The concrete per-runner commands
32
+ live in the `qa-sentinel` agent (which is stack-aware); this module states the principle only.
33
+
34
+ > The executable tier table + runner cascade is owned by `qa-sentinel` (`.claude/agents/qa-sentinel.md`),
35
+ > kept stack-specific there. Optional smoke convention: a `test:smoke` script or `tests/smoke/`
36
+ > directory, if present, is used as a minimal sanity set — its absence is fine, not a misconfiguration.
37
+
12
38
  ## Do
13
39
 
14
40
  - Run project build command before marking a card done.
@@ -36,8 +36,9 @@ Output is a tabular summary grouped by `phase × domain × decision × applied_b
36
36
 
37
37
  | Pattern | What it means |
38
38
  |---|---|
39
- | `3 \| doc \| ... \| applied_by=orchestrator` | Phase 3 doc-review fix applied inline (violates `SKILL.md:1116` "invoke the coder agent once"). |
40
- | `3.7 \| codex-security \| ... \| applied_by=orchestrator` | Codex HIGH security finding fixed inline (violates `SKILL.md:1249` "spawn `coder` agent"). |
39
+ | `3 \| doc \| ... \| applied_by=orchestrator` | Phase 3 doc-review fix applied inline (violates the `doc` Domain-Override rule doc fixes are owned by `doc-reviewer`). |
40
+ | `3 \| doc \| ... \| applied_by=coder` | Phase 3 doc fix routed to `coder` (violates the `doc` Domain-Override rule since v3.40.0 — doc fixes are owned by `doc-reviewer`, which has the doc-invariant contract the coder lacks). |
41
+ | `3.7 \| codex-security \| ... \| applied_by=orchestrator` | Codex HIGH security finding fixed inline (violates `SKILL.md` Phase 3.7 "spawn `coder` agent"). |
41
42
  | `* \| * \| ... \| applied_by=orchestrator-fallback` | Sub-agent failed and orchestrator fell back to inline. Expected to be rare; investigate if frequent. |
42
43
 
43
44
  The analyzer script flags these in its footer automatically.
@@ -55,7 +56,8 @@ The analyzer script flags these in its footer automatically.
55
56
  | Pattern | What it means |
56
57
  |---|---|
57
58
  | `2.55 \| simplify-* \| est_lines∈[1-5] \| decision=inline \| applied_by=orchestrator` | Small inline fixes — efficient, low cost. Expected majority of Phase 2.55. |
58
- | `3 \| doc \| ... \| decision=coder-batch \| applied_by=coder` | Doc fixes batched into one coder spawn. Expected for every card with doc impact. |
59
+ | `3 \| doc \| ... \| decision=doc-reviewer \| applied_by=doc-reviewer` | Doc fixes applied by the doc-reviewer in write mode (since v3.40.0). Expected for every card with doc impact. |
60
+ | `D.4a \| doc \| ... \| decision=doc-reviewer \| applied_by=doc-reviewer` | Team-mode doc fixes applied by the doc-reviewer over the group. Expected for every group with doc impact. |
59
61
  | `3.7 \| * \| decision=skipped \| applied_by=- \| severity=FALSE-POSITIVE` | Codex false positives correctly filtered. |
60
62
 
61
63
  ## When to act on the data
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "baldart",
3
- "version": "3.39.0",
3
+ "version": "3.41.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"