baldart 4.26.0 → 4.27.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 +31 -0
- package/VERSION +1 -1
- package/framework/.claude/skills/prd/SKILL.md +24 -16
- package/framework/.claude/skills/prd/assets/state-template.md +5 -3
- package/framework/.claude/skills/prd/references/discovery-phase.md +11 -0
- package/framework/.claude/skills/prd/references/obsidian-backref.md +120 -0
- package/framework/.claude/skills/prd/references/prd-writing-phase.md +3 -2
- package/framework/.claude/skills/prd/references/validation-phase.md +21 -65
- package/framework/.claude/workflows/new2-resolve.js +11 -1
- package/framework/.claude/workflows/new2.js +50 -16
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,37 @@ 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.27.0] - 2026-06-11
|
|
9
|
+
|
|
10
|
+
**`/prd`: the Obsidian back-reference is now two-phase — the spec note is linked the moment the PRD starts, not only at merge.** The end-of-run snippet (v4.22.0) is gated on a successful merge, so a PRD that is paused or abandoned mid-flow (e.g. parked at the UI-design step, worktree never merged) left its origin note empty — even though detection already happened at Step 1. Observed in the wild: a `realtime-order-collaboration` PRD stuck at `ui-design` never wrote back, while a completed sibling PRD did. **MINOR** (additive capability on `/prd`; reuses the existing slug-keyed markers + the snippet `status:` lifecycle field; no `baldart.config.yml` key, so the schema-change propagation rule does not apply).
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`framework/.claude/skills/prd/references/obsidian-backref.md`** — new shared SSOT for the back-reference: the marker contract (`<!-- BALDART:prd:<slug> START/END -->`), the idempotent read-replace-or-append write procedure, the skip conditions (no note / `path: none` / unwritable → log, never fatal), and the two write modes. Both call sites cite it, so the snippet shape can't drift between them.
|
|
15
|
+
- **Provisional write at detection (discovery-phase Step 1 point 2).** Right after the Obsidian note is detected, `/prd` writes a lightweight `status: drafting` block (slug + deterministic branch `prd/<slug>` + planned PRD path + trunk) into the note and sets state `## Obsidian Spec Note / status: provisional-written`. The note reflects "a PRD was started here" immediately.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- **Final write upgrades the provisional block in place (validation-phase Step 7.6).** Same slug-keyed markers, so the post-merge full payload (epic + card IDs, merge SHA/PR, `resources:`, `status: planned`) replaces the `drafting` block — no duplicates. Step 7.6 is now a lean delegation to `obsidian-backref.md § Mode: FINAL` instead of an inlined copy of the snippet.
|
|
20
|
+
- **Snippet `status:` enum tightened to `drafting | planned | done`** (was `planned | in-progress | done`) — a clean ladder: PRD in stesura → mergiato/pianificato → in produzione.
|
|
21
|
+
- **State `## Obsidian Spec Note / status` enum** gains `provisional-written`: `none → detected → provisional-written → back-reference-written`.
|
|
22
|
+
- **Coupling fix:** `prd-writing-phase.md` Step 4 point 2 now adds the PRD Canonical Sources row when the note status is anything other than `none` (was hard-coded to `== detected`), so the provisional-write status change doesn't suppress the traceability row.
|
|
23
|
+
- **`SKILL.md` HARD RULE 19** rewritten for the two-phase lifecycle; flow-table gains a `1.2` row and annotates `7.6` as the FINAL upgrade.
|
|
24
|
+
|
|
25
|
+
## [4.26.1] - 2026-06-11
|
|
26
|
+
|
|
27
|
+
**`new2`: specialization integrity — a full audit of every agent spawn; no role mixing anywhere.** User principle: code is written ONLY by `coder`, UI only by `ui-expert`, each agent does one thing. The audit of all 16 spawn sites found two genuine violations and three under-specified plumbing roles. **PATCH** (role-integrity fixes on the EXPERIMENTAL `new2` surface; no config key, no change to `/new`).
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- **`new2.js` E2 — the ops pre-flight agent no longer repairs the baseline.** "baseline FAILS → fix once" had a general-purpose git agent editing source code. Now: the pre-flight returns `baseline:'fail'` + an actionable log (explicit role boundary: it never edits source/doc files), and the WORKFLOW spawns the **coder** specialist for one bounded repair attempt — verified by deterministically re-running the baseline gates (no claim to trust), `E2-baseline: FIXED-BY-CODER` ledger row; still failing → batch-fatal as before. Zero extra spawns in the happy path.
|
|
32
|
+
- **`new2.js` — the Codex driver never reviews.** Its runtime fallback was "perform the review yourself with the code-reviewer lens" — a general-purpose agent doing code review. Now the driver returns `note:'codex-unavailable'` with empty findings, and the workflow spawns the REAL `code-reviewer` (same `stdReview` call as the matrix), ledgered as `review-codex: FALLBACK`.
|
|
33
|
+
- **`new2-resolve.js` — judge map completed per domain.** `doc` fixes are now judged by **doc-reviewer** (code-reviewer judging prose was cross-domain) and `test` fixes by **qa-sentinel**; `security`/`migration` → security-reviewer and `perf` → api-perf-cost-auditor unchanged; `ui`/`code` stay with code-reviewer (the judge verifies a CODE change — its charter, incl. DS rule 8 for UI). Fixer and judge of the same type remain two independent adversarial instances.
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- **`new2.js` — explicit ROLE BOUNDARY lines on every plumbing agent** (pre-flight, commit, merge, production-readiness): they never edit source/doc files; commit/merge touch ONLY card YAML status fields + registry rows; a content-level merge conflict is leave+report, never hand-resolved; production-readiness executes stack-matched commands but reports (never edits) code/config changes. The full writer map is now: code/perf/migration/test fixes → `coder`; UI → `ui-expert`; security fixes → `security-reviewer`; docs → `doc-reviewer`; backlog YAML → `prd-card-writer`; bookkeeping (status/registry) → mechanical commit/merge agents.
|
|
38
|
+
|
|
8
39
|
## [4.26.0] - 2026-06-11
|
|
9
40
|
|
|
10
41
|
**`new2`: Phase 1 decomposed into specialist agents — the owner implements, it no longer explores.** The old per-card pipeline gave the owner agent one mega-prompt ("you ARE claim + architect + plan-auditor + owner"), so the owner absorbed the whole codebase exploration into its own context and reached the actual coding with a degraded window. Phase 1 now runs as dedicated specialists with file handoff (`/tmp`), per the "ognuno fa una cosa" principle. Verified premise: nested subagent spawning does NOT exist in Claude Code (official docs: "Subagents cannot spawn other subagents" + 2 empirical probes), so the decomposition lives at the WORKFLOW level — the JS is the orchestrator, exactly what dynamic workflows are for. **MINOR** (pipeline capability on the EXPERIMENTAL `new2` surface only; no config key, no change to `/new`).
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.
|
|
1
|
+
4.27.0
|
|
@@ -38,6 +38,7 @@ message. You ask questions, wait for answers, and iterate.
|
|
|
38
38
|
|------|-------|-----------|
|
|
39
39
|
| 0 | Context Recovery | [discovery-phase.md](references/discovery-phase.md) |
|
|
40
40
|
| 1 | Kickoff | [discovery-phase.md](references/discovery-phase.md) |
|
|
41
|
+
| 1.2 | Obsidian provisional back-reference (only if a spec note was given) | [discovery-phase.md](references/discovery-phase.md) + [obsidian-backref.md](references/obsidian-backref.md) |
|
|
41
42
|
| 1.6 | Mockup Intake | [discovery-phase.md](references/discovery-phase.md) |
|
|
42
43
|
| 2 | Discovery Question Loop (+ Codex completeness cross-check at exit, v4.19.0) | [discovery-phase.md](references/discovery-phase.md) |
|
|
43
44
|
| 2→CR | Change Request (auto/manual) | the canonical **`prd-add` skill** ([../prd-add/SKILL.md](../prd-add/SKILL.md)) — worktree-aware (`references/prd-add-phase.md` is a redirect stub, not the implementation) |
|
|
@@ -49,7 +50,7 @@ message. You ask questions, wait for answers, and iterate.
|
|
|
49
50
|
| 5 | Backlog Cards | [backlog-phase.md](references/backlog-phase.md) |
|
|
50
51
|
| 6 | Quality Audit | [validation-phase.md](references/validation-phase.md) |
|
|
51
52
|
| 7 | Resolution, Commit & Merge | [validation-phase.md](references/validation-phase.md) |
|
|
52
|
-
| 7.6 | Obsidian back-reference (only if a spec note was given) | [validation-phase.md](references/validation-phase.md) |
|
|
53
|
+
| 7.6 | Obsidian back-reference — FINAL (upgrades the 1.2 provisional block; only if a spec note was given) | [validation-phase.md](references/validation-phase.md) + [obsidian-backref.md](references/obsidian-backref.md) |
|
|
53
54
|
|
|
54
55
|
**Before starting each phase, read the corresponding reference file.**
|
|
55
56
|
|
|
@@ -234,21 +235,28 @@ message. You ask questions, wait for answers, and iterate.
|
|
|
234
235
|
transcripts for REAL per-role token + wall-clock cost. The token itself is NOT part
|
|
235
236
|
of the feature description — strip it before recording the user's verbatim feature
|
|
236
237
|
text in `## Feature Description`.
|
|
237
|
-
19. **Obsidian back-reference (MANDATORY when a spec note was given
|
|
238
|
-
kicked off the PRD from an Obsidian note (any of: `[[Note]]`
|
|
239
|
-
`obsidian://open?…` URI, or a pasted `.md` path),
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
238
|
+
19. **Obsidian back-reference (MANDATORY when a spec note was given — TWO-PHASE since
|
|
239
|
+
v4.27.0).** If the user kicked off the PRD from an Obsidian note (any of: `[[Note]]`
|
|
240
|
+
wikilink, `obsidian://open?…` URI, or a pasted `.md` path), the same note becomes the
|
|
241
|
+
durable index the user can later query ("come è stata sviluppata la feature X, dov'è
|
|
242
|
+
finita"). It is written in two phases against ONE slug-keyed marker block
|
|
243
|
+
(`<!-- BALDART:prd:<slug> START/END -->`), so the second write replaces the first in
|
|
244
|
+
place (idempotent, no duplicates):
|
|
245
|
+
- **Provisional** — at Step 1 detection (discovery-phase Step 1 point 2): a lightweight
|
|
246
|
+
`status: drafting` block with slug + branch `prd/<slug>` + planned PRD path. So the
|
|
247
|
+
note reflects "a PRD was started here" **immediately**, even if the run is later
|
|
248
|
+
paused or abandoned before merge.
|
|
249
|
+
- **Final** — at the END of the run, **after** the merge (validation-phase Step 7 item
|
|
250
|
+
9) so paths/IDs/SHA are final (Step 7.6): the full payload — feature slug, PRD path,
|
|
251
|
+
epic + card IDs, branch, merge commit/PR, Canonical Sources as `resources:`,
|
|
252
|
+
`status: planned`.
|
|
253
|
+
|
|
254
|
+
The note lives in the user's **vault, outside the worktree and outside git** — a plain
|
|
255
|
+
filesystem write, never staged/committed. Both writes are **NON-BLOCKING**: no spec
|
|
256
|
+
note, unresolved vault path, or an unwritable note all skip with a log line, never
|
|
257
|
+
aborting the PRD. SSOT for the marker contract + write procedure + both modes:
|
|
258
|
+
[obsidian-backref.md](references/obsidian-backref.md). See also discovery-phase.md Step
|
|
259
|
+
1 point 2 and validation-phase.md Step 7.6.
|
|
252
260
|
|
|
253
261
|
---
|
|
254
262
|
|
|
@@ -16,10 +16,12 @@ stats_enabled: {{true if invoked with -stats/--stats, else false}}
|
|
|
16
16
|
|
|
17
17
|
<!-- Populated by discovery-phase.md Step 1 point 2 ONLY when the user passed an
|
|
18
18
|
Obsidian note as the documental starting point. When no note was given, leave
|
|
19
|
-
status: none and
|
|
19
|
+
status: none and both back-reference writes are skipped. -->
|
|
20
20
|
wikilink: none # [[Note Name]] form — feeds PRD Canonical Sources row
|
|
21
|
-
path: none # absolute fs path of the note — target of the
|
|
22
|
-
status: none # none | detected | back-reference-written
|
|
21
|
+
path: none # absolute fs path of the note — target of the back-reference writeback
|
|
22
|
+
status: none # none | detected | provisional-written | back-reference-written
|
|
23
|
+
# detected → provisional snippet written at Step 1.2 (provisional-written)
|
|
24
|
+
# → upgraded to the full payload at Step 7.6 (back-reference-written)
|
|
23
25
|
|
|
24
26
|
## Feature Description
|
|
25
27
|
{{user's description verbatim}}
|
|
@@ -84,6 +84,17 @@ this is a fresh session (no existing state file).
|
|
|
84
84
|
Then read the note content (from `path:`) to pre-populate discovery dimensions
|
|
85
85
|
where possible — treat it as a user-provided spec. This same note becomes the
|
|
86
86
|
target of the back-reference snippet at the end of the run (HARD RULE 19).
|
|
87
|
+
|
|
88
|
+
**Provisional back-reference (NON-BLOCKING — v4.27.0).** Immediately after detection,
|
|
89
|
+
write a *provisional* snippet into the note so it reflects "a PRD was started here" even
|
|
90
|
+
if this session is later paused or abandoned before merge (the FINAL snippet at
|
|
91
|
+
validation-phase Step 7.6 is gated on a successful merge and would otherwise never run
|
|
92
|
+
for an incomplete PRD). Follow [obsidian-backref.md](obsidian-backref.md) **§ Mode:
|
|
93
|
+
PROVISIONAL** — the marker block is keyed on `<slug>`, so the FINAL write later replaces
|
|
94
|
+
it in place (no duplicates). All skip conditions in that module apply (no note / `path:
|
|
95
|
+
none` / unwritable → log and continue, never fatal). On success the module sets
|
|
96
|
+
`## Obsidian Spec Note / status: provisional-written`. The branch is the deterministic
|
|
97
|
+
`prd/<slug>` that `nw-docs` creates in the next point.
|
|
87
98
|
3. **Create the docs worktree (MANDATORY — HARD RULE 17)** — invoke the
|
|
88
99
|
`worktree-manager` skill in programmatic docs mode BEFORE any file write:
|
|
89
100
|
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Obsidian Back-Reference (shared SSOT)
|
|
2
|
+
|
|
3
|
+
Single source of truth for the **idempotent back-reference snippet** that `/prd` writes
|
|
4
|
+
into the user's Obsidian spec note. Two phases call this module; both target the SAME
|
|
5
|
+
marker block keyed on the slug, so the later write **replaces the earlier one in place** —
|
|
6
|
+
the note never accumulates duplicates.
|
|
7
|
+
|
|
8
|
+
| Phase | Caller | Mode | Trigger |
|
|
9
|
+
|---|---|---|---|
|
|
10
|
+
| Provisional | `discovery-phase.md` Step 1 point 2 | `PROVISIONAL` | right after the note is detected — the PRD has only just started |
|
|
11
|
+
| Final | `validation-phase.md` Step 7.6 | `FINAL` | after the merge succeeds — paths, card IDs, branch, SHA are now final |
|
|
12
|
+
|
|
13
|
+
**Why two phases (v4.27.0).** A PRD can be paused or abandoned mid-flow (e.g. parked at the
|
|
14
|
+
UI-design step, worktree never merged). The FINAL write is gated on a successful merge, so
|
|
15
|
+
without a provisional write the note would stay empty for every incomplete PRD — even
|
|
16
|
+
though detection already happened at Step 1. The provisional write makes the note reflect
|
|
17
|
+
"a PRD was started here" immediately; the FINAL write upgrades it to the full,
|
|
18
|
+
queryable payload once the run completes.
|
|
19
|
+
|
|
20
|
+
## Marker contract
|
|
21
|
+
|
|
22
|
+
The block is delimited by HTML-comment markers keyed on `<slug>`:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
<!-- BALDART:prd:<slug> START -->
|
|
26
|
+
…block…
|
|
27
|
+
<!-- BALDART:prd:<slug> END -->
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Write procedure (identical for both modes):**
|
|
31
|
+
1. Read the current note content at the absolute `path:` from state `## Obsidian Spec Note`.
|
|
32
|
+
2. If both markers are present → REPLACE everything between them (inclusive) with the new block.
|
|
33
|
+
3. If markers are absent → APPEND the new block at the end of the note, preceded by one blank line.
|
|
34
|
+
4. Write the note back to `path:`.
|
|
35
|
+
|
|
36
|
+
The note lives in the user's **vault, outside the worktree and outside git** — it is a
|
|
37
|
+
plain filesystem write to the absolute `path:`, NEVER staged or committed.
|
|
38
|
+
|
|
39
|
+
## Skip conditions (each logged, NEVER fatal — both phases)
|
|
40
|
+
|
|
41
|
+
- `## Obsidian Spec Note / status` is `none` (no note was given) → skip silently.
|
|
42
|
+
- `path:` is `none`/empty (vault root never resolved at Step 1) → skip with a one-line note.
|
|
43
|
+
- The file at `path:` does not exist or is not writable → skip with a one-line warning.
|
|
44
|
+
|
|
45
|
+
The back-reference is a convenience index, never a gate. Any failure logs
|
|
46
|
+
`"Obsidian back-reference: SKIPPED (<reason>)"` and the run continues.
|
|
47
|
+
|
|
48
|
+
## Mode: PROVISIONAL — `discovery-phase.md` Step 1 point 2
|
|
49
|
+
|
|
50
|
+
**Known here:** `<slug>`, the deterministic branch `prd/<slug>` (the name `worktree-manager
|
|
51
|
+
nw-docs` always uses), the planned PRD path, `git.trunk_branch`, the detection timestamp.
|
|
52
|
+
**Not yet known:** epic/card IDs (Step 5), the merge SHA/PR (Step 7), the full Canonical
|
|
53
|
+
Sources list (Step 4). Write the lighter block:
|
|
54
|
+
|
|
55
|
+
````markdown
|
|
56
|
+
<!-- BALDART:prd:<slug> START -->
|
|
57
|
+
## 🛠 Sviluppo BALDART — <slug>
|
|
58
|
+
|
|
59
|
+
```yaml
|
|
60
|
+
feature: <slug>
|
|
61
|
+
prd: ${paths.prd_dir}/<slug>/PRD.md # path pianificato — il file viene scritto a Step 4
|
|
62
|
+
branch: prd/<slug>
|
|
63
|
+
trunk_branch: <git.trunk_branch>
|
|
64
|
+
started: <ISO-8601 UTC>
|
|
65
|
+
status: drafting # drafting | planned | done
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
- **Stato:** PRD in stesura sul branch `prd/<slug>`. Lo snippet verrà completato al merge.
|
|
69
|
+
|
|
70
|
+
_Avviato da `/prd` il <date>. Questo blocco si aggiorna automaticamente con card, merge e sorgenti canoniche quando il PRD viene mergiato._
|
|
71
|
+
<!-- BALDART:prd:<slug> END -->
|
|
72
|
+
````
|
|
73
|
+
|
|
74
|
+
After the write, set `## Obsidian Spec Note / status: provisional-written` in the state file.
|
|
75
|
+
|
|
76
|
+
## Mode: FINAL — `validation-phase.md` Step 7.6
|
|
77
|
+
|
|
78
|
+
Runs AFTER the merge (Step 7 item 9). All identifiers are final. Write the full block,
|
|
79
|
+
REPLACING the provisional one in place:
|
|
80
|
+
|
|
81
|
+
````markdown
|
|
82
|
+
<!-- BALDART:prd:<slug> START -->
|
|
83
|
+
## 🛠 Sviluppo BALDART — <slug>
|
|
84
|
+
|
|
85
|
+
```yaml
|
|
86
|
+
feature: <slug>
|
|
87
|
+
prd: ${paths.prd_dir}/<slug>/PRD.md
|
|
88
|
+
epic: FEAT-XXXX-00-<slug>-epic
|
|
89
|
+
cards:
|
|
90
|
+
- FEAT-XXXX-01-<sub-slug>
|
|
91
|
+
- FEAT-XXXX-02-<sub-slug>
|
|
92
|
+
resources: # other Canonical Sources from the PRD (mockups, ADRs, ref docs, research)
|
|
93
|
+
- ${paths.prd_dir}/<slug>/design.html # omit if no design
|
|
94
|
+
- <ssot-registry entry / ADR path / research link>
|
|
95
|
+
branch: prd/<slug>
|
|
96
|
+
merge_commit: <SHA from state ## Validation>
|
|
97
|
+
pr: <PR number, or n/a for local-push>
|
|
98
|
+
trunk_branch: <git.trunk_branch>
|
|
99
|
+
date: <ISO-8601 UTC>
|
|
100
|
+
status: planned # drafting | planned | done
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
- **PRD:** `${paths.prd_dir}/<slug>/PRD.md`
|
|
104
|
+
- **Cards:** FEAT-XXXX-00 (epic) · FEAT-XXXX-01 · FEAT-XXXX-02 …
|
|
105
|
+
- **Merge:** `<SHA>` su `<git.trunk_branch>`<!-- · PR #N -->
|
|
106
|
+
|
|
107
|
+
_Generato da `/prd` il <date>. Aggiorna `status:` a `done` quando la feature è in produzione._
|
|
108
|
+
<!-- BALDART:prd:<slug> END -->
|
|
109
|
+
````
|
|
110
|
+
|
|
111
|
+
Fill the `yaml` from the final state: `<slug>`, the real epic + child card IDs created in
|
|
112
|
+
Step 5, the merge SHA written under `## Validation` (Step 7 item 8), the PR number from the
|
|
113
|
+
`mw-docs` return (or `n/a`), and the Canonical Sources the PRD already assembled
|
|
114
|
+
(`prd-writing-phase.md` Step 4 point 3) for `resources:`. The inner ```yaml``` block is
|
|
115
|
+
fenced, NOT real top-of-file frontmatter — it is the queryable payload Claude reads back
|
|
116
|
+
when asked about the feature later.
|
|
117
|
+
|
|
118
|
+
After the write, set `## Obsidian Spec Note / status: back-reference-written` in the state
|
|
119
|
+
file. (The state file is already committed; this is a local marker for a possible resume —
|
|
120
|
+
do NOT re-commit just for this field.)
|
|
@@ -8,8 +8,9 @@ Mark task 3 as `in_progress`.
|
|
|
8
8
|
|
|
9
9
|
1. Create directory `${paths.prd_dir}/<slug>/` if it doesn't exist.
|
|
10
10
|
2. **Obsidian spec note (if present):** if the state file `## Obsidian Spec Note`
|
|
11
|
-
section has `status
|
|
12
|
-
|
|
11
|
+
section has `status` other than `none` (i.e. `detected`, `provisional-written`, or
|
|
12
|
+
`back-reference-written` — a note was given), add a row to the Canonical Sources table
|
|
13
|
+
in the PRD using its `wikilink:` value:
|
|
13
14
|
`| Obsidian spec | [[Note Name]] |`
|
|
14
15
|
This preserves traceability from the user's original spec note to the final PRD, and
|
|
15
16
|
the same note receives the end-of-run back-reference snippet (validation-phase Step 7.6).
|
|
@@ -249,76 +249,32 @@ branch is gone (or its deletion is explicitly user-deferred).
|
|
|
249
249
|
|
|
250
250
|
### Step 7.6 — Obsidian back-reference (NON-BLOCKING — runs only when a spec note was given)
|
|
251
251
|
|
|
252
|
-
**Why this exists.** When the user kicked off the PRD from an Obsidian note (state
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
machine-and-human readable snippet back into the note.
|
|
252
|
+
**Why this exists.** When the user kicked off the PRD from an Obsidian note (state file
|
|
253
|
+
`## Obsidian Spec Note / status` is not `none`, see HARD RULE 19), they want that same note
|
|
254
|
+
to become the durable index of *how the feature was developed and where it landed* — so
|
|
255
|
+
they can later ask "come è stata sviluppata la feature X?" and get the PRD, cards, and
|
|
256
|
+
merge from the note itself.
|
|
258
257
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
**Skip conditions (each logged, never fatal):**
|
|
265
|
-
- `## Obsidian Spec Note / status` is `none` (no note was given) → skip silently.
|
|
266
|
-
- `path:` is `none` or empty (vault root never resolved at Step 1) → skip with a note.
|
|
267
|
-
- The file at `path:` does not exist or is not writable → skip with a one-line warning.
|
|
268
|
-
|
|
269
|
-
**Procedure:**
|
|
270
|
-
|
|
271
|
-
1. Read the current content of the note at `path:`.
|
|
272
|
-
2. Build the snippet, delimited by HTML-comment markers keyed on the slug so the step
|
|
273
|
-
is **idempotent** — a re-run (or a later `/prd-add`) REPLACES the block between the
|
|
274
|
-
markers in place instead of appending a duplicate. If the markers are absent, append
|
|
275
|
-
the block at the end of the note (preceded by one blank line).
|
|
276
|
-
|
|
277
|
-
```markdown
|
|
278
|
-
<!-- BALDART:prd:<slug> START -->
|
|
279
|
-
## 🛠 Sviluppo BALDART — <slug>
|
|
280
|
-
|
|
281
|
-
```yaml
|
|
282
|
-
feature: <slug>
|
|
283
|
-
prd: ${paths.prd_dir}/<slug>/PRD.md
|
|
284
|
-
epic: FEAT-XXXX-00-<slug>-epic
|
|
285
|
-
cards:
|
|
286
|
-
- FEAT-XXXX-01-<sub-slug>
|
|
287
|
-
- FEAT-XXXX-02-<sub-slug>
|
|
288
|
-
resources: # other Canonical Sources from the PRD (mockups, ADRs, ref docs, research)
|
|
289
|
-
- ${paths.prd_dir}/<slug>/design.html # omit if no design
|
|
290
|
-
- <ssot-registry entry / ADR path / research link>
|
|
291
|
-
branch: prd/<slug>
|
|
292
|
-
merge_commit: <SHA from state ## Validation>
|
|
293
|
-
pr: <PR number, or n/a for local-push>
|
|
294
|
-
trunk_branch: <git.trunk_branch>
|
|
295
|
-
date: <ISO-8601 UTC>
|
|
296
|
-
status: planned # planned | in-progress | done
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
- **PRD:** [`${paths.prd_dir}/<slug>/PRD.md`](...)
|
|
300
|
-
- **Cards:** FEAT-XXXX-00 (epic) · FEAT-XXXX-01 · FEAT-XXXX-02 …
|
|
301
|
-
- **Merge:** `<SHA>` su `<git.trunk_branch>`<!-- · PR #N -->
|
|
258
|
+
This is the **FINAL** write of the two-phase back-reference (the provisional write already
|
|
259
|
+
happened at discovery-phase Step 1 point 2, status `provisional-written`). It **upgrades
|
|
260
|
+
the provisional block in place** — same slug-keyed markers — replacing the lightweight
|
|
261
|
+
"PRD in stesura" block with the full, queryable payload.
|
|
302
262
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
Fill the `yaml` block from the final state: `<slug>`, the real epic + child card
|
|
308
|
-
IDs created in Step 5, the merge SHA written under `## Validation` (Step 7 item 8),
|
|
309
|
-
the PR number from the `mw-docs` return (or `n/a`), and the Canonical Sources list
|
|
310
|
-
the PRD already assembled (prd-writing-phase.md Step 4 point 3) for `resources:`.
|
|
311
|
-
The inner ```` ```yaml ```` block is fenced, NOT real top-of-file frontmatter — it
|
|
312
|
-
is the queryable payload Claude reads back when asked about the feature later.
|
|
263
|
+
**Runs AFTER the merge (Step 7 item 9) succeeds** — only then are the canonical paths,
|
|
264
|
+
card IDs, branch, PR/SHA final.
|
|
313
265
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
266
|
+
**Procedure:** follow [obsidian-backref.md](obsidian-backref.md) **§ Mode: FINAL** — it is
|
|
267
|
+
the SSOT for the marker contract, the idempotent read-replace-or-append write procedure,
|
|
268
|
+
the full snippet template, the skip conditions (no note / `path: none` / unwritable → log
|
|
269
|
+
and continue, never fatal), and the final state update to
|
|
270
|
+
`## Obsidian Spec Note / status: back-reference-written`. Fill the `yaml` from the final
|
|
271
|
+
state: the real epic + child card IDs (Step 5), the merge SHA under `## Validation` (Step 7
|
|
272
|
+
item 8), the PR number from the `mw-docs` return (or `n/a`), and the Canonical Sources the
|
|
273
|
+
PRD assembled (prd-writing-phase.md Step 4 point 3) for `resources:`.
|
|
318
274
|
|
|
319
275
|
**This step is NON-BLOCKING** — any failure (note moved, vault unmounted, permission
|
|
320
|
-
denied) logs "Obsidian back-reference: SKIPPED (<reason>)" and the run completes. The
|
|
321
|
-
|
|
276
|
+
denied) logs "Obsidian back-reference: SKIPPED (<reason>)" and the run completes. The PRD
|
|
277
|
+
merge has already happened; the back-reference is a convenience index, never a gate.
|
|
322
278
|
|
|
323
279
|
### Final output
|
|
324
280
|
|
|
@@ -99,7 +99,17 @@ const FOLLOWUP_SCHEMA = {
|
|
|
99
99
|
// F-024 — domain-specialized fixer + judge (full map; reviewer-owns-its-domain — a doc
|
|
100
100
|
// finding is fixed by doc-reviewer, a security finding by security-reviewer, never coder).
|
|
101
101
|
const fixerAgent = ({ doc: 'doc-reviewer', ui: 'ui-expert', security: 'security-reviewer' })[domain] || 'coder'
|
|
102
|
-
|
|
102
|
+
// Specialization integrity (v4.26.1) — the judge is the VERIFICATION specialist of the
|
|
103
|
+
// finding's domain: doc fixes judged by doc-reviewer (code-reviewer judging prose was
|
|
104
|
+
// cross-domain), test fixes by qa-sentinel (THE test specialist). `ui` and `code` stay with
|
|
105
|
+
// code-reviewer: the judge verifies a CODE change, which is code-reviewer's charter
|
|
106
|
+
// (including DS-coherence rule 8 for UI). Fixer and judge of the same type are still two
|
|
107
|
+
// independent instances — the judge prompt is adversarial and greps the files itself.
|
|
108
|
+
const judgeAgent = (domain === 'security' || domain === 'migration') ? 'security-reviewer'
|
|
109
|
+
: domain === 'perf' ? 'api-perf-cost-auditor'
|
|
110
|
+
: domain === 'doc' ? 'doc-reviewer'
|
|
111
|
+
: domain === 'test' ? 'qa-sentinel'
|
|
112
|
+
: 'code-reviewer'
|
|
103
113
|
|
|
104
114
|
const findingsBlock = findings.map((f, i) => ` ${i + 1}. [${f.kind || kind}/${f.domain || domain}] ${f.evidence}`).join('\n')
|
|
105
115
|
const brief = [
|
|
@@ -219,9 +219,10 @@ try {
|
|
|
219
219
|
`You are the deterministic PRE-FLIGHT for an autonomous /new batch (variant new2). Follow ${REF}/setup.md (Phase 0 + Pre-flight) and ${REF}/implement.md (Phase 1 depends-on gate) for the SEMANTICS, but replace EVERY AskUserQuestion with the deterministic policy below. You run all git/bash yourself (the workflow cannot).\n\n` +
|
|
220
220
|
`${projectBrief}\n\nCards in batch (Read each YAML):\n${cardPaths.join('\n')}\nCard IDs: ${cardIds.join(' ')}\n\n` +
|
|
221
221
|
`Create/maintain the recovery tracker at /tmp/batch-tracker-${firstCard}.md (per setup.md § Context Tracking).\n\n` +
|
|
222
|
+
`ROLE BOUNDARY (specialization integrity): you are the OPS/GIT agent. You NEVER edit source or doc files — any needed content change belongs to the coder specialist; report it instead.\n\n` +
|
|
222
223
|
`DETERMINISTIC GATE POLICIES (NO user prompts):\n` +
|
|
223
224
|
`• G1 dirty-tree (main repo ${MAIN}): partition framework-managed noise exactly as setup.md step 3 ($METRICS=${METRICS}, .baldart/generated|state.json|skill-conflicts.json — NOT overlays/). Genuine user work → auto-stash 'baldart-new2-${firstCard}' (main checkout) and record the label. Never commit/abort/prompt.\n` +
|
|
224
|
-
`• Worktree (setup.md step 4): create ONE code worktree off ${TRUNK}; install deps; assign a port; run the baseline (tsc+lint+build). Copy ONLY the artifacts needed (env/.env.local/.env.example/supabase/.temp) — do NOT bulk-copy untracked files from the main repo (avoids stray backlog cards in the worktree). Use the git-authoritative idempotency pre-check. E2: baseline FAILS → fix
|
|
225
|
+
`• Worktree (setup.md step 4): create ONE code worktree off ${TRUNK}; install deps; assign a port; run the baseline (tsc+lint+build). Copy ONLY the artifacts needed (env/.env.local/.env.example/supabase/.temp) — do NOT bulk-copy untracked files from the main repo (avoids stray backlog cards in the worktree). Use the git-authoritative idempotency pre-check. E2: baseline FAILS → do NOT fix it yourself (role boundary — the coder specialist repairs it); return baseline:'fail' + a baselineLog precise enough for a coder to act (failing command, error excerpt, suspect files).\n` +
|
|
225
226
|
codexResolveBullet +
|
|
226
227
|
g3Bullet +
|
|
227
228
|
`• G4 card-field validation (setup.md 1b/1c): card missing requirements/acceptance_criteria/files_likely_touched → EXCLUDE (excluded[] + reason). Never HALT for one bad card.\n` +
|
|
@@ -239,9 +240,28 @@ try {
|
|
|
239
240
|
return finalReturn({ fatal: true, reason: 'pre-flight failed: ' + String(e && e.message) })
|
|
240
241
|
}
|
|
241
242
|
|
|
242
|
-
if (!preflight || preflight.ok === false
|
|
243
|
-
ledger(firstCard, '
|
|
244
|
-
return finalReturn({ fatal: true, reason: '
|
|
243
|
+
if (!preflight || preflight.ok === false) {
|
|
244
|
+
ledger(firstCard, 'preflight', 'BATCH-FATAL', (preflight && preflight.workspaceNote) || 'workspace unworkable')
|
|
245
|
+
return finalReturn({ fatal: true, reason: 'workspace unworkable — see pre-flight' })
|
|
246
|
+
}
|
|
247
|
+
if (preflight.baseline === 'fail') {
|
|
248
|
+
// E2 (specialization integrity) — baseline repair is CODE work: it belongs to the coder
|
|
249
|
+
// specialist, not the ops pre-flight agent (which never edits source). ONE bounded attempt;
|
|
250
|
+
// the verification is the deterministic re-run of the baseline gates themselves (no claim
|
|
251
|
+
// to trust — and every card's G26 re-exercises them anyway). Still failing → batch-fatal.
|
|
252
|
+
let repair = null
|
|
253
|
+
try {
|
|
254
|
+
repair = await agentSafe(
|
|
255
|
+
`You are the coder. The batch worktree BASELINE is failing on trunk-derived code (this is NOT card work — no card has run yet). Worktree: ${preflight.worktreePath} (cd into it).\n\nFailure log:\n${preflight.baselineLog || '(missing — re-run tsc/lint/build to reproduce)'}\n\nApply the minimal correct fix so the baseline gates (tsc + lint + build) pass, RE-RUN them, and report honestly (fixed:true ONLY if they now pass). Return: { fixed, log }`,
|
|
256
|
+
{ label: 'baseline-repair', phase: 'Pre-flight', agentType: 'coder',
|
|
257
|
+
schema: { type: 'object', required: ['fixed'], additionalProperties: true, properties: { fixed: { type: 'boolean' }, log: { type: 'string' } } } }
|
|
258
|
+
)
|
|
259
|
+
} catch (e) { if (e && e.transientExhausted) noteDegraded('outage'); repair = null }
|
|
260
|
+
if (repair && repair.fixed) ledger(firstCard, 'E2-baseline', 'FIXED-BY-CODER', String(repair.log || '').slice(0, 200))
|
|
261
|
+
else {
|
|
262
|
+
ledger(firstCard, 'E2-baseline', 'BATCH-FATAL', preflight.baselineLog || 'baseline irrecoverable')
|
|
263
|
+
return finalReturn({ fatal: true, reason: 'baseline build irrecoverable — see baselineLog' })
|
|
264
|
+
}
|
|
245
265
|
}
|
|
246
266
|
|
|
247
267
|
for (const ex of preflight.excluded || []) ledger(ex.card, 'preflight-exclude', 'EXCLUDED', ex.reason)
|
|
@@ -563,28 +583,41 @@ async function runCard(cardId, cardPath) {
|
|
|
563
583
|
const reviewSchema = { type: 'object', required: ['blocks', 'scopeExpansion'], additionalProperties: true,
|
|
564
584
|
properties: { blocks: { type: 'array', items: { type: 'object', additionalProperties: true } }, scopeExpansion: { type: 'array', items: { type: 'object', additionalProperties: true } }, note: { type: 'string' } } }
|
|
565
585
|
let reviewResults = []
|
|
586
|
+
const onErr = (e) => { if (e && e.transientExhausted) noteDegraded('outage'); return null }
|
|
587
|
+
// The standard SPECIALIST reviewer spawn — also reused as the JS-level fallback when the
|
|
588
|
+
// Codex companion dies at runtime (specialization integrity: the driver never reviews).
|
|
589
|
+
const stdReview = (ra) => agentSafe(
|
|
590
|
+
`You are ${ra}. Review card ${cardId} per ${REF}/review-cycle.md + ${REF}/codex-gate.md (your domain only). Run your gates on the COMMITTED-or-working state.\n\n${cardBrief}\nDiff: /tmp/diff-${cardId}.txt\n\n` +
|
|
591
|
+
`Report ONLY blocking failures that survive your retry cap as blocks:[{gate,domain,evidence}] (each MUST have non-empty gate AND evidence — F-014). Report legitimate findings BEYOND this card's AC as scopeExpansion:[{evidence,domain,withinOwnership,newAC}].\n\n` +
|
|
592
|
+
`Return: { blocks:[...], scopeExpansion:[...], note }`,
|
|
593
|
+
{ label: `review:${cardId}:${ra}`, phase: 'Implement', agentType: ra, schema: reviewSchema }
|
|
594
|
+
).catch(onErr)
|
|
566
595
|
try {
|
|
567
596
|
reviewResults = (await parallel(reviewers.map((ra) => () => {
|
|
568
|
-
const onErr = (e) => { if (e && e.transientExhausted) noteDegraded('outage'); return null }
|
|
569
597
|
if (ra === 'codex') {
|
|
570
|
-
// Codex-light finder: a general-purpose agent
|
|
598
|
+
// Codex-light finder: a general-purpose agent DRIVES the resolved companion (Bash,
|
|
599
|
+
// --wait). Driver role only — it never reviews; runtime failure → note flag, and the
|
|
600
|
+
// workflow spawns the real code-reviewer below.
|
|
571
601
|
return agentSafe(
|
|
572
|
-
`You are the Codex review
|
|
602
|
+
`You are the Codex review DRIVER for card ${cardId} (review_profile=light — Codex is the SOLE finder since v4.18.0; you are a driver, NOT a reviewer). Run the OpenAI Codex companion as a REVIEW-ONLY adversarial pass over this card's diff, then return its material findings in the schema below.\n\n${cardBrief}\nDiff: /tmp/diff-${cardId}.txt\nMAY-EDIT: ${JSON.stringify(mayEdit)}\n\n` +
|
|
573
603
|
`Run it in the FOREGROUND (it blocks; do NOT pass run_in_background):\n node "${sharedCtx.codexScriptPath}" task "Review-only — DO NOT make edits, no --write flag. Adversarial review of card ${cardId} using the diff at /tmp/diff-${cardId}.txt. Focus: auth/permission boundaries, data-loss paths, race conditions, rollback safety, schema drift, invariant violations. Report ONLY material findings with file+line evidence." --wait\n` +
|
|
574
|
-
`Read the Codex output ONLY through a [codex]-trace-stripping filter. **Fallback**: if it exits non-zero / prints CODEX_NOT_FOUND / stays empty,
|
|
604
|
+
`Read the Codex output ONLY through a [codex]-trace-stripping filter. **Fallback**: if it exits non-zero / prints CODEX_NOT_FOUND / stays empty, return note:'codex-unavailable' with EMPTY blocks/scopeExpansion — do NOT review yourself (role boundary); the workflow spawns the real code-reviewer.\n\n` +
|
|
575
605
|
`Map Codex BLOCKER/HIGH findings to blocks:[{gate:'codex-light',domain,evidence}] (each non-empty gate AND evidence — F-014). Map legitimate findings BEYOND this card's AC to scopeExpansion:[{evidence,domain,withinOwnership,newAC}].\n\n` +
|
|
576
606
|
`Return: { blocks:[...], scopeExpansion:[...], note }`,
|
|
577
607
|
{ label: `review:${cardId}:codex`, phase: 'Implement', agentType: 'general-purpose', schema: reviewSchema }
|
|
578
608
|
).catch(onErr)
|
|
579
609
|
}
|
|
580
|
-
return
|
|
581
|
-
`You are ${ra}. Review card ${cardId} per ${REF}/review-cycle.md + ${REF}/codex-gate.md (your domain only). Run your gates on the COMMITTED-or-working state.\n\n${cardBrief}\nDiff: /tmp/diff-${cardId}.txt\n\n` +
|
|
582
|
-
`Report ONLY blocking failures that survive your retry cap as blocks:[{gate,domain,evidence}] (each MUST have non-empty gate AND evidence — F-014). Report legitimate findings BEYOND this card's AC as scopeExpansion:[{evidence,domain,withinOwnership,newAC}].\n\n` +
|
|
583
|
-
`Return: { blocks:[...], scopeExpansion:[...], note }`,
|
|
584
|
-
{ label: `review:${cardId}:${ra}`, phase: 'Implement', agentType: ra, schema: reviewSchema }
|
|
585
|
-
).catch(onErr)
|
|
610
|
+
return stdReview(ra)
|
|
586
611
|
}))).filter(Boolean)
|
|
587
612
|
} catch (_) { /* parallel never rejects; nulls filtered */ }
|
|
613
|
+
// Specialization integrity — companion died at runtime: replace the empty driver result
|
|
614
|
+
// with a REAL code-reviewer pass (the same fallback the JS-level codexAvail gate uses).
|
|
615
|
+
if (reviewResults.some((r) => r && r.note === 'codex-unavailable')) {
|
|
616
|
+
reviewResults = reviewResults.filter((r) => !(r && r.note === 'codex-unavailable'))
|
|
617
|
+
g('review-codex', 'FALLBACK', 'companion failed at runtime → code-reviewer spawned (driver never reviews)')
|
|
618
|
+
const fb = await stdReview('code-reviewer')
|
|
619
|
+
if (fb) reviewResults.push(fb)
|
|
620
|
+
}
|
|
588
621
|
|
|
589
622
|
// F-014 — only route well-formed blocks (non-empty gate+evidence).
|
|
590
623
|
const blocks = reviewResults.flatMap((r) => (r.blocks || [])).filter((b) => b && b.gate && b.evidence)
|
|
@@ -642,7 +675,7 @@ async function runCard(cardId, cardPath) {
|
|
|
642
675
|
let commitRes
|
|
643
676
|
try {
|
|
644
677
|
commitRes = await agentSafe(
|
|
645
|
-
`Commit card ${cardId} in worktree ${sharedCtx.worktreePath}. MECHANICAL — do NOT re-read reference modules.\n` +
|
|
678
|
+
`Commit card ${cardId} in worktree ${sharedCtx.worktreePath}. MECHANICAL — do NOT re-read reference modules. ROLE BOUNDARY: you NEVER modify file contents except the card YAML status/note fields and the ssot-registry row — source/doc changes are not yours.\n` +
|
|
646
679
|
`Steps: (1) \`git status --porcelain\`; (2) stage = MAY-EDIT (${JSON.stringify(mayEdit)}) ∩ dirty — NEVER \`git add -A\`, NEVER \`git stash\`; if dirty has files OUTSIDE MAY-EDIT, do NOT stage them and set reconcileNote; (3) commit message \`[${cardId}] <concise>\`; ${doneStep} (5) 'nothing to commit' = already committed (record HEAD).\n` +
|
|
647
680
|
`On COMMIT_LOCK: clear stale lock + retry once. Still locked → committed:false.\n\n` +
|
|
648
681
|
`Return: { committed, commit, filesChanged, reconcileNote }`,
|
|
@@ -863,6 +896,7 @@ if (!committed.length) {
|
|
|
863
896
|
mergeResult = await agentSafe(
|
|
864
897
|
`Auto-merge the batch worktree to ${TRUNK} per ${REF}/merge-cleanup.md (Phase 6 via /mw programmatic checksAlreadyPassed:true, Phase 6b status reconciliation, Phase 6c hygiene). Run git yourself.\n\n${projectBrief}\nWorktree: ${sharedCtx.worktreePath}\nBranch: ${sharedCtx.branch}\nmerge_strategy: ${mergeStrategy}\nCommitted cards: ${committed.map((r) => r.card).join(' ')}\nPhase-0 stash to restore (if any): see /tmp/batch-tracker-${firstCard}.md.\n\n` +
|
|
865
898
|
`DETERMINISTIC POLICIES (NO prompts):\n` +
|
|
899
|
+
`• ROLE BOUNDARY: you are the OPS/GIT agent — you NEVER edit source or doc files. Reconciliation touches ONLY card YAML status fields + registry rows. A merge conflict on content is leave+report, never hand-resolved here.\n` +
|
|
866
900
|
`• G24 → auto-merge via merge_strategy.\n` +
|
|
867
901
|
`• F-030 HARD RULE: NEVER \`git add\`/commit code that did not pass the per-card gates. If the worktree is dirty with uncommitted code → DO NOT commit it; leave it, set uncommittedLeft:true, and report. NO "safety commit". Security/migration code is NEVER swept in.\n` +
|
|
868
902
|
`• F-029 HARD RULE: Phase 6b reconciliation marks a card DONE ONLY if it has a real commit in ${TRUNK}..HEAD AND its gates are green. NEVER force a non-implemented card to DONE. Return forcedDone:[] (must be empty).\n` +
|
|
@@ -894,7 +928,7 @@ phase('Production')
|
|
|
894
928
|
if (mergeResult && mergeResult.merged) {
|
|
895
929
|
try {
|
|
896
930
|
prodReadiness = await agentSafe(
|
|
897
|
-
`Run the post-merge Production Readiness checklist per ${REF}/production-readiness.md (Phase 7) over the batch's changed files. Auto-EXECUTE only stack-matched index/access-rule/cron deploys; REPORT (do not execute) env vars, feature flags, DB migrations, secrets, DNS. NON-BLOCKING.\n\n${projectBrief}\nChanged files: ${dedupe(committed.flatMap((r) => r.filesChanged || [])).join(', ') || '(derive from git)'}\n\nReturn: { autoExecuted:[...], manualItems:[...], note }`,
|
|
931
|
+
`Run the post-merge Production Readiness checklist per ${REF}/production-readiness.md (Phase 7) over the batch's changed files. Auto-EXECUTE only stack-matched index/access-rule/cron deploys; REPORT (do not execute) env vars, feature flags, DB migrations, secrets, DNS. NON-BLOCKING. ROLE BOUNDARY: you EXECUTE commands, you never edit repository files — a needed code/config change is reported as a manual item.\n\n${projectBrief}\nChanged files: ${dedupe(committed.flatMap((r) => r.filesChanged || [])).join(', ') || '(derive from git)'}\n\nReturn: { autoExecuted:[...], manualItems:[...], note }`,
|
|
898
932
|
{ label: 'production-readiness', phase: 'Production', agentType: 'general-purpose',
|
|
899
933
|
schema: { type: 'object', required: ['manualItems'], additionalProperties: true, properties: { autoExecuted: { type: 'array', items: { type: 'string' } }, manualItems: { type: 'array', items: { type: 'string' } }, note: { type: 'string' } } } }
|
|
900
934
|
)
|