baldart 4.94.0 → 4.96.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 +21 -0
- package/VERSION +1 -1
- package/framework/.claude/agents/REGISTRY.md +1 -1
- package/framework/.claude/agents/prd-card-writer.md +108 -1
- package/framework/.claude/skills/new/references/completeness.md +9 -0
- package/framework/.claude/skills/prd/CHANGELOG.md +11 -0
- package/framework/.claude/skills/prd/assets/card-template.yml +13 -1
- package/framework/.claude/skills/prd/references/audit-phase.md +42 -51
- package/framework/.claude/skills/prd/references/validation-phase.md +2 -1
- package/framework/agents/card-schema.md +60 -1
- package/framework/docs/ROOT-PRIMITIVES.md +1 -0
- package/framework/scripts/validate-card-baseline.js +62 -4
- package/framework/templates/baldart.config.template.yml +9 -0
- package/framework/templates/primitives/AGENTS.CHANGELOG.md +13 -0
- package/framework/templates/primitives/AGENTS.md +4 -2
- package/package.json +1 -1
- package/src/commands/configure.js +35 -0
- package/src/commands/update.js +6 -0
- package/src/utils/root-primitives.js +5 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,27 @@ 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.96.0] - 2026-07-02
|
|
9
|
+
|
|
10
|
+
**Protected branch decoupled from the integration trunk — `git.protected_branch`.** The root `AGENTS.md` primitive expressed the "NEVER push directly to X" MUST via `{{ git.trunk_branch }}`, conflating two distinct roles that in a two-branch gitflow are different branches: the *integration trunk* (worktree base + feature PR target in `/prd` + `/new`) and the *protected branch* (production/release line that must not receive direct pushes). A consumer with `trunk_branch: develop` therefore had its agents (Claude + Codex) refuse a direct `develop` push — even though `develop` was the integration branch they push to and `main` was the truly-protected line. `trunk_branch` could not simply be flipped to `main` because it also drives worktree creation (`update.js`), which must branch from `develop`.
|
|
11
|
+
|
|
12
|
+
- **New optional config key `git.protected_branch`** — the branch the "never push directly" rule protects. **Defaults to `git.trunk_branch`** when omitted, so single-trunk repos are byte-unchanged; a two-branch gitflow sets it explicitly (e.g. `protected_branch: main` while `trunk_branch: develop` stays the worktree base + PR target).
|
|
13
|
+
- **Primitive `AGENTS.md` skeleton → 1.5.0**: the push MUST now resolves `{{ git.protected_branch }}` and explicitly permits direct pushes to other branches (including the integration trunk when it differs) when the owner authorizes it.
|
|
14
|
+
- **Slot resolution** (`src/utils/root-primitives.js`): `git.protected_branch` resolves to `git.protected_branch || git.trunk_branch || 'main'`.
|
|
15
|
+
- **Schema-change propagation** (per the rule): template (`baldart.config.template.yml` — new commented key under `git:`), `configure` (new `detectProtectedBranch` probe + serialized `git.protected_branch` + `_probes`), `update` detector (already diffs all `git.*` keys → surfaces `git.protected_branch`; added a dedicated advisory), CHANGELOG. No doctor diagnostic needed (the update advisory + configure autodetect cover it).
|
|
16
|
+
|
|
17
|
+
**MINOR** — new optional config key, back-compat default, no install-layout change. **Codex parity: portable** — pure slot resolution in the shared `AGENTS.md` skeleton (transpiled to `.codex/agents/*` unchanged; the generated `AGENTS.md` is the cross-tool SSOT read by both runtimes).
|
|
18
|
+
|
|
19
|
+
## [4.95.0] - 2026-07-02
|
|
20
|
+
|
|
21
|
+
**`/prd` — applier economico + EARS+verify (i due interventi strutturali dal forensics della deep analysis).**
|
|
22
|
+
|
|
23
|
+
- **Apply dei finding delegato** (`prd-card-writer` `MODE: apply-findings` — un agente + token, pattern analysis-profiles v4.94.0): l'orchestratore `/prd` non edita più YAML a piena finestra (misurato: le fasi meccaniche erano il 48% del costo di un run reale, ~48 Edit a ~663k cache-read/chiamata). L'applier gira in contesto fresco (Claude: override `model: sonnet`; Codex: by-name), riceve tutto BY PATH, applica solo campi `[Target:]` con grounding obbligatorio, ESEGUE `validate-card-baseline.js --prd` + `stamp-holistic-audit.js`, ritorna compact; `audit-phase.md` 6.9 = delega thin + drift guard + fallback inline visibile. Verificato con spawn reale su fixture.
|
|
24
|
+
- **AC in grammatica EARS** (child/standalone; SSOT `card-schema.md` § "Acceptance-criteria grammar"): binari per forma (5 pattern + `SHALL CONTINUE TO`), ban-list verbi vaghi, BLOCKER in `--prd`; card legacy mai retro-bloccate.
|
|
25
|
+
- **`ac_verification`** — oracolo eseguibile per-AC definito a planning time (o `manual:` esplicito), authoring groundato (mai comandi inventati), eseguito da `/new` Phase 2.5 (step 4b — exit status = evidenza primaria); `qa-sentinel` intatto per contratto. Enforcement WARN-first; fixtures CI (Check D).
|
|
26
|
+
|
|
27
|
+
**MINOR** — capability su skill/agent/moduli condivisi; nessun cambio di layout install. **No new `baldart.config.yml` key**; la propagazione dello schema CARD viaggia completa nella release (matrice + template + writer + validator + consumer + attack-surface — pattern v4.35.0). **Codex parity: portable.**
|
|
28
|
+
|
|
8
29
|
## [4.94.0] - 2026-07-02
|
|
9
30
|
|
|
10
31
|
**Analysis profiles — `codebase-architect` specializzato per-tipo-di-analisi, un solo agente, un solo SSOT.** L'assessment dei ~50 callsite reali ha mostrato che "capire il codebase" copre 6 lavori diversi (grounding pre-implementazione, tracing di un bug, blast-radius, contesto di planning, inventario UI/design-system, baseline di review) serviti da UN prompt monolitico (~455 righe caricate a ogni spawn) con la conoscenza di specializzazione dispersa nei prompt dei chiamanti (dual-SSOT: il template di `context-primer` ri-dichiarava la search strategy dell'agente). Fix architetturale, stesso meccanismo di `OUTPUT=terse`:
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.
|
|
1
|
+
4.96.0
|
|
@@ -35,7 +35,7 @@ Quick-reference for all custom agents. Use this to route tasks to the right spec
|
|
|
35
35
|
| **hyper-gamification-designer** | Design | Analyze game features and retention mechanics | Progression, reward loops | No | MDA analysis, economy balance |
|
|
36
36
|
| **remotion-animator-orchestrator** | Design | Create Remotion video animations | Motion graphics, asset coordination | Yes | Remotion, visual agent coordination |
|
|
37
37
|
| **prd** | Product | Create PRDs, plans, and backlog cards | Requirements, execution planning | No | PRD writing, backlog management |
|
|
38
|
-
| **prd-card-writer** | Product | Generate atomic backlog cards from approved PRD | Card YAML, traceability, parallel groups | Yes | Backlog writing, dependency analysis |
|
|
38
|
+
| **prd-card-writer** | Product | Generate atomic backlog cards from approved PRD; `MODE: apply-findings` = fresh-context applier of audit findings to card YAML (spawned by /prd Step 6.9, sonnet override) | Card YAML, traceability, parallel groups | Yes | Backlog writing, dependency analysis |
|
|
39
39
|
| **onboarding-architect-lead** | Product | Design/improve user onboarding experiences | Activation flows, experiments | No | Onboarding research, metrics |
|
|
40
40
|
| **website-orchestrator** | Product | Coordinate multi-agent website development | Project orchestration | No | Phase management, quality gates |
|
|
41
41
|
| **senior-researcher** | Research | Comprehensive research on technical topics | Evidence-based analysis | Yes | Web search, citations |
|
|
@@ -476,7 +476,20 @@ Every card MUST include ALL fields from the template:
|
|
|
476
476
|
- `context` (background + PRD reference)
|
|
477
477
|
- `scope` (summary, users, value)
|
|
478
478
|
- `requirements` (concrete, specific)
|
|
479
|
-
- `acceptance_criteria` (testable `[ ] [AC-N]` items
|
|
479
|
+
- `acceptance_criteria` (testable `[ ] [AC-N]` items — **EARS grammar MANDATORY on child/
|
|
480
|
+
standalone cards**: the clause matches one of `THE SYSTEM SHALL …` / `WHEN … THE SYSTEM
|
|
481
|
+
SHALL …` / `WHILE …` / `WHERE …` / `IF … THEN THE SYSTEM SHALL …` (+ `SHALL CONTINUE TO`
|
|
482
|
+
for non-regression). Body in the project language, EARS keywords in English. NO vague
|
|
483
|
+
verbs ("correttamente", "properly", "as expected", … — the `--prd` validator ban-list
|
|
484
|
+
BLOCKS them): name the observable behavior. SSOT + worked example: `card-schema.md`
|
|
485
|
+
§ "Acceptance-criteria grammar (EARS)". Epic AC-EPIC roll-ups are exempt.)
|
|
486
|
+
- `ac_verification` (OPTIONAL map `AC-N → executable oracle`) — **grounding rule, same class
|
|
487
|
+
as the Field Grounding Rule**: write a command ONLY if you verified it exists (an npm
|
|
488
|
+
script in `package.json`, a command in `toolchain.commands.*`, a cited test file that
|
|
489
|
+
exists — Grep/Read to confirm). NEVER invent commands. Not automatable → explicit
|
|
490
|
+
`manual: <what a human checks>`. Genuinely undecidable → `[NEEDS CLARIFICATION: …]`.
|
|
491
|
+
Non-manual oracles SHOULD also appear in `validation_commands` (the validator warns on
|
|
492
|
+
drift). Consumed by `/new` Phase 2.5, which EXECUTES them.
|
|
480
493
|
- `definition_of_done` (checklist including doc invariants). **When `features.has_i18n: true`** AND the
|
|
481
494
|
card introduces user-facing strings, the DoD MUST carry an i18n-completeness line — NOT merely "strings
|
|
482
495
|
via `t()` + registered", but: *"new keys authored in the source locale + the i18n context registry (by
|
|
@@ -724,3 +737,97 @@ Return to the caller a structured summary:
|
|
|
724
737
|
- Cross-reference every requirement against the PRD source section
|
|
725
738
|
- Verify file paths in `files_likely_touched` exist in the codebase (use Glob to check)
|
|
726
739
|
- Count total files across all cards to verify no file is assigned to conflicting cards without a conflict edge
|
|
740
|
+
|
|
741
|
+
## MODE: apply-findings (audit-fix applier — invoked by `/prd` audit-phase Step 6.9)
|
|
742
|
+
|
|
743
|
+
**Trigger:** the invocation prompt contains `MODE: apply-findings`. In this mode you do NOT
|
|
744
|
+
author new cards — you apply an audit's consolidated findings to EXISTING card YAML files.
|
|
745
|
+
(Same one-agent+token mechanism as `PROFILE=` / `OUTPUT=terse`: a mode is a parameter, not a
|
|
746
|
+
twin agent.)
|
|
747
|
+
|
|
748
|
+
**Why this mode exists (measured):** the `/prd` orchestrator used to apply findings inline at
|
|
749
|
+
full context window (~48 Edits at ~663k cache-read per call — the mechanical phases were 48%
|
|
750
|
+
of a real run's cost). In a fresh subagent context the same work costs a fraction. On Claude
|
|
751
|
+
the caller spawns this mode with a `model: sonnet` override (batch card AUTHORING keeps the
|
|
752
|
+
frontmatter model); on Codex the by-name agent inherits the session model — the primary
|
|
753
|
+
enabler is the fresh context + structured I/O, not the model tier.
|
|
754
|
+
|
|
755
|
+
**Inputs (all BY PATH — never pasted content):**
|
|
756
|
+
- `REPORT_FILE` — the consolidated audit report (audit-phase Step 6.7 format: findings grouped
|
|
757
|
+
per card, each tagged `(Severity: HIGH/MEDIUM/LOW)` + `[Target: <field>]`)
|
|
758
|
+
- `CARD_PATHS` — the card YAML files to edit
|
|
759
|
+
- `AUDITED_AT` / `AUDITED_COMMIT` / `AUDITED_SET` — the run-level provenance values
|
|
760
|
+
(audit-phase § "Holistic-audit provenance stamp")
|
|
761
|
+
- `VALIDATOR` — path of `validate-card-baseline.js`; `STAMPER` — path of `stamp-holistic-audit.js`
|
|
762
|
+
|
|
763
|
+
**Workflow:**
|
|
764
|
+
1. Read the report; group findings per card ID.
|
|
765
|
+
2. Per card, apply HIGH first, then MEDIUM, per the FIELD MAPPING table below:
|
|
766
|
+
- Edit ONLY the field named by each finding's `[Target: <field>]` tag. NEVER touch a field
|
|
767
|
+
no finding targets; NEVER "improve" untargeted content. The Field Grounding Rule above
|
|
768
|
+
binds here too (no invented field names).
|
|
769
|
+
- A finding that genuinely requires a human decision → do NOT apply; record it as `[MANUAL]`.
|
|
770
|
+
- A finding whose suggested content is UNGROUNDED (a path that doesn't exist in the repo,
|
|
771
|
+
a command not in the project's convention) or ALREADY SATISFIED by the card → do NOT
|
|
772
|
+
apply it as-is; ground it against the repo first (Glob/Grep), apply the grounded form,
|
|
773
|
+
or record it in the audit trail as `already-satisfied` / `ungrounded (<why>)`.
|
|
774
|
+
- LOW findings: audit-trail note only — no structured-field edits.
|
|
775
|
+
- Append the audit trail to `notes` (format below).
|
|
776
|
+
3. **EXECUTE** `node <VALIDATOR> --prd <card>` on every edited card — exit 0 required. On
|
|
777
|
+
failure, fix your own edit and re-run (max 2 attempts, then report that card as
|
|
778
|
+
`validator: FAIL` with the error output — never ship a silently-invalid card).
|
|
779
|
+
4. **EXECUTE** the stamper ONCE for the whole set:
|
|
780
|
+
`node <STAMPER> --at <AUDITED_AT> --commit <AUDITED_COMMIT> --set <AUDITED_SET> <CARD_PATHS>`
|
|
781
|
+
5. Return the COMPACT summary below. Do NOT echo card bodies or the report back.
|
|
782
|
+
|
|
783
|
+
### FIELD MAPPING (Target tag → card field → action)
|
|
784
|
+
|
|
785
|
+
| Target tag | Card field | Action |
|
|
786
|
+
|---|---|---|
|
|
787
|
+
| `[Target: requirements]` | `requirements` | Append missing requirement or rewrite existing one |
|
|
788
|
+
| `[Target: acceptance_criteria]` | `acceptance_criteria` | Append new `"[ ] [AC-N] ..."` item or rewrite vague AC |
|
|
789
|
+
| `[Target: definition_of_done]` | `definition_of_done` | Append new `"[ ] ..."` item |
|
|
790
|
+
| `[Target: files_likely_touched]` | `files_likely_touched` | Append missing path (no duplicates) |
|
|
791
|
+
| `[Target: depends_on]` | `depends_on` | Append missing card ID |
|
|
792
|
+
| `[Target: git_strategy]` | `git_strategy` | Replace `TBD` with `feat/<CARD-ID>-<slug> from ${git.trunk_branch}` |
|
|
793
|
+
| `[Target: areas]` | `areas` | Add missing area key/value |
|
|
794
|
+
| `[Target: unknowns]` | `unknowns` | Append new `[U-N] UNKNOWN: ...` entry |
|
|
795
|
+
| `[Target: existing_patterns]` | `existing_patterns` | Append missing pattern reference or fix stale line_range/anchor_text |
|
|
796
|
+
| `[Target: validation_commands]` | `validation_commands` | Append missing verification command |
|
|
797
|
+
| `[Target: anti_patterns]` | `anti_patterns` | Append missing DO NOT constraint |
|
|
798
|
+
| `[Target: scope_boundaries]` | `scope_boundaries` | Add missing in_scope or out_of_scope item |
|
|
799
|
+
| `[Target: input_output_examples]` | `input_output_examples` | Append missing scenario or fix incorrect example |
|
|
800
|
+
| `[Target: error_handling]` | `error_handling` | Append missing failure mode or fix incorrect action |
|
|
801
|
+
| `[Target: reuse_analysis]` | `reuse_analysis` | Add missing reuse opportunity or correct file path |
|
|
802
|
+
| `[Target: notes]` | `notes` | Audit trail only |
|
|
803
|
+
|
|
804
|
+
### Severity policy
|
|
805
|
+
|
|
806
|
+
- **HIGH**: MUST apply. Card cannot be safely implemented without these.
|
|
807
|
+
- **MEDIUM**: SHOULD apply. Skip only if human judgment needed (mark `[MANUAL]`).
|
|
808
|
+
- **LOW**: Do NOT edit structured fields. Audit trail note only.
|
|
809
|
+
|
|
810
|
+
### Audit trail in `notes`
|
|
811
|
+
|
|
812
|
+
After applying all edits to a card, append to its `notes`:
|
|
813
|
+
|
|
814
|
+
```yaml
|
|
815
|
+
## Applied by quality audit — YYYY-MM-DD
|
|
816
|
+
Applied N findings to structured fields (H high, M medium).
|
|
817
|
+
Manual review needed: [list [MANUAL] items, or "none"].
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
### Return contract (COMPACT — the ONLY output)
|
|
821
|
+
|
|
822
|
+
```
|
|
823
|
+
APPLY-FINDINGS DONE
|
|
824
|
+
- <CARD-ID>: applied <H> high / <M> medium; manual: [<finding titles> | none]; validator: PASS|FAIL
|
|
825
|
+
- ...
|
|
826
|
+
stamp: <the stamper's one-line summary>
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
### Boundaries (mode-specific)
|
|
830
|
+
|
|
831
|
+
Read-only outside the card files; no Task/Agent spawns; no git commits/staging (the
|
|
832
|
+
orchestrator's Step 7 owns git); no card creation, deletion, or renaming; no edits to
|
|
833
|
+
fields not targeted by a finding.
|
|
@@ -54,6 +54,15 @@ Before triggering any review, you MUST verify that the coder agent implemented *
|
|
|
54
54
|
index of WHERE to look; you still verify independently (do NOT trust its `status` alone), just
|
|
55
55
|
without pulling entire files inline.
|
|
56
56
|
|
|
57
|
+
**4b. Per-AC executable oracles (`ac_verification` — EXECUTE, don't judge).** When the card
|
|
58
|
+
carries an `ac_verification` map (card-schema.md § "Acceptance-criteria grammar (EARS) +
|
|
59
|
+
per-AC oracles"), an AC whose oracle is NOT `manual:` is verified by **running the command**
|
|
60
|
+
— the exit status is the PRIMARY evidence for that row (`Done` on expected exit, `Missing/
|
|
61
|
+
Partial` otherwise, with the command + exit code in the Evidence column). Reading-based
|
|
62
|
+
judgment then covers only `manual:` oracles, oracle-less ACs, and the non-AC requirement
|
|
63
|
+
rows. A failing oracle is never argued around: either the implementation is incomplete
|
|
64
|
+
(gap-fixer path) or the oracle itself is stale — surface which, never silently reclassify.
|
|
65
|
+
|
|
57
66
|
**Verification depth per requirement type:**
|
|
58
67
|
- **"Create endpoint/route"** → verify: route file exists, handler has correct HTTP method,
|
|
59
68
|
request validation present, response shape matches requirement, auth middleware applied.
|
|
@@ -6,6 +6,17 @@ Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://sem
|
|
|
6
6
|
|
|
7
7
|
- **Analysis-profile contract (framework v4.94.0)**: the discovery-loop targeted architect question now passes `PROFILE=discovery`, and the Step 2 ISA scan passes `PROFILE=impact` (structural tier primary) — per `framework/agents/analysis-profiles.md`. Prompt semantics otherwise unchanged.
|
|
8
8
|
|
|
9
|
+
## 1.3.0 — 2026-07-02
|
|
10
|
+
|
|
11
|
+
**Applier economico + pacchetto EARS+verify** — i due interventi strutturali dal forensics (48% del costo nelle fasi meccaniche; gate-prosa aggirabili):
|
|
12
|
+
|
|
13
|
+
- **`MODE: apply-findings` su `prd-card-writer`** (un agente + token, pattern analysis-profiles — mai un twin): l'apply dei finding di audit esce dall'orchestratore (che non edita più YAML). L'applier riceve tutto BY PATH, applica solo campi `[Target:]`, grounding obbligatorio (path/comandi verificati, mai inventati), marca `[MANUAL]` senza decidere, ESEGUE validator `--prd` + stamper, ritorna compact. `audit-phase.md` Step 6.9 = delega thin + drift guard `git diff --stat` + fallback inline visibile. Field-mapping/severity/audit-trail SPOSTATE nel corpo agente. Testato con spawn reale su fixture.
|
|
14
|
+
- **Grammatica EARS sugli AC child/standalone** (SSOT: `card-schema.md` § "Acceptance-criteria grammar"): 5 pattern (`THE SYSTEM SHALL` / `WHEN` / `WHILE` / `WHERE` / `IF…THEN`) + `SHALL CONTINUE TO`; keyword inglesi, corpo nella lingua del progetto; ban-list verbi vaghi. BLOCKER nel validator `--prd` — le card legacy non sono mai retro-bloccate (il baseline check di `/new` non applica EARS). AC-EPIC esenti.
|
|
15
|
+
- **`ac_verification`** (mappa opzionale `AC-N → oracolo eseguibile` o `manual:`): authoring groundato (comando scritto solo se verificato esistente — classe Field Grounding Rule), consumo in `/new` Phase 2.5 che ESEGUE gli oracoli non-manual (exit status = evidenza primaria; `qa-sentinel` deliberatamente NON è il consumer — AC verification fuori dal suo contratto). Enforcement WARN-first (hard-gate solo dopo telemetria). Cross-check drift con `validation_commands`.
|
|
16
|
+
- Validator: canale warnings separato (mai exit 1 da un WARN); fixtures EARS nel self-test CI (`check-card-baseline.js` Check D).
|
|
17
|
+
|
|
18
|
+
Codex parity: portable (token in prosa agente → transpile TOML; script zero-dep; prosa condivisa).
|
|
19
|
+
|
|
9
20
|
## 1.2.0 — 2026-07-02
|
|
10
21
|
|
|
11
22
|
**Gate-hygiene wave** — dai driver misurati sul forensics di una sessione /prd reale (61.6M token; 48% del costo in fasi meccaniche a piena finestra; una famiglia di card spedita con zero audit-trail; ~20 righe di progress bar × 30+ messaggi):
|
|
@@ -85,8 +85,12 @@ existing_patterns:
|
|
|
85
85
|
anti_patterns:
|
|
86
86
|
- "DO NOT {{specific prohibited action — be concrete, not generic}}"
|
|
87
87
|
|
|
88
|
+
# EARS grammar (card-schema.md § "Acceptance-criteria grammar"): the clause matches one of
|
|
89
|
+
# THE SYSTEM SHALL … / WHEN … THE SYSTEM SHALL … / WHILE … / WHERE … / IF … THEN THE SYSTEM SHALL …
|
|
90
|
+
# Body in the project language, EARS keywords in English. No vague verbs (banned by the validator).
|
|
88
91
|
acceptance_criteria:
|
|
89
|
-
- "[ ] [AC-1] {{
|
|
92
|
+
- "[ ] [AC-1] WHEN {{trigger}} THE SYSTEM SHALL {{observable behavior}}"
|
|
93
|
+
- "[ ] [AC-2] IF {{error condition}} THEN THE SYSTEM SHALL {{recovery behavior}}"
|
|
90
94
|
|
|
91
95
|
# --- Validation Commands (OPTIONAL — include for cards with testable outputs) ---
|
|
92
96
|
# Executable commands the agent runs for self-verification after implementation.
|
|
@@ -98,6 +102,14 @@ validation_commands:
|
|
|
98
102
|
expect: "exit 0"
|
|
99
103
|
- cmd: "{{verification command, e.g. grep -c 'pattern' src/file.tsx}}"
|
|
100
104
|
expect: "{{expected: 'exit 0', '>= N', 'contains X', '== N'}}"
|
|
105
|
+
|
|
106
|
+
# --- Per-AC oracles (OPTIONAL map — card-schema.md § ac_verification) ---
|
|
107
|
+
# One entry per AC: an EXECUTABLE command verified to exist (npm script / toolchain command /
|
|
108
|
+
# existing test file) — never invented — or an explicit "manual: <what a human checks>".
|
|
109
|
+
# /new Phase 2.5 EXECUTES non-manual oracles; exit status is the primary AC evidence.
|
|
110
|
+
ac_verification:
|
|
111
|
+
AC-1: "{{command verified to exist, e.g. npm run test -- --grep '{{feature}}'}}"
|
|
112
|
+
AC-2: "manual: {{what a human checks}}"
|
|
101
113
|
note: "{{what this verifies — maps to AC-N}}"
|
|
102
114
|
|
|
103
115
|
definition_of_done:
|
|
@@ -165,7 +165,7 @@ INVEST criteria violations:
|
|
|
165
165
|
- Valuable: card does not deliver user-visible or system-critical value
|
|
166
166
|
- Estimable: scope unclear, cannot estimate effort
|
|
167
167
|
- Small: card too large for one dev session
|
|
168
|
-
- Testable: acceptance criteria not binary pass/fail
|
|
168
|
+
- Testable: acceptance criteria not binary pass/fail (the EARS grammar in card-schema.md § "Acceptance-criteria grammar" is the norm — a clause outside the 5 EARS patterns is a finding)
|
|
169
169
|
|
|
170
170
|
Requirements smell detection:
|
|
171
171
|
- Ambiguous pronouns without clear antecedent
|
|
@@ -199,7 +199,7 @@ Card structure:
|
|
|
199
199
|
files_likely_touched — flag any unowned consumer (the "hidden-still-shown-in-the-other-mode" class).
|
|
200
200
|
- areas field incomplete
|
|
201
201
|
- git_strategy set to TBD
|
|
202
|
-
- acceptance_criteria not binary testable
|
|
202
|
+
- acceptance_criteria not EARS-conformant (5 patterns, card-schema.md) or not binary testable
|
|
203
203
|
- definition_of_done missing items
|
|
204
204
|
- existing_patterns with stale line_range or missing anchor_text
|
|
205
205
|
- validation_commands missing for cards with testable outputs
|
|
@@ -337,48 +337,42 @@ Present the consolidated report to the user.
|
|
|
337
337
|
|
|
338
338
|
Use `SendMessage` with `type: "shutdown_request"` to shut down all teammates, then `TeamDelete`.
|
|
339
339
|
|
|
340
|
-
## Step 6.9 — Apply Findings to Cards
|
|
341
|
-
|
|
342
|
-
**Goal**:
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
- **
|
|
370
|
-
|
|
371
|
-
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
```yaml
|
|
378
|
-
## Applied by quality audit — YYYY-MM-DD
|
|
379
|
-
Applied N findings to structured fields (H high, M medium).
|
|
380
|
-
Manual review needed: [list [MANUAL] items, or "none"].
|
|
381
|
-
```
|
|
340
|
+
## Step 6.9 — Apply Findings to Cards (DELEGATED — the orchestrator does not edit YAML)
|
|
341
|
+
|
|
342
|
+
**Goal**: transform each card from "audited" to "implementation-ready". The mechanical apply
|
|
343
|
+
is DELEGATED to `prd-card-writer` in **MODE: apply-findings** — its § "MODE: apply-findings"
|
|
344
|
+
is the SSOT for the field mapping, severity policy, audit trail, self-validation and stamp
|
|
345
|
+
execution; do NOT restate or re-execute them here. (Measured driver: the inline apply ran
|
|
346
|
+
~48 Edits at full context window — the mechanical phases were 48% of a real run's cost.)
|
|
347
|
+
|
|
348
|
+
1. Capture the three run-level stamp values ONCE (snippet in § "Holistic-audit provenance
|
|
349
|
+
stamp" below) and read `$REPORT_FILE` **as a path only** from the session tracker
|
|
350
|
+
(pattern: `/tmp/check-audit-report-<CARD-ID-SLUG>-<SESSION-ID>.md`) — do not load it.
|
|
351
|
+
2. Spawn ONE `prd-card-writer` subagent — Claude: Task tool, `subagent_type: prd-card-writer`,
|
|
352
|
+
`model: sonnet` override, `mode: "bypassPermissions"`; Codex: spawn by name from
|
|
353
|
+
`.codex/agents/` (inherits the session model). THIN prompt, paths only:
|
|
354
|
+
|
|
355
|
+
```
|
|
356
|
+
MODE: apply-findings
|
|
357
|
+
REPORT_FILE: <path>
|
|
358
|
+
CARD_PATHS: <newline-separated card paths>
|
|
359
|
+
AUDITED_AT: <ts> AUDITED_COMMIT: <sha-or-empty> AUDITED_SET: <ID1,ID2,...>
|
|
360
|
+
VALIDATOR: .framework/framework/scripts/validate-card-baseline.js
|
|
361
|
+
STAMPER: .framework/framework/scripts/stamp-holistic-audit.js
|
|
362
|
+
```
|
|
363
|
+
(Resolve the two script paths for this install — same base as the validation-phase 0-i
|
|
364
|
+
gate.)
|
|
365
|
+
3. Consume the COMPACT return (`APPLY-FINDINGS DONE` block):
|
|
366
|
+
- any card `validator: FAIL` → HALT and surface the validator output verbatim;
|
|
367
|
+
- route each `[MANUAL]` item to `AskUserQuestion` (they legally survive to
|
|
368
|
+
validation-phase Step 7 item 1);
|
|
369
|
+
- **drift guard (zero-cost)**: run `git diff --stat -- "${paths.backlog_dir}"` — the
|
|
370
|
+
touched files MUST be ⊆ CARD_PATHS; anything else → HALT (the applier edited outside
|
|
371
|
+
its mandate). Never re-read the card bodies to "double-check" — the validator already
|
|
372
|
+
did.
|
|
373
|
+
4. **Fallback (delegate-or-else-inline, visible never silent)**: if the spawn is impossible
|
|
374
|
+
in this install, apply inline following the agent's § "MODE: apply-findings" and log
|
|
375
|
+
"apply-findings: INLINE FALLBACK (<reason>)" in the audit log.
|
|
382
376
|
|
|
383
377
|
### Holistic-audit provenance stamp (since v4.3.0) — consumed by `/new` Step 3d
|
|
384
378
|
|
|
@@ -420,13 +414,10 @@ Hand-edit the YAML only when the script is genuinely unreachable in this install
|
|
|
420
414
|
|
|
421
415
|
### Per-card workflow
|
|
422
416
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
4. Write the `metadata.holistic_audit` provenance block (see "Holistic-audit provenance stamp" above) — **by EXECUTING `stamp-holistic-audit.js` once for the whole card set** (after the last card's field edits), not by hand-editing YAML. **MANDATORY when the audit ran** (see the Fail-safe contract above) — this is what lets `/new`/`new2` skip the per-card plan-auditor when the card is implemented right after `/prd`; `validation-phase.md` Step 7 item 5b deterministically backstops it, but do not rely on the backstop.
|
|
428
|
-
5. Write updated card YAML.
|
|
429
|
-
6. Re-read to verify edits landed correctly.
|
|
417
|
+
The per-card loop (read findings → edit targeted fields → audit trail → validate → stamp)
|
|
418
|
+
is OWNED by `prd-card-writer` § "MODE: apply-findings" (Step 6.9). The stamper runs ONCE for
|
|
419
|
+
the whole card set inside the applier; `validation-phase.md` Step 7 item 5b remains the
|
|
420
|
+
deterministic backstop.
|
|
430
421
|
|
|
431
422
|
**Note**: No separate commit here — the validation-phase.md Step 7 handles committing all PRD artifacts together.
|
|
432
423
|
|
|
@@ -21,6 +21,7 @@ Mark task 5 as `in_progress`.
|
|
|
21
21
|
- **`review_profile` enum + epic `skip`** — `skip|light|balanced|deep`; missing/empty = blocker (the card-writer must compute it via `prd-card-writer.md § Rule C`); epics MUST be `skip`;
|
|
22
22
|
- **conditional `requirements` presence (PO1)** — a child/standalone card with non-empty `acceptance_criteria` + `scope` MUST carry `requirements` (a fresh `/prd` card without them shifts a measured ~3–4M-token back-fill into `/new`'s orchestrator — see `card-schema.md` § "Authoring completeness invariants");
|
|
23
23
|
- **unresolved `[NEEDS CLARIFICATION: …]` markers** — a card carrying a planning ambiguity is NOT implementable. Resolve each marker (with the user via `AskUserQuestion` when it needs a human answer, or from Discovery/PRD material when it does not), edit the card, re-run;
|
|
24
|
+
- **EARS grammar on child/standalone AC** (5 patterns + vague-verb ban-list — `card-schema.md` § "Acceptance-criteria grammar") = BLOCKER; `ac_verification` coverage = WARN-only;
|
|
24
25
|
- the universal field-state matrix + remaining enums (`card-schema.md`).
|
|
25
26
|
|
|
26
27
|
**Why executed and not recited:** the prose version of this gate has already failed silently on a real run (a whole card family shipped with zero audit trail). A recited check degrades under context pressure; an executed one cannot.
|
|
@@ -77,7 +78,7 @@ Mark task 5 as `in_progress`.
|
|
|
77
78
|
Then execute the audit protocol described within, using the card IDs from Step 5.
|
|
78
79
|
The protocol auto-detects which agents to include (security and performance
|
|
79
80
|
are signal-triggered, no manual profile selection needed).
|
|
80
|
-
**I/O contract:** `audit-phase.md` may edit card YAML files (
|
|
81
|
+
**I/O contract:** `audit-phase.md` may edit card YAML files (its Step 6.9 apply runs via the `prd-card-writer` MODE: apply-findings subagent). The list of all card paths modified by the audit (including those edited in Step 6.9) is the complete set that Step 7 item 6 must stage. The glob `FEAT-XXXX-*.yml (all cards from Step 5)` in item 6 covers this; note it explicitly so that context compaction does not cause a model to stage only the pre-audit originals.
|
|
81
82
|
3. Wait for audit report and card edits.
|
|
82
83
|
|
|
83
84
|
**Gate:** all HIGH/MEDIUM findings resolved, no open `[MANUAL]` items that require external human input beyond this orchestrator. (Items that the orchestrator can resolve autonomously are resolved before this gate; items that need a human decision were already routed to `AskUserQuestion` inside `audit-phase.md`. Items that survive to Step 7 item 1 are those that received a human answer but required card re-edits — these are explicitly handled there.)
|
|
@@ -56,7 +56,8 @@ Legend: **R** = required, present **and non-empty** · **E** = required key, val
|
|
|
56
56
|
| `scope` | R | R | R | **never omittable** — encodes intent |
|
|
57
57
|
| `scope_boundaries` | R | C | C | "Omit for standalone cards with no siblings" (`prd-card-writer.md`) |
|
|
58
58
|
| `requirements` | C | R | R | EPIC tracks via AC-EPIC; CHILD/STANDALONE: faithfully derivable from AC+scope when both present — see consumer contract |
|
|
59
|
-
| `acceptance_criteria` | R (AC-EPIC) | R | R | |
|
|
59
|
+
| `acceptance_criteria` | R (AC-EPIC) | R | R | CHILD/STANDALONE: EARS grammar — see § "Acceptance-criteria grammar (EARS)" |
|
|
60
|
+
| `ac_verification` | — | C | C | OPTIONAL map `AC-N → executable oracle` (or `manual: <check>`); authored by `/prd`, executed by `/new` Phase 2.5 — see § below |
|
|
60
61
|
| `definition_of_done` | R | R | R | |
|
|
61
62
|
| `depends_on` / `blocks` | E (`[]`) | E | E | |
|
|
62
63
|
| `estimated_complexity` | C | R | R | |
|
|
@@ -212,6 +213,64 @@ Rules:
|
|
|
212
213
|
- Omit any sub-field whose session id is unavailable (env var unset) rather than writing a
|
|
213
214
|
placeholder.
|
|
214
215
|
|
|
216
|
+
## Acceptance-criteria grammar (EARS) + per-AC oracles (since the EARS+verify wave)
|
|
217
|
+
|
|
218
|
+
**Why**: a prose AC passes gates without being verifiable ("handle it correctly"), and
|
|
219
|
+
prose gates get gamed (measured: visible/holdout gap +28pt per 10x code; exploit rate
|
|
220
|
+
0.6→13.9% post-RL). Two complementary halves: the AC is binary BY FORM (EARS), and its
|
|
221
|
+
verification is EXECUTABLE, defined at planning time.
|
|
222
|
+
|
|
223
|
+
### EARS grammar (CHILD/STANDALONE `acceptance_criteria` — epic AC-EPIC roll-ups exempt)
|
|
224
|
+
|
|
225
|
+
Every AC entry is `"[ ] [AC-N] <EARS clause>"` where the clause matches ONE of the five
|
|
226
|
+
EARS patterns:
|
|
227
|
+
|
|
228
|
+
| Pattern | Form |
|
|
229
|
+
|---|---|
|
|
230
|
+
| Ubiquitous | `THE SYSTEM SHALL <behavior>` |
|
|
231
|
+
| Event-driven | `WHEN <trigger> THE SYSTEM SHALL <behavior>` |
|
|
232
|
+
| State-driven | `WHILE <state> THE SYSTEM SHALL <behavior>` |
|
|
233
|
+
| Optional-feature | `WHERE <feature is present> THE SYSTEM SHALL <behavior>` |
|
|
234
|
+
| Unwanted-behavior | `IF <error condition> THEN THE SYSTEM SHALL <behavior>` |
|
|
235
|
+
|
|
236
|
+
Non-regression variant: `… THE SYSTEM SHALL CONTINUE TO <existing behavior>`.
|
|
237
|
+
|
|
238
|
+
- The clause body stays in the project language (`identity.language`); the EARS keywords
|
|
239
|
+
stay English — they are syntactic markers, like field names.
|
|
240
|
+
- **Vague verbs are BANNED** (validator ban-list: `correttamente`, `appropriato/a`,
|
|
241
|
+
`properly`, `appropriately`, `as expected`, `gracefully`, `handle correctly`, `gestire
|
|
242
|
+
corrett*`) — name the observable behavior instead.
|
|
243
|
+
- Enforced by `validate-card-baseline.js --prd` (BLOCKER) — i.e. at `/prd` authoring time
|
|
244
|
+
only. Legacy cards are never retro-blocked: `/new`'s baseline conformance (1b-iii) does
|
|
245
|
+
NOT apply the EARS check.
|
|
246
|
+
|
|
247
|
+
Worked example (rewritten from a real card):
|
|
248
|
+
- Before: `"[ ] [AC-2] La lista fornitori si aggiorna correttamente dopo il salvataggio"`
|
|
249
|
+
- After: `"[ ] [AC-2] WHEN il salvataggio di un fornitore va a buon fine THE SYSTEM SHALL mostrare il fornitore aggiornato nella lista senza reload manuale"`
|
|
250
|
+
|
|
251
|
+
### `ac_verification` — per-AC executable oracle (OPTIONAL map)
|
|
252
|
+
|
|
253
|
+
```yaml
|
|
254
|
+
ac_verification:
|
|
255
|
+
AC-1: "npm run test -- --grep 'supplier hub layout'"
|
|
256
|
+
AC-2: "grep -q 'useSupplierList' src/app/settings/suppliers/page.tsx"
|
|
257
|
+
AC-3: "manual: verifica visiva dello stato empty su viewport 375px"
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
- **Authoring** (`prd-card-writer`): a command is written ONLY if verified to exist — an
|
|
261
|
+
npm script in `package.json`, a command in `toolchain.commands.*`, a cited test file
|
|
262
|
+
that exists. NEVER invent commands (same class as the Field Grounding Rule). Not
|
|
263
|
+
automatable → explicit `manual: <what a human checks>`.
|
|
264
|
+
- **Consumption** (`/new` Phase 2.5 completeness check — the AC-verification owner): an AC
|
|
265
|
+
with a non-`manual:` oracle is verified by EXECUTING it; the exit status is the primary
|
|
266
|
+
evidence. Judgment covers only `manual:` oracles and oracle-less ACs. (`qa-sentinel` is
|
|
267
|
+
deliberately NOT the consumer — AC verification is out of its scope by contract.)
|
|
268
|
+
- **Relation to `validation_commands`**: that field stays the card-level suite;
|
|
269
|
+
`ac_verification` is the per-AC mapping. The validator WARNs when a non-manual oracle
|
|
270
|
+
is absent from `validation_commands` (drift guard between the two).
|
|
271
|
+
- **Enforcement is WARN-first** (the `--prd` validator warns on missing entries for cards
|
|
272
|
+
with code-testable outputs); any hard gate comes only after telemetry on real runs.
|
|
273
|
+
|
|
215
274
|
## Writer contract
|
|
216
275
|
|
|
217
276
|
The canonical writer is the `prd-card-writer` agent. It emits the full baseline for the
|
|
@@ -44,6 +44,7 @@ key was introduced:
|
|
|
44
44
|
| Commands table | `toolchain.commands.*` → `package.json` scripts (rows with no source are omitted) |
|
|
45
45
|
| Repo map | non-empty `paths.*` + a derived source root |
|
|
46
46
|
| `git.trunk_branch` | `git.trunk_branch` |
|
|
47
|
+
| `git.protected_branch` | `git.protected_branch` → `git.trunk_branch` (default) — the "never push directly" branch; differs from the trunk only in a two-branch gitflow |
|
|
47
48
|
| feature gates | `features.has_backlog` / `has_adrs` / `has_i18n` / `has_design_system` |
|
|
48
49
|
|
|
49
50
|
## Overlay
|
|
@@ -29,7 +29,11 @@
|
|
|
29
29
|
* acceptance_criteria + scope MUST have non-empty requirements;
|
|
30
30
|
* - epic `review_profile` must be `skip`;
|
|
31
31
|
* - `[NEEDS CLARIFICATION: …]` ambiguity markers in the raw YAML are BLOCKERS —
|
|
32
|
-
* a card ships only when every planning ambiguity has been resolved
|
|
32
|
+
* a card ships only when every planning ambiguity has been resolved;
|
|
33
|
+
* - EARS grammar on child/standalone acceptance_criteria (5 patterns + vague-verb
|
|
34
|
+
* ban-list) — BLOCKER; epics exempt. SSOT: card-schema.md § "Acceptance-criteria
|
|
35
|
+
* grammar (EARS)";
|
|
36
|
+
* - `ac_verification` per-AC oracle coverage — WARN-only (never fails the run).
|
|
33
37
|
*
|
|
34
38
|
* Module API (for CI self-tests in scripts/check-card-baseline.js):
|
|
35
39
|
* const { validateCard, detectProfile, loadSchema, loadEnums } = require(...)
|
|
@@ -373,12 +377,30 @@ function scanAmbiguityMarkers(text) {
|
|
|
373
377
|
return out;
|
|
374
378
|
}
|
|
375
379
|
|
|
380
|
+
// EARS grammar — SSOT: card-schema.md § "Acceptance-criteria grammar (EARS)".
|
|
381
|
+
// Keywords are English syntactic markers; the clause body may be any language.
|
|
382
|
+
const EARS_PATTERNS = [
|
|
383
|
+
/^THE SYSTEM SHALL .+/,
|
|
384
|
+
/^WHEN .+ THE SYSTEM SHALL .+/,
|
|
385
|
+
/^WHILE .+ THE SYSTEM SHALL .+/,
|
|
386
|
+
/^WHERE .+ THE SYSTEM SHALL .+/,
|
|
387
|
+
/^IF .+ THEN THE SYSTEM SHALL .+/,
|
|
388
|
+
];
|
|
389
|
+
const VAGUE_VERB_RE = /(correttamente|appropriat[oa]|properly|appropriately|as expected|gracefully|handle correctly|gestire corrett)/i;
|
|
390
|
+
|
|
391
|
+
/** "[ ] [AC-3] WHEN … THE SYSTEM SHALL …" → { id: 'AC-3', clause: 'WHEN … …' } (null if unparseable) */
|
|
392
|
+
function parseAcEntry(entry) {
|
|
393
|
+
const m = String(entry).match(/^\s*\[[ xX]?\]\s*\[(AC-\d+)\]\s*(.+)$/);
|
|
394
|
+
return m ? { id: m[1], clause: m[2].trim() } : null;
|
|
395
|
+
}
|
|
396
|
+
|
|
376
397
|
/**
|
|
377
398
|
* The deterministic subset of /prd validation-phase Step 6 item 0 (see file header).
|
|
378
|
-
* Returns
|
|
399
|
+
* Returns { errors, warnings }. `text` is the raw YAML (marker scan), `card` the parsed map.
|
|
379
400
|
*/
|
|
380
401
|
function prdGateChecks(card, text, profile) {
|
|
381
402
|
const errors = [];
|
|
403
|
+
const warnings = [];
|
|
382
404
|
if (profile !== 'EPIC' && nonEmpty(card.acceptance_criteria) && nonEmpty(card.scope) && !nonEmpty(card.requirements)) {
|
|
383
405
|
errors.push("has acceptance_criteria + scope but no 'requirements' — emit requirements (faithful restatement of AC + scope) before commit (PO1)");
|
|
384
406
|
}
|
|
@@ -388,7 +410,37 @@ function prdGateChecks(card, text, profile) {
|
|
|
388
410
|
for (const m of scanAmbiguityMarkers(text)) {
|
|
389
411
|
errors.push(`unresolved ambiguity marker — resolve with the user before commit: ${m}`);
|
|
390
412
|
}
|
|
391
|
-
|
|
413
|
+
// EARS grammar + vague-verb ban (child/standalone only; epic AC-EPIC roll-ups exempt)
|
|
414
|
+
if (profile !== 'EPIC' && Array.isArray(card.acceptance_criteria)) {
|
|
415
|
+
for (const entry of card.acceptance_criteria) {
|
|
416
|
+
const ac = parseAcEntry(entry);
|
|
417
|
+
if (!ac) continue; // malformed entry shape is the matrix/consumer contract's concern
|
|
418
|
+
if (VAGUE_VERB_RE.test(ac.clause)) {
|
|
419
|
+
errors.push(`${ac.id} uses a banned vague verb — name the observable behavior (card-schema.md § EARS): "${ac.clause.slice(0, 90)}"`);
|
|
420
|
+
} else if (!EARS_PATTERNS.some((re) => re.test(ac.clause))) {
|
|
421
|
+
errors.push(`${ac.id} is not an EARS clause (THE SYSTEM SHALL / WHEN|WHILE|WHERE … THE SYSTEM SHALL / IF … THEN THE SYSTEM SHALL): "${ac.clause.slice(0, 90)}"`);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
// ac_verification coverage — WARN-only (enforcement is data-driven, never a blocker here)
|
|
425
|
+
const av = card.ac_verification;
|
|
426
|
+
const hasCodeOutputs = nonEmpty(card.files_likely_touched);
|
|
427
|
+
if (!nonEmpty(av)) {
|
|
428
|
+
if (hasCodeOutputs) warnings.push("no 'ac_verification' map — consider per-AC executable oracles (or 'manual: …') per card-schema.md § ac_verification");
|
|
429
|
+
} else if (typeof av === 'object' && !Array.isArray(av)) {
|
|
430
|
+
const vcText = JSON.stringify(card.validation_commands || []);
|
|
431
|
+
for (const entry of card.acceptance_criteria) {
|
|
432
|
+
const ac = parseAcEntry(entry);
|
|
433
|
+
if (!ac) continue;
|
|
434
|
+
const oracle = av[ac.id];
|
|
435
|
+
if (oracle == null || String(oracle).trim() === '') {
|
|
436
|
+
warnings.push(`ac_verification has no entry for ${ac.id}`);
|
|
437
|
+
} else if (!/^manual:/i.test(String(oracle)) && !vcText.includes(String(oracle).slice(0, 40))) {
|
|
438
|
+
warnings.push(`${ac.id} oracle is not in validation_commands (drift guard): "${String(oracle).slice(0, 60)}"`);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
return { errors, warnings };
|
|
392
444
|
}
|
|
393
445
|
|
|
394
446
|
// --- CLI --------------------------------------------------------------------
|
|
@@ -420,7 +472,12 @@ function main(argv) {
|
|
|
420
472
|
continue;
|
|
421
473
|
}
|
|
422
474
|
const { profile, errors } = validateCard(card, { matrix, enums, filename: path.basename(file) });
|
|
423
|
-
|
|
475
|
+
let warnings = [];
|
|
476
|
+
if (prdMode) {
|
|
477
|
+
const g = prdGateChecks(card, text, profile);
|
|
478
|
+
errors.push(...g.errors);
|
|
479
|
+
warnings = g.warnings;
|
|
480
|
+
}
|
|
424
481
|
if (errors.length) {
|
|
425
482
|
failed++;
|
|
426
483
|
process.stdout.write(`✖ ${file} [${profile}] — ${errors.length} issue(s):\n`);
|
|
@@ -428,6 +485,7 @@ function main(argv) {
|
|
|
428
485
|
} else {
|
|
429
486
|
process.stdout.write(`✓ ${file} [${profile}]\n`);
|
|
430
487
|
}
|
|
488
|
+
for (const w of warnings) process.stdout.write(` ⚠ ${w}\n`);
|
|
431
489
|
}
|
|
432
490
|
return failed ? 1 : 0;
|
|
433
491
|
}
|
|
@@ -416,6 +416,15 @@ git:
|
|
|
416
416
|
# (falling back to asking the user). Default kept as "develop" for back-compat.
|
|
417
417
|
trunk_branch: develop
|
|
418
418
|
|
|
419
|
+
# Protected branch — the branch that MUST NOT receive direct pushes (the
|
|
420
|
+
# production/release line). Skills resolve this key for the "never push
|
|
421
|
+
# directly to X" rule. DEFAULTS to `trunk_branch` when omitted, so single-trunk
|
|
422
|
+
# repos need not set it. Set it explicitly ONLY for a two-branch gitflow where
|
|
423
|
+
# the protected line differs from the integration trunk — e.g.
|
|
424
|
+
# `protected_branch: main` while `trunk_branch: develop` (features integrate
|
|
425
|
+
# into develop; main is reached only via a release/hotfix PR).
|
|
426
|
+
protected_branch: develop
|
|
427
|
+
|
|
419
428
|
# How `/mw` lands the feature branch onto the trunk:
|
|
420
429
|
#
|
|
421
430
|
# pr (default) — push feature branch, open a PR via `gh pr create`,
|
|
@@ -4,6 +4,19 @@ Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://sem
|
|
|
4
4
|
Questo è il changelog INTERNO dello skeleton spedito (`primitive_version`), separato dal
|
|
5
5
|
file `AGENTS.md` generato nel consumer e indipendente dal `VERSION` del framework.
|
|
6
6
|
|
|
7
|
+
## 1.5.0 — 2026-07-02
|
|
8
|
+
|
|
9
|
+
- **Protected branch decoupled from the integration trunk**: the "NEVER push directly"
|
|
10
|
+
MUST now resolves the new `{{ git.protected_branch }}` slot instead of
|
|
11
|
+
`{{ git.trunk_branch }}`, and explicitly permits direct pushes to other branches
|
|
12
|
+
(including the integration trunk when it differs) when the owner authorizes it.
|
|
13
|
+
`git.protected_branch` defaults to `git.trunk_branch`, so single-trunk repos are
|
|
14
|
+
byte-unchanged; a two-branch gitflow sets it (e.g. `protected_branch: main` while
|
|
15
|
+
`trunk_branch: develop` stays the worktree base + PR target). Fixes the case where a
|
|
16
|
+
consumer with `trunk_branch: develop` had its agents refuse a direct `develop` push
|
|
17
|
+
even though `develop` was the integration branch and `main` was the protected line.
|
|
18
|
+
Cross-tool: pure slot resolution in the shared skeleton — portable (Claude + Codex).
|
|
19
|
+
|
|
7
20
|
## 1.4.0 — 2026-07-02
|
|
8
21
|
|
|
9
22
|
- **Analysis-profile vocabulary on the understand-before-implement MUST**: the
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- baldart-primitive: name=AGENTS primitive_version=1.
|
|
1
|
+
<!-- baldart-primitive: name=AGENTS primitive_version=1.5.0
|
|
2
2
|
This is the SHIPPED SKELETON. The writer (src/utils/root-primitives.js) strips
|
|
3
3
|
this banner, resolves every {{ slot }} / {{> partial }} / {{#flag}} from
|
|
4
4
|
baldart.config.yml (+ package.json/README), merges .baldart/overlays/AGENTS.md,
|
|
@@ -55,7 +55,9 @@ mechanics — this file remains the single source of truth for the protocol.
|
|
|
55
55
|
- MUST use commit format `{{ git.commit_format }}`; small, traceable commits.
|
|
56
56
|
- MUST NOT commit with failing lint/type checks. Full build only before opening a PR.
|
|
57
57
|
- MUST pre-sync (`git fetch`, clean status, confirm branch, `--ff-only`) and NEVER push
|
|
58
|
-
directly to `{{ git.
|
|
58
|
+
directly to `{{ git.protected_branch }}` — the protected branch reaches its state only
|
|
59
|
+
via PR. Other branches (including the integration branch `{{ git.trunk_branch }}` when it
|
|
60
|
+
differs) MAY be pushed to directly when the owner authorizes it.
|
|
59
61
|
- MUST get owner approval before force-push/reset or deleting an UNMERGED branch (create a
|
|
60
62
|
`backup/<YYYYMMDD>-<reason>` tag first). Deleting an already-merged branch is routine.
|
|
61
63
|
- MUST NOT hardcode secrets / tokens / API keys (env vars only), expose internal stack
|
package/package.json
CHANGED
|
@@ -102,6 +102,38 @@ function detectTrunkBranch(cwd = process.cwd()) {
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Autodetect the protected branch (git.protected_branch) — the branch that MUST
|
|
107
|
+
* NOT receive direct pushes (production/release line). In a two-branch gitflow
|
|
108
|
+
* this differs from the integration `trunk_branch`: features integrate into the
|
|
109
|
+
* trunk (worktree base + PR target), while a separate protected branch is only
|
|
110
|
+
* reached via release/hotfix PRs.
|
|
111
|
+
* Heuristic: if trunk is NOT already `main`/`master` and such a branch exists,
|
|
112
|
+
* that's the protected line; otherwise the protected branch IS the trunk
|
|
113
|
+
* (single-trunk repo — the back-compat default, since protected_branch defaults
|
|
114
|
+
* to trunk_branch when absent).
|
|
115
|
+
*/
|
|
116
|
+
function detectProtectedBranch(cwd = process.cwd(), trunk = 'develop') {
|
|
117
|
+
try {
|
|
118
|
+
const { execSync } = require('child_process');
|
|
119
|
+
const opts = { cwd, stdio: ['ignore', 'pipe', 'ignore'], timeout: 5000, encoding: 'utf8' };
|
|
120
|
+
const branches = (() => {
|
|
121
|
+
try { return execSync('git branch --format=%(refname:short)', opts).split('\n').map((s) => s.trim()); }
|
|
122
|
+
catch (_) { return []; }
|
|
123
|
+
})();
|
|
124
|
+
if (trunk !== 'main' && trunk !== 'master') {
|
|
125
|
+
for (const cand of ['main', 'master']) {
|
|
126
|
+
if (branches.includes(cand)) {
|
|
127
|
+
return { value: cand, reason: `two-branch gitflow: '${cand}' is the protected line (trunk is '${trunk}').` };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return { value: trunk, reason: `single-trunk repo — protected branch is the trunk ('${trunk}').` };
|
|
132
|
+
} catch (_) {
|
|
133
|
+
return { value: trunk, reason: `probe failed — protected branch defaults to trunk ('${trunk}').` };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
105
137
|
/**
|
|
106
138
|
* Autodetect whether this project develops in parallel git worktrees
|
|
107
139
|
* (`git worktree list` shows linked worktrees beyond the main one). Used to
|
|
@@ -371,6 +403,7 @@ function detect(cwd = process.cwd()) {
|
|
|
371
403
|
: '';
|
|
372
404
|
|
|
373
405
|
const trunkBranchProbe = detectTrunkBranch(cwd);
|
|
406
|
+
const protectedBranchProbe = detectProtectedBranch(cwd, trunkBranchProbe.value);
|
|
374
407
|
const mergeStrategyProbe = detectMergeStrategy(cwd, trunkBranchProbe.value);
|
|
375
408
|
// Default schema_deploy_from_trunk_only ON when parallel worktrees are in use
|
|
376
409
|
// (the topology prone to migration-history desync). Otherwise keep the
|
|
@@ -532,6 +565,7 @@ function detect(cwd = process.cwd()) {
|
|
|
532
565
|
// Set to plain strings — the structured probe details (`protected`,
|
|
533
566
|
// `reason`) are kept on a sibling key so mergePreserving stays type-safe.
|
|
534
567
|
trunk_branch: trunkBranchProbe.value,
|
|
568
|
+
protected_branch: protectedBranchProbe.value,
|
|
535
569
|
merge_strategy: mergeStrategyProbe.value,
|
|
536
570
|
},
|
|
537
571
|
_probes: {
|
|
@@ -539,6 +573,7 @@ function detect(cwd = process.cwd()) {
|
|
|
539
573
|
// interactivePrompts to surface the autodetection reasoning.
|
|
540
574
|
merge_strategy: mergeStrategyProbe,
|
|
541
575
|
trunk_branch: trunkBranchProbe,
|
|
576
|
+
protected_branch: protectedBranchProbe,
|
|
542
577
|
},
|
|
543
578
|
};
|
|
544
579
|
|
package/src/commands/update.js
CHANGED
|
@@ -1551,6 +1551,12 @@ async function update(options = {}, unknownArgs = []) {
|
|
|
1551
1551
|
+ 'Until it is set, those skills autodetect the trunk (origin/HEAD → develop/main/master) '
|
|
1552
1552
|
+ 'and warn on each run; run `npx baldart configure` to persist it and make it deterministic.');
|
|
1553
1553
|
}
|
|
1554
|
+
if (allMissing.includes('git.protected_branch')) {
|
|
1555
|
+
UI.warning('`git.protected_branch` is the branch the "never push directly" rule protects '
|
|
1556
|
+
+ '(AGENTS.md). It DEFAULTS to `git.trunk_branch`, so single-trunk repos are unchanged; '
|
|
1557
|
+
+ 'set it (e.g. `main`) only for a two-branch gitflow where the protected line differs from '
|
|
1558
|
+
+ 'the integration trunk (e.g. trunk `develop`, protected `main`).');
|
|
1559
|
+
}
|
|
1554
1560
|
}
|
|
1555
1561
|
}
|
|
1556
1562
|
} catch (_) {
|
|
@@ -159,6 +159,11 @@ function resolveSlots(cwd, cfg) {
|
|
|
159
159
|
'stack.summary': stackSummary,
|
|
160
160
|
'stack.node_suffix': nodeSuffix,
|
|
161
161
|
'git.trunk_branch': git.trunk_branch || 'main',
|
|
162
|
+
// The branch that MUST NOT receive direct pushes (production/protected).
|
|
163
|
+
// Defaults to trunk_branch so single-trunk repos are unchanged; two-branch
|
|
164
|
+
// gitflows set it explicitly (e.g. protected_branch: main while
|
|
165
|
+
// trunk_branch: develop is the integration/PR target + worktree base).
|
|
166
|
+
'git.protected_branch': git.protected_branch || git.trunk_branch || 'main',
|
|
162
167
|
'git.feature_prefix': 'feat', // STATIC BALDART convention
|
|
163
168
|
'git.commit_format': '[FEAT-XXXX] description', // STATIC default (override via overlay)
|
|
164
169
|
'i18n.registry': paths.i18n_registry || 'the i18n registry',
|