baldart 3.41.0 → 4.0.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.
Files changed (49) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/VERSION +1 -1
  3. package/framework/.claude/agents/REGISTRY.md +72 -24
  4. package/framework/.claude/agents/api-perf-cost-auditor.md +12 -5
  5. package/framework/.claude/agents/code-reviewer.md +30 -23
  6. package/framework/.claude/agents/codebase-architect.md +47 -43
  7. package/framework/.claude/agents/coder.md +29 -18
  8. package/framework/.claude/agents/doc-reviewer.md +55 -28
  9. package/framework/.claude/agents/plan-auditor.md +77 -12
  10. package/framework/.claude/agents/prd-card-writer.md +43 -13
  11. package/framework/.claude/agents/prd.md +22 -3
  12. package/framework/.claude/agents/qa-sentinel.md +33 -15
  13. package/framework/.claude/agents/security-reviewer.md +65 -10
  14. package/framework/.claude/agents/senior-researcher.md +8 -1
  15. package/framework/.claude/agents/ui-expert.md +22 -7
  16. package/framework/.claude/commands/check.md +31 -11
  17. package/framework/.claude/commands/codexreview.md +48 -29
  18. package/framework/.claude/commands/new.md +29 -330
  19. package/framework/.claude/commands/qa.md +57 -37
  20. package/framework/.claude/skills/context-primer/SKILL.md +29 -8
  21. package/framework/.claude/skills/doc-writing-for-rag/SKILL.md +36 -36
  22. package/framework/.claude/skills/frontend-design/SKILL.md +10 -8
  23. package/framework/.claude/skills/new/SKILL.md +409 -302
  24. package/framework/.claude/skills/prd/SKILL.md +67 -38
  25. package/framework/.claude/skills/prd/assets/card-template.yml +22 -26
  26. package/framework/.claude/skills/prd/assets/epic-template.yml +5 -5
  27. package/framework/.claude/skills/prd/assets/state-template.md +25 -3
  28. package/framework/.claude/skills/prd/references/api-perf-gate.md +143 -33
  29. package/framework/.claude/skills/prd/references/audit-phase.md +48 -34
  30. package/framework/.claude/skills/prd/references/backlog-phase.md +38 -11
  31. package/framework/.claude/skills/prd/references/discovery-phase.md +121 -44
  32. package/framework/.claude/skills/prd/references/impact-analysis.md +127 -23
  33. package/framework/.claude/skills/prd/references/prd-add-phase.md +18 -214
  34. package/framework/.claude/skills/prd/references/prd-writing-phase.md +52 -42
  35. package/framework/.claude/skills/prd/references/research-phase.md +105 -19
  36. package/framework/.claude/skills/prd/references/ui-design-phase.md +20 -8
  37. package/framework/.claude/skills/prd/references/validation-phase.md +97 -72
  38. package/framework/.claude/skills/prd-add/SKILL.md +70 -20
  39. package/framework/.claude/skills/simplify/SKILL.md +22 -12
  40. package/framework/.claude/skills/ui-design/SKILL.md +26 -7
  41. package/framework/.claude/skills/webapp-testing/SKILL.md +6 -4
  42. package/framework/.claude/skills/worktree-manager/SKILL.md +206 -143
  43. package/framework/agents/coding-standards.md +85 -0
  44. package/framework/agents/skills-mapping.md +85 -82
  45. package/framework/agents/testing.md +6 -4
  46. package/framework/templates/baldart.config.template.yml +29 -7
  47. package/package.json +1 -1
  48. package/src/commands/configure.js +43 -9
  49. package/framework/.claude/skills/prd-add/references/impact-analysis.md +0 -233
@@ -4,7 +4,7 @@ description: >
4
4
  Manage fully independent git worktrees for parallel coding agents.
5
5
  Commands: /nw (new worktree) creates an isolated workspace with its own
6
6
  node_modules, .next cache, and dev server port; /mw (merge worktree) commits,
7
- pushes, creates PR to develop, merges, and cleans up; /lw (list worktrees)
7
+ pushes, creates PR to the trunk branch (git.trunk_branch), merges, and cleans up; /lw (list worktrees)
8
8
  shows status of all active worktrees; /cw (cleanup worktrees) removes stale
9
9
  or already-merged worktrees without the full PR flow.
10
10
  Also used programmatically by /new orchestrator for batch card worktree ops.
@@ -15,8 +15,10 @@ description: >
15
15
  # Worktree Manager
16
16
 
17
17
  **Project Context (resolved from `baldart.config.yml`)**:
18
- - `git.merge_strategy` — `pr` (default, GitHub PR via `gh`) or `local-push` (direct fast-forward to `origin/develop`). Drives `/mw` step 4c.
18
+ - `git.trunk_branch` — the integration trunk branch (resolved value, default autodetected). Every worktree branches from `origin/<git.trunk_branch>` and merges back into it. NEVER hardcode `develop` or `main`.
19
+ - `git.merge_strategy` — `pr` (default, GitHub PR via `gh`) or `local-push` (direct fast-forward to `origin/<git.trunk_branch>`). Drives `/mw` step 4c.
19
20
  - `paths.backlog_dir` — used for syncing untracked backlog cards (`/nw` step 3b).
21
+ - `paths.metrics` — JSONL telemetry dir (default `docs/metrics`) used by the rebase conflict-resolution table.
20
22
  - Protocol reference: `framework/agents/project-context.md`. Skills must ASK when a needed key is missing — never assume.
21
23
 
22
24
  **IMMEDIATE EXECUTION**: When invoked via `/nw`, `/mw`, `/lw`, or `/cw`, do NOT explain the process. Start executing the matching command flow immediately:
@@ -61,11 +63,11 @@ What docs mode SKIPS vs the standard flow:
61
63
  - No build / lint / tsc gates at merge time
62
64
 
63
65
  What docs mode KEEPS:
64
- - Pre-flight `git fetch origin develop` (read-only on the main repo).
65
- - `git worktree add` branched from `origin/develop`.
66
+ - Pre-flight `git fetch origin <git.trunk_branch>` (read-only on the main repo).
67
+ - `git worktree add` branched from `origin/<git.trunk_branch>`.
66
68
  - Registry entry with `kind: "docs"` so `/lw` and `/cw` can list and clean docs worktrees alongside code worktrees.
67
69
  - Safety commit at merge time (uncommitted files are never lost).
68
- - Rebase onto `origin/develop` before merging (prevents PR/FF conflicts).
70
+ - Rebase onto `origin/<git.trunk_branch>` before merging (prevents PR/FF conflicts).
69
71
  - The configured `git.merge_strategy` (`pr` or `local-push`).
70
72
  - Cleanup of worktree + local branch + remote branch after merge.
71
73
 
@@ -108,8 +110,18 @@ if [ ! -f "$MAIN_ROOT/.gitignore" ] || ! grep -qE '^\.worktrees/?$' "$MAIN_ROOT/
108
110
  exit 1
109
111
  fi
110
112
 
113
+ # 0b. Resolve the trunk branch from config (R7) — no bare 'develop' literal.
114
+ # TRUNK MUST be non-empty before any fetch/branch.
115
+ TRUNK=$(grep -A40 '^git:' "$MAIN_ROOT/baldart.config.yml" 2>/dev/null \
116
+ | grep 'trunk_branch:' | head -1 \
117
+ | sed -E 's/.*trunk_branch:[[:space:]]*([A-Za-z0-9._\/-]+).*/\1/' || true)
118
+ if [ -z "$TRUNK" ]; then
119
+ echo "ERROR: git.trunk_branch not set in baldart.config.yml — ASK the user (never assume 'develop')." >&2
120
+ exit 1
121
+ fi
122
+
111
123
  # 1. Pre-flight — read-only fetch, NEVER touches main repo HEAD
112
- git fetch origin develop
124
+ git fetch origin "$TRUNK"
113
125
 
114
126
  # 2. Duplicate check — read registry; if an entry with the same slug AND kind=docs
115
127
  # exists, return the existing { path, branch, kind } instead of creating a new
@@ -120,16 +132,21 @@ git fetch origin develop
120
132
  # 3. Create the worktree. The path returned MUST be ABSOLUTE — Claude Code's
121
133
  # Write/Edit require absolute paths, and downstream skills (/prd, ui-design,
122
134
  # prd-card-writer) interpolate this path verbatim.
135
+ # SLUG must be kebab-case + deterministic (R8): the same slug always maps to
136
+ # the same worktree path, so a resumed /prd run finds the existing worktree.
123
137
  SLUG="<slug>"
124
138
  BRANCH_PREFIX="${branchPrefix:-prd/}"
125
139
  BRANCH="${BRANCH_PREFIX}${SLUG}"
126
- WORKTREE_REL=".worktrees/prd-${SLUG}"
140
+ WORKTREE_REL=".worktrees/prd-${SLUG}" # deterministic: derived from slug only, no timestamp
127
141
  WORKTREE_PATH="$MAIN_ROOT/$WORKTREE_REL" # absolute, anchored to main repo root
128
- git -C "$MAIN_ROOT" worktree add "$WORKTREE_REL" -b "$BRANCH" origin/develop
142
+ git -C "$MAIN_ROOT" worktree add "$WORKTREE_REL" -b "$BRANCH" "origin/$TRUNK"
129
143
 
130
144
  # 4. Registry entry — kind: "docs", port: null, buildVerified: null
131
145
  # Same registry file (.worktrees/registry.json) as code worktrees.
132
146
  # The `path` field MUST hold $WORKTREE_PATH (absolute), not $WORKTREE_REL.
147
+ # R6: persist $MAIN_ROOT, slug, branch AND the resolved trunk on the entry
148
+ # (fields: path, slug, branch, trunkBranch) so every later consumer reads them
149
+ # from the registry with a presence guard — never relies on in-context vars.
133
150
  ```
134
151
 
135
152
  **Registry entry shape for docs worktrees:**
@@ -141,6 +158,8 @@ git -C "$MAIN_ROOT" worktree add "$WORKTREE_REL" -b "$BRANCH" origin/develop
141
158
  "groupParent": null,
142
159
  "slug": "menu-nutritional-info",
143
160
  "branch": "prd/menu-nutritional-info",
161
+ "trunkBranch": "develop",
162
+ "mainRoot": "/abs/repo-root",
144
163
  "path": "/abs/.worktrees/prd-menu-nutritional-info",
145
164
  "port": null,
146
165
  "kind": "docs",
@@ -177,6 +196,19 @@ Output:
177
196
  **Steps (run exactly these, nothing else):**
178
197
 
179
198
  ```bash
199
+ # Presence guard (R6): $WORKTREE_PATH and the resolved $TRUNK MUST be read from
200
+ # the registry entry (fields path / trunkBranch) and be non-empty before any op.
201
+ if [ -z "$WORKTREE_PATH" ]; then
202
+ echo "ERROR: \$WORKTREE_PATH unset — read it from the registry entry before mw-docs." >&2
203
+ exit 1
204
+ fi
205
+ # If $TRUNK is unset, read the `trunkBranch` field from this worktree's registry
206
+ # entry in .worktrees/registry.json (do NOT assume 'develop').
207
+ if [ -z "$TRUNK" ]; then
208
+ echo "ERROR: trunk branch unresolved — read trunkBranch from the registry entry (do NOT assume 'develop')." >&2
209
+ exit 1
210
+ fi
211
+
180
212
  cd "$WORKTREE_PATH"
181
213
 
182
214
  # 1. Safety commit (MANDATORY — same as /mw step 3, never skippable).
@@ -191,18 +223,17 @@ cd "$WORKTREE_PATH"
191
223
  # - ${paths.references_dir}/project-status.md (if updated by Step 7)
192
224
  # NEVER `git add .` or `git add -A`. NEVER `git stash` (see Safety Rules).
193
225
 
194
- # 2. Rebase onto latest develop. Reuses the /mw step 4b conflict-resolution
195
- # table (additive auto-strip for docs/yaml/jsonl-in-docs-metrics; abort on
196
- # structured registries when validation fails; abort on code/tests/fixtures).
226
+ # 2. Rebase onto the latest trunk branch ($TRUNK = git.trunk_branch). Reuses the
227
+ # /mw step 4b conflict-resolution table (additive auto-strip for
228
+ # docs/yaml/jsonl-in-${paths.metrics}; abort on structured registries when
229
+ # validation fails; abort on code/tests/fixtures).
197
230
  #
198
- # IMPORTANT — NO `git stash` in mw-docs. The safety commit in step 1
199
- # above guarantees a clean working tree, so the stash logic at the top of
200
- # /mw step 4b ("stash dirty files before rebase, pop after") is a no-op
201
- # for mw-docs and must be SKIPPED. Running `git stash push` from a
231
+ # NO `git stash` in mw-docs (or anywhere in a worktree). The safety commit in
232
+ # step 1 above guarantees a clean working tree. Running `git stash push` from a
202
233
  # worktree violates the Safety Rule "NEVER use git stash in worktrees"
203
234
  # (refs/stash is shared across all worktrees via git.commondir — see the
204
- # FEAT-0522 incident note). Two parallel mw-docs runs that both stash
205
- # would race on the shared stash list and could pop into the wrong tree.
235
+ # FEAT-0522 incident note). Two parallel runs that both stash would race on the
236
+ # shared stash list and could pop into the wrong tree.
206
237
  #
207
238
  # Direct rebase, no stash. If the working tree is unexpectedly dirty here
208
239
  # (someone wrote between step 1 and step 2), STOP and report — a second
@@ -214,14 +245,14 @@ if [ -n "$DIRTY" ]; then
214
245
  echo "Investigate — never stash to recover (refs/stash shared across worktrees)." >&2
215
246
  exit 1
216
247
  fi
217
- git -C "$WORKTREE_PATH" fetch origin develop
218
- git -C "$WORKTREE_PATH" rebase origin/develop
248
+ git -C "$WORKTREE_PATH" fetch origin "$TRUNK"
249
+ git -C "$WORKTREE_PATH" rebase "origin/$TRUNK"
219
250
  # (apply /mw 4b conflict-resolution table if rebase reports conflicts)
220
251
 
221
- # 3. Land on develop — reuse /mw step 4c verbatim. Read git.merge_strategy from
222
- # baldart.config.yml. NEVER `git checkout develop` on the main repo.
252
+ # 3. Land on trunk — reuse /mw step 4c verbatim. Read git.merge_strategy from
253
+ # baldart.config.yml. NEVER `git checkout <trunk>` on the main repo.
223
254
  # - pr → gh pr create + gh pr merge --merge --delete-branch
224
- # - local-push → git push origin <branch>:develop (fast-forward only)
255
+ # - local-push → git push origin <branch>:$TRUNK (fast-forward only)
225
256
  #
226
257
  # Note: many consumers prefer `local-push` for docs worktrees specifically,
227
258
  # because a PRD is rarely worth a GitHub PR roundtrip. The CLI does not
@@ -232,8 +263,8 @@ git -C "$WORKTREE_PATH" rebase origin/develop
232
263
  # Skip /mw step 5 (post-merge `npm run build`) — docs worktrees have nothing
233
264
  # to build.
234
265
 
235
- # 5. Sync local develop ref — reuse /mw "Common" block: ff-only pull when main
236
- # repo HEAD is already develop; otherwise just fetch.
266
+ # 5. Sync local trunk ref — reuse /mw "Common" block: ff-only pull when main
267
+ # repo HEAD is already $TRUNK; otherwise just fetch.
237
268
 
238
269
  # 6. Remove the registry entry for this worktree.
239
270
  ```
@@ -248,7 +279,7 @@ can investigate. Cleanup runs only after a successful merge.
248
279
 
249
280
  The merge path depends on `git.merge_strategy` in `baldart.config.yml`:
250
281
  - `pr` (default, since v3.4.0): merge via `gh pr create` + `gh pr merge`. `prNumber` is the PR number, `mergeCommit` is the SHA returned by the GitHub merge API.
251
- - `local-push` (since v3.9.0): direct fast-forward push of the rebased feature branch to `origin/develop` — no PR, no `gh`. `prNumber` is `null`, `mergeCommit` is the local SHA of the feature branch tip.
282
+ - `local-push` (since v3.9.0): direct fast-forward push of the rebased feature branch to `origin/<git.trunk_branch>` — no PR, no `gh`. `prNumber` is `null`, `mergeCommit` is the local SHA of the feature branch tip.
252
283
 
253
284
  Neither path runs `git checkout` / `git switch` / `git branch` on the main repo. The orchestrator can branch on `strategy` if it needs PR linking (only meaningful in `pr` mode).
254
285
 
@@ -276,6 +307,8 @@ All worktree operations read/write `.worktrees/registry.json`:
276
307
  "groupParent": "FEAT-0200",
277
308
  "slug": "menu-ranking",
278
309
  "branch": "feat/FEAT-0200-menu-ranking",
310
+ "trunkBranch": "develop",
311
+ "mainRoot": "/absolute/path",
279
312
  "path": "/absolute/path/.worktrees/feat-FEAT-0200-menu-ranking",
280
313
  "port": 3001,
281
314
  "createdAt": "2026-03-09T10:00:00Z",
@@ -289,6 +322,8 @@ All worktree operations read/write `.worktrees/registry.json`:
289
322
  - `card`: primary card ID (first card or group parent), or `null` for slug-only worktrees
290
323
  - `cards`: all card IDs sharing this worktree (for batches), or `[]` for slug-only
291
324
  - `groupParent`: from backlog card `group.parent` field, or `null` for standalone/slug-only
325
+ - `trunkBranch`: the resolved `git.trunk_branch` this worktree was created from (R6 — persisted so `/mw`/`/cw` never re-guess or assume `develop`)
326
+ - `mainRoot`: absolute path of the main repo root (R6 — persisted so `$MAIN` is read from the registry, not from in-context state that does not survive compaction)
292
327
  - `buildVerified`: true if `npm run build` passed after setup
293
328
 
294
329
  Create the file if it doesn't exist. Update it on every `/nw`, `/mw`, `/cw` operation.
@@ -305,14 +340,26 @@ Supports three modes:
305
340
  ### 1. Pre-flight checks
306
341
 
307
342
  ```bash
308
- # Fetch latest develop from remote read-only, does NOT touch the main repo's HEAD.
309
- # We branch the new worktree from `origin/develop` directly (step 3), so we never
310
- # need to checkout develop on the main repo. This is the absolute rule:
343
+ # Resolve $MAIN (main repo root) and $TRUNK (git.trunk_branch) up front, and
344
+ # persist both onto the registry entry created in step 6 (R6). Every later step
345
+ # reads them from the registry with a presence guard never from in-context state.
346
+ MAIN="$(git rev-parse --show-toplevel)"
347
+ TRUNK=$(grep -A40 '^git:' "$MAIN/baldart.config.yml" 2>/dev/null \
348
+ | grep 'trunk_branch:' | head -1 \
349
+ | sed -E 's/.*trunk_branch:[[:space:]]*([A-Za-z0-9._\/-]+).*/\1/' || true)
350
+ if [ -z "$TRUNK" ]; then
351
+ echo "ERROR: git.trunk_branch not set in baldart.config.yml — ASK the user (never assume 'develop')." >&2
352
+ exit 1
353
+ fi
354
+
355
+ # Fetch latest trunk from remote — read-only, does NOT touch the main repo's HEAD.
356
+ # We branch the new worktree from `origin/<trunk>` directly (step 3), so we never
357
+ # need to checkout the trunk on the main repo. This is the absolute rule:
311
358
  # /nw must not run `git checkout`, `git switch`, `git checkout -b`, or
312
359
  # `git branch` on the main repo (it's shared across parallel terminals; many
313
360
  # consumer projects declare this as a hard rule in CLAUDE.md and the classifier
314
361
  # will block the call preemptively).
315
- git fetch origin develop
362
+ git fetch origin "$TRUNK"
316
363
  ```
317
364
 
318
365
  (There is no need to check `git status --porcelain` on the main repo here — we
@@ -347,22 +394,24 @@ Branch name: `feat/<CARD-ID>-<slug>` (with card) or `feat/<slug>` (slug-only). U
347
394
 
348
395
  ### 3. Create the worktree
349
396
 
350
- Base the new worktree on `origin/develop` (the remote tip we just fetched in step 1) rather than the local `develop` branch — that way we never need the local `develop` to be checked out or up to date on the main repo.
397
+ Base the new worktree on `origin/<$TRUNK>` (the remote tip we just fetched in step 1) rather than the local trunk branch — that way we never need the local trunk to be checked out or up to date on the main repo.
398
+
399
+ The worktree path is **deterministic** (R8): derived from the card ID + kebab-case slug only, never a timestamp — so a resumed run resolves to the same path and the duplicate check in step 1 is meaningful.
351
400
 
352
401
  ```bash
353
402
  # With card ID:
354
403
  WORKTREE_PATH=".worktrees/feat-<CARD-ID>-<slug>"
355
- git worktree add "$WORKTREE_PATH" -b "feat/<CARD-ID>-<slug>" origin/develop
404
+ git worktree add "$WORKTREE_PATH" -b "feat/<CARD-ID>-<slug>" "origin/$TRUNK"
356
405
 
357
406
  # Slug-only (no card):
358
407
  WORKTREE_PATH=".worktrees/feat-<slug>"
359
- git worktree add "$WORKTREE_PATH" -b "feat/<slug>" origin/develop
408
+ git worktree add "$WORKTREE_PATH" -b "feat/<slug>" "origin/$TRUNK"
360
409
  ```
361
410
 
362
411
  ### 3b. Sync untracked backlog cards from main repo
363
412
 
364
413
  Backlog cards created during `/prd` sessions may exist as untracked files in the main repo
365
- but are NOT on `develop` yet. The worktree (branched from develop) won't have them.
414
+ but are NOT on the trunk branch yet. The worktree (branched from the trunk) won't have them.
366
415
 
367
416
  ```bash
368
417
  MAIN_ROOT="$(git -C "$WORKTREE_PATH" rev-parse --show-superproject-working-tree 2>/dev/null || pwd)"
@@ -432,8 +481,8 @@ if [ -n "$HOOK_SRC" ]; then
432
481
  case "$ACTIVE" in
433
482
  "$HOOK_SRC"|"$HOOK_SRC"/*|.husky/_) : ;; # active dir serves the versioned hooks (incl. husky v9 .husky/_)
434
483
  *)
435
- echo "⚠️ WARNING: git hooks INATTIVIgli hook versionati sono in '$HOOK_SRC' ma core.hooksPath = '$ACTIVE'." >&2
436
- echo " Esegui per riattivarli: git config core.hooksPath $HOOK_SRC" >&2
484
+ echo "⚠️ WARNING: git hooks INACTIVEthe versioned hooks live in '$HOOK_SRC' but core.hooksPath = '$ACTIVE'." >&2
485
+ echo " Re-activate them with: git config core.hooksPath $HOOK_SRC" >&2
437
486
  ;;
438
487
  esac
439
488
  fi
@@ -451,11 +500,11 @@ npm run build
451
500
  ```
452
501
 
453
502
  If build fails → STOP and report. Do NOT continue — the worktree is broken.
454
- If only tsc/lint fails → report but continue (develop should be clean, may be a transient issue).
503
+ If only tsc/lint fails → report but continue (the trunk branch should be clean, may be a transient issue).
455
504
 
456
505
  ### 6. Update registry
457
506
 
458
- Add entry to `.worktrees/registry.json` with all fields: card, cards, groupParent, slug, branch, absolute path, port, `createdAt`, `envSyncedAt`, and `buildVerified`.
507
+ Add entry to `.worktrees/registry.json` with all fields: card, cards, groupParent, slug, branch, `trunkBranch` (the resolved `$TRUNK`), `mainRoot` (the resolved `$MAIN`), absolute path, port, `createdAt`, `envSyncedAt`, and `buildVerified`. Persisting `trunkBranch` + `mainRoot` (R6) lets `/mw`/`/cw` read them back with a presence guard instead of re-deriving or assuming `develop`.
459
508
 
460
509
  ### 7. Auto-start dev server (if `--dev` flag or `/nw --dev`)
461
510
 
@@ -503,8 +552,18 @@ Read `.worktrees/registry.json`. If multiple entries exist or user didn't specif
503
552
 
504
553
  Fallback: `git worktree list` if registry is missing.
505
554
 
506
- Determine:
555
+ Determine, reading them from the worktree's registry entry (R6):
507
556
  - `WORKTREE_PATH`, `BRANCH`, `CARD` (or `CARDS` for batch worktrees)
557
+ - `TRUNK` (the entry's `trunkBranch`) and `MAIN` (the entry's `mainRoot`)
558
+
559
+ These were persisted at `/nw` time. Before any step below, presence-guard them — if `WORKTREE_PATH`, `TRUNK`, or `MAIN` is empty, HALT with a clear message (never assume `develop` or re-derive `$MAIN` silently):
560
+
561
+ ```bash
562
+ if [ -z "$WORKTREE_PATH" ] || [ -z "$BRANCH" ] || [ -z "$TRUNK" ] || [ -z "$MAIN" ]; then
563
+ echo "ERROR: missing WORKTREE_PATH/BRANCH/TRUNK/MAIN — read them from the registry entry (fields path/branch/trunkBranch/mainRoot)." >&2
564
+ exit 1
565
+ fi
566
+ ```
508
567
 
509
568
  ### 2. Env sync check
510
569
 
@@ -544,7 +603,7 @@ if [ -n "$CHANGED" ] || [ -n "$UNTRACKED" ]; then
544
603
 
545
604
  Files were uncommitted when /mw started. This safety commit
546
605
  prevents file loss during rebase. Squash-merge will fold this
547
- into the final commit on develop.
606
+ into the final commit on the trunk branch.
548
607
 
549
608
  Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>"
550
609
 
@@ -552,8 +611,8 @@ Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>"
552
611
  fi
553
612
 
554
613
  # 2. Run quality checks
555
- # Lint only changed files (vs develop)
556
- npx eslint --max-warnings=0 $(git diff --name-only develop -- '*.ts' '*.tsx')
614
+ # Lint only changed files (vs the trunk branch)
615
+ npx eslint --max-warnings=0 $(git diff --name-only "origin/$TRUNK" -- '*.ts' '*.tsx')
557
616
  npx tsc --noEmit
558
617
 
559
618
  # Full build (required before PR per AGENTS.md)
@@ -586,48 +645,51 @@ git commit -m "[<CARD-ID>] <description>"
586
645
 
587
646
  **Programmatic mode**: The orchestrator handles per-card commits in Phase 4. `/mw` only needs to rebase and push (see steps 4b and 4c below).
588
647
 
589
- ### 4b. Rebase onto latest develop (MUST — prevents PR merge conflicts)
648
+ ### 4b. Rebase onto latest trunk (MUST — prevents PR merge conflicts)
590
649
 
591
- Before pushing, ALWAYS rebase the feature branch onto the latest develop to avoid merge conflicts at PR merge time.
650
+ Before pushing, ALWAYS rebase the feature branch onto the latest trunk branch to avoid merge conflicts at PR merge time. The trunk branch is `git.trunk_branch` from `baldart.config.yml` (resolved below).
592
651
 
593
652
  ```bash
653
+ # Presence guard (R6): $MAIN and $WORKTREE_PATH MUST already be set + non-empty
654
+ # (written to the registry at /nw time; re-read from there if absent). Never
655
+ # proceed with an undefined $MAIN — it would resolve git operations against the
656
+ # wrong repo.
657
+ if [ -z "$MAIN" ] || [ -z "$WORKTREE_PATH" ]; then
658
+ echo "ERROR: \$MAIN and/or \$WORKTREE_PATH unset — read them from .worktrees/registry.json before step 4b." >&2
659
+ exit 1
660
+ fi
661
+
662
+ # Resolve the trunk branch from config (default: read by the caller; no bare
663
+ # 'develop' literal). TRUNK MUST be non-empty.
664
+ if [ -z "$TRUNK" ]; then
665
+ echo "ERROR: \$TRUNK (git.trunk_branch) unset — resolve it from baldart.config.yml before step 4b." >&2
666
+ exit 1
667
+ fi
668
+
594
669
  cd "$WORKTREE_PATH"
595
670
 
596
- # 1. Check for uncommitted changes BEFORE stashing
597
- # If the safety commit in step 3 ran, this should be clean.
598
- # But if called outside /mw flow, there might be leftovers.
671
+ # 1. NO `git stash` in a worktree (refs/stash is shared across all worktrees via
672
+ # git.commondir see FEAT-0522 incident + Safety Rules). The safety commit in
673
+ # step 3 already guarantees a clean working tree. If the tree is unexpectedly
674
+ # dirty here (someone wrote between step 3 and step 4b), STOP and report — the
675
+ # right recovery is a SECOND safety commit, never a stash.
599
676
  DIRTY=$(git status --porcelain 2>/dev/null)
600
- STASHED="false"
601
677
  if [ -n "$DIRTY" ]; then
602
- echo "⚠️ Dirty working tree before rebase stashing"
603
- STASH_MSG="pre-rebase-$(date +%s)"
604
- git stash push --include-untracked -m "$STASH_MSG"
605
- STASHED="true"
678
+ echo "ERROR: working tree dirty in $WORKTREE_PATH after the step-3 safety commit." >&2
679
+ echo "Files: $DIRTY" >&2
680
+ echo "Make a second safety commit (explicit file names) — NEVER stash (refs/stash shared across worktrees)." >&2
681
+ exit 1
606
682
  fi
607
683
 
608
- # 2. Fetch latest develop
609
- git fetch origin develop
684
+ # 2. Fetch latest trunk
685
+ git fetch origin "$TRUNK"
610
686
 
611
- # 3. Rebase onto origin/develop
612
- git rebase origin/develop 2>&1
687
+ # 3. Rebase onto origin/<trunk>
688
+ git rebase "origin/$TRUNK" 2>&1
613
689
  REBASE_EXIT=$?
614
690
  ```
615
691
 
616
- **If rebase succeeds** (exit 0): restore stash and continue to push.
617
-
618
- ```bash
619
- if [ "$STASHED" = "true" ]; then
620
- STASH_NUM=$(git stash list | grep "$STASH_MSG" | sed -E 's/stash@\{([0-9]+)\}.*/\1/')
621
- if [ -n "$STASH_NUM" ]; then
622
- if ! git stash pop "stash@{$STASH_NUM}" 2>&1; then
623
- echo "⚠️ STASH POP FAILED — files preserved in stash@{$STASH_NUM} ($STASH_MSG)"
624
- echo "Run 'git stash show stash@{$STASH_NUM}' to see affected files"
625
- echo "Run 'git stash pop stash@{$STASH_NUM}' to recover manually"
626
- # DO NOT silently continue — report this to the user
627
- fi
628
- fi
629
- fi
630
- ```
692
+ **If rebase succeeds** (exit 0): continue straight to push — there is no stash to restore.
631
693
 
632
694
  **If rebase fails with conflicts**: apply the auto-resolution protocol below.
633
695
 
@@ -645,8 +707,8 @@ internal structure (tables, key/value blocks, code).
645
707
  | Category | Patterns | Action | Note |
646
708
  |----------|----------|--------|------|
647
709
  | **Structured registries** | `project-status.md`, `ssot-registry.md`, `field-registry.*`, `traceability-matrix.md`, `REGISTRY.md` | Strip markers, then **structural validation** — if valid, accept; if invalid, abort | The auto-strip "keep both sides" usually produces an additive result (new bullet rows appended to a table or section). The validation step catches the rare case where the two sides modified the same row. See `validate_structured_md` below. |
648
- | **JSONL logs under `docs/metrics/`** | `docs/metrics/*.jsonl`, `docs/metrics/**/*.jsonl` | Auto-resolve: strip markers, keep both sides | JSONL is line-oriented and additive — duplicating both branches' lines yields a valid file. |
649
- | **Other `.jsonl`** | `*.jsonl` outside `docs/metrics/` | STOP — abort and report | Fixtures, seeds, training data: auto-strip can corrupt them. The user must inspect. |
710
+ | **JSONL logs under `${paths.metrics}/`** | `${paths.metrics}/*.jsonl`, `${paths.metrics}/**/*.jsonl` | Auto-resolve: strip markers, keep both sides | JSONL is line-oriented and additive — duplicating both branches' lines yields a valid file. (`paths.metrics` default `docs/metrics`.) |
711
+ | **Other `.jsonl`** | `*.jsonl` outside `${paths.metrics}/` | STOP — abort and report | Fixtures, seeds, training data: auto-strip can corrupt them. The user must inspect. |
650
712
  | **Docs/config** | `docs/**/*.md`, `backlog/*.yml`, `*.md` outside `src/`, `*.json`/`*.yml` outside `src/` (excluding rows above) | Auto-resolve: strip markers, keep both sides | The canonical additive case. |
651
713
  | **Code** | `src/**/*.ts`, `*.tsx`, `*.js`, `*.jsx` | STOP — abort rebase, report to user | |
652
714
  | **Tests** | `*.test.*`, `*.spec.*` | Manual only — report to user | |
@@ -681,10 +743,8 @@ for FILE in $CONFLICTED; do
681
743
  case "$FILE" in
682
744
  src/*.ts|src/*.tsx|src/*.js|src/*.jsx|*.test.*|*.spec.*)
683
745
  git rebase --abort
684
- # When invoked from /mw (code worktree) a stash may have been created
685
- # at the start of step 4b. Pop it back. mw-docs never stashes
686
- # (safety commit step 1 leaves the tree clean) so the pop is a no-op.
687
- git stash pop 2>/dev/null || true
746
+ # No stash to restore: step 3's safety commit leaves the tree clean, and
747
+ # step 4b NEVER stashes (refs/stash is shared across worktrees).
688
748
  echo "CONFLICT in $FILE — cannot auto-resolve" >&2
689
749
  exit 1 ;;
690
750
  esac
@@ -696,7 +756,6 @@ for FILE in $CONFLICTED; do
696
756
  if ! validate_structured_md "$FILE"; then
697
757
  git checkout --conflict=merge "$FILE" 2>/dev/null || true
698
758
  git rebase --abort
699
- git stash pop 2>/dev/null || true
700
759
  echo "CONFLICT in structured registry $FILE produced invalid structure after auto-strip — manual resolution required." >&2
701
760
  exit 1
702
761
  fi
@@ -707,21 +766,19 @@ for FILE in $CONFLICTED; do
707
766
  field-registry.*)
708
767
  # field-registry.json typically — strict JSON, can't auto-strip safely
709
768
  git rebase --abort
710
- git stash pop 2>/dev/null || true
711
769
  echo "CONFLICT in structured registry $FILE — JSON cannot be auto-stripped." >&2
712
770
  exit 1 ;;
713
771
  esac
714
772
 
715
- # 3. JSONL — only auto-resolve under docs/metrics/
773
+ # 3. JSONL — only auto-resolve under ${paths.metrics}/
716
774
  case "$FILE" in
717
- docs/metrics/*.jsonl|docs/metrics/**/*.jsonl)
775
+ ${paths.metrics}/*.jsonl|${paths.metrics}/**/*.jsonl)
718
776
  sed -i '' '/^<<<<<<< /d; /^=======/d; /^>>>>>>> /d' "$FILE"
719
777
  git add "$FILE"
720
778
  continue ;;
721
779
  *.jsonl)
722
780
  git rebase --abort
723
- git stash pop 2>/dev/null || true
724
- echo "CONFLICT in $FILE (.jsonl outside docs/metrics/ — may be fixture/seed) — manual resolution required." >&2
781
+ echo "CONFLICT in $FILE (.jsonl outside ${paths.metrics}/ may be fixture/seed) — manual resolution required." >&2
725
782
  exit 1 ;;
726
783
  esac
727
784
 
@@ -733,26 +790,30 @@ for FILE in $CONFLICTED; do
733
790
  esac
734
791
  done
735
792
 
736
- # Continue rebase + restore stash + verify build
793
+ # Continue rebase + verify build (no stash to restore — step 4b never stashes)
737
794
  git rebase --continue
738
- git stash pop 2>/dev/null || true
739
795
  npm run build
740
796
  ```
741
797
 
742
798
  If build fails after rebase → STOP and report. The rebase introduced incompatibilities.
743
799
 
744
- ### 4c. Land the feature branch onto develop (strategy-branched)
800
+ ### 4c. Land the feature branch onto the trunk branch (strategy-branched)
745
801
 
746
- **IMPORTANT — NO local checkout/switch/branch on the main repo, regardless of strategy.** Consumer projects share the main repo as a parallel resource across multiple terminals; running `git checkout develop` on it preempts whatever the other terminals are doing and breaks worktree-driven workflows. Both strategies below operate from the worktree and the main repo's `HEAD` is never touched.
802
+ **IMPORTANT — NO local checkout/switch/branch on the main repo, regardless of strategy.** Consumer projects share the main repo as a parallel resource across multiple terminals; running `git checkout <trunk>` on it preempts whatever the other terminals are doing and breaks worktree-driven workflows. Both strategies below operate from the worktree and the main repo's `HEAD` is never touched.
747
803
 
748
804
  Read `git.merge_strategy` from `baldart.config.yml` (default: `pr`):
749
805
 
750
806
  ```bash
751
- MAIN=$(git -C "$WORKTREE_PATH" rev-parse --git-common-dir)/..
752
- MAIN=$(cd "$MAIN" && pwd)
807
+ # $MAIN and $TRUNK were resolved in step 1 (read from the registry entry, R6) and
808
+ # presence-guarded. Fall back to deriving $MAIN from git-common-dir ONLY if it is
809
+ # still unset — never silently re-derive over a value the registry already supplied.
810
+ if [ -z "$MAIN" ]; then
811
+ MAIN=$(git -C "$WORKTREE_PATH" rev-parse --git-common-dir)/..
812
+ MAIN=$(cd "$MAIN" && pwd)
813
+ fi
753
814
 
754
815
  # Resolve strategy from baldart.config.yml (default: pr)
755
- STRATEGY=$(grep -A1 '^git:' "$MAIN/baldart.config.yml" 2>/dev/null \
816
+ STRATEGY=$(grep -A40 '^git:' "$MAIN/baldart.config.yml" 2>/dev/null \
756
817
  | grep 'merge_strategy:' | head -1 \
757
818
  | sed -E 's/.*merge_strategy:[[:space:]]*([a-z-]+).*/\1/' || echo "pr")
758
819
  STRATEGY=${STRATEGY:-pr}
@@ -764,7 +825,7 @@ If neither `pr` nor `local-push` resolves, ASK the user once which to use and su
764
825
 
765
826
  #### Strategy A — `pr` (default, GitHub PR via `gh`)
766
827
 
767
- Use this when `develop` is protected on origin (required status checks, reviews, merge queue) — a direct push would be rejected.
828
+ Use this when the trunk branch is protected on origin (required status checks, reviews, merge queue) — a direct push would be rejected.
768
829
 
769
830
  ```bash
770
831
  # 1. Push the feature branch to origin (force-with-lease in case of rebase in 4b).
@@ -776,7 +837,7 @@ PR_NUMBER=$(gh -R "$(gh repo view --json nameWithOwner -q .nameWithOwner)" pr li
776
837
 
777
838
  if [ -z "$PR_NUMBER" ]; then
778
839
  PR_URL=$(gh pr create \
779
- --base develop \
840
+ --base "$TRUNK" \
780
841
  --head "$BRANCH" \
781
842
  --title "[<CARD-ID>] $(echo "$BRANCH" | sed 's|.*/||')" \
782
843
  --body "Auto-merged by /mw after final review + QA passed." \
@@ -797,7 +858,7 @@ if [ "$PR_STATE" != "MERGED" ]; then
797
858
  REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
798
859
  gh api -X PUT "repos/$REPO/pulls/$PR_NUMBER/merge" \
799
860
  -f merge_method=merge \
800
- -f commit_title="[<CARD-ID>] Merge $BRANCH into develop"
861
+ -f commit_title="[<CARD-ID>] Merge $BRANCH into $TRUNK"
801
862
  PR_STATE=$(gh pr view "$PR_NUMBER" --json state -q .state)
802
863
  fi
803
864
 
@@ -805,47 +866,47 @@ if [ "$PR_STATE" != "MERGED" ]; then
805
866
  echo "❌ PR #$PR_NUMBER did not merge. Inspect manually: $PR_URL"
806
867
  exit 1
807
868
  fi
808
- echo "✅ PR #$PR_NUMBER merged to develop."
869
+ echo "✅ PR #$PR_NUMBER merged to $TRUNK."
809
870
  ```
810
871
 
811
872
  If `gh` is not installed in the project, STOP and ask the user to install GitHub CLI (`brew install gh` or equivalent) and run `gh auth login`. Do NOT fall back to a local checkout — it violates terminal isolation.
812
873
 
813
- If both gh CLI and REST API merge fail → STOP and report. Suggest switching to `merge_strategy: local-push` only if `develop` is NOT protected on origin.
874
+ If both gh CLI and REST API merge fail → STOP and report. Suggest switching to `merge_strategy: local-push` only if the trunk branch is NOT protected on origin.
814
875
 
815
876
  ---
816
877
 
817
878
  #### Strategy B — `local-push` (direct FF push, no PR)
818
879
 
819
- Use this when `develop` is NOT protected on origin and the user wants to skip the GitHub PR roundtrip. After step 4b the feature branch is rebased onto `origin/develop`, so it can fast-forward `develop` on origin in a single push.
880
+ Use this when the trunk branch is NOT protected on origin and the user wants to skip the GitHub PR roundtrip. After step 4b the feature branch is rebased onto `origin/$TRUNK`, so it can fast-forward the trunk on origin in a single push.
820
881
 
821
882
  ```bash
822
- # 1. Sanity: confirm the feature branch is strictly ahead of origin/develop
823
- # (i.e. the rebase in step 4b succeeded and we can FF develop on origin).
824
- git -C "$WORKTREE_PATH" fetch origin develop --quiet
825
- AHEAD=$(git -C "$WORKTREE_PATH" rev-list --count "origin/develop..$BRANCH")
826
- BEHIND=$(git -C "$WORKTREE_PATH" rev-list --count "$BRANCH..origin/develop")
883
+ # 1. Sanity: confirm the feature branch is strictly ahead of origin/$TRUNK
884
+ # (i.e. the rebase in step 4b succeeded and we can FF the trunk on origin).
885
+ git -C "$WORKTREE_PATH" fetch origin "$TRUNK" --quiet
886
+ AHEAD=$(git -C "$WORKTREE_PATH" rev-list --count "origin/$TRUNK..$BRANCH")
887
+ BEHIND=$(git -C "$WORKTREE_PATH" rev-list --count "$BRANCH..origin/$TRUNK")
827
888
  if [ "$BEHIND" -ne 0 ]; then
828
- echo "❌ local-push aborted: $BRANCH is $BEHIND commit(s) behind origin/develop." >&2
829
- echo " Re-run step 4b (rebase onto origin/develop), then retry." >&2
889
+ echo "❌ local-push aborted: $BRANCH is $BEHIND commit(s) behind origin/$TRUNK." >&2
890
+ echo " Re-run step 4b (rebase onto origin/$TRUNK), then retry." >&2
830
891
  exit 1
831
892
  fi
832
893
  if [ "$AHEAD" -eq 0 ]; then
833
- echo "ℹ️ Nothing to push — $BRANCH is identical to origin/develop. Skipping."
894
+ echo "ℹ️ Nothing to push — $BRANCH is identical to origin/$TRUNK. Skipping."
834
895
  else
835
- echo "Fast-forwarding origin/develop by $AHEAD commit(s) via local-push…"
896
+ echo "Fast-forwarding origin/$TRUNK by $AHEAD commit(s) via local-push…"
836
897
  fi
837
898
 
838
- # 2. Direct fast-forward push to develop on origin. No PR.
839
- # If develop is protected on origin, this fails — surface the hint and stop.
840
- if ! PUSH_OUTPUT=$(git -C "$WORKTREE_PATH" push origin "$BRANCH:develop" 2>&1); then
899
+ # 2. Direct fast-forward push to the trunk on origin. No PR.
900
+ # If the trunk is protected on origin, this fails — surface the hint and stop.
901
+ if ! PUSH_OUTPUT=$(git -C "$WORKTREE_PATH" push origin "$BRANCH:$TRUNK" 2>&1); then
841
902
  echo "$PUSH_OUTPUT"
842
903
  if echo "$PUSH_OUTPUT" | grep -qiE "protected branch|required status|review required"; then
843
- echo "❌ local-push rejected: 'develop' is protected on origin." >&2
904
+ echo "❌ local-push rejected: '$TRUNK' is protected on origin." >&2
844
905
  echo " Switch baldart.config.yml → git.merge_strategy: pr, or remove the protection." >&2
845
906
  fi
846
907
  exit 1
847
908
  fi
848
- echo "✅ $BRANCH merged into develop on origin (fast-forward, no PR)."
909
+ echo "✅ $BRANCH merged into $TRUNK on origin (fast-forward, no PR)."
849
910
 
850
911
  # 3. Delete the remote feature branch (best-effort — never fatal).
851
912
  git -C "$WORKTREE_PATH" push origin --delete "$BRANCH" 2>/dev/null || true
@@ -856,60 +917,62 @@ PR_NUMBER=""
856
917
 
857
918
  ---
858
919
 
859
- #### Common — sync local develop ref (both strategies)
920
+ #### Common — sync local trunk ref (both strategies)
860
921
 
861
922
  ```bash
862
- # Sync local develop ONLY if the main repo's HEAD is already develop.
863
- # NEVER `git checkout develop` here. If the user (or another terminal) had
864
- # develop checked out, ff-only updates it cleanly. Otherwise we leave their
865
- # HEAD alone and they'll pull next time.
923
+ # Sync the local trunk ($TRUNK) ONLY if the main repo's HEAD is already on it.
924
+ # NEVER `git checkout "$TRUNK"` here. If the user (or another terminal) had the
925
+ # trunk checked out, ff-only updates it cleanly. Otherwise we leave their HEAD
926
+ # alone and they'll pull next time. $TRUNK and $MAIN were resolved + presence-
927
+ # guarded in step 1 (read from the registry, R6).
866
928
  CURRENT_BRANCH=$(git -C "$MAIN" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
867
- if [ "$CURRENT_BRANCH" = "develop" ]; then
868
- if git -C "$MAIN" pull --ff-only origin develop 2>/dev/null; then
869
- echo "✅ Main repo develop fast-forwarded to origin/develop."
929
+ if [ "$CURRENT_BRANCH" = "$TRUNK" ]; then
930
+ if git -C "$MAIN" pull --ff-only origin "$TRUNK" 2>/dev/null; then
931
+ echo "✅ Main repo $TRUNK fast-forwarded to origin/$TRUNK."
870
932
  else
871
933
  # FF blocked. NEVER leave this as a passive "Leaving as-is" TODO for the user
872
934
  # (v3.39.0 — the finalizer MUST conclude). The cause is almost always an
873
935
  # uncommitted append to the framework-owned telemetry log left by a prior run.
874
936
  # Partition the dirty tree:
875
- # - framework-owned artifact (docs/metrics/skill-runs.jsonl) → auto-reconcile
937
+ # - framework-owned artifact (${paths.metrics}/skill-runs.jsonl) → auto-reconcile
876
938
  # (commit + rebase) so the ff completes with zero data loss;
877
939
  # - ANY foreign file → emit a decision marker the orchestrator MUST convert
878
940
  # into ONE explicit user gate. Never auto-commit work this run did not own.
941
+ METRICS_LOG="${paths.metrics}/skill-runs.jsonl" # paths.metrics default: docs/metrics
879
942
  DIRTY=$(git -C "$MAIN" status --porcelain --untracked-files=no | sed 's/^...//')
880
943
  FOREIGN=""
881
944
  OWNED=""
882
945
  for f in $DIRTY; do
883
946
  case "$f" in
884
- docs/metrics/skill-runs.jsonl) OWNED="$OWNED $f" ;;
947
+ "$METRICS_LOG") OWNED="$OWNED $f" ;;
885
948
  *) FOREIGN="$FOREIGN $f" ;;
886
949
  esac
887
950
  done
888
951
  if [ -n "$FOREIGN" ]; then
889
- echo "[SYNC-NEEDS-DECISION] main repo develop cannot fast-forward; foreign uncommitted files:${FOREIGN} — orchestrator MUST raise ONE explicit user gate (commit / user-handles), never a passive note."
952
+ echo "[SYNC-NEEDS-DECISION] main repo $TRUNK cannot fast-forward; foreign uncommitted files:${FOREIGN} — orchestrator MUST raise ONE explicit user gate (commit / user-handles), never a passive note."
890
953
  elif [ -n "$OWNED" ]; then
891
954
  # Only framework-owned telemetry is dirty → reconcile autonomously, lossless.
892
- git -C "$MAIN" add docs/metrics/skill-runs.jsonl
893
- git -C "$MAIN" commit -m "chore(metrics): reconcile skill-runs telemetry before develop sync" --quiet
894
- if git -C "$MAIN" pull --rebase origin develop 2>/dev/null; then
895
- git -C "$MAIN" push origin develop 2>/dev/null \
896
- && echo "✅ Reconciled stale metrics log + synced develop to origin." \
897
- || echo "✅ Reconciled stale metrics log (local develop 1 commit ahead; syncs on next push). Tree clean."
955
+ git -C "$MAIN" add "$METRICS_LOG"
956
+ git -C "$MAIN" commit -m "chore(metrics): reconcile skill-runs telemetry before $TRUNK sync" --quiet
957
+ if git -C "$MAIN" pull --rebase origin "$TRUNK" 2>/dev/null; then
958
+ git -C "$MAIN" push origin "$TRUNK" 2>/dev/null \
959
+ && echo "✅ Reconciled stale metrics log + synced $TRUNK to origin." \
960
+ || echo "✅ Reconciled stale metrics log (local $TRUNK 1 commit ahead; syncs on next push). Tree clean."
898
961
  else
899
962
  git -C "$MAIN" rebase --abort 2>/dev/null || true
900
- echo "[SYNC-NEEDS-DECISION] metrics rebase conflict on develop — orchestrator MUST raise ONE explicit user gate."
963
+ echo "[SYNC-NEEDS-DECISION] metrics rebase conflict on $TRUNK — orchestrator MUST raise ONE explicit user gate."
901
964
  fi
902
965
  else
903
- echo "[SYNC-NEEDS-DECISION] main repo develop cannot fast-forward and the working tree is clean (diverged?) — orchestrator MUST raise ONE explicit user gate."
966
+ echo "[SYNC-NEEDS-DECISION] main repo $TRUNK cannot fast-forward and the working tree is clean (diverged?) — orchestrator MUST raise ONE explicit user gate."
904
967
  fi
905
968
  fi
906
969
  else
907
- echo "ℹ️ Main repo HEAD is '$CURRENT_BRANCH' — not switching. Next manual `git pull` on develop will sync."
908
- git -C "$MAIN" fetch origin develop --quiet
970
+ echo "ℹ️ Main repo HEAD is '$CURRENT_BRANCH' — not switching. Next manual \`git pull\` on $TRUNK will sync."
971
+ git -C "$MAIN" fetch origin "$TRUNK" --quiet
909
972
  # Structured marker for upstream orchestrators (e.g. /new Phase 6c).
910
973
  # `/mw` standalone keeps fetch-only behavior (terminal isolation); orchestrators
911
974
  # parse this marker and route reconciliation through an explicit user gate.
912
- echo "[SYNC-DEFERRED] main repo HEAD=${CURRENT_BRANCH}, local develop not fast-forwarded — orchestrator workspace-hygiene phase must reconcile."
975
+ echo "[SYNC-DEFERRED] main repo HEAD=${CURRENT_BRANCH}, local $TRUNK not fast-forwarded — orchestrator workspace-hygiene phase must reconcile."
913
976
  fi
914
977
  ```
915
978
 
@@ -941,10 +1004,10 @@ git branch -d "$BRANCH"
941
1004
  ```
942
1005
 
943
1006
  ```bash
944
- # 3. Delete the remote branch. The branch is ALREADY MERGED into develop at this
1007
+ # 3. Delete the remote branch. The branch is ALREADY MERGED into the trunk at this
945
1008
  # point, so this is routine cleanup — NOT the gated "branch deletion" that needs
946
1009
  # owner approval (see AGENTS.md § branch deletion). Report the outcome; never
947
- # degrade a failure into a passive "puoi cancellarlo a mano" note handed to the user.
1010
+ # degrade a failure into a passive "you can delete it by hand" note handed to the user.
948
1011
  if git push origin --delete "$BRANCH" 2>/dev/null; then
949
1012
  echo "✅ Remote branch '$BRANCH' deleted (merged → routine cleanup)."
950
1013
  else
@@ -971,9 +1034,9 @@ Merge complete:
971
1034
  Branch: <BRANCH> (deleted on remote — merged branch deletion is routine cleanup, never gated)
972
1035
  Strategy: <pr | local-push>
973
1036
  PR: #<PR_NUMBER> merged via `gh pr merge` ← only in `pr` strategy
974
- Push: fast-forward to origin/develop ← only in `local-push` strategy
1037
+ Push: fast-forward to origin/<trunk> ← only in `local-push` strategy
975
1038
  Worktree: <WORKTREE_PATH> (removed)
976
- develop: remote up to date; local main-repo synced (auto-reconciled framework
1039
+ <trunk>: remote up to date; local main-repo synced (auto-reconciled framework
977
1040
  telemetry if it blocked the ff). Any [SYNC-NEEDS-DECISION] /
978
1041
  [SYNC-DEFERRED] marker above is for the orchestrator to gate on —
979
1042
  NOT a residue to hand the user as a manual TODO.
@@ -1010,7 +1073,7 @@ Active worktrees:
1010
1073
  ```
1011
1074
 
1012
1075
  **Stale detection**: Worktrees older than 7 days get a warning.
1013
- **Merged detection**: If the branch has been merged into develop (`git branch -a --merged develop`), flag as "already merged — safe to /cw".
1076
+ **Merged detection**: If the branch has been merged into the trunk branch — read each entry's `trunkBranch`, e.g. `git branch -a --merged "$TRUNK"` flag as "already merged — safe to /cw".
1014
1077
  **Orphan detection**: Worktrees in `git worktree list` but NOT in registry get flagged as "unregistered".
1015
1078
 
1016
1079
  ---
@@ -1028,8 +1091,8 @@ Remove worktrees without the full PR/merge flow. Use for:
1028
1091
  git worktree list
1029
1092
  ```
1030
1093
 
1031
- Cross-reference with registry. For each worktree, check:
1032
- - Is the branch merged into develop? → "safe to remove"
1094
+ Cross-reference with registry. For each worktree, check (using its `trunkBranch`):
1095
+ - Is the branch merged into the trunk branch? → "safe to remove"
1033
1096
  - Is the branch pushed to remote? → "has remote backup"
1034
1097
  - Are there uncommitted changes? → "has uncommitted work — DANGEROUS"
1035
1098
 
@@ -1038,7 +1101,7 @@ Cross-reference with registry. For each worktree, check:
1038
1101
  ```
1039
1102
  Worktrees available for cleanup:
1040
1103
 
1041
- 1. feat/FEAT-0200-menu-ranking — merged into develop ✓ safe
1104
+ 1. feat/FEAT-0200-menu-ranking — merged into trunk ✓ safe
1042
1105
  2. feat/BUG-0300-old-fix — not merged, pushed to remote
1043
1106
  3. feat/FEAT-0100-abandoned — not merged, uncommitted changes ✗ DANGEROUS
1044
1107
 
@@ -1085,16 +1148,16 @@ Cleanup complete:
1085
1148
 
1086
1149
  ## Safety Rules
1087
1150
 
1088
- - **NEVER `git checkout`, `git switch`, `git checkout -b`, or `git branch` on the main repo from inside `/mw` / `/nw` / `/cw`.** The main repo is a shared resource across parallel terminals; touching its `HEAD` preempts other agents and breaks worktree-driven workflows. Many consumer projects declare this as an absolute rule in CLAUDE.md and Claude Code's classifier will pattern-match and block these commands preemptively. Use the configured `git.merge_strategy` for develop merges and `git -C <main> pull --ff-only` (only when `HEAD = develop` already) for sync. See `/mw` step 4c.
1089
- - NEVER merge to `main` — only to `develop`, via the configured `git.merge_strategy` (`pr` → `gh pr merge`; `local-push` → `git push origin <feat>:develop`). NEVER via local checkout.
1151
+ - **NEVER `git checkout`, `git switch`, `git checkout -b`, or `git branch` on the main repo from inside `/mw` / `/nw` / `/cw`.** The main repo is a shared resource across parallel terminals; touching its `HEAD` preempts other agents and breaks worktree-driven workflows. Many consumer projects declare this as an absolute rule in CLAUDE.md and Claude Code's classifier will pattern-match and block these commands preemptively. Use the configured `git.merge_strategy` for trunk-branch merges and `git -C <main> pull --ff-only` (only when `HEAD = git.trunk_branch` already) for sync. See `/mw` step 4c.
1152
+ - NEVER merge to a production/release branch — only to the configured trunk branch (`git.trunk_branch`), via the configured `git.merge_strategy` (`pr` → `gh pr merge`; `local-push` → `git push origin <feat>:<git.trunk_branch>`). NEVER via local checkout.
1090
1153
  - NEVER force push from a worktree (`--force-with-lease` on the feature branch after rebase is the only allowed force variant).
1091
1154
  - NEVER `git add .` or `git add -A` — always explicit file names.
1092
1155
  - NEVER remove a worktree with uncommitted changes without explicit user confirmation.
1093
1156
  - NEVER delete a branch before successful merge verification.
1094
- - NEVER cleanup a worktree before confirming develop is stable post-merge.
1157
+ - NEVER cleanup a worktree before confirming the trunk branch is stable post-merge.
1095
1158
  - If build/lint/tsc fails during /mw, STOP and report. Do not skip checks.
1096
1159
  - If merge conflicts during /mw, STOP and ask the user. Do not auto-resolve.
1097
- - Always verify `.worktrees/` is in `.gitignore` before creating (already done in this project).
1160
+ - 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).
1098
1161
  - Commit lock protocol: Each worktree has its own `COMMIT_LOCK` in its git dir — no cross-worktree interference.
1099
1162
  - Port persistence: On dev server restart, reuse the port from registry (grep .env.local for PORT=) instead of picking a new one.
1100
1163
  - **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.