baldart 4.45.0 → 4.47.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 +25 -0
- package/VERSION +1 -1
- package/framework/.claude/skills/new/SKILL.md +39 -70
- package/framework/.claude/skills/new/references/commit.md +1 -1
- package/framework/.claude/skills/new/references/final-review.md +1 -1
- package/framework/.claude/skills/new/references/implement.md +1 -1
- package/framework/.claude/skills/new/references/merge-cleanup.md +1 -1
- package/framework/.claude/skills/new/references/setup.md +5 -4
- package/framework/.claude/skills/new/references/team-mode.md +3 -5
- package/framework/.claude/skills/worktree-manager/SKILL.md +87 -17
- package/framework/.claude/workflows/new2.js +5 -1
- package/framework/docs/PROJECT-CONFIGURATION.md +20 -0
- package/framework/templates/baldart.config.template.yml +18 -0
- package/package.json +1 -1
- package/src/commands/configure.js +28 -0
- package/src/commands/update.js +9 -5
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,31 @@ 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.47.0] - 2026-06-16
|
|
9
|
+
|
|
10
|
+
**`/new`'s orchestrator context economy is re-aimed at its real driver — turn count — and the user-visible Progress Bar + native Task spine are removed.** Telemetry of two real 8-card batches (FEAT-0028/0029 on a consumer) showed the orchestrator paying ~285M `cache_read`: 613 turns each replaying a ~490k-token accumulated context (growing toward ~800k), so total cost ≈ **turn count × accumulated context**. A 3-lens **adversarial review before implementation** refuted the obvious diagnoses: the static prefix is only ~77k (not the ~225k first assumed — context is ~86% *accumulated*, not static); narration prose is only ~7% of the fuel; and the existing § "Context economy" (bulk-content-inline) rule targets a channel that totals only ~119k cumulatively. The measurement reviewer surfaced the actual missed lever — **0 of 274 tool turns batched any calls, and ~55% of turns carried no tool call at all** — and the correctness + prior-art reviewers established that delegating bookkeeping out of the orchestrator is a previously-trodden trap (v4.15.0 reverted a Write-from-memory tracker flush; the tracker is the recovery SSOT; `card_status: DONE` needs orchestrator-side disk re-read; the weak-subagent fabrication precedent applies). What survived: (1) a new turn-economy HARD RULE (batch independent tool calls; no narration-only turns; never poll/wait), and (2) since the Progress Bar + Task spine are pure *mirrors* of the internal tracker (recovery reads the tracker, never the spine), removing them is correctness-safe and eliminates ~45 dedicated visibility turns (~8% of a batch's `cache_read`, guaranteed, not batching-dependent). **MINOR** (skill behaviour change; **no new `baldart.config.yml` key** ⇒ schema-change propagation rule N/A; no removed agent/command/skill/routine).
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **`framework/.claude/skills/new/SKILL.md`** — replaced the `## Progress Visibility (MANDATORY)` section (native Task spine + transition Progress Bar + Phase→ledger mapping) with `## State surface — the tracker only`: the internal `/tmp/batch-tracker-*.md` is now the SINGLE state surface (it was already the recovery SSOT; the spine/bar were never read by recovery). Added a second HARD RULE to § "Context economy" — **"turn count is the multiplier"** — instructing the orchestrator to (1) batch independent tool calls into one message, (2) never emit a narration-only turn, (3) never poll/wait on background work. The § "Context Tracking" mirror bullet and the § "Routing" core-invariant list are reconciled.
|
|
15
|
+
- **`framework/.claude/skills/new/references/{setup,implement,commit,team-mode,final-review,merge-cleanup}.md`** — every `**→ Visibility**: TaskUpdate … / emit a Progress Bar` step is removed; the underlying *state* writes (card `IN_PROGRESS` claim at implement.md 2b, `card_status: DONE (verified)` at commit.md 29 / team D.6) are **preserved** (they are correctness, not visibility) and now explicitly note the tracker is the only surface. Pre-flight no longer creates a Task spine (setup.md step 2b).
|
|
16
|
+
|
|
17
|
+
## [4.46.0] - 2026-06-15
|
|
18
|
+
|
|
19
|
+
**Worktree env-file copy is unified onto one stack-agnostic config key, `stack.env_files` — closing the v4.42.0-deferred divergence (`/nw` copied `.env.local`+`.env`; new2's pre-flight copied `env/.env.local/.env.example/supabase/.temp`) WITHOUT the superset the adversarial pass had refuted.** A worktree is a fresh checkout, so the gitignored env artifacts a build needs must be copied from main — but the SET was hard-coded and divergent across paths. This release makes the set a single SSOT list (`stack.env_files`, default `['.env.local', '.env']`) read identically by `worktree-manager` (`/nw`), `/new`, and `new2`. A 3-skeptic **adversarial review before implementation** shaped the design: it (1) confirmed `framework/agents/runbook.md:36` (`cp .env.example .env`) is a documentation-template onboarding idiom — a DIFFERENT context — and **excluded** it; (2) refuted folding `supabase/.temp` into the copy set (it carries the remote project-ref → every copying worktree auto-links to the shared remote, the exact footgun `stack.schema_deploy_from_trunk_only` exists to prevent, and worse unattended in `/new` than in manual `/nw`; it is not even a build input); and (3) found the bash bug class fixed below. Crucially, the layer is **stack-agnostic**: the generic skill/installer/template never name Supabase or any stack — copying a tool's local-state directory is a per-project opt-in the user adds to their own `stack.env_files` / overlay, never a framework default. **MINOR** (additive: a new `baldart.config.yml` key, propagated end-to-end per the schema-change rule; no removed surface).
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- **`stack.env_files` config key** (`framework/templates/baldart.config.template.yml`) — list of gitignored env artifacts (files OR dirs) copied into each worktree, default `['.env.local', '.env']`. Files are copied with `cp` (dereferences symlinks — never `cp -P`, which would dangle a relative env symlink inside `.worktrees/`), directories with `cp -r` (mirrored: stale files removed on a `/nw` resume). A missing FILE is WARNed at copy time (never `exit 1` — that would strand `/new`'s programmatic path; the build gate is the real enforcement); nothing-copied escalates to a loud WARN. Replaces the old `cp … 2>/dev/null || true` that silently swallowed a missing critical env → cryptic later build failure.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- **`framework/.claude/skills/worktree-manager/SKILL.md` — `/nw` step 4 copy loop reads `stack.env_files`** (file/dir-aware, WARN-not-fatal). The dev-server PORT is now written to the first FILE actually copied (not `ENV_FILES[0]`, which could be a directory — `echo >> dir` errors), falling back to `.env.local`. The env-sync staleness check (`/mw` step 2) compares the NEWEST mtime among all `stack.env_files` (portable `stat -f %m || stat -c %Y`, recursive for dirs) instead of only `.env.local`; the `/lw` status line, the port-reuse rule, the docs-mode "forbidden" list, and the Project-Context header are reconciled to the list. The skill stays **stack-agnostic** — it iterates the list and never names a stack.
|
|
28
|
+
- **`framework/.claude/workflows/new2.js` + `framework/.claude/skills/new/references/setup.md`** — the pre-flight worktree briefing stops hard-coding `env/.env.local/.env.example/supabase/.temp` and copies `stack.env_files` instead (new2 interpolates the list resolved from `args.config`). `.env.example` is dropped (tracked → already in the checkout).
|
|
29
|
+
- **`src/commands/configure.js`** — autodetects `stack.env_files` from the universal gitignored env conventions (`.env.local`/`.env`) only (stack-agnostic — no per-database special-casing), a comma-separated interactive prompt, and a summary-box line.
|
|
30
|
+
- **`src/commands/update.js`** — the `missingStack` detector now also catches top-level ARRAY stack keys (`stack.env_files` is `typeof 'object'`, so it needs `Array.isArray`); sub-object keys (`charting`/`animation`/`testing`) stay excluded (`Array.isArray({}) === false`). Without this the new key would silently never be asked on update.
|
|
31
|
+
- **`framework/docs/PROJECT-CONFIGURATION.md`** — documents `stack.env_files` (§4.4) as a stack-agnostic list.
|
|
32
|
+
|
|
8
33
|
## [4.45.0] - 2026-06-15
|
|
9
34
|
|
|
10
35
|
**The main-repo-root (`$MAIN`) resolution across `worktree-manager` + `/prd` + `/new` is unified onto one correct, `separate-git-dir`-safe canonical — fixing two latent bugs while *refuting* the obvious "unify on `--git-common-dir`" fix the deferred plan proposed.** v4.42.0 deferred the `$MAIN` cleanup after an adversarial pass flagged it as a likely trap; this release does it properly. The root resolution lived in ~5 forms across three genuine execution contexts (main-checkout cwd, worktree cwd, the `allocate-id.sh` startup), and the deferred plan wanted to collapse them onto `git rev-parse --git-common-dir` + `/..` (the recipe already in `allocate-id.sh` `resolve_main()`). A 3-skeptic adversarial review **before implementation** demolished that plan with git experiments: (1) `--git-common-dir` + parent returns the *parent of the git dir*, which is the WRONG directory under `git init --separate-git-dir` (the git dir lives outside the working tree) — so the "canonical" recipe was itself fragile, and `resolve_main()` only worked because BALDART repos use in-tree `.git`; (2) the alleged `prd/SKILL.md` Step-1 bug ("`--show-toplevel` from inside a worktree") does **not** exist — Step 1 runs only at fresh kickoff on the main checkout, and resume reads the persisted `main_path`, so `--show-toplevel` is correct there *and* is the only form that survives `separate-git-dir`; (3) `git worktree list` head is also not `separate-git-dir`-safe (returns the git dir). The corrected design keeps the task's **context classification** but fixes the **primitive**: every site resolves the root through `--show-toplevel` (the true working-tree root in all cases), using `git -C .. rev-parse --show-toplevel` from a worktree (its parent `.worktrees/` lives inside the main repo). `resolve_main()` is rewritten as the canonical reference (detects a linked worktree via `git-dir != git-common-dir`, walks one level up) — **byte-identical output to the old form for in-tree repos** (verified), correct for `separate-git-dir`, and fails cleanly (`return 1`) instead of emitting a garbage path. Two real fragilities fixed along the way: the `/mw` `$MAIN` fallback (`--git-common-dir` + `/..` under `git -C`, relative-base hazard + `separate-git-dir` break) and the `3b` card-sync (`--show-superproject-working-tree || pwd`, which returned the SUPERPROJECT for a real submodule and only worked otherwise by a cwd accident). All recipes dogfooded across normal + `separate-git-dir` repos in every cwd context. **MINOR** (hardening + bug fixes across skills/agents; no removed surface, **no new `baldart.config.yml` key** ⇒ schema-change propagation rule N/A).
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.
|
|
1
|
+
4.47.0
|
|
@@ -129,80 +129,25 @@ Trunk branch: [resolved git.trunk_branch — Phase 0 step 0 populates]
|
|
|
129
129
|
- **card_status: DONE (verified)** — confirms the backlog YAML was updated and re-read to verify
|
|
130
130
|
- **When blocked**: log the blocker in `## Issues & Flags`.
|
|
131
131
|
- **On context recovery**: if you ever feel lost or after context compaction, IMMEDIATELY read your tracker file (`/tmp/batch-tracker-<FIRST-CARD-ID>.md`) to restore your state.
|
|
132
|
-
- **
|
|
132
|
+
- **The tracker is internal and is the ONLY state surface** (since v4.47.0 — see § "State surface — the tracker only"): the user does NOT see it, and there is no separate user-visible mirror to keep in sync. Do NOT spend a dedicated turn restating a transition to the user (no TaskUpdate, no Progress-Bar block) — surface progress only as a short prose line folded into a turn you are already taking for real work (§ "Context economy" turn-count rule).
|
|
133
133
|
|
|
134
134
|
---
|
|
135
135
|
|
|
136
|
-
##
|
|
136
|
+
## State surface — the tracker only
|
|
137
137
|
|
|
138
|
-
|
|
138
|
+
`/new` keeps a **single** state surface: the internal `/tmp/batch-tracker-<FIRST-CARD-ID>.md`
|
|
139
|
+
recovery SSOT (§ "Context Tracking"). There is **no separate user-visible mirror**. The Progress
|
|
140
|
+
Bar markdown block and the per-transition native Task spine (TaskCreate/TaskUpdate) were **removed
|
|
141
|
+
for context economy (v4.47.0)**: every standalone TaskUpdate / Progress-Bar emission was its own
|
|
142
|
+
orchestrator turn paying a full accumulated-context replay (~490k tokens median, measured ~8% of a
|
|
143
|
+
batch's cache_read) for zero correctness value — the tracker already holds the authoritative state,
|
|
144
|
+
and recovery (§ "Context recovery protocol") reads the tracker, never the Task spine.
|
|
139
145
|
|
|
140
|
-
> **HARD RULE —
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
**When to create (once):** right after Pre-flight has resolved the execution mode (sequential vs team) and, in team mode, the wave layout (`execution_strategy.groups[].level`) — because only then do you know the wave label for each card. Create, in this order:
|
|
147
|
-
|
|
148
|
-
1. `Pre-flight` — framing task (already in progress by the time you create the spine; mark it `in_progress` immediately and `completed` as soon as the worktree is ready).
|
|
149
|
-
2. One task **per card**, in queue order. Subject:
|
|
150
|
-
- **team mode** (wave-labelled): `Wave <level> · <CARD-ID> — <title>` (e.g. `Wave 1 · FEAT-0502 — Add merchant filter`).
|
|
151
|
-
- **sequential mode**: `<CARD-ID> — <title>` (no wave prefix).
|
|
152
|
-
3. `Final review` — framing task.
|
|
153
|
-
4. `Merge & cleanup` — framing task.
|
|
154
|
-
|
|
155
|
-
**State transitions (TaskUpdate):**
|
|
156
|
-
|
|
157
|
-
| Task | → `in_progress` | → `completed` |
|
|
158
|
-
|------|-----------------|---------------|
|
|
159
|
-
| `Pre-flight` | at spine creation | worktree ready (end of Pre-flight) |
|
|
160
|
-
| card task | **Phase 1 step 2b** (claim — when you set the card `status: IN_PROGRESS`) | **Phase 4 step 28** (after commit + `card_status: DONE (verified)`) |
|
|
161
|
-
| `Final review` | start of Final Review (F.1) | Final Review complete (fixes applied, build green) |
|
|
162
|
-
| `Merge & cleanup` | start of Phase 6 | end of Phase 6c |
|
|
163
|
-
|
|
164
|
-
**Live sub-state in the subject (optional but encouraged):** while a card task is `in_progress`, you MAY append the current phase to its subject so the panel shows live movement between Progress Bar emissions — e.g. `Wave 1 · FEAT-0502 — Add merchant filter · Phase 3.7 Codex 🔄`. Strip the suffix when the card completes.
|
|
165
|
-
|
|
166
|
-
In team mode the cards of a wave run in parallel: set ALL of a wave's card tasks `in_progress` when that wave's coders are spawned (Step B), and complete each as its per-card pipeline reaches Phase 4 (team Step D.5/D.6).
|
|
167
|
-
|
|
168
|
-
### B. Progress Bar — markdown, at transition boundaries
|
|
169
|
-
|
|
170
|
-
Append this block to your message **at the heavy transition boundaries only** (NOT on every message, and NOT on every intra-card phase change — that accumulates markdown across long autonomous runs; per § "Context economy"). A **heavy transition boundary** is any of: a **card change**, a **wave change**, **any gate decision** (resolved or skipped), and every `AskUserQuestion` / STOP. **Intra-card phase movement** (e.g. Phase 2 → 2.5 → 2.55 within the same card, with no gate decision) is shown via the **Task spine live sub-state** (TaskUpdate, § A above) — NOT by re-emitting this full markdown block each phase.
|
|
171
|
-
|
|
172
|
-
```
|
|
173
|
-
---
|
|
174
|
-
📋 **Progresso /new: <batch-id>** — modalità <sequential|team> · Wave <X>/<Y>
|
|
175
|
-
|
|
176
|
-
| Card | Wave | Fase corrente | Stato |
|
|
177
|
-
|------|------|---------------|-------|
|
|
178
|
-
| FEAT-0501 | 0 | Phase 4 Commit | ✅ |
|
|
179
|
-
| FEAT-0502 | 1 | Phase 3.7 Codex | 🔄 |
|
|
180
|
-
| FEAT-0503 | 1 | — | ⬜ |
|
|
181
|
-
|
|
182
|
-
Gate ledger (card corrente): 2.5b AC-Closure ✅ · 2.55 Simplify ✅ · 2.6 E2E ⏭️ (backend-only) · 3 Doc ⏭️ (light, no-doc-diff→Final) · 3.5 QA ⏭️ (balanced→Final) · 3.7 Codex 🔄
|
|
183
|
-
Prossimo passo: <cosa succede dopo>
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
- Legend: `⬜ da fare · 🔄 in corso · ✅ risolto · ⏭️ skippato`.
|
|
187
|
-
- The `Wave` column and the `Wave <X>/<Y>` header are present in **team mode** only; in sequential mode drop the column and write `modalità sequential` with no wave counter.
|
|
188
|
-
- The **Gate ledger** line shows the current card's per-card gates (see § C for which). Each entry is `<gate> <state>`; a skipped entry MUST carry its reason **verbatim from the enumerated Gate-table / fast-lane skip reasons** — e.g. `IS_TRIVIAL`, `review_profile=light`, `backend-only diff`, `features.has_e2e_review:false`, `balanced→Final`, `holistic_audit provenance`. **Never invent a reason.** A ledger entry reading `⏭️ (time budget)` / `(to save tokens)` / any model-invented constraint is itself a protocol violation (see the top-of-file "NO PHASE SKIP FOR PERCEIVED TIME" clause) — the ledger exists to make skips auditable, not to license them.
|
|
189
|
-
|
|
190
|
-
### C. Phase → ledger mapping
|
|
191
|
-
|
|
192
|
-
These are the per-card gates tracked in the Gate ledger line (the rest of the pipeline — Phase 1 context, Phase 2 implement, Phase 4 commit — is shown in the table's "Fase corrente" column, not the ledger):
|
|
193
|
-
|
|
194
|
-
| Ledger entry | Phase | Skipped when (enumerated reason) |
|
|
195
|
-
|--------------|-------|----------------------------------|
|
|
196
|
-
| `2.5b AC-Closure` | Phase 2.5b | never (BLOCKING, unconditional) |
|
|
197
|
-
| `2.55 Simplify` | Phase 2.55 | `IS_TRIVIAL` |
|
|
198
|
-
| `2.6 E2E` | Phase 2.6 | `features.has_e2e_review:false`, backend-only diff, or card type ∈ {backend/api/db/infra/docs/chore/config} |
|
|
199
|
-
| `3 Doc` | Phase 3 | `review_profile=light` AND no doc files in diff → deferred to Final F.3 |
|
|
200
|
-
| `3.5 QA` | Phase 3.5 | `skip`/`light` (tests already ran Phase 2), or `balanced`→Final (unless Step-A escalation) |
|
|
201
|
-
| `3.7 Codex` | Phase 3.7 | `IS_TRIVIAL` only (otherwise unconditional; `light`/`full` is depth, not skip) |
|
|
202
|
-
|
|
203
|
-
In **team mode** the same gates map to the per-card team sub-steps (D.3a AC-Closure, D.3b Simplify, D.3c E2E, D.4 QA, D.4a/D.2 doc, D.4b Codex) — track them identically. The pre-flight cross-card Codex check (Step 3d) and plan-auditor grounding (Phase 1 step 4) are batch/card-setup gates: surface their resolve/skip in the `Prossimo passo` line or a one-off ledger note, not as recurring per-card rows.
|
|
204
|
-
|
|
205
|
-
---
|
|
146
|
+
> **HARD RULE — no dedicated visibility turns.** Do NOT spend a turn (a TaskUpdate, a Progress-Bar
|
|
147
|
+
> markdown block, or a narration-only message) whose sole purpose is to restate progress. Surface
|
|
148
|
+
> progress to the user ONLY as a short natural-prose line **folded into a turn you are already
|
|
149
|
+
> taking** for real work — per § "Context economy" → turn-count rule. `STOP` / `AskUserQuestion`
|
|
150
|
+
> moments are surfaced as before (they carry a real decision, not a status restatement).
|
|
206
151
|
|
|
207
152
|
## Context economy (MANDATORY)
|
|
208
153
|
|
|
@@ -241,6 +186,30 @@ baselines. Keep that bulk on disk and pass **paths**, not bodies.
|
|
|
241
186
|
> nor what the orchestrator is allowed to read at a decision point — only that bulk arrives via a
|
|
242
187
|
> path the consumer opens itself, not via the orchestrator's own context.
|
|
243
188
|
|
|
189
|
+
> **HARD RULE — turn count is the multiplier (this is the dominant cost).** Measurement of real
|
|
190
|
+
> batches shows the bulk-content rule above is necessary but NOT where most tokens go: the
|
|
191
|
+
> orchestrator's accumulated context (~490k tokens median, growing toward ~800k on long batches) is
|
|
192
|
+
> **replayed in full on EVERY turn** via `cache_read`. So total cost ≈ **turn count × accumulated
|
|
193
|
+
> context** — and the cheapest turn (a lone `cd`, a single TaskUpdate, a one-line narration) pays the
|
|
194
|
+
> SAME ~490k replay as the most expensive one. A measured 8-card batch ran **613 orchestrator turns,
|
|
195
|
+
> of which 0 batched any tool calls and ~55% carried no tool call at all.** Cut turns:
|
|
196
|
+
> 1. **Batch independent tool calls into ONE message.** Whenever you need ≥2 tool calls with no data
|
|
197
|
+
> dependency between them — several `Read`s, a `cd` plus the command that follows it, `Edit`s to
|
|
198
|
+
> different files, a status `Edit` to the tracker alongside the next real action — emit them in a
|
|
199
|
+
> **single assistant message**, not one-per-turn. A run that issues 270 tool calls one-per-turn
|
|
200
|
+
> pays ~270 full-context replays; batched ~3-for-1 it pays ~90. Never split independent mechanical
|
|
201
|
+
> calls across turns "for clarity."
|
|
202
|
+
> 2. **No narration-only turns.** Never emit a turn whose only content is a progress recap, a
|
|
203
|
+
> "now I will…" preamble, or a status restatement. If a status note matters, fold it into the same
|
|
204
|
+
> message as the next tool call. (Per § "State surface" the tracker is the only state surface — and
|
|
205
|
+
> you write it via batched `Edit`s, never narrate it.)
|
|
206
|
+
> 3. **Never poll or wait.** Background subagents and background `Bash` re-invoke you automatically on
|
|
207
|
+
> completion — end your turn after spawning; never `sleep N; echo "waiting…"` (already stated for
|
|
208
|
+
> team mode in `references/team-mode.md` — it applies to every barrier in both modes).
|
|
209
|
+
>
|
|
210
|
+
> These rules reduce the *number* of replays; the bulk-content rule above reduces the *size* of each.
|
|
211
|
+
> Both compound — apply them together.
|
|
212
|
+
|
|
244
213
|
---
|
|
245
214
|
|
|
246
215
|
## Toolchain gates
|
|
@@ -268,7 +237,7 @@ Reference modules cite this section as `§ "Toolchain gates"`.
|
|
|
268
237
|
sistema, ri-letto a OGNI turno. Per non pagare 60k+ token di istruzioni di fase a
|
|
269
238
|
ogni turno, il dettaglio passo-passo di ogni fase vive in un **modulo `references/<x>.md`**
|
|
270
239
|
caricato on-demand. Questo file (il core) tiene solo gli invarianti cross-fase
|
|
271
|
-
(Context Tracking,
|
|
240
|
+
(Context Tracking, State surface, § "Context economy", § "Toolchain gates",
|
|
272
241
|
Agent Routing, QA Profile, Trivial fast-lane, Risk-signal detector, Fix Application
|
|
273
242
|
Log) + questa mappa di navigazione.
|
|
274
243
|
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
c. **Update `${paths.references_dir}/ssot-registry.md`** — add/update the entry for this card's feature area. The pre-commit doc-freshness hook BLOCKS commits that touch `${paths.backlog_dir}/` without a corresponding ssot-registry update. Always include ssot-registry.md in the same commit as the backlog YAML.
|
|
36
36
|
d. **Verify the write**: re-read the YAML file and confirm `status: DONE` is present. If not, retry the edit.
|
|
37
37
|
e. Stage BOTH the updated YAML AND ssot-registry.md, then commit (or as an immediate follow-up commit if the Phase 4 implementation commit already happened). When this produces a SECOND commit for the card, record BOTH hashes in the tracker (`commit: <impl-hash> + <done-hash>`) so traceability/bisect is unambiguous.
|
|
38
|
-
29. **Update tracker**: move card to `## Completed Cards` with commit hash(es), summary, flags, **and `card_status: DONE (verified)
|
|
38
|
+
29. **Update tracker**: move card to `## Completed Cards` with commit hash(es), summary, flags, **and `card_status: DONE (verified)`** — the tracker is the only state surface (§ "State surface — the tracker only"); do not also emit a TaskUpdate or Progress-Bar turn for the transition. If you surface the card completion to the user at all, do it as a short prose line folded into the commit turn, never a dedicated turn.
|
|
39
39
|
|
|
40
40
|
### Sub-agent failure protocol (since v3.28.3)
|
|
41
41
|
|
|
@@ -42,7 +42,7 @@ Once ALL cards are committed in the worktree:
|
|
|
42
42
|
|
|
43
43
|
### Step F.1 — Resolve scope
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
(All cards are committed; the batch now enters Final Review. No visibility emission — the internal tracker is the only state surface, see SKILL.md § "State surface — the tracker only". Record the Final-Review start/finish in the tracker only.)
|
|
46
46
|
|
|
47
47
|
1. **Read the tracker file** to get the full picture: card IDs, files changed, commit hashes.
|
|
48
48
|
2. Gather git evidence in the worktree:
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
- Read that card's backlog YAML and check its `status` field.
|
|
9
9
|
- If NOT `DONE` → HALT: log in `## Issues & Flags` and ask the user: "Card <CARD-ID> depends on <DEP-ID> which is `<status>`. Proceed anyway, or wait?" Do not start implementation until the user responds explicitly. **The card status is NOT written until this gate passes** — so a HALT leaves the card untouched (no stale `IN_PROGRESS` for the next run to mis-read).
|
|
10
10
|
- If `DONE` (or the user chose "proceed anyway") → continue.
|
|
11
|
-
2b. **Claim** — only now set the card status to `IN_PROGRESS` and assign yourself.
|
|
11
|
+
2b. **Claim** — only now set the card status to `IN_PROGRESS` (in the backlog YAML / tracker) and assign yourself. This is a state write, not a visibility emission — do not also spend a TaskUpdate / Progress-Bar turn (§ "State surface — the tracker only").
|
|
12
12
|
2c. **Trivial-card classification (BLOCKING gate for steps 3–4)** — evaluate `IS_TRIVIAL(card)` per § "Trivial-card fast-lane". Note: condition 3 (non-source diff) cannot be fully evaluated until the coder has produced the diff, so at this point compute the **provisional** trivial flag from conditions 1+2 only (`review_profile == skip` AND no Step-A trigger sourced from the card YAML text + `files_likely_touched` extensions — if EVERY path in `files_likely_touched` is non-source, condition 3 is provisionally satisfied). If provisionally trivial → **SKIP steps 3, 3a, and 4** (architecture grounding); log `trivial: architecture grounding skipped (review_profile=skip + non-source files_likely_touched + 0 triggers)` and jump to Phase 2. Re-confirm `IS_TRIVIAL` on the ACTUAL committed diff at the review gates (Phase 2.55/3.5/3.7); if the coder unexpectedly touched a source file, the guard flips the card back onto the normal review path there. If NOT provisionally trivial → run steps 3, 3a, 4 as normal.
|
|
13
13
|
3. **(skip when provisionally trivial — see 2c)** Invoke the **codebase-architect** agent (MUST per AGENTS.md) to understand the relevant codebase area, existing patterns, and architecture before any implementation. When `features.has_lsp_layer: true`, the architect uses LSP find-references for identifier-shaped lookups — this needs NO handoff from the orchestrator: the architect reads `features.has_lsp_layer` from `baldart.config.yml` directly (the flag is ambient) per `agents/code-search-protocol.md`. Likewise, when `features.has_code_graph: true`, the architect uses the Graphify code graph for structural/relational lookups (ambient flag) per `agents/code-graph-protocol.md`. The orchestrator does NOT propagate either flag. (Earlier doc versions numbered this step 4; the step that read project-status BEFORE the architect was removed because it persisted pre-analysis context — see step 3a.)
|
|
14
14
|
3a. Update `${paths.references_dir}/project-status.md` Active Code Context (skip when the file does not exist in the project) — do this AFTER the codebase-architect run (step 3) so the "Active Code Context" reflects the architect's findings (which files are actually in scope), not just the card YAML's `files_likely_touched`. Writing it before the architect run would persist pre-analysis claims that downstream agents (e.g. a parallel card) would then read as truth.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
## Phase 6 — Post-batch merge & cleanup (delegated to worktree-manager skill)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
(The batch now enters Merge & cleanup. No visibility emission — the internal tracker is the only state surface, see SKILL.md § "State surface — the tracker only". Record merge/cleanup completion in the tracker only.)
|
|
8
8
|
|
|
9
9
|
After the final review passes AND all cards are committed in the worktree, delegate the entire merge and cleanup to the **worktree-manager** skill (`/mw` in programmatic mode):
|
|
10
10
|
|
|
@@ -182,7 +182,7 @@
|
|
|
182
182
|
|
|
183
183
|
When `mode == sequential`, the per-card pipeline below runs exactly as documented. The `execution_strategy.groups` levels are simply ignored. When `mode == team`, skip the per-card pipeline and follow the **Team Mode** section at the end of this document.
|
|
184
184
|
|
|
185
|
-
|
|
185
|
+
(No user-visible Task spine / Progress Bar is created — the internal tracker is the only state surface; see SKILL.md § "State surface — the tracker only".)
|
|
186
186
|
|
|
187
187
|
3d. **Codex batch cross-card grounding check** (background — launched together with the worktree-setup subagent in step 4, then a single barrier in step 5)
|
|
188
188
|
|
|
@@ -287,7 +287,9 @@
|
|
|
287
287
|
{ cards: [<all card IDs>], groupParent: <PARENT-ID|null>, slug: "<slug>" }
|
|
288
288
|
Let it: group cards, derive the branch from git_strategy.branch (fallback
|
|
289
289
|
feat/<PARENT-ID>-<slug>), create the worktree in .worktrees/, install deps,
|
|
290
|
-
copy env
|
|
290
|
+
copy the gitignored env artifacts in stack.env_files (files via cp, dirs via
|
|
291
|
+
cp -r; missing file → WARN, never abort), assign a free port, update
|
|
292
|
+
.worktrees/registry.json (all card
|
|
291
293
|
IDs in the `cards` field), and run the baseline (tsc + lint + build)
|
|
292
294
|
UNDER A HARD TIMEOUT — wrap the build step in `timeout 600 <build-cmd>`
|
|
293
295
|
(10 min) so a hung or interactive build cannot stall the pre-flight barrier.
|
|
@@ -322,7 +324,6 @@
|
|
|
322
324
|
- `## Worktree` — path / branch / slug / port, plus `Created:` = **the subagent block's `created_at`** (worktree-creation time, NOT resume time, so Phase 8's `cycle_time_mins` still spans the build window). On the **4a2 resume** path, `Created:` = the registry entry's `createdAt`; on the **4d inline fallback**, stamp `date -u +%Y-%m-%dT%H:%M:%SZ` at creation. (Never leave `Created:` empty — `cycle_time_mins` anchors on it.)
|
|
323
325
|
- `## Cross-Card Conflicts (Codex)` — distilled findings (the 3d skip-decision already wrote the `SKIPPED`/`RUN — reason` line; append the distilled findings on the RUN path, nothing to add on SKIP).
|
|
324
326
|
- In team mode: `## Team Mode` + `## Parallel Groups` (per team-mode.md).
|
|
325
|
-
`## Execution Mode` was already written at step 3c
|
|
326
|
-
d. **→ Visibility**: mark the `Pre-flight` Task spine entry → `completed` and emit the first wave's Progress Bar.
|
|
327
|
+
`## Execution Mode` was already written at step 3c — do NOT rewrite it here. **Rationale**: pre-flight is idempotent and cheap to redo (step 4a2's git pre-check guards worktree re-creation), so the data sections do not need mid-flight persistence; per-phase incremental writes resume for card execution, where mid-phase recovery actually matters. The file already exists (the skeleton was created at batch start per § Context Tracking; Phase 0 wrote `## Phase 0`) — backfill, do NOT re-create.
|
|
327
328
|
|
|
328
329
|
---
|
|
@@ -57,7 +57,7 @@ its `arch_baseline_path` at this file, so the review reuses the group baseline i
|
|
|
57
57
|
|
|
58
58
|
#### Step B: Spawn parallel coder agents
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
(No visibility emission on wave change — the internal tracker is the only state surface; see SKILL.md § "State surface — the tracker only". Record the wave start in the tracker only.)
|
|
61
61
|
|
|
62
62
|
For each card in the current group, spawn a coder agent using the Agent tool. ALL agents for the group MUST be spawned in a **SINGLE message** (multiple Agent tool calls) to run truly in parallel.
|
|
63
63
|
|
|
@@ -278,8 +278,7 @@ After ALL agents in the group complete successfully:
|
|
|
278
278
|
a. Edit the backlog YAML (`${paths.backlog_dir}/<CARD-ID>.yml`): set `status: DONE`, add `completed_date: <today>`, add implementation notes (NEVER include `[USER-APPROVED DEFERRAL]` lines that didn't actually pass through D.3a's gate).
|
|
279
279
|
b. **Verify the write**: re-read the YAML file and confirm `status: DONE` is present. If not, retry.
|
|
280
280
|
c. Stage the updated YAML and include it in the card's commit (or as an immediate follow-up commit).
|
|
281
|
-
d. Log in tracker: `card_status: DONE (verified)` for each card.
|
|
282
|
-
e. **→ Visibility**: TaskUpdate each card's spine task → `completed` (strip any live phase suffix) as it reaches DONE here.
|
|
281
|
+
d. Log in tracker: `card_status: DONE (verified)` for each card — the tracker is the only state surface (§ "State surface — the tracker only"); do not emit a TaskUpdate turn per card.
|
|
283
282
|
Note: Phase 6b (Status Reconciliation) will catch any card missed here, but aim for zero misses.
|
|
284
283
|
|
|
285
284
|
#### Step D coverage assertion (MANDATORY end-of-group check)
|
|
@@ -302,8 +301,7 @@ A missing entry means a sub-step was skipped. An entry whose value is a **`revie
|
|
|
302
301
|
After committing all cards in the group:
|
|
303
302
|
1. Update tracker: move group to done, log all results per card.
|
|
304
303
|
2. **PURGE**: forget all implementation details, review findings, architect context.
|
|
305
|
-
3.
|
|
306
|
-
4. Move to the next pending group (Step A again).
|
|
304
|
+
3. Move to the next pending group (Step A again).
|
|
307
305
|
|
|
308
306
|
### Sequential fallback within team mode
|
|
309
307
|
|
|
@@ -20,6 +20,7 @@ description: >
|
|
|
20
20
|
- `git.merge_strategy` — `pr` (default, GitHub PR via `gh`) or `local-push` (direct fast-forward to `origin/<git.trunk_branch>`). Drives `/mw` step 4c.
|
|
21
21
|
- `paths.backlog_dir` — used for syncing untracked backlog cards (`/nw` step 3b).
|
|
22
22
|
- `paths.metrics` — JSONL telemetry dir (default `docs/metrics`) used by the rebase conflict-resolution table.
|
|
23
|
+
- `stack.env_files` — list of gitignored env artifacts (files OR dirs) copied into each worktree (`/nw` step 4). Default `['.env.local', '.env']`. The SAME list drives /nw, /new, and new2 — no per-path divergence. NEVER list a tracked file (already in the checkout). The skill is stack-agnostic — it just iterates the list; any project-specific entry (e.g. a tool's local-state dir) is the project's own opt-in in its config/overlay.
|
|
23
24
|
- `features.has_toolchain` + `toolchain.commands.{typecheck,lint,build,test}` — when the flag is `true`, the mechanical gates below run the consumer's configured commands verbatim instead of the hardcoded `npx tsc`/`npx eslint`/`npm run build` defaults (see "## Toolchain-aware gates"). Absent/`false` → defaults, identical to pre-toolchain behavior.
|
|
24
25
|
- Protocol reference: `framework/agents/project-context.md`. Skills must ASK when a needed key is missing — never assume.
|
|
25
26
|
|
|
@@ -108,7 +109,7 @@ backlog cards, design references — and never touch source code.
|
|
|
108
109
|
What docs mode SKIPS vs the standard flow:
|
|
109
110
|
- No `npm install` (no `node_modules/` inside the worktree)
|
|
110
111
|
- No port allocation (no dev server)
|
|
111
|
-
- No
|
|
112
|
+
- No env-artifact (`stack.env_files`) copy
|
|
112
113
|
- No `npm run build` baseline verification
|
|
113
114
|
- No lint / tsc baseline checks
|
|
114
115
|
- No `--dev` server bootstrap
|
|
@@ -240,7 +241,7 @@ git -C "$MAIN_ROOT" worktree add "$WORKTREE_REL" -b "$BRANCH" "origin/$TRUNK"
|
|
|
240
241
|
}
|
|
241
242
|
```
|
|
242
243
|
|
|
243
|
-
**Forbidden in docs mode**: `npm install`, port scan, `.env
|
|
244
|
+
**Forbidden in docs mode**: `npm install`, port scan, `.env*`/`stack.env_files` copy, `npm run build`,
|
|
244
245
|
`npx tsc`, `npx eslint`, dev server start, `git checkout`/`switch`/`branch` on main.
|
|
245
246
|
|
|
246
247
|
### mw-docs programmatic
|
|
@@ -633,8 +634,19 @@ npm install
|
|
|
633
634
|
# 2. Own .next cache — already isolated since each worktree has its own
|
|
634
635
|
# directory tree. The default .next inside the worktree is sufficient.
|
|
635
636
|
|
|
636
|
-
# 3. Copy environment
|
|
637
|
-
#
|
|
637
|
+
# 3. Copy environment artifacts from main repo root.
|
|
638
|
+
# The build typically requires gitignored env secrets, so this copy is
|
|
639
|
+
# critical — but the SET is project-specific and lives in `stack.env_files`
|
|
640
|
+
# (baldart.config.yml). Resolve that list and emit it LITERALLY below as a bash
|
|
641
|
+
# array (default ('.env.local' '.env') when the key is absent). List ONLY
|
|
642
|
+
# gitignored artifacts: a tracked file (e.g. an example env committed to git)
|
|
643
|
+
# is already in the worktree checkout — copying it is redundant. Entries may be
|
|
644
|
+
# FILES (cp) or DIRECTORIES (cp -r). The SAME list drives /nw, /new, and new2 —
|
|
645
|
+
# no per-path divergence. (A directory entry that carries remote-link/credential
|
|
646
|
+
# state makes the worktree act on the same remote as main; that is a per-project
|
|
647
|
+
# opt-in the user adds to stack.env_files / their overlay — the skill itself is
|
|
648
|
+
# stack-agnostic and just iterates the list.)
|
|
649
|
+
#
|
|
638
650
|
# cwd is the worktree; its parent (`.worktrees/`) lives inside the main repo,
|
|
639
651
|
# so `git -C ..` resolves the MAIN repo's toplevel — correct under a
|
|
640
652
|
# `--separate-git-dir` repo too (unlike `--git-common-dir`+parent). The old
|
|
@@ -642,8 +654,42 @@ npm install
|
|
|
642
654
|
# guess; fail loud instead. See § "Resolving the main repo root".
|
|
643
655
|
MAIN_ROOT="$(git -C .. rev-parse --show-toplevel 2>/dev/null)"
|
|
644
656
|
[ -n "$MAIN_ROOT" ] || { echo "ERROR: cannot resolve main repo root from worktree $(pwd)" >&2; exit 1; }
|
|
645
|
-
|
|
646
|
-
|
|
657
|
+
|
|
658
|
+
# <resolve stack.env_files and emit each entry quoted; default if the key is absent>
|
|
659
|
+
ENV_FILES=( ".env.local" ".env" )
|
|
660
|
+
copied_any=0
|
|
661
|
+
primary_env="" # first FILE actually copied — PORT target (step 4)
|
|
662
|
+
for ef in "${ENV_FILES[@]}"; do
|
|
663
|
+
src="$MAIN_ROOT/$ef"
|
|
664
|
+
dest_dir="$(dirname "$ef")"
|
|
665
|
+
if [ -d "$src" ]; then
|
|
666
|
+
# Directory artifact. cp -r MERGES into an existing
|
|
667
|
+
# dest, so mirror it: remove first to avoid stale files lingering on a /nw
|
|
668
|
+
# resume. Judge success by dest existence, NOT cp's exit code (BSD `cp -r`
|
|
669
|
+
# returns non-zero on a broken inner symlink while still copying the rest).
|
|
670
|
+
mkdir -p "$dest_dir"
|
|
671
|
+
rm -rf "$ef"
|
|
672
|
+
cp -R "$src" "$dest_dir"/ 2>/dev/null || true
|
|
673
|
+
[ -e "$ef" ] && copied_any=1 || echo "WARN: env dir '$ef' (stack.env_files) present in main but copy failed — worktree may be incomplete." >&2
|
|
674
|
+
elif [ -f "$src" ]; then
|
|
675
|
+
# File artifact. PLAIN cp (NEVER cp -P): it dereferences a symlinked source,
|
|
676
|
+
# so an `.env.local -> ../shared/.env` symlink yields real content here — a
|
|
677
|
+
# preserved relative symlink would dangle inside `.worktrees/feat-X/`.
|
|
678
|
+
mkdir -p "$dest_dir"
|
|
679
|
+
if cp "$src" "$ef"; then copied_any=1; [ -z "$primary_env" ] && primary_env="$ef"; fi
|
|
680
|
+
else
|
|
681
|
+
# Absent in main. A missing FILE is the critical case (build likely needs it):
|
|
682
|
+
# WARN loudly so a later cryptic build failure is diagnosable — but NEVER exit 1
|
|
683
|
+
# (this runs on /new's programmatic path; aborting would strand the batch — the
|
|
684
|
+
# build gate in step 5 is the real enforcement). A missing DIR is best-effort
|
|
685
|
+
# (the worktree just isn't linked, which is safe) — we cannot stat a nonexistent
|
|
686
|
+
# path to know it was a dir, so a single neutral WARN per entry covers both.
|
|
687
|
+
echo "WARN: env artifact '$ef' (stack.env_files) not found in $MAIN_ROOT — the worktree build may fail if it is required." >&2
|
|
688
|
+
fi
|
|
689
|
+
done
|
|
690
|
+
if [ "$copied_any" = 0 ] && [ "${#ENV_FILES[@]}" -gt 0 ]; then
|
|
691
|
+
echo "WARN: NO env artifacts copied into the worktree (none of: ${ENV_FILES[*]} exist in $MAIN_ROOT). If the build needs env vars it WILL fail — add the file(s) to the main repo or fix stack.env_files." >&2
|
|
692
|
+
fi
|
|
647
693
|
|
|
648
694
|
# 4. Pick a free dev server port (avoid 3000 used by main)
|
|
649
695
|
PORT=3001
|
|
@@ -651,11 +697,15 @@ while lsof -i :$PORT -sTCP:LISTEN >/dev/null 2>&1; do
|
|
|
651
697
|
PORT=$((PORT + 1))
|
|
652
698
|
if [ $PORT -gt 3099 ]; then echo "ERROR: No free port in 3001-3099" >&2; exit 1; fi
|
|
653
699
|
done
|
|
654
|
-
# Write PORT to
|
|
655
|
-
|
|
656
|
-
|
|
700
|
+
# Write PORT to the project's PRIMARY env file — the first FILE that was actually
|
|
701
|
+
# copied (NOT ENV_FILES[0], which could be a directory entry, where
|
|
702
|
+
# `echo >> dir` / `grep dir` error out). Fall back to .env.local when nothing was
|
|
703
|
+
# copied so a port is still persisted somewhere the dev server can read.
|
|
704
|
+
PORT_ENV_FILE="${primary_env:-.env.local}"
|
|
705
|
+
if grep -q "^PORT=" "$PORT_ENV_FILE" 2>/dev/null; then
|
|
706
|
+
sed -i '' "s/^PORT=.*/PORT=$PORT/" "$PORT_ENV_FILE"
|
|
657
707
|
else
|
|
658
|
-
echo "PORT=$PORT" >>
|
|
708
|
+
echo "PORT=$PORT" >> "$PORT_ENV_FILE"
|
|
659
709
|
fi
|
|
660
710
|
|
|
661
711
|
# 5. ASSERT git hooks are ACTIVE — not just readable.
|
|
@@ -776,12 +826,32 @@ fi
|
|
|
776
826
|
|
|
777
827
|
### 2. Env sync check
|
|
778
828
|
|
|
779
|
-
Compare
|
|
780
|
-
|
|
829
|
+
Compare the NEWEST modification time among the `stack.env_files` artifacts in the
|
|
830
|
+
main repo against the registry `envSyncedAt`. If any is newer, WARN. Use a
|
|
831
|
+
portable mtime read (`stat -f %m` is BSD/macOS, `stat -c %Y` is GNU/Linux — the
|
|
832
|
+
snippet runs on both) and scan directory entries recursively (a directory's own
|
|
833
|
+
mtime does NOT bump when a nested file is edited in place):
|
|
781
834
|
|
|
782
|
-
```
|
|
783
|
-
|
|
784
|
-
|
|
835
|
+
```bash
|
|
836
|
+
# Resolve stack.env_files the same way step 4 does; emit it literally here.
|
|
837
|
+
ENV_FILES=( ".env.local" ".env" )
|
|
838
|
+
mtime() { stat -f %m "$1" 2>/dev/null || stat -c %Y "$1" 2>/dev/null; }
|
|
839
|
+
newest=0
|
|
840
|
+
for ef in "${ENV_FILES[@]}"; do
|
|
841
|
+
src="$MAIN/$ef"
|
|
842
|
+
if [ -d "$src" ]; then
|
|
843
|
+
# newest mtime of any file inside the dir (recursive)
|
|
844
|
+
while IFS= read -r f; do m=$(mtime "$f"); [ "${m:-0}" -gt "$newest" ] && newest=$m; done \
|
|
845
|
+
< <(find "$src" -type f 2>/dev/null)
|
|
846
|
+
elif [ -f "$src" ]; then
|
|
847
|
+
m=$(mtime "$src"); [ "${m:-0}" -gt "$newest" ] && newest=$m
|
|
848
|
+
fi
|
|
849
|
+
done
|
|
850
|
+
SYNCED=$(mtime "$WORKTREE_PATH/.env-synced-marker" 2>/dev/null) # or parse registry envSyncedAt → epoch
|
|
851
|
+
if [ "${newest:-0}" -gt "${SYNCED:-0}" ]; then
|
|
852
|
+
echo "Warning: a main-repo env artifact (stack.env_files) changed since this worktree was created."
|
|
853
|
+
echo "Consider re-copying the env files into the worktree before merging."
|
|
854
|
+
fi
|
|
785
855
|
```
|
|
786
856
|
|
|
787
857
|
### 3. Pre-merge safety commit + checks inside the worktree
|
|
@@ -1314,7 +1384,7 @@ Active worktrees:
|
|
|
1314
1384
|
Age: 2 days
|
|
1315
1385
|
Status: 3 uncommitted changes
|
|
1316
1386
|
Build: verified ✓
|
|
1317
|
-
Env: in sync | STALE (main
|
|
1387
|
+
Env: in sync | STALE (a main env artifact changed)
|
|
1318
1388
|
|
|
1319
1389
|
2. BUG-0500 fix-auth
|
|
1320
1390
|
Branch: feat/BUG-0500-fix-auth
|
|
@@ -1418,5 +1488,5 @@ Cleanup complete:
|
|
|
1418
1488
|
- If merge conflicts during /mw, STOP and ask the user. Do not auto-resolve.
|
|
1419
1489
|
- Always verify `.worktrees/` is in `.gitignore` before creating it (the nw-docs pre-flight in step 0 enforces this; add it before running if absent).
|
|
1420
1490
|
- Commit lock protocol: Each worktree has its own `COMMIT_LOCK` in its git dir — no cross-worktree interference.
|
|
1421
|
-
- Port persistence: On dev server restart, reuse the port from registry (grep .env.local for PORT
|
|
1491
|
+
- Port persistence: On dev server restart, reuse the port from registry (or grep the worktree's primary env file — the first FILE entry of `stack.env_files` that was copied, default `.env.local` — for `PORT=`) instead of picking a new one.
|
|
1422
1492
|
- **NEVER use `git stash` in worktrees.** Stashes are globally shared across all worktrees (`refs/stash` via `git.commondir`). A stash created in one worktree or in the main repo can be popped in another, causing conflicts, data loss, and cascading merge failures (see FEAT-0522 incident). Use explicit file staging only — the file ownership map ensures no overlap between cards. The stash pattern in AGENTS.md/CLAUDE.md applies ONLY to the main repo working tree.
|
|
@@ -62,6 +62,10 @@ const paths = cfg.paths || {}
|
|
|
62
62
|
const gitCfg = cfg.git || {}
|
|
63
63
|
const stack = cfg.stack || {}
|
|
64
64
|
const highRisk = paths.high_risk_modules || []
|
|
65
|
+
// Gitignored env artifacts copied into the worktree (v4.46.0). SAME contract as
|
|
66
|
+
// /nw + /new (worktree-manager step 4): the SSOT is stack.env_files; never list a
|
|
67
|
+
// tracked file (.env.example is already in the checkout). Default minimal set.
|
|
68
|
+
const ENV_FILES = (Array.isArray(stack.env_files) && stack.env_files.length) ? stack.env_files : ['.env.local', '.env']
|
|
65
69
|
const mergeStrategy = gitCfg.merge_strategy || 'pr'
|
|
66
70
|
// Stack-agnostic schema-deploy safety invariant (v4.33.0). When true, a remote
|
|
67
71
|
// schema/RLS/index/migration deploy is auto-executed ONLY from the trunk branch;
|
|
@@ -260,7 +264,7 @@ try {
|
|
|
260
264
|
`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` +
|
|
261
265
|
`DETERMINISTIC GATE POLICIES (NO user prompts):\n` +
|
|
262
266
|
`• 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` +
|
|
263
|
-
`• Worktree (setup.md step 4): create ONE code worktree off ${TRUNK}; install deps; assign a port; run the baseline (tsc+lint+build). Copy
|
|
267
|
+
`• Worktree (setup.md step 4): create ONE code worktree off ${TRUNK}; install deps; assign a port; run the baseline (tsc+lint+build). Copy the gitignored env artifacts in stack.env_files (resolved: ${JSON.stringify(ENV_FILES)}) — FILES via \`cp\` (plain cp, dereferences symlinks; never cp -P), DIRS via \`cp -r\`; a missing FILE → WARN (never abort). Do NOT add .env.example (tracked → already in the checkout) and 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). Wrap the build in \`timeout 600 <build-cmd>\` (10 min); if killed, return baseline:'timeout' + the partial log. On baseline PASS, VERIFY the worktree on disk and return EVIDENCE (not just a flag): set worktreeVerified:true ONLY after running \`git -C ${MAIN} worktree list --porcelain\` (the worktree path MUST appear in the output) AND \`test -d <wt>/node_modules\` AND confirming the branch; put the LITERAL stdout into worktreeEvidence{ worktreeListPorcelain, artifactsLs:\`ls -la <wt>/node_modules <wt>/.next 2>/dev/null | head\`, baselineLogTail }. The workflow string-matches this evidence — NEVER report worktreeVerified:true without actually running the commands.\n` +
|
|
264
268
|
codexResolveBullet +
|
|
265
269
|
g3Bullet +
|
|
266
270
|
`• 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` +
|
|
@@ -162,6 +162,12 @@ stack:
|
|
|
162
162
|
|
|
163
163
|
# Schema-deploy safety invariant (since v4.33.0). Default false (no-op).
|
|
164
164
|
schema_deploy_from_trunk_only: false # true → /new + new2 auto-deploy schema only from git.trunk_branch
|
|
165
|
+
|
|
166
|
+
# Gitignored env artifacts copied into each git worktree (v4.46.0).
|
|
167
|
+
env_files: # default ['.env.local', '.env']
|
|
168
|
+
- .env.local
|
|
169
|
+
- .env
|
|
170
|
+
# - some/state-dir # a DIR entry is copied with cp -r (project-specific opt-in)
|
|
165
171
|
```
|
|
166
172
|
|
|
167
173
|
Skills propose only `canonical` libraries and refuse `forbidden` ones. Empty
|
|
@@ -196,6 +202,20 @@ the skill/agent set:
|
|
|
196
202
|
multiple cards in parallel worktrees against one shared remote datastore. The actual
|
|
197
203
|
command + env loader stay in the project overlay — core only enforces the branch gate.
|
|
198
204
|
`baldart configure` defaults it to `true` when it detects parallel worktree usage.
|
|
205
|
+
- `stack.env_files` (list, v4.46.0+, default `['.env.local', '.env']`) is the
|
|
206
|
+
SSOT for **gitignored env artifacts copied into each git worktree** by
|
|
207
|
+
`worktree-manager` (`/nw`), reused identically by `/new` and `new2` — no
|
|
208
|
+
per-path divergence. A worktree is a fresh checkout, so any gitignored env the
|
|
209
|
+
build needs must be copied from main. **List only gitignored artifacts** — a
|
|
210
|
+
tracked example env is already present in every checkout. Entries may be files
|
|
211
|
+
(copied with `cp`, which dereferences symlinks) or directories (copied with
|
|
212
|
+
`cp -r`). The mechanism is **stack-agnostic**: add whatever your project needs.
|
|
213
|
+
A directory that carries remote-link/credential state makes the worktree act on
|
|
214
|
+
the same remote as main — add it intentionally (and consider pairing with
|
|
215
|
+
`schema_deploy_from_trunk_only: true`). Such project-specific opinions live in
|
|
216
|
+
the project's own config value / overlay, not in the generic framework. A
|
|
217
|
+
missing FILE is WARNed at copy time (never fatal); a build that truly needs it
|
|
218
|
+
fails clearly.
|
|
199
219
|
- `security-reviewer` evaluates access rules per `stack.database`
|
|
200
220
|
(Firestore rules, Supabase RLS, Mongo validators, DynamoDB IAM,
|
|
201
221
|
Postgres RLS+GRANT).
|
|
@@ -122,6 +122,24 @@ stack:
|
|
|
122
122
|
# "render" | "fly" | "self-hosted" | "none" | "".
|
|
123
123
|
deployment: ""
|
|
124
124
|
|
|
125
|
+
# Gitignored environment artifacts copied into each git worktree (since
|
|
126
|
+
# v4.46.0). A worktree is a fresh checkout, so any GITIGNORED env file/dir the
|
|
127
|
+
# build/run needs (it is NOT in the checkout) must be copied from the main repo.
|
|
128
|
+
# worktree-manager (`/nw`) reads this list; `/new` + new2 reuse the SAME list
|
|
129
|
+
# (no per-path divergence). Rules:
|
|
130
|
+
# - List ONLY gitignored artifacts. A tracked file (e.g. an example env
|
|
131
|
+
# committed to git) is already in every checkout — listing it is redundant.
|
|
132
|
+
# - Entries may be FILES (copied with `cp`) or DIRECTORIES (copied with
|
|
133
|
+
# `cp -r`). Add any project-specific entry your stack needs (e.g. a CLI's
|
|
134
|
+
# gitignored local-state dir). NOTE: a directory carrying remote-link or
|
|
135
|
+
# credential state makes the worktree act on the same remote as main — add
|
|
136
|
+
# such a dir intentionally (and consider schema_deploy_from_trunk_only).
|
|
137
|
+
# - A missing FILE here is WARNed at copy time (never fatal); a build that
|
|
138
|
+
# truly needs it fails clearly. Default: the minimal secret set.
|
|
139
|
+
env_files:
|
|
140
|
+
- .env.local
|
|
141
|
+
- .env
|
|
142
|
+
|
|
125
143
|
# Schema-deploy-from-trunk-only safety invariant (since v4.33.0). Stack-agnostic
|
|
126
144
|
# guard against migration-history desync / "code-ahead-of-schema" production
|
|
127
145
|
# outages on projects that develop multiple cards in PARALLEL git worktrees
|
package/package.json
CHANGED
|
@@ -384,6 +384,17 @@ function detect(cwd = process.cwd()) {
|
|
|
384
384
|
else if (exists('app.yaml') || exists('cloudbuild.yaml')) detectedDeployment = 'gcp';
|
|
385
385
|
// Note: bare Dockerfile → leave empty; user picks "self-hosted" or actual target.
|
|
386
386
|
|
|
387
|
+
// ---- Worktree env artifacts (stack.env_files) --------------------------
|
|
388
|
+
// GITIGNORED env files that must be copied into each worktree (they are NOT in
|
|
389
|
+
// the git checkout). Probe only the UNIVERSAL gitignored env conventions
|
|
390
|
+
// (.env.local / .env) — never a tracked file like .env.example, and never a
|
|
391
|
+
// stack-specific artifact (those are a per-PROJECT opinion: a project that
|
|
392
|
+
// needs e.g. a tool's local-state dir adds it to stack.env_files itself / via
|
|
393
|
+
// its overlay; the generic installer stays stack-agnostic). Fall back to the
|
|
394
|
+
// minimal set so the list is never empty.
|
|
395
|
+
const detectedEnvFiles = ['.env.local', '.env'].filter((f) => exists(f));
|
|
396
|
+
if (detectedEnvFiles.length === 0) detectedEnvFiles.push('.env.local', '.env');
|
|
397
|
+
|
|
387
398
|
const detected = {
|
|
388
399
|
paths: {
|
|
389
400
|
design_system: designSystemPath,
|
|
@@ -433,6 +444,8 @@ function detect(cwd = process.cwd()) {
|
|
|
433
444
|
// Stack-agnostic schema-deploy safety invariant (v4.33.0). Defaulted ON when
|
|
434
445
|
// parallel worktrees are detected (the desync-prone topology), else false.
|
|
435
446
|
schema_deploy_from_trunk_only: usesWorktrees,
|
|
447
|
+
// Gitignored env artifacts copied into each worktree (v4.46.0).
|
|
448
|
+
env_files: detectedEnvFiles,
|
|
436
449
|
// New: surface the detected monorepo + DS signals so skills can read them.
|
|
437
450
|
monorepo: isMonorepo ? {
|
|
438
451
|
detected: true,
|
|
@@ -1057,6 +1070,20 @@ async function interactivePrompts(merged, detected) {
|
|
|
1057
1070
|
'confirm'
|
|
1058
1071
|
);
|
|
1059
1072
|
|
|
1073
|
+
// Worktree env artifacts (stack.env_files, v4.46.0). Gitignored files/dirs
|
|
1074
|
+
// copied into each worktree (stack-agnostic — the user lists whatever their
|
|
1075
|
+
// project needs; a directory entry is copied recursively).
|
|
1076
|
+
const envFilesDefault = (Array.isArray(merged.stack.env_files) && merged.stack.env_files.length
|
|
1077
|
+
? merged.stack.env_files
|
|
1078
|
+
: detected.stack.env_files) || ['.env.local', '.env'];
|
|
1079
|
+
const envFilesAns = await promptForKey(
|
|
1080
|
+
'Gitignored env files/dirs copied into each worktree — comma-separated (gitignored only; NEVER a tracked file like .env.example; a directory entry is copied recursively)',
|
|
1081
|
+
envFilesDefault.join(',')
|
|
1082
|
+
);
|
|
1083
|
+
merged.stack.env_files = envFilesAns
|
|
1084
|
+
? envFilesAns.split(',').map((s) => s.trim()).filter(Boolean)
|
|
1085
|
+
: [];
|
|
1086
|
+
|
|
1060
1087
|
return merged;
|
|
1061
1088
|
}
|
|
1062
1089
|
|
|
@@ -1141,6 +1168,7 @@ async function configure(opts = {}) {
|
|
|
1141
1168
|
`Auth provider: ${detected.stack.auth_provider || '—'}`,
|
|
1142
1169
|
`Framework: ${detected.stack.framework || '—'}`,
|
|
1143
1170
|
`Deployment: ${detected.stack.deployment || '—'}`,
|
|
1171
|
+
`Worktree env: ${(detected.stack.env_files || []).join(', ') || '—'}`,
|
|
1144
1172
|
]);
|
|
1145
1173
|
|
|
1146
1174
|
if (opts.nonInteractive) {
|
package/src/commands/update.js
CHANGED
|
@@ -1285,12 +1285,16 @@ async function update(options = {}, unknownArgs = []) {
|
|
|
1285
1285
|
const missingGit = Object.keys(tpl.git || {})
|
|
1286
1286
|
.filter((k) => !(k in (cur2.git || {})));
|
|
1287
1287
|
// Scalar top-level stack.* keys — e.g. stack.database,
|
|
1288
|
-
// stack.auth_provider, stack.framework, stack.deployment (strings)
|
|
1289
|
-
// stack.schema_deploy_from_trunk_only (boolean, since v4.33.0)
|
|
1290
|
-
// keys (
|
|
1291
|
-
//
|
|
1288
|
+
// stack.auth_provider, stack.framework, stack.deployment (strings),
|
|
1289
|
+
// stack.schema_deploy_from_trunk_only (boolean, since v4.33.0) AND
|
|
1290
|
+
// top-level ARRAY keys (stack.env_files, v4.46.0 —
|
|
1291
|
+
// `typeof [] === 'object'`, so it needs Array.isArray to be caught;
|
|
1292
|
+
// without it the new key would silently never be asked). Sub-OBJECT
|
|
1293
|
+
// keys (charting, animation, testing) stay skipped — Array.isArray({})
|
|
1294
|
+
// is false, so they are not flagged — their drift is handled by
|
|
1295
|
+
// individual sub-prompts in configure.
|
|
1292
1296
|
const missingStack = Object.keys(tpl.stack || {})
|
|
1293
|
-
.filter((k) => ['string', 'boolean'].includes(typeof tpl.stack[k]))
|
|
1297
|
+
.filter((k) => ['string', 'boolean'].includes(typeof tpl.stack[k]) || Array.isArray(tpl.stack[k]))
|
|
1294
1298
|
.filter((k) => !(k in (cur2.stack || {})));
|
|
1295
1299
|
// Top-level nested config namespaces (e.g. `graph:` since v4.21.0).
|
|
1296
1300
|
// Unlike `lsp:` (which propagates only via its `has_lsp_layer` flag),
|