baldart 4.27.1 → 4.28.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 +17 -0
- package/VERSION +1 -1
- package/framework/.claude/skills/new/references/implement.md +8 -0
- package/framework/.claude/skills/new/references/setup.md +13 -0
- package/framework/.claude/skills/new2/SKILL.md +43 -1
- package/framework/.claude/skills/prd/assets/epic-template.yml +28 -0
- package/framework/.claude/workflows/new2-resolve.js +20 -10
- package/framework/.claude/workflows/new2.js +21 -1
- package/framework/templates/overlays/new.migration-example.md +60 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,23 @@ 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
|
+
## [4.28.0] - 2026-06-11
|
|
9
|
+
|
|
10
|
+
**`new2`: front-loaded Migration Gate — declared DB migrations are applied BEFORE the batch, so downstream cards verify against a live schema.** The workflow runs autonomously and cannot apply an owner-gated DB migration mid-run, so a declared migration was deferred to end-of-batch — and every downstream card was built/verified against a schema not yet live (`validation_commands` / QA / E2E / DB-generated `tsc` types fail falsely → those cards cascade into deferral). The new gate runs in the **skill** (the main loop, which can interact — the workflow's zero-ask contract is untouched), mirroring `/new` Phase 0 step 1b: it finds the batch epic's `migration_plan` (`required: true`), verifies artifacts on disk, assembles apply modalities (epic `apply_modalities` + `.baldart/overlays/new.md` `## Migration modalities` + project memory + built-in `Già applicata`/`Abort`), asks ONE pre-launch question, executes the chosen modality in `$MAIN`, and passes a `migration` manifest into the workflow. **MINOR** (additive capability on the EXPERIMENTAL `new2` surface; the gate is a silent no-op when no migration is declared and degrades to today's end-of-batch deferral when artifacts are missing — no regression; the `migration` arg is the skill→workflow contract only, NOT a `baldart.config.yml` key, so the schema-change propagation rule does not apply).
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`framework/.claude/skills/new2/SKILL.md` Step 3.5 — Migration Gate.** BLOCKING only when a migration is declared; otherwise a silent no-op. Emits the `migration = { status: 'none'|'applied'|'skipped'|'degraded', modality?, summary?, artifacts?, affects_cards? }` manifest passed to the workflow (Step 4 args block). The final-record step also logs `migration_gate: <status>` so the A/B comparison stays honest about when a migration was front-loaded (a pre-launch interaction, NOT a mid-batch question — the zero-ask-during-batch invariant holds).
|
|
15
|
+
- **`framework/.claude/workflows/new2.js` — migration manifest consumption.** Reads `a.migration` (args contract documented), and when `status === 'applied'` injects an EXCEPTION to the F-016 owner-gated rule in the preflight (the schema-apply / db-push AC of the affected cards must NOT be classified as a policy-deferred AC — it is satisfied) and a `MIGRATION LIVE` note into each affected card's brief (run REAL validation against the live schema, do not defer or build against an absent schema). A migration NOT covered by the manifest still follows the existing end-of-batch owner-gated deferral.
|
|
16
|
+
|
|
17
|
+
## [4.27.2] - 2026-06-11
|
|
18
|
+
|
|
19
|
+
**`new2` resolve: kill the second self-judging adversarial pass for `security` too.** v4.27.1 removed the wasteful adversarial doc review (doc-reviewer judging doc-reviewer). An audit of every fixer→judge pair in the resolution pass found `security` is the exact same structural case: the fixer is `security-reviewer` (protected domain) and so is the judge, so the mandatory cross-check was `security-reviewer` judging `security-reviewer` — no cross-model diversity, the same waste. The skip is now driven by a structural guard, `selfJudges = (fixerAgent === judgeAgent)`, instead of a per-domain allowlist, so it covers doc + security today and can't silently regress if the fixer/judge map changes. Every domain where fixer ≠ judge (ui/perf/test/code/migration) keeps the mandatory adversarial cross-check unchanged. **PATCH** (cost/latency optimization on the EXPERIMENTAL `new2` surface; no config key, no change to `/new`).
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- **`framework/.claude/workflows/new2-resolve.js`** — introduced `selfJudges`; both `judgeVerify()` and the terminal-judge ratification branch short-circuit when `selfJudges` is true (was `domain === 'doc'`). `meta.description` updated to describe the structural fixer===judge rule.
|
|
24
|
+
|
|
8
25
|
## [4.27.1] - 2026-06-11
|
|
9
26
|
|
|
10
27
|
**`new2` resolve: never run an adversarial doc review.** When a resolution pass fixed a `doc`-domain finding, the fixer was `doc-reviewer` (which applies *and* self-verifies the fix in one pass) — but `judgeVerify()` then spawned `doc-reviewer` *again* as the mandatory adversarial judge, plus the terminal-judge ratification used it a third time. That is `doc-reviewer` judging `doc-reviewer`: zero cross-model diversity, pure waste of tokens and time. The doc domain now trusts the single reviewer-writer pass — the adversarial judge and the terminal-judge ratification are both skipped for `doc` only (every other domain keeps the mandatory cross-check unchanged, since their fixer and judge are genuinely independent specialists). **PATCH** (cost/latency optimization on the EXPERIMENTAL `new2` surface; no config key, no change to `/new`, no behavior change for code/ui/security/perf/test resolutions).
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.
|
|
1
|
+
4.28.0
|
|
@@ -93,6 +93,14 @@
|
|
|
93
93
|
You MUST actively verify and produce one of the two outcomes (implementation OR TODO comment).
|
|
94
94
|
See `coder.md § Conditional Requirements — Binary-Outcome Items`.
|
|
95
95
|
|
|
96
|
+
### Migration context (include ONLY when this card appears in the tracker `## Migration` manifest's `affects_cards`)
|
|
97
|
+
The required DB migration `<summary>` was ALREADY APPLIED to the active DB before this batch
|
|
98
|
+
started (Phase 0 step 1b, modality `<id>`). The schema is LIVE — run REAL validation against it
|
|
99
|
+
(`validation_commands`, queries, DB-generated types). Do NOT defer the schema-apply AC and do NOT
|
|
100
|
+
build against a stubbed/absent schema: the table/column exists. If `## Migration` shows
|
|
101
|
+
`status: degraded` instead (artifact missing / not applied), treat the schema as NOT yet live and
|
|
102
|
+
follow the card's normal owner-gated deferral path.
|
|
103
|
+
|
|
96
104
|
### Business Context (WHY this feature exists)
|
|
97
105
|
[paste business_rationale field from card YAML]
|
|
98
106
|
[if field is empty/missing, read PRD Section 1b from card's links.prd path]
|
|
@@ -17,6 +17,19 @@
|
|
|
17
17
|
|
|
18
18
|
1. **Resolve `$MAIN`** — the absolute path of the main repo (not a worktree). If `/new` was invoked from inside a worktree, walk up to the parent repo via `git rev-parse --show-superproject-working-tree` or `git worktree list` until you find the non-worktree root. Persist as `Main repo:` in the tracker `## Worktree` section. **Write `$MAIN` to the tracker the moment it is computed** — every later consumer (Phase 6c, Phase 6b) MUST re-read it from the tracker and HALT with "`$MAIN` absent from tracker" if the field is missing or empty, never silently use an undefined `$MAIN` (it does not survive context compaction).
|
|
19
19
|
|
|
20
|
+
1b. **Migration Gate (BLOCKING only when a migration is *declared* — else a silent no-op)** — resolve DB migrations interactively **before** the worktree exists, so the schema is live before any card builds against it. *Why this exists*: a migration applied to a shared/remote DB is owner-gated, so without this gate it is deferred to the END of the batch — and every downstream card in the batch is then built and verified against a schema that is not yet live (`validation_commands` / QA / E2E / DB-generated `tsc` types fail falsely → those cards cascade into deferral/blocked). Front-loading the migration removes that root cause. **The declaration lives in the EPIC card** (`migration_plan` block — project-specific, authored by the user, typically via the `.baldart/overlays/new.md` overlay). Steps:
|
|
21
|
+
|
|
22
|
+
1. **Find the batch's epic card(s).** Collect the distinct `group.parent` of the batch cards (or, for a `-full` invocation, the parent already known). For each, locate the epic YAML in `${paths.backlog_dir}` (filename `<PARENT>-*-epic.yml`, or the card whose `id == <PARENT>` / `group.is_epic: true`) and Read it.
|
|
23
|
+
2. **Look for `migration_plan` with `required: true`.** If **no** epic in the batch declares one → write `## Migration\nnone (no migration_plan declared)` to the tracker and **proceed to step 2** (behaviour is identical to today — zero extra prompts). This is the common case; do not surface anything.
|
|
24
|
+
3. **(declared) Verify the artifacts exist on disk.** For each path in `migration_plan.artifacts`, check it exists under `$MAIN`. If **any is missing** → do **NOT** apply or prompt; write `## Migration\ndeclared but artifact(s) missing: <paths> — degraded to deferred (author the migration first)` to the tracker and **proceed to step 2** (the migration falls back to the current end-of-batch owner-gated deferral — no regression). *(Auto-generating a missing artifact is out of scope; the migration must already be authored to be applied up-front.)*
|
|
25
|
+
4. **(declared + artifacts present) Assemble the apply modalities**, in this source order, de-duplicating by `id`: (a) `migration_plan.apply_modalities` from the epic block; (b) a `## Migration modalities` section in `.baldart/overlays/new.md` if present; (c) any project-memory note on how this project applies migrations; (d) the built-in tail `["Già applicata — prosegui", "Abort"]`. Each modality is `{ id, label, command? }`.
|
|
26
|
+
5. **`AskUserQuestion`** — `"La epic dichiara una migrazione DB (<summary>). Va applicata PRIMA del batch così le card downstream verificano contro lo schema reale. Come procedo?"` with up to 4 of the assembled modalities (always include "Già applicata — prosegui" and "Abort" as the last options). This is a legitimate Phase 0 question (Auto Mode does not override it).
|
|
27
|
+
6. **Execute the choice in `$MAIN`** (project env):
|
|
28
|
+
- a **command** modality → run it with output to disk (`<cmd> > /tmp/migration-<FIRST-CARD-ID>.log 2>&1`), surface only the exit code; on exit 0 run the optional `migration_plan.verify` probe and require it green too. On **failure** → surface a bounded extract (`tail -n 30`) and re-ask (re-offer the modalities + Abort); never silently proceed against a non-live schema. **Never run a command without the user having selected it.**
|
|
29
|
+
- **"Già applicata — prosegui"** → run the optional `verify` probe if present; otherwise trust the user.
|
|
30
|
+
- **"Abort"** → HALT the batch cleanly (leave the tracker in place); do not create a worktree.
|
|
31
|
+
7. **Record the manifest** in the tracker `## Migration` section: `status: applied|skipped|degraded`, `modality: <id>`, `artifacts: [...]`, `affects_cards: [...]`, `applied_at: <timestamp>`. Phase 1/2 briefings (implement.md) MUST surface, for any card in `affects_cards`, the note *"migration `<summary>` already applied to the active DB — run REAL validation (do not defer the schema AC)"*, so the live schema is actually exercised and the card's migration-apply AC is treated as satisfied, not deferred.
|
|
32
|
+
|
|
20
33
|
2. **Fetch remote state**:
|
|
21
34
|
```bash
|
|
22
35
|
git -C "$MAIN" fetch origin --quiet
|
|
@@ -86,6 +86,43 @@ to its backlog YAML path under `${paths.backlog_dir}`.
|
|
|
86
86
|
Read them for the review SEMANTICS, so `new2` stays a thin orchestration host and
|
|
87
87
|
the A/B test isolates the host, not the protocol).
|
|
88
88
|
|
|
89
|
+
### Step 3.5 — Migration Gate (BLOCKING only when a migration is *declared* — else a silent no-op)
|
|
90
|
+
|
|
91
|
+
The workflow runs autonomously and **cannot** apply an owner-gated DB migration mid-run, so today a
|
|
92
|
+
declared migration is deferred to the end of the batch — and every downstream card is built and
|
|
93
|
+
verified against a schema that is not yet live (`validation_commands` / QA / E2E / DB-generated `tsc`
|
|
94
|
+
types fail falsely → those cards cascade into deferral). This gate front-loads the migration: it runs
|
|
95
|
+
**here, in the skill** (the main loop — it can interact; the workflow's zero-ask contract is
|
|
96
|
+
untouched), so the schema is live **before** the workflow starts. It mirrors `/new` Phase 0 step 1b
|
|
97
|
+
(`references/setup.md`) — same algorithm, same `migration_plan` epic-card convention.
|
|
98
|
+
|
|
99
|
+
1. **Find the batch's epic card(s)** — distinct `group.parent` of the resolved cards (or the `-full`
|
|
100
|
+
parent). Read each epic YAML in `${paths.backlog_dir}` (`<PARENT>-*-epic.yml`, or the card whose
|
|
101
|
+
`id == <PARENT>` / `group.is_epic: true`).
|
|
102
|
+
2. **Look for `migration_plan` with `required: true`.** None → set `migration = { status: 'none' }`
|
|
103
|
+
and go to Step 4 (identical to today — zero extra prompts; do not surface anything).
|
|
104
|
+
3. **(declared) Verify `migration_plan.artifacts` exist on disk under `$MAIN`.** Any missing → do NOT
|
|
105
|
+
apply or prompt; set `migration = { status: 'degraded', reason: 'artifact missing', artifacts }`
|
|
106
|
+
and go to Step 4 (the migration falls back to the current end-of-batch owner-gated deferral — no
|
|
107
|
+
regression).
|
|
108
|
+
4. **(declared + present) Assemble the apply modalities**, de-duped by `id`, in source order:
|
|
109
|
+
(a) `migration_plan.apply_modalities`; (b) a `## Migration modalities` section in
|
|
110
|
+
`.baldart/overlays/new.md`; (c) any project-memory note on how this project applies migrations;
|
|
111
|
+
(d) built-in tail `["Già applicata — prosegui", "Abort"]`.
|
|
112
|
+
5. **`AskUserQuestion`** — `"La epic dichiara una migrazione DB (<summary>). La applico PRIMA del
|
|
113
|
+
batch così le card downstream verificano contro lo schema reale. Come procedo?"` with up to 4
|
|
114
|
+
modalities (always include "Già applicata — prosegui" and "Abort"). This is the SAME class as the
|
|
115
|
+
Step-2 "ONE pre-launch question" — pre-launch, not a mid-run gate; the zero-ask contract is about
|
|
116
|
+
the *workflow*, which is untouched.
|
|
117
|
+
6. **Execute the choice in `$MAIN`**:
|
|
118
|
+
- a **command** modality → run with output to `/tmp/migration-<firstCard>.log`, surface only the
|
|
119
|
+
exit code; on exit 0 run the optional `migration_plan.verify` probe and require it green. On
|
|
120
|
+
failure → bounded extract (`tail -n 30`) + re-ask. **Never run a command the user did not pick.**
|
|
121
|
+
- **"Già applicata — prosegui"** → run `verify` if present; else trust the user.
|
|
122
|
+
- **"Abort"** → HALT cleanly; do NOT call the workflow.
|
|
123
|
+
7. **Build the manifest** to pass to the workflow:
|
|
124
|
+
`migration = { status: 'applied'|'skipped', modality: <id>, summary, artifacts: [...], affects_cards: [...], appliedAt: ts }`.
|
|
125
|
+
|
|
89
126
|
### Step 4 — Delegate the whole batch to the workflow
|
|
90
127
|
|
|
91
128
|
Call the `Workflow` tool:
|
|
@@ -100,6 +137,7 @@ Workflow({ name: 'new2', args: {
|
|
|
100
137
|
refModulesBase, // .claude/skills/new/references (semantic SSOT)
|
|
101
138
|
config, // the parsed baldart.config.yml (paths.*/stack.*/features.*/git.*)
|
|
102
139
|
ts, // ISO timestamp NOW — the workflow has no clock (Date.now() unavailable there)
|
|
140
|
+
migration, // Step-3.5 manifest: { status:'none'|'applied'|'skipped'|'degraded', modality?, summary?, artifacts?, affects_cards? }
|
|
103
141
|
flags: { stats, effort, full }
|
|
104
142
|
}})
|
|
105
143
|
```
|
|
@@ -185,4 +223,8 @@ returns when the batch is done. It returns:
|
|
|
185
223
|
`residualFollowups.length` — which double-counts). `total_tokens`/`agent_count` come from the
|
|
186
224
|
workflow; if `total_tokens` is null, run the `/new` Phase-8 `-stats` script to backfill real
|
|
187
225
|
`usage`. Keep `degraded`/`degradation_reasons` + `cards_deferred_done_pending` in the record so
|
|
188
|
-
the A/B comparison stays honest.
|
|
226
|
+
the A/B comparison stays honest. Also record `migration_gate: <migration.status>`
|
|
227
|
+
(`none`|`applied`|`skipped`|`degraded`) — the Step-3.5 gate is a pre-launch interaction, NOT a
|
|
228
|
+
mid-batch question, so it does not break the zero-ask-during-batch invariant; logging it keeps the
|
|
229
|
+
A/B honest about when a migration was front-loaded. Do NOT re-summarise the cards — the workflow
|
|
230
|
+
already did.
|
|
@@ -143,6 +143,34 @@ execution_strategy:
|
|
|
143
143
|
- "{{FEAT-XXXX-N}} e {{FEAT-XXXX-M}} entrambi modificano {{file}} — forzato sequenziale"
|
|
144
144
|
# omit if no conflicts
|
|
145
145
|
|
|
146
|
+
# =============================================================================
|
|
147
|
+
# Migration Plan (OPTIONAL — declare ONLY when the epic needs a DB schema change
|
|
148
|
+
# applied BEFORE its cards run). Read by /new (Phase 0 step 1b) and new2 (Step 3.5):
|
|
149
|
+
# the skill front-loads the migration interactively so downstream cards verify
|
|
150
|
+
# against the LIVE schema instead of a deferred one. Project-specific — the shape of
|
|
151
|
+
# `apply_modalities` (db:push, local apply, …) belongs in the consumer's
|
|
152
|
+
# `.baldart/overlays/new.md`; see framework/templates/overlays/new.migration-example.md.
|
|
153
|
+
# OMIT this block entirely when the epic introduces no schema change (the common case;
|
|
154
|
+
# the gate then stays a silent no-op). The migration `artifacts` must already exist on
|
|
155
|
+
# disk to be applied up-front — if absent, the gate degrades to the current
|
|
156
|
+
# end-of-batch owner-gated deferral.
|
|
157
|
+
# =============================================================================
|
|
158
|
+
|
|
159
|
+
# migration_plan:
|
|
160
|
+
# required: true
|
|
161
|
+
# summary: "{{Add bookings.status enum column + composite index}}"
|
|
162
|
+
# artifacts:
|
|
163
|
+
# - {{supabase/migrations/20260611_add_status.sql}}
|
|
164
|
+
# apply_modalities: # de-duped by id; merged with overlays/new.md + project memory
|
|
165
|
+
# - id: remote-push
|
|
166
|
+
# label: "db:push al DB remoto (eseguito dalla skill su tua approvazione)"
|
|
167
|
+
# command: "{{npx supabase db push}}"
|
|
168
|
+
# - id: local
|
|
169
|
+
# label: "Applica in locale"
|
|
170
|
+
# command: "{{npx supabase db reset --local}}"
|
|
171
|
+
# affects_cards: [{{FEAT-XXXX-02}}, {{FEAT-XXXX-03}}] # downstream che richiedono lo schema live
|
|
172
|
+
# verify: "{{npx supabase gen types typescript --local | head}}" # opzionale — probe post-apply
|
|
173
|
+
|
|
146
174
|
# =============================================================================
|
|
147
175
|
# Canonical Docs (epic-level — children inherit subset)
|
|
148
176
|
# =============================================================================
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const meta = {
|
|
2
2
|
name: 'new2-resolve',
|
|
3
3
|
description:
|
|
4
|
-
"Self-healing resolution pass for the autonomous new2 batch workflow. Called by new2 whenever a deterministic gate would otherwise need a human: a card fail/blocker (ac-unmet | blocker | qa-fail | e2e-blocked | merge-blocker) or a legitimate scope-EXPANDING finding (scope-expansion). Tier-1 targeted fix with a TERMINAL short-circuit (skips the costly multi-attempt when the problem is impossible-by-definition, verified not trusted), then a judged multi-attempt; a MANDATORY adversarial judge cross-checks every verified claim against the real diff (prevents fabricated success)
|
|
4
|
+
"Self-healing resolution pass for the autonomous new2 batch workflow. Called by new2 whenever a deterministic gate would otherwise need a human: a card fail/blocker (ac-unmet | blocker | qa-fail | e2e-blocked | merge-blocker) or a legitimate scope-EXPANDING finding (scope-expansion). Tier-1 targeted fix with a TERMINAL short-circuit (skips the costly multi-attempt when the problem is impossible-by-definition, verified not trusted), then a judged multi-attempt; a MANDATORY adversarial judge cross-checks every verified claim against the real diff (prevents fabricated success) whenever the judge is a DIFFERENT specialist than the fixer. When fixer === judge (doc→doc-reviewer, security→security-reviewer) the second pass would just judge its own work — no cross-model diversity, pure waste — so the judge AND the terminal-verdict ratification are skipped (the reviewer-writer self-verifies in its single pass). Specialized per domain (doc→doc-reviewer single pass, ui→ui-expert fix + code-reviewer judge, security→security-reviewer single pass, perf→api-perf-cost-auditor judge). Terminal is a tracked follow-up. Accepts a `findings` list (batched per area). Uses agent()/parallel() only — no nested workflows.",
|
|
5
5
|
phases: [
|
|
6
6
|
{ title: 'Diagnose', detail: 'classify + terminal short-circuit + scope-expansion boundary' },
|
|
7
7
|
{ title: 'Repair', detail: 'targeted fix, then judged multi-attempt if needed' },
|
|
@@ -110,6 +110,15 @@ const judgeAgent = (domain === 'security' || domain === 'migration') ? 'security
|
|
|
110
110
|
: domain === 'doc' ? 'doc-reviewer'
|
|
111
111
|
: domain === 'test' ? 'qa-sentinel'
|
|
112
112
|
: 'code-reviewer'
|
|
113
|
+
// Anti-self-judging — when the fixer and the adversarial judge resolve to the SAME agent type
|
|
114
|
+
// (doc→doc-reviewer, security→security-reviewer), the "judge" would just review its own work:
|
|
115
|
+
// no cross-model diversity, pure waste of tokens and time. These are reviewer-writer specialists
|
|
116
|
+
// that own their domain and self-verify (re-run the gate) inside the fix pass, so we trust the
|
|
117
|
+
// single pass and skip both the judge cross-check and the terminal-verdict ratification. Domains
|
|
118
|
+
// where fixer ≠ judge (ui/perf/test/code/migration) keep the mandatory adversarial cross-check —
|
|
119
|
+
// there the judge is a genuinely independent specialist. Condition is structural (not a domain
|
|
120
|
+
// allowlist), so a future fixer/judge map change can't silently re-introduce self-judging.
|
|
121
|
+
const selfJudges = fixerAgent === judgeAgent
|
|
113
122
|
|
|
114
123
|
const findingsBlock = findings.map((f, i) => ` ${i + 1}. [${f.kind || kind}/${f.domain || domain}] ${f.evidence}`).join('\n')
|
|
115
124
|
const brief = [
|
|
@@ -197,10 +206,10 @@ if (attempt && attempt.terminal) {
|
|
|
197
206
|
let confirmed = false
|
|
198
207
|
if (tr === 'out-of-ownership') {
|
|
199
208
|
confirmed = !filesInScope(attempt.remedyFiles) // genuinely terminal iff remedy files are NOT in MAY-EDIT
|
|
200
|
-
} else if (
|
|
201
|
-
//
|
|
202
|
-
//
|
|
203
|
-
//
|
|
209
|
+
} else if (selfJudges) {
|
|
210
|
+
// fixer === judge (doc→doc-reviewer, security→security-reviewer): ratifying the terminal
|
|
211
|
+
// verdict with a SECOND instance of the same specialist is self-judging — no cross-model
|
|
212
|
+
// diversity, pure waste. Trust the reviewer-writer's single pass; no adversarial re-run.
|
|
204
213
|
confirmed = true
|
|
205
214
|
} else {
|
|
206
215
|
// owner-gated / not-a-code-defect / baseline-not-reached — ratify with the judge.
|
|
@@ -265,11 +274,12 @@ return await materialiseFollowup(kind, (attempt && attempt.note) || 'unresolved
|
|
|
265
274
|
// returns the files it independently confirmed changed; we cross-check ⊆ MAY-EDIT.
|
|
266
275
|
async function judgeVerify(verifiedAttempts) {
|
|
267
276
|
if (!verifiedAttempts.length) return { ok: false, best: 0 }
|
|
268
|
-
// doc-reviewer
|
|
269
|
-
// self-
|
|
270
|
-
//
|
|
271
|
-
// and time. Trust the single pass; accept the first
|
|
272
|
-
|
|
277
|
+
// fixer === judge (doc→doc-reviewer, security→security-reviewer): the reviewer-writer
|
|
278
|
+
// already applied the fix AND self-verified (re-ran the gate) in the same pass. A SECOND
|
|
279
|
+
// adversarial instance of the same specialist would just judge its own work — no cross-model
|
|
280
|
+
// diversity, a pure waste of tokens and time. Trust the single pass; accept the first
|
|
281
|
+
// verified attempt without re-spawning. Domains where fixer ≠ judge keep the cross-check.
|
|
282
|
+
if (selfJudges) {
|
|
273
283
|
const first = verifiedAttempts.find((v) => v.r && v.r.verified) || verifiedAttempts[0]
|
|
274
284
|
return { ok: true, best: first.i }
|
|
275
285
|
}
|
|
@@ -20,6 +20,10 @@ export const meta = {
|
|
|
20
20
|
// metricsDir string paths.metrics (telemetry dir)
|
|
21
21
|
// refModulesBase string dir holding /new reference modules (semantic SSOT)
|
|
22
22
|
// config object parsed baldart.config.yml (paths.*/stack.*/features.*/git.*)
|
|
23
|
+
// migration object Migration Gate manifest (skill Step 3.5): { status:'none'|'applied'|
|
|
24
|
+
// 'skipped'|'degraded', modality?, summary?, artifacts?, affects_cards? }.
|
|
25
|
+
// status:'applied' → the declared schema is LIVE before the batch, so the
|
|
26
|
+
// affected cards' apply-AC is NOT owner-gated-deferred (F-016 exception).
|
|
23
27
|
// flags { stats, effort, full }
|
|
24
28
|
// ts string ISO timestamp injected by the skill (Date.now() is unavailable here)
|
|
25
29
|
//
|
|
@@ -45,6 +49,12 @@ const METRICS = a.metricsDir || 'docs/metrics'
|
|
|
45
49
|
const REF = (a.refModulesBase || '.claude/skills/new/references').replace(/\/$/, '')
|
|
46
50
|
const FLAGS = a.flags || {}
|
|
47
51
|
const TS = a.ts || ''
|
|
52
|
+
// Migration Gate manifest (skill Step 3.5 — front-loaded BEFORE this workflow ran). When
|
|
53
|
+
// status:'applied', the declared schema is LIVE on the active DB, so its apply-AC must NOT be
|
|
54
|
+
// re-classified as an owner-gated policy-deferred AC (F-016) — the downstream cards verify for real.
|
|
55
|
+
const migration = a.migration || { status: 'none' }
|
|
56
|
+
const migrationApplied = migration && migration.status === 'applied'
|
|
57
|
+
const migrationAffects = Array.isArray(migration.affects_cards) ? migration.affects_cards : []
|
|
48
58
|
const features = cfg.features || {}
|
|
49
59
|
const paths = cfg.paths || {}
|
|
50
60
|
const gitCfg = cfg.git || {}
|
|
@@ -230,6 +240,9 @@ try {
|
|
|
230
240
|
`• cardGraph (REQUIRED, F-021): for every runnable card return { id, dependsOn:[IN-BATCH deps only], ownerAgent (the card's owner_agent; G25 unknown→'coder'), reviewProfile (the card's review_profile; default 'balanced'), policyDeferredACs, alreadyCommitted, alreadyCommittedSha, isEpic (implement.md §6b epic guard: id ends '-00' OR filename ends '-epic.yml' OR group.is_epic:true OR review_profile 'skip' with no requirements), filesLikelyTouched (verbatim from the YAML) }.\n` +
|
|
231
241
|
`• B1/F-026 idempotency (per card, AFTER the worktree exists): set alreadyCommitted:true (+ alreadyCommittedSha) IFF ALL hold: (a) a commit referencing the card id exists in ${TRUNK}..HEAD of the worktree; (b) the card's validation_commands re-run GREEN right now; (c) NO open follow-up card for it exists in ${paths.backlog_dir || 'backlog'}. On a FRESH worktree ${TRUNK}..HEAD is empty → all false, zero extra work.\n` +
|
|
232
242
|
`• F-016 AC↔ownership consistency: for each acceptance_criterion, derive the file(s) it requires editing. If those files are NOT a subset of the card's MAY-EDIT/files_likely_touched → add the AC to policyDeferredACs:[{n,text,owningCard|owningFile,reason}] (it will become ONE follow-up, never a resolve). Do the same for any AC whose remedy is an owner-gated infra action (remote db push / deploy / secret / DNS).\n` +
|
|
243
|
+
(migrationApplied
|
|
244
|
+
? `• MIGRATION ALREADY APPLIED (skill Migration Gate, Step 3.5): the DB migration "${migration.summary || '(declared)'}" was applied to the active DB BEFORE this batch — the schema is LIVE. EXCEPTION to the F-016 owner-gated rule above: do NOT classify the schema-apply / "run migration" / db-push AC of card(s) [${migrationAffects.join(', ') || '(affects_cards from the epic)'}] as policyDeferredAC — it is SATISFIED. Those cards must run REAL validation against the live schema (validation_commands, queries, DB-generated types), not defer it. (A migration NOT covered by this manifest still follows the owner-gated deferral above.)\n`
|
|
245
|
+
: '') +
|
|
233
246
|
`• Ownership (setup.md 3c): build the file-ownership map → /tmp; return ownershipMapPath. F-040: each card's MAY-EDIT = files_likely_touched ∪ every path NAMED EXPLICITLY in that card's acceptance_criteria/definition_of_done (an ADR the DoD says to update, the data-model / ER doc for a schema-change, etc.) — so editing a DoD-mandated doc is NOT a file-diff violation. Do NOT add another card's files this way.\n` +
|
|
234
247
|
`• Do NOT write architecture baselines — the per-card codebase-architect specialist does that during the card pipeline (B7).\n\n` +
|
|
235
248
|
`Return the structured PREFLIGHT object. ok:false ONLY if the workspace is unworkable.`,
|
|
@@ -429,7 +442,10 @@ async function runCard(cardId, cardPath) {
|
|
|
429
442
|
return { card: cardId, status: 'committed', commit: node.alreadyCommittedSha || '-', filesChanged: [], scopeFiles: [], archBaselinePath: `/tmp/arch-baseline-${cardId}.md`, gates }
|
|
430
443
|
}
|
|
431
444
|
|
|
432
|
-
const
|
|
445
|
+
const migrationNote = (migrationApplied && migrationAffects.includes(cardId))
|
|
446
|
+
? `\nMIGRATION LIVE: the DB migration "${migration.summary || '(declared)'}" was applied to the active DB BEFORE this batch (skill Migration Gate). The schema is REAL — run actual validation against it (validation_commands, queries, DB-generated types); do NOT defer the schema-apply AC or build against an absent schema.`
|
|
447
|
+
: ''
|
|
448
|
+
const cardBrief = `${projectBrief}\n\nCard: ${cardId}\nCard YAML: ${cardPath}\nOwner agent: ${ownerAgent} · Review profile: ${reviewProfile}\nWorktree: ${sharedCtx.worktreePath} (cd into it)\nFile-ownership map: ${sharedCtx.ownershipMapPath}\nNOTE: ACs already pre-classified as policy-deferred MUST NOT be implemented or routed — they are tracked as follow-ups.${migrationNote}`
|
|
433
449
|
|
|
434
450
|
// --- Phase 1 (B7, v4.26.0) — SPECIALIST decomposition: each Phase-1 duty runs as its own
|
|
435
451
|
// agent ("ognuno fa una cosa"), with handoff via /tmp files so the owner's context stays
|
|
@@ -966,6 +982,10 @@ function buildTelemetry() {
|
|
|
966
982
|
cards_followup: perCardResults.filter((r) => r.status === 'followup').length,
|
|
967
983
|
cards_blocked: runnableCards.filter((id) => state[id] === 'blocked').length,
|
|
968
984
|
cards_deferred: residuals.filter((x) => x.kind === 'policy-deferred-ac').length,
|
|
985
|
+
// Migration Gate (skill Step 3.5, pre-launch — NOT a mid-batch question, so the zero-ask-during
|
|
986
|
+
// -batch invariant is intact). 'applied' = schema front-loaded; the affected cards' apply-AC was
|
|
987
|
+
// satisfied up-front instead of deferred owner-gated.
|
|
988
|
+
migration_gate: (migration && migration.status) || 'none',
|
|
969
989
|
residuals_total: residuals.length,
|
|
970
990
|
// followups_on_disk is filled by the SKILL after it materialises pending residuals.
|
|
971
991
|
followups_materialized_in_workflow: residuals.filter((x) => x.materialized).length,
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
base_skill: new
|
|
3
|
+
base_skill_version: 4.28.0
|
|
4
|
+
mode: extend
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# new — migration-gate overlay (example)
|
|
8
|
+
|
|
9
|
+
> Drop this into your repo at `.baldart/overlays/new.md` (it is shared by `/new`
|
|
10
|
+
> **and** `new2`) to teach the **Migration Gate** how *your* project applies a DB
|
|
11
|
+
> schema change. The gate runs **before** the batch (/new Phase 0 step 1b · new2
|
|
12
|
+
> Step 3.5): when an epic card declares a `migration_plan`, the skill front-loads
|
|
13
|
+
> the migration interactively so downstream cards verify against the **live**
|
|
14
|
+
> schema instead of one deferred to the end of the batch.
|
|
15
|
+
>
|
|
16
|
+
> Two layers cooperate:
|
|
17
|
+
> 1. **The epic card** declares *that* a migration is needed + the artifacts
|
|
18
|
+
> (`migration_plan` block — see `epic-template.yml`).
|
|
19
|
+
> 2. **This overlay** declares *how* this project applies migrations (the
|
|
20
|
+
> `## Migration modalities` section below). The gate merges the epic's
|
|
21
|
+
> `apply_modalities` with these, de-duped by `id`, and adds the built-in
|
|
22
|
+
> `["Già applicata — prosegui", "Abort"]` tail.
|
|
23
|
+
>
|
|
24
|
+
> Adapt the commands to your stack (Supabase / Prisma / Drizzle / raw SQL / …).
|
|
25
|
+
|
|
26
|
+
## Migration modalities
|
|
27
|
+
|
|
28
|
+
These are offered (via `AskUserQuestion`) whenever an epic declares
|
|
29
|
+
`migration_plan.required: true` and its `artifacts` exist on disk. The skill runs
|
|
30
|
+
the chosen `command` in the main repo (`$MAIN`), captures output to
|
|
31
|
+
`/tmp/migration-<first-card>.log`, surfaces only the exit code, and — when the
|
|
32
|
+
`migration_plan.verify` probe is present — requires it green too. A command is
|
|
33
|
+
**never** run without the user selecting it.
|
|
34
|
+
|
|
35
|
+
- **id: `remote-push`** — applica al DB remoto/condiviso (owner-gated; eseguito
|
|
36
|
+
dalla skill SOLO su tua approvazione esplicita).
|
|
37
|
+
- command: `npx supabase db push`
|
|
38
|
+
- When to pick: dev DB condiviso o staging dove le card verificano contro il
|
|
39
|
+
DB reale.
|
|
40
|
+
- **id: `local`** — applica a un DB locale/shadow; la prod resta un deploy
|
|
41
|
+
manuale post-merge (Phase 7 lo riporta come item manuale).
|
|
42
|
+
- command: `npx supabase db reset --local` *(oppure `npx prisma migrate dev`)*
|
|
43
|
+
- When to pick: il batch verifica contro un DB locale effimero.
|
|
44
|
+
- **id: `prisma-deploy`** — esempio Prisma per uno stack diverso.
|
|
45
|
+
- command: `npx prisma migrate deploy`
|
|
46
|
+
|
|
47
|
+
> Verifica post-apply consigliata (referenziata da `migration_plan.verify`
|
|
48
|
+
> nell'epic): rigenera i tipi dal DB così `tsc` downstream è onesto, es.
|
|
49
|
+
> `npx supabase gen types typescript --local > src/types/db.ts` oppure
|
|
50
|
+
> `npx prisma generate`.
|
|
51
|
+
|
|
52
|
+
## Notes
|
|
53
|
+
|
|
54
|
+
- Se l'epic NON dichiara `migration_plan` (o `required: false`), il gate è un
|
|
55
|
+
**no-op silenzioso** — nessun prompt aggiuntivo, comportamento identico a oggi.
|
|
56
|
+
- Se gli `artifacts` non esistono ancora su disco, il gate **degrada**: non
|
|
57
|
+
applica nulla e la migrazione segue il path owner-gated di fine batch (nessuna
|
|
58
|
+
regressione). La migrazione va autorata prima per essere front-loaddata.
|
|
59
|
+
- Le modalità possono anche arrivare dalla **memoria di progetto** (oltre che da
|
|
60
|
+
questo overlay e dal blocco epic): la skill le unisce tutte.
|