valent-pipeline 0.19.53 → 0.19.55
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/package.json +1 -1
- package/pipeline/orchestrators/claude-code/sprint.workflow.js +40 -10
- package/pipeline/steps/qa-a/self-attack.md +12 -0
- package/skills/valent-configure/SKILL.md +13 -1
- package/skills/valent-run-epic-workflow/SKILL.md +1 -1
- package/skills/valent-run-project-workflow/SKILL.md +1 -1
- package/skills/valent-run-story-workflow/SKILL.md +1 -1
- package/src/commands/init.js +4 -0
- package/src/lib/config-schema.js +6 -0
package/package.json
CHANGED
|
@@ -885,16 +885,27 @@ const parCfg = (() => {
|
|
|
885
885
|
const parallelEnabled = gitFlowEnabled && parCfg.enabled === true
|
|
886
886
|
const maxParallelStories = Math.max(1, Number.isInteger(parCfg.maxStories) ? parCfg.maxStories
|
|
887
887
|
: Number.isInteger(parCfg.max_stories) ? parCfg.max_stories : 2)
|
|
888
|
+
// Worktree ISOLATION (review 2026-06-24, M-152): each story builds in its OWN worktree under
|
|
889
|
+
// worktreeDir/<id> instead of the shared main checkout. Parallel mode REQUIRES it (concurrent
|
|
890
|
+
// stories cannot share one checkout); serial mode can opt in via `git.worktree_isolation: true` to
|
|
891
|
+
// stop cross-story working-tree residue from bleeding into a story's review-pin `git add -A` sweep
|
|
892
|
+
// (the contamination that swept 3.10 files into story/3.1). Isolation is DECOUPLED from CONCURRENCY:
|
|
893
|
+
// the ship-lock, work queue, and post-merge re-verify stay keyed on `parallelEnabled` (concurrency),
|
|
894
|
+
// while the worktree create/cd/workdir paths + --require-evidence-on-assert (evidence must be
|
|
895
|
+
// self-contained in the worktree) key on THIS. Parallel implies isolation. Default off => no
|
|
896
|
+
// behavior change until a project opts in at a clean boundary.
|
|
897
|
+
const worktreeIsolation = gitFlowEnabled
|
|
898
|
+
&& (gitCfg.worktree_isolation === true || gitCfg.worktreeIsolation === true || parallelEnabled)
|
|
888
899
|
const worktreeDir = (typeof (parCfg.worktreeDir ?? parCfg.worktree_dir) === 'string'
|
|
889
900
|
&& (parCfg.worktreeDir ?? parCfg.worktree_dir).trim()) || '.valent-worktrees'
|
|
890
|
-
const wtFlag =
|
|
901
|
+
const wtFlag = worktreeIsolation
|
|
891
902
|
? ` --worktree${worktreeDir !== '.valent-worktrees' ? ` --worktree-dir ${worktreeDir}` : ''}`
|
|
892
903
|
: ''
|
|
893
904
|
const storyWorkDir = (sid) => `${worktreeDir}/${sid}`
|
|
894
|
-
// Where a story's commands run: its worktree
|
|
905
|
+
// Where a story's commands run: its own worktree when isolation is on, the project root otherwise.
|
|
895
906
|
// `inWorktree` is false for sprint-level runners (backlog/integration) and the revalidation path.
|
|
896
907
|
const runDirPhrase = (sid, inWorktree = true) =>
|
|
897
|
-
|
|
908
|
+
worktreeIsolation && inWorktree ? `the story worktree at \`${storyWorkDir(sid)}\` (cd into it FIRST)` : 'the project root'
|
|
898
909
|
|
|
899
910
|
// Ship/leave mutex + ORDERED ship queue (review 2026-06-10 #9): merges mutate the shared target
|
|
900
911
|
// ref + main working tree — strictly one at a time, and in QUEUE ORDER, not completion order, so
|
|
@@ -1762,10 +1773,10 @@ async function runStory(story) {
|
|
|
1762
1773
|
}
|
|
1763
1774
|
}
|
|
1764
1775
|
|
|
1765
|
-
//
|
|
1776
|
+
// When isolation is on, every per-story agent works inside the story's worktree (the revalidation
|
|
1766
1777
|
// and re-ship paths are the exceptions — they work the MAIN tree: the fix already merged there /
|
|
1767
1778
|
// sync-story checks the retained branch out there).
|
|
1768
|
-
const storyWorkCopy =
|
|
1779
|
+
const storyWorkCopy = worktreeIsolation && !revalidate && !reshipOnly ? storyWorkDir(storyId) : null
|
|
1769
1780
|
const spawn = (role, promptFile, taskSubject, opts = {}) =>
|
|
1770
1781
|
agent(buildPrompt({ role, promptFile, storyId, taskSubject, workDir: storyWorkCopy, ...opts }), {
|
|
1771
1782
|
label: opts.label || `${role.toLowerCase()}:${storyId}`,
|
|
@@ -2076,6 +2087,25 @@ async function runStory(story) {
|
|
|
2076
2087
|
if (has('uxa')) {
|
|
2077
2088
|
await spawn('UXA', 'uxa.md', 'Translate the brief into uxa-spec.md.', { phase: 'Spec' })
|
|
2078
2089
|
}
|
|
2090
|
+
// Fix D (rework-reduction, 2026-06-24): QA-A proves its acceptance suite is honestly RED BEFORE
|
|
2091
|
+
// handoff, instead of the RED gate discovering a premature-pass a cycle later — after the dev
|
|
2092
|
+
// fan-out is already staged (the 2.29 class: 10 cycles, an escalation, a build break, zero
|
|
2093
|
+
// implementation burned). The lever mirrors the dev pre-handoff self-gate (Fix #3): thread the
|
|
2094
|
+
// SAME acceptance command the RED gate will run into QA-A's authoring turn so it self-checks in
|
|
2095
|
+
// context. Deliberately the RAW command (not the `evidence run` wrapper) — QA-A only needs to
|
|
2096
|
+
// OBSERVE pass/fail, and minting a labeled `acceptance` assert here would shadow the baseline the
|
|
2097
|
+
// RED/green/evidence gates freeze against. The RED gate stays the mechanical backstop. atddEnabled
|
|
2098
|
+
// + args only => journal-replay safe.
|
|
2099
|
+
const qaaRedSelfCheck = atddEnabled
|
|
2100
|
+
? ` Then prove the suite is honestly RED before handoff (Fix D — shift-left the RED gate): run ` +
|
|
2101
|
+
`\`${subCmd(atddCommandTpl, storyId, effectiveWorkspaces)}\` against the current (unimplemented) tree. ` +
|
|
2102
|
+
`Every \`required: true\` case MUST run and FAIL for a behavioral reason (404 / empty / missing element) — ` +
|
|
2103
|
+
`exactly what the RED gate will demand. A required case that PASSES now is a premature-pass spec bug: ` +
|
|
2104
|
+
`re-target it to assert a NEW endpoint/element this story has not built yet, or — if it only guards an ` +
|
|
2105
|
+
`already-established infrastructure invariant — mark it \`required: false\` ` +
|
|
2106
|
+
`(steps/qa-a/write-acceptance-tests.md Step C), never weakening or deleting the guard. Fix every ` +
|
|
2107
|
+
`premature-pass before handoff so the RED gate passes first-try.`
|
|
2108
|
+
: ''
|
|
2079
2109
|
await spawn('QA-A', 'qa-a.md',
|
|
2080
2110
|
'Produce qa-test-spec.md before any code is written.' +
|
|
2081
2111
|
(atddEnabled
|
|
@@ -2083,7 +2113,7 @@ async function runStory(story) {
|
|
|
2083
2113
|
`under ${atddDir}/${storyId}/ — one+ per AC, TC id in the test title, black-box against the running system, ` +
|
|
2084
2114
|
`no mocks — plus the manifest kind:"acceptance"/testFile/files[] fields. The prose spec is companion notes. ` +
|
|
2085
2115
|
`Then self-attack your tests (steps/qa-a/self-attack.md) and fix what you find before handoff.`
|
|
2086
|
-
: ''),
|
|
2116
|
+
: '') + qaaRedSelfCheck,
|
|
2087
2117
|
{ phase: 'Spec' })
|
|
2088
2118
|
|
|
2089
2119
|
phase('SpecCheck')
|
|
@@ -2721,10 +2751,10 @@ async function runStory(story) {
|
|
|
2721
2751
|
const commands = [
|
|
2722
2752
|
`node .valent-pipeline/bin/cli.js evidence pin --story ${sid} --gate evidence`,
|
|
2723
2753
|
`node .valent-pipeline/bin/cli.js trace check --story ${sid} --label ${evidenceLabel}`,
|
|
2724
|
-
// --require-evidence
|
|
2725
|
-
// accidentally run at the main root would otherwise find nothing and no-op PASS. Serial
|
|
2726
|
-
// keeps the graceful no-op (the legacy/pre-evidence story path).
|
|
2727
|
-
`node .valent-pipeline/bin/cli.js evidence assert --story ${sid} --label ${evidenceLabel}${pinsFlag}${
|
|
2754
|
+
// --require-evidence when isolation is on (review #37): evidence lives in the WORKTREE — a gate
|
|
2755
|
+
// accidentally run at the main root would otherwise find nothing and no-op PASS. Serial WITHOUT
|
|
2756
|
+
// isolation keeps the graceful no-op (the legacy/pre-evidence story path).
|
|
2757
|
+
`node .valent-pipeline/bin/cli.js evidence assert --story ${sid} --label ${evidenceLabel}${pinsFlag}${worktreeIsolation ? ' --require-evidence' : ''}`,
|
|
2728
2758
|
]
|
|
2729
2759
|
let rejections = 0
|
|
2730
2760
|
let reverify = null // last rework re-review (pin coherence v2) — open Highs ride to JUDGE
|
|
@@ -12,3 +12,15 @@ Probe each acceptance case as the laziest implementer who wants green with minim
|
|
|
12
12
|
- **Implementation sniffing (ATDD):** the dev can read your tests. Avoid magic fixture values an implementation could special-case; randomize or parameterize identifying data where cheap. (CRITIC's acceptance pass audits the implementation side of this.)
|
|
13
13
|
|
|
14
14
|
Rate residual gameability **low | medium | high** per AC. HIGH means you have not finished — strengthen the test and re-rate. Record the final ratings + what you hardened in `qa-test-spec.md#self-attack` so CRITIC and JUDGE see what was already attacked.
|
|
15
|
+
|
|
16
|
+
## Prove the suite is RED before handoff (ATDD)
|
|
17
|
+
|
|
18
|
+
This is the mechanical close-out, run AFTER the gameability pass above. The orchestrator hands you the exact acceptance command to run — execute it against the current, **unimplemented** tree and read the result:
|
|
19
|
+
|
|
20
|
+
- **Every `required: true` case MUST run and FAIL for a behavioral reason** — a 404, an empty/absent response, a missing element. That is precisely what the RED gate checks a cycle later; proving it here means the gate passes first-try instead of bouncing your spec back after the dev fan-out is already staged.
|
|
21
|
+
- **A `required: true` case that PASSES now is a premature-pass spec bug.** It does not assert anything this story builds — it asserts behavior that already exists. Two fixes, both before handoff:
|
|
22
|
+
- **Re-target it** so it asserts a NEW endpoint/element/field this story has not built yet (e.g. route the assertion through a new path that 404s until implemented). This is the right fix when the case is meant to prove new behavior.
|
|
23
|
+
- **Or mark it `required: false`** (write-acceptance-tests.md Step C) when it only guards an already-established infrastructure invariant — still executed and asserted at the GREEN gate, but exempt from the red-required set. **Never weaken or delete the guard** — just make red honest.
|
|
24
|
+
- A required case that **errors for a non-behavioral reason** (import failure, harness misconfig, syntax) is not honest red either — fix the test so it fails *because the feature is absent*, not because it cannot run.
|
|
25
|
+
|
|
26
|
+
Do not hand off while any `required: true` case passes against the unimplemented tree. The RED gate will dead-end the story if you do; catching it here costs one self-run instead of a full round-trip.
|
|
@@ -82,7 +82,19 @@ Present defaults and let the user adjust:
|
|
|
82
82
|
| `retrospective_every_n_stories` | `5` | How often the Retrospective Agent runs (range: 3-10) |
|
|
83
83
|
| `stall_threshold_minutes` | `15` | Minutes before a stalled agent gets a check-in (range: 5-30) |
|
|
84
84
|
|
|
85
|
-
### Step 4b:
|
|
85
|
+
### Step 4b: Worktree isolation (`git.worktree_isolation`)
|
|
86
|
+
|
|
87
|
+
Off by default. When on, each story builds in its OWN git worktree (under
|
|
88
|
+
`git.parallelism.worktree_dir/<id>`) even in serial mode — so cross-story working-tree residue
|
|
89
|
+
can't bleed into a story's review-pin `git add -A` sweep and ship under the wrong story. It is the
|
|
90
|
+
precondition parallelism builds on (parallel mode forces it on); enabling it serially is the safe
|
|
91
|
+
first step toward parallel execution.
|
|
92
|
+
|
|
93
|
+
| Setting | Default | Description |
|
|
94
|
+
|---------|---------|-------------|
|
|
95
|
+
| `git.worktree_isolation` | `false` | Build each story in its own worktree even when serial (parallel forces on) |
|
|
96
|
+
|
|
97
|
+
### Step 4c: Parallel story execution (`git.parallelism`)
|
|
86
98
|
|
|
87
99
|
Off by default (`enabled: false` = strictly sequential). Turning it on runs up to `max_stories`
|
|
88
100
|
stories concurrently, each in its own git worktree under `worktree_dir` (default
|
|
@@ -170,7 +170,7 @@ Feed the planned batch straight into `sprint.workflow.js`:
|
|
|
170
170
|
```js
|
|
171
171
|
Workflow({
|
|
172
172
|
scriptPath: '.valent-pipeline/orchestrators/claude-code/sprint.workflow.js',
|
|
173
|
-
args: { stories: <plan output .stories>, backlogPath: '{backlog_path}', maxRejectionCycles: {quality.max_rejection_cycles or 3}, maxJudgeRejects: {quality.max_judge_rejects or 2}, targetedReverify: {quality.targeted_reverify or false}, models: <config.models or omit>, reasoning: <config.reasoning or omit>, preCriticGate: <config.pre_critic_gate or omit>, packGate: <config.pack_gate or omit>, evidenceGate: <config.evidence_gate or omit>, atdd: <config.atdd or omit>, monorepo: <config.monorepo or omit>, git: { enabled: <config.git.enabled ?? true>, targetBranch: <config.git.target_branch or ''>, storyBranchPrefix: <config.git.story_branch_prefix or 'story/'>, parallelism: <config.git.parallelism or omit> }, ref: <config.ref or omit>, knowledge: { mode: <config.knowledge.mode or 'sqlite'>, curatedPath: <config.knowledge.curated_files_path or 'knowledge/curated'> }, visual: { onMissingMcp: <config.visual.on_missing_mcp or 'block'>, browserMcp: <tech_stack.browser_automation_mcp or 'playwright-mcp'> } }
|
|
173
|
+
args: { stories: <plan output .stories>, backlogPath: '{backlog_path}', maxRejectionCycles: {quality.max_rejection_cycles or 3}, maxJudgeRejects: {quality.max_judge_rejects or 2}, targetedReverify: {quality.targeted_reverify or false}, models: <config.models or omit>, reasoning: <config.reasoning or omit>, preCriticGate: <config.pre_critic_gate or omit>, packGate: <config.pack_gate or omit>, evidenceGate: <config.evidence_gate or omit>, atdd: <config.atdd or omit>, monorepo: <config.monorepo or omit>, git: { enabled: <config.git.enabled ?? true>, targetBranch: <config.git.target_branch or ''>, storyBranchPrefix: <config.git.story_branch_prefix or 'story/'>, worktreeIsolation: <config.git.worktree_isolation ?? false>, parallelism: <config.git.parallelism or omit> }, ref: <config.ref or omit>, knowledge: { mode: <config.knowledge.mode or 'sqlite'>, curatedPath: <config.knowledge.curated_files_path or 'knowledge/curated'> }, visual: { onMissingMcp: <config.visual.on_missing_mcp or 'block'>, browserMcp: <tech_stack.browser_automation_mcp or 'playwright-mcp'> } }
|
|
174
174
|
})
|
|
175
175
|
```
|
|
176
176
|
|
|
@@ -178,7 +178,7 @@ Feed the planned batch straight into `sprint.workflow.js`:
|
|
|
178
178
|
```js
|
|
179
179
|
Workflow({
|
|
180
180
|
scriptPath: '.valent-pipeline/orchestrators/claude-code/sprint.workflow.js',
|
|
181
|
-
args: { stories: <plan output .stories>, backlogPath: '{backlog_path}', maxRejectionCycles: {quality.max_rejection_cycles or 3}, maxJudgeRejects: {quality.max_judge_rejects or 2}, targetedReverify: {quality.targeted_reverify or false}, models: <config.models or omit>, reasoning: <config.reasoning or omit>, preCriticGate: <config.pre_critic_gate or omit>, packGate: <config.pack_gate or omit>, evidenceGate: <config.evidence_gate or omit>, atdd: <config.atdd or omit>, monorepo: <config.monorepo or omit>, git: { enabled: <config.git.enabled ?? true>, targetBranch: <config.git.target_branch or ''>, storyBranchPrefix: <config.git.story_branch_prefix or 'story/'>, parallelism: <config.git.parallelism or omit> }, ref: <config.ref or omit>, knowledge: { mode: <config.knowledge.mode or 'sqlite'>, curatedPath: <config.knowledge.curated_files_path or 'knowledge/curated'> }, visual: { onMissingMcp: <config.visual.on_missing_mcp or 'block'>, browserMcp: <tech_stack.browser_automation_mcp or 'playwright-mcp'> } }
|
|
181
|
+
args: { stories: <plan output .stories>, backlogPath: '{backlog_path}', maxRejectionCycles: {quality.max_rejection_cycles or 3}, maxJudgeRejects: {quality.max_judge_rejects or 2}, targetedReverify: {quality.targeted_reverify or false}, models: <config.models or omit>, reasoning: <config.reasoning or omit>, preCriticGate: <config.pre_critic_gate or omit>, packGate: <config.pack_gate or omit>, evidenceGate: <config.evidence_gate or omit>, atdd: <config.atdd or omit>, monorepo: <config.monorepo or omit>, git: { enabled: <config.git.enabled ?? true>, targetBranch: <config.git.target_branch or ''>, storyBranchPrefix: <config.git.story_branch_prefix or 'story/'>, worktreeIsolation: <config.git.worktree_isolation ?? false>, parallelism: <config.git.parallelism or omit> }, ref: <config.ref or omit>, knowledge: { mode: <config.knowledge.mode or 'sqlite'>, curatedPath: <config.knowledge.curated_files_path or 'knowledge/curated'> }, visual: { onMissingMcp: <config.visual.on_missing_mcp or 'block'>, browserMcp: <tech_stack.browser_automation_mcp or 'playwright-mcp'> } }
|
|
182
182
|
})
|
|
183
183
|
```
|
|
184
184
|
|
|
@@ -29,7 +29,7 @@ If no argument is provided, resolve the next work item from the backlog (see Ste
|
|
|
29
29
|
|
|
30
30
|
### Step 1: Load Pipeline Config
|
|
31
31
|
|
|
32
|
-
Read `.valent-pipeline/pipeline-config.yaml` and resolve the variables you need below (project type, paths, quality caps, `tech_stack`, `knowledge`). Capture `project.type` — it feeds `--project-type` in Step 1b, and the resolved candidate object then carries `projectType`, `profiles`, and the execution-mode flags for Step 4 (when the user gave an explicit story argument instead, run the Step 1b resolver command anyway and select that story's candidate object from its buckets). Also capture the entire `models` section (the `{ opus:[...], sonnet:[...], haiku:[...] }` tier→roles map) — pass it as the `models` arg so per-agent model tiers stay config-driven (editable via `/valent-configure`). If the config has no `models` section, omit the arg and the workflow uses its baked-in default assignment. Likewise capture the `reasoning` section (the `{ ultrathink:[...], 'think-harder':[...], 'think-hard':[...], think:[...] }` level→roles map) and pass it as the `reasoning` arg; it is blank by default (injects nothing). Omit the arg if the section is absent or all levels are empty. Also capture the `pre_critic_gate` section (deterministic lint/type/static checks) and pass it as the `preCriticGate` arg; omit it if absent or its `commands` list is empty (the gate is then a no-op). Likewise capture the `pack_gate` section (run-level pack-time freshness checks — they run ONCE at sprint intake, before the story spends tokens) and pass it as the `packGate` arg; omit it if absent or its `commands` list is empty. Capture the `atdd` section and pass it as the `atdd` arg — it drives the RED gate (pre-implementation acceptance-suite run that must fail) and the GREEN gate (post-CRITIC re-run that must pass with the acceptance sources unchanged); omit it if absent or its `command` is blank (both gates are then a loud no-op). Capture the `git` section and pass it as the `git` arg `{ enabled: <git.enabled ?? true>, targetBranch: <git.target_branch or ''>, storyBranchPrefix: <git.story_branch_prefix or 'story/'>, parallelism: <git.parallelism or omit> }` — it drives the story git flow (branch off the target, full commit trail, merge --no-ff on SHIP); `parallelism` ({ enabled, max_stories, worktree_dir, setup_commands }) additionally enables concurrent stories in worktrees (off by default). Omit the whole arg only to accept the defaults (flow ON, parallelism OFF, graceful no-op outside a git repo). Capture the `visual` section together with `tech_stack.browser_automation_mcp` and pass them as the `visual` arg `{ onMissingMcp: <visual.on_missing_mcp or 'block'>, browserMcp: <tech_stack.browser_automation_mcp or 'playwright-mcp'> }` — this drives the Visual (PMCP) phase for `ui`-profile stories.
|
|
32
|
+
Read `.valent-pipeline/pipeline-config.yaml` and resolve the variables you need below (project type, paths, quality caps, `tech_stack`, `knowledge`). Capture `project.type` — it feeds `--project-type` in Step 1b, and the resolved candidate object then carries `projectType`, `profiles`, and the execution-mode flags for Step 4 (when the user gave an explicit story argument instead, run the Step 1b resolver command anyway and select that story's candidate object from its buckets). Also capture the entire `models` section (the `{ opus:[...], sonnet:[...], haiku:[...] }` tier→roles map) — pass it as the `models` arg so per-agent model tiers stay config-driven (editable via `/valent-configure`). If the config has no `models` section, omit the arg and the workflow uses its baked-in default assignment. Likewise capture the `reasoning` section (the `{ ultrathink:[...], 'think-harder':[...], 'think-hard':[...], think:[...] }` level→roles map) and pass it as the `reasoning` arg; it is blank by default (injects nothing). Omit the arg if the section is absent or all levels are empty. Also capture the `pre_critic_gate` section (deterministic lint/type/static checks) and pass it as the `preCriticGate` arg; omit it if absent or its `commands` list is empty (the gate is then a no-op). Likewise capture the `pack_gate` section (run-level pack-time freshness checks — they run ONCE at sprint intake, before the story spends tokens) and pass it as the `packGate` arg; omit it if absent or its `commands` list is empty. Capture the `atdd` section and pass it as the `atdd` arg — it drives the RED gate (pre-implementation acceptance-suite run that must fail) and the GREEN gate (post-CRITIC re-run that must pass with the acceptance sources unchanged); omit it if absent or its `command` is blank (both gates are then a loud no-op). Capture the `git` section and pass it as the `git` arg `{ enabled: <git.enabled ?? true>, targetBranch: <git.target_branch or ''>, storyBranchPrefix: <git.story_branch_prefix or 'story/'>, worktreeIsolation: <git.worktree_isolation ?? false>, parallelism: <git.parallelism or omit> }` — it drives the story git flow (branch off the target, full commit trail, merge --no-ff on SHIP); `worktreeIsolation` builds each story in its own worktree even when serial (parallel mode forces it on); `parallelism` ({ enabled, max_stories, worktree_dir, setup_commands }) additionally enables concurrent stories in worktrees (off by default). Omit the whole arg only to accept the defaults (flow ON, parallelism OFF, graceful no-op outside a git repo). Capture the `visual` section together with `tech_stack.browser_automation_mcp` and pass them as the `visual` arg `{ onMissingMcp: <visual.on_missing_mcp or 'block'>, browserMcp: <tech_stack.browser_automation_mcp or 'playwright-mcp'> }` — this drives the Visual (PMCP) phase for `ui`-profile stories.
|
|
33
33
|
|
|
34
34
|
### Step 1b: Resolve Next Work Item (when no argument provided)
|
|
35
35
|
|
package/src/commands/init.js
CHANGED
|
@@ -546,6 +546,10 @@ git:
|
|
|
546
546
|
# Blank = whatever branch the sprint starts on (recorded per story by git start-story).
|
|
547
547
|
target_branch: "${config.git.target_branch}"
|
|
548
548
|
story_branch_prefix: "${config.git.story_branch_prefix}"
|
|
549
|
+
# Worktree isolation: build each story in its own worktree (under parallelism.worktree_dir/<id>)
|
|
550
|
+
# even when serial, so cross-story working-tree residue can't ship under the wrong story. Parallel
|
|
551
|
+
# mode forces this ON. OFF by default for serial runs — opt in at a clean boundary.
|
|
552
|
+
worktree_isolation: ${config.git.worktree_isolation ?? false}
|
|
549
553
|
# Parallel story execution (worktrees): up to max_stories run concurrently, each in its own git
|
|
550
554
|
# worktree under worktree_dir; the main checkout stays on the target, merges are serialized, and
|
|
551
555
|
# each ship gets a post-merge acceptance verify (ATDD armed). OFF by default — sequential sprint.
|
package/src/lib/config-schema.js
CHANGED
|
@@ -528,6 +528,12 @@ export const defaults = {
|
|
|
528
528
|
// (recorded per story by `git start-story`, so ship-story returns to the same place).
|
|
529
529
|
target_branch: '',
|
|
530
530
|
story_branch_prefix: 'story/',
|
|
531
|
+
// Worktree isolation: build each story in its OWN worktree (under parallelism.worktree_dir/<id>)
|
|
532
|
+
// even when running serially, so cross-story working-tree residue can't bleed into a story's
|
|
533
|
+
// review-pin `git add -A` sweep and ship under the wrong story. Parallel mode forces this ON
|
|
534
|
+
// (concurrent stories cannot share a checkout). OFF by default for serial runs — opt in at a
|
|
535
|
+
// clean boundary; it's the precondition the parallelism rollout builds on.
|
|
536
|
+
worktree_isolation: false,
|
|
531
537
|
// Parallel story execution: up to max_stories run concurrently, each in its own git worktree
|
|
532
538
|
// (main checkout stays on the target; merges are serialized; each ship gets a post-merge
|
|
533
539
|
// acceptance verify when ATDD is armed). OFF by default — the sprint is strictly sequential.
|