@deftai/directive-content 0.87.0 → 0.88.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 (40) hide show
  1. package/.agents/skills/deft-directive-swarm/SKILL.md +1 -996
  2. package/QUICK-START.md +4 -0
  3. package/Taskfile.yml +1 -0
  4. package/UPGRADING.md +24 -2
  5. package/coding/coding.md +5 -1
  6. package/commands.md +1 -1
  7. package/contracts/runtime-authority.md +34 -8
  8. package/docs/getting-started.md +4 -0
  9. package/docs/no-deft-directive.md +87 -0
  10. package/docs/openclaw-agent-host.md +34 -1
  11. package/docs/product-signal.md +2 -0
  12. package/docs/writing-ste100.md +53 -0
  13. package/glossary.md +37 -39
  14. package/package.json +2 -1
  15. package/packs/rules/rules-pack-0.1.json +25 -1
  16. package/packs/skills/skills-pack-0.1.json +24 -24
  17. package/packs/strategies/strategies-pack-0.1.json +4 -4
  18. package/skills/deft-directive-setup/SKILL.md +35 -8
  19. package/skills/deft-directive-swarm/SKILL.md +81 -978
  20. package/skills/deft-directive-swarm/references/core-ops.md +144 -0
  21. package/skills/deft-directive-swarm/references/core-phase-0.md +200 -0
  22. package/skills/deft-directive-swarm/references/core-phase-1-2.md +73 -0
  23. package/skills/deft-directive-swarm/references/core-phase-3.md +145 -0
  24. package/skills/deft-directive-swarm/references/core-phase-4.md +71 -0
  25. package/skills/deft-directive-swarm/references/core-phase-5-6.md +317 -0
  26. package/skills/deft-directive-swarm/references/host-cursor.md +25 -0
  27. package/skills/deft-directive-swarm/references/host-generic.md +27 -0
  28. package/skills/deft-directive-swarm/references/host-grok-build.md +37 -0
  29. package/skills/deft-directive-swarm/references/host-openclaw.md +70 -0
  30. package/skills/deft-directive-swarm/references/host-warp.md +37 -0
  31. package/skills/deft-directive-write-skill/SKILL.md +17 -0
  32. package/strategies/artifact-guards.md +24 -14
  33. package/strategies/discuss.md +40 -1
  34. package/strategies/interview.md +103 -30
  35. package/strategies/probe.md +27 -1
  36. package/tasks/engine-invoke.cjs +69 -13
  37. package/tasks/engine-invoke.test.cjs +188 -0
  38. package/tasks/verify.yml +7 -0
  39. package/templates/agent-prompt-preamble.md +6 -0
  40. package/vbrief/vbrief.md +4 -2
@@ -0,0 +1,37 @@
1
+ # Host adapter: Warp
2
+
3
+ Legend (RFC2119): !=MUST, ~=SHOULD, ≉=SHOULD NOT, ⊗=MUST NOT, ?=MAY.
4
+
5
+ Descriptor: `warp-orchestrated` (`start_agent`) or `warp-manual` (`WARP_*` without `start_agent`).
6
+
7
+ Load this file only after detect selects Warp. Do not load other host adapters.
8
+
9
+ ### Step 2a: Orchestrated Launch (start_agent available)
10
+
11
+ ! When `start_agent` is detected in the tool set, use it directly to launch each agent.
12
+
13
+ - ! Launch one agent per worktree using `start_agent` with the generated prompt and worktree path as the working directory
14
+ - ! Agents inherit the current environment's MCP servers, Warp Drive rules, and codebase index — equivalent to interactive Warp tabs but without manual tab management
15
+ - ! No user intervention needed — launch is fully automated
16
+ - ~ This is the preferred path: richest context with zero manual overhead
17
+
18
+ ### Step 2b: Interactive Warp Tabs (start_agent unavailable, Warp detected)
19
+
20
+ ! When `start_agent` is not available but Warp is detected (via `WARP_*` environment variables), fall back to manual Warp tab launch — briefly note that orchestrated launch is not available in this session, then proceed with the tab instructions below.
21
+
22
+ ! **Warp tabs cannot be opened programmatically.** There is no API or CLI command to open a new Warp terminal tab from an agent or script.
23
+
24
+ Ask the user to open N new Warp terminal tabs. For each tab, the user:
25
+ 1. Navigates to the worktree: `cd <worktree>`
26
+ 2. Pastes the prompt directly into the **Warp agent chat input** (not the terminal)
27
+
28
+ **Context advantages of Warp tabs:**
29
+ - Global Warp Drive rules (personal rules auto-injected)
30
+ - MCP servers via UUID (GitHub, etc. — zero-config)
31
+ - Warp Drive notebooks, workflows, and other auto-injected context
32
+ - Warm codebase index from the active Warp session (no cold-start delay)
33
+ - Agent is interruptible and steerable mid-run
34
+
35
+ **Tradeoff:** Requires the user to manually open and manage one Warp tab per agent.
36
+
37
+ ? If not running inside Warp at all (no `WARP_*` variables, no `start_agent`), use the same tab approach but with any terminal emulator — the user pastes prompts into their preferred terminal or agent interface.
@@ -162,6 +162,23 @@ The description is **the only thing the agent sees** when deciding whether to lo
162
162
 
163
163
  - ⊗ Omitting RFC2119 notation — deft skills use it consistently
164
164
  - ⊗ Putting all content in SKILL.md when it exceeds 150 lines — split into `references/*.md` or `REFERENCE.md`
165
+
166
+ - ⊗ Putting every agent-host branch (Warp, Cursor, OpenClaw, Grok Build, generic) in one always-loaded SKILL body when a host-neutral core + per-host adapter split is feasible (#2928)
167
+
168
+ ---
169
+
170
+ ## Host-adapter skills (multi-host) (#2928)
171
+
172
+ Large skills that branch on **agent host** (the runtime: Warp, Cursor, OpenClaw, Grok Build, generic-terminal) SHOULD use progressive host adapters:
173
+
174
+ 1. ! Keep a **thin host-neutral core** in `SKILL.md` (triggers, phase overview, detect, route table, cross-host MUST gates).
175
+ 2. ! Put shared depth in `references/core-*.md`.
176
+ 3. ! Put each host path in `references/host-<id>.md` (spawn primitive, cwd rules, monitor liveness, host MUST NOT list).
177
+ 4. ! Default load path: detect host → read core + **one** adapter.
178
+ 5. ⊗ Load all host adapters “just in case.”
179
+
180
+ Exemplar: `skills/deft-directive-swarm/` (route table in SKILL + `references/host-openclaw.md` etc.).
181
+
165
182
  - ⊗ Vague trigger phrases — use phrases the user would actually type
166
183
  - ⊗ Overlapping triggers without negative triggers — add `Do NOT trigger on …` to the description
167
184
  - ⊗ Naming a GitHub-integrated skill without `gh` in the name
@@ -18,7 +18,7 @@ Legend (from RFC2119): !=MUST, ~=SHOULD, ≉=SHOULD NOT, ⊗=MUST NOT, ?=MAY.
18
18
 
19
19
  This file is not a standalone strategy — it is a shared guard referenced by other strategies. It activates automatically when a strategy writes to vBRIEF artifacts.
20
20
 
21
- - ! Referenced by spec-generating strategies: [speckit](./speckit.md), [enterprise](./enterprise.md), [rapid](./rapid.md)
21
+ - ! Referenced by spec-generating strategies: [speckit](./speckit.md), [enterprise](./enterprise.md), [rapid](./rapid.md), [interview](./interview.md), [yolo](./yolo.md)
22
22
  - ! Referenced by preparatory strategies: [bdd](./bdd.md), [discuss](./discuss.md), [research](./research.md), [map](./map.md), [probe](./probe.md)
23
23
 
24
24
  ## Workflow
@@ -32,11 +32,16 @@ This file is not a standalone strategy — it is a shared guard referenced by ot
32
32
 
33
33
  ## Spec-Generating Guard (Full)
34
34
 
35
- Applies to strategies that write to `PROJECT-DEFINITION.vbrief.json` (⊗ never `specification.vbrief.json` per [v0-20-contract.md](./v0-20-contract.md)): **speckit**, **enterprise**, **rapid**.
35
+ Applies to strategies that write project identity as `PROJECT-DEFINITION` (⊗ never `specification.vbrief.json` / `specification.xbrief.json` per [v0-20-contract.md](./v0-20-contract.md)): **speckit**, **enterprise**, **rapid**, **interview**, **yolo**.
36
36
 
37
- ! Before writing to `vbrief/PROJECT-DEFINITION.vbrief.json`, the strategy MUST perform the following checks (⊗ Never target the legacy `specification.vbrief.json`):
37
+ ! **Target resolution (xbrief-first)**: guard the file that is the live project identity:
38
+ 1. `./xbrief/PROJECT-DEFINITION.xbrief.json` when it exists (canonical current layout)
39
+ 2. else `./vbrief/PROJECT-DEFINITION.vbrief.json` when it exists (legacy layout)
40
+ 3. else the intended write path for a greenfield create (usually `./xbrief/PROJECT-DEFINITION.xbrief.json`)
38
41
 
39
- 1. ! **Check existence**: Does the target file already exist?
42
+ ! Before writing or updating that target, the strategy MUST perform the following checks (⊗ Never target the legacy `specification.*.json`):
43
+
44
+ 1. ! **Check existence**: Does the resolved target file already exist?
40
45
  - If NO: proceed with the write — no guard needed.
41
46
  - If YES: continue to step 2.
42
47
 
@@ -51,35 +56,40 @@ Applies to strategies that write to `PROJECT-DEFINITION.vbrief.json` (⊗ never
51
56
  - ! Display: "A `{status}` version of this artifact already exists. Replace it? (yes/no)"
52
57
  - ~ If the user declines, offer to enrich (merge narratives) instead of replacing
53
58
 
54
- 5. ! **Narrative preservation**: When overwriting `PROJECT-DEFINITION.vbrief.json`, read existing `narratives` keys first. If the strategy only writes a subset of narratives (e.g. speckit Phase 1 writes only `Principles`), merge the new narrative into the existing set — do NOT replace all narratives with only the new key.
55
- - ⊗ Overwrite all `PROJECT-DEFINITION.vbrief.json` narratives when the strategy only produces a single narrative key
59
+ 5. ! **Narrative preservation**: When overwriting `PROJECT-DEFINITION`, read existing `narratives` keys first. If the strategy only writes a subset of narratives (e.g. speckit Phase 1 writes only `Principles`), merge the new narrative into the existing set — do NOT replace all narratives with only the new key.
60
+ - ⊗ Overwrite all `PROJECT-DEFINITION` narratives when the strategy only produces a single narrative key
61
+ - ⊗ Guard only the legacy `vbrief/` path while an `xbrief/PROJECT-DEFINITION.xbrief.json` is the live identity
56
62
 
57
63
  ---
58
64
 
59
65
  ## Preparatory Guard (Light)
60
66
 
61
- Applies to strategies that write scoped vBRIEFs to `vbrief/proposed/`: **bdd**, **discuss**, **research**, **map**.
67
+ Applies to strategies that write scoped records to `proposed/`: **bdd**, **discuss**, **research**, **map**, **probe**, and brownfield **Add scope** paths from interview/setup.
68
+
69
+ ! **Target folder resolution (xbrief-first)**: scan `./xbrief/proposed/` when it exists; else legacy `./vbrief/proposed/`. Prefer writing new scopes under the same root the project already uses.
62
70
 
63
- ! Before writing a scope vBRIEF to `vbrief/proposed/`, the strategy MUST check for existing files with the same scope or feature name:
71
+ ! Before writing a scope record to `proposed/`, the strategy MUST check for existing files with the same scope or feature name:
64
72
 
65
- 1. ! **Check for existing file**: Scan `vbrief/proposed/` for files matching the target scope/feature slug (e.g. `*-{feature}-bdd.vbrief.json`, `*-{scope}-context.vbrief.json`).
73
+ 1. ! **Check for existing file**: Scan the resolved `proposed/` folder for files matching the target scope/feature slug (e.g. `*-{feature}-bdd.xbrief.json`, `*-{scope}-context.vbrief.json`).
66
74
  - If NO match: proceed with the write — no guard needed.
67
75
  - If a match exists: continue to step 2.
68
76
 
69
- 2. ! **Warn and ask**: Display: "A vBRIEF for this scope already exists at `{path}`. Replace it, or create a new version? (replace/new)"
77
+ 2. ! **Warn and ask**: Display: "A scope record for this feature already exists at `{path}`. Replace it, or create a new version? (replace/new)"
70
78
  - ! If replace: overwrite the existing file
71
79
  - ! If new: append a numeric suffix or use today's date to create a distinct file
72
80
 
73
81
  3. ~ **Read existing decisions**: If the existing file contains a `LockedDecisions` or similar narrative, surface those decisions to the agent context so they are not lost even if the user chooses to replace.
74
82
 
75
- ⊗ Silently overwrite an existing scope vBRIEF with the same feature/scope name without warning the user.
83
+ ⊗ Silently overwrite an existing scope record with the same feature/scope name without warning the user.
84
+ ⊗ Scan only legacy `vbrief/proposed/` when `xbrief/proposed/` is the live lifecycle root.
76
85
 
77
86
  ---
78
87
 
79
88
  ## Anti-Patterns
80
89
 
81
- - ⊗ Writing to `PROJECT-DEFINITION.vbrief.json` without checking the file's current status (⊗ never write `specification.vbrief.json` at all per v0-20-contract.md)
90
+ - ⊗ Writing to `PROJECT-DEFINITION` without checking the file's current status (⊗ never write `specification.vbrief.json` / `specification.xbrief.json` at all per v0-20-contract.md)
91
+ - ⊗ Guarding only `vbrief/PROJECT-DEFINITION.vbrief.json` while `xbrief/PROJECT-DEFINITION.xbrief.json` is the live identity
82
92
  - ⊗ Silently overwriting an `approved` or `completed` artifact — this discards locked decisions and approval state
83
- - ⊗ Replacing all `PROJECT-DEFINITION.vbrief.json` narratives when the strategy only contributes one narrative key (e.g. `Principles`)
84
- - ⊗ Overwriting a scope vBRIEF in `vbrief/proposed/` without checking if one already exists for the same scope
93
+ - ⊗ Replacing all `PROJECT-DEFINITION` narratives when the strategy only contributes one narrative key (e.g. `Principles`)
94
+ - ⊗ Overwriting a scope record in `proposed/` without checking if one already exists for the same scope
85
95
  - ⊗ Accepting vague confirmation (`proceed`, `ok`) when the guard requires explicit confirmation for approved/completed artifacts
@@ -72,6 +72,7 @@ Adapt question focus to what's being built:
72
72
 
73
73
  - ! Produce a `vbrief/proposed/{scope}-context.vbrief.json` scope vBRIEF with a `LockedDecisions` narrative
74
74
  - ! Each decision includes: **what** was decided, **why**, and **alternatives considered**
75
+ - ! When the lock is an intentional under-build (weaker Now + decided end-product Later), the decision MUST also include dual-path graduation fields: `now`, `later`, `graduationRef`, `trigger`, and `status` (`open` | `shipped` | `cancelled`) — see [Graduation (Now+Later)](#graduation-nowlater-dual-path-locks-2899)
75
76
  - ! This vBRIEF is injected into all downstream work: planning, execution, verification
76
77
  - ! Persist decisions as vBRIEF narratives on the relevant plan items
77
78
  - ⊗ Write decisions to a hand-authored markdown context file -- use vBRIEF narratives for token-efficient agent consumption
@@ -85,6 +86,44 @@ Adapt question focus to what's being built:
85
86
  - ⊗ Silently making a different choice because the agent forgot what was decided
86
87
  - ⊗ Re-debating a settled decision without explicit user approval
87
88
 
89
+ ## Graduation (Now+Later) dual-path locks (#2899)
90
+
91
+ **Graduation** (alias: Now+Later) is a first-class decision shape for intentional under-builds:
92
+
93
+ > We *decided* to ship a weaker path **now**, and we also decided what the end-product path is **later**.
94
+
95
+ It is **not** the same as other "later" concepts:
96
+
97
+ | Concept | Meaning |
98
+ |---|---|
99
+ | `DeferredDecisions` (probe) | Not decided yet; open question with justification |
100
+ | deferred plan item / `triage:defer` | Out of scope, or not accepted into the workspace backlog |
101
+ | rapid **graduate** | Spike / prototype → fresh full interview/spec cycle |
102
+ | **Graduation** | Decided weaker **Now** + decided end-product **Later** |
103
+
104
+ Glossary naming for this term is owned by sibling work (#2907). Strategy prose here is the Wave A contract agents must follow until the glossary entry lands.
105
+
106
+ ### When a lock is an under-build
107
+
108
+ - ! When a `LockedDecisions` entry chooses a temporary, weaker, MVP, or shortcut approach **and** a stronger end-product approach is also decided, the entry MUST record a dual-path graduation shape with all of:
109
+ - `now` — what this scope ships
110
+ - `later` — the end-product approach
111
+ - `graduationRef` — GitHub issue URL and/or scope xBRIEF path that tracks Later work
112
+ - `trigger` — free-text condition that makes Later required (examples are illustrative only, e.g. "before multi-tenant customers", "when latency SLO is adopted")
113
+ - `status` — `open` | `shipped` | `cancelled` (cancellation MUST include justification)
114
+ - ! A permanent approach lock (no weaker temporary path) does **not** require graduation fields
115
+ - ! Chat-only "we'll harden this later" is insufficient — same anti-pattern as decisions that exist only in conversation history
116
+ - ~ Emitting `graduationRef` SHOULD use existing [emit-hints](./emit-hints.md) patterns (none / `--umbrella` / `--per-vbrief`) rather than a parallel SCM ontology
117
+ - ⊗ Routing a *decided* under-build into `DeferredDecisions`, a deferred plan item, or `triage:defer` — those surfaces mean undecided or out-of-scope, not dual-path delivery
118
+ - ⊗ Treating rapid prototype **graduate** as Graduation dual-path tracking inside a normal production build
119
+
120
+ ### Lifecycle: Now complete ≠ Later closed
121
+
122
+ - ! `task scope:complete` on the Now story MUST NOT imply closure of linked graduation work (`graduationRef` issue and/or Later scope xBRIEF stay open until Later ships or is explicitly cancelled)
123
+ - ⊗ Closing a graduation ticket solely because the MVP / Now story completed
124
+ - ~ Wave A (soft): when completing a story whose `LockedDecisions` contain an open graduation, warn and audit if `graduationRef` is missing or invalid — hard fail / policy flag is Wave B follow-on
125
+ - ! Durable surfaces for graduation are: (1) strategy output narratives (this contract), (2) optional always-loadable ProjectRules for stricter consumer policy — ⊗ Cursor-rule-only as the sole persistence path
126
+
88
127
  ---
89
128
 
90
129
  ## Then: Chaining Gate
@@ -115,7 +154,7 @@ run additional preparatory strategies or proceed to spec generation.
115
154
 
116
155
  1. **Open** -- Start with the user's goal statement; restate it in your own words
117
156
  2. **Explore** -- Follow energy, challenge vagueness, ask domain-sensitive questions
118
- 3. **Lock** -- Record each decision in `vbrief/proposed/{scope}-context.vbrief.json` `LockedDecisions` narrative with what/why/alternatives
157
+ 3. **Lock** -- Record each decision in `vbrief/proposed/{scope}-context.vbrief.json` `LockedDecisions` narrative with what/why/alternatives (and dual-path graduation fields when the lock is an under-build; #2899)
119
158
  4. **Verify** -- Explain the full picture back to the user (Feynman check)
120
159
  5. **Chain** -- Return to [interview.md Chaining Gate](./interview.md#chaining-gate), or -- if invoked from a standalone strategy (e.g. map's standalone next-step menu) -- return to the invoking strategy's menu per the [standalone-context rule](#then-chaining-gate) above
121
160
 
@@ -13,7 +13,7 @@ follow this strategy.
13
13
 
14
14
  Legend (from RFC2119): !=MUST, ~=SHOULD, ≉=SHOULD NOT, ⊗=MUST NOT, ?=MAY.
15
15
 
16
- **⚠️ See also**: [strategies/discuss.md](./discuss.md) | [strategies/yolo.md](./yolo.md) | [core/glossary.md](../glossary.md)
16
+ **⚠️ See also**: [strategies/discuss.md](./discuss.md) | [strategies/yolo.md](./yolo.md) | [strategies/artifact-guards.md](./artifact-guards.md) | [core/glossary.md](../glossary.md)
17
17
 
18
18
  ## When to Use
19
19
 
@@ -42,13 +42,37 @@ and wait for the user to choose before proceeding.
42
42
  - ! After each completed preparatory strategy (recursive — the gate reappears)
43
43
  - ! After the [Acceptance Gate](#acceptance-gate) when the user chooses "Revise" or "Switch"
44
44
 
45
+ ### Brownfield Detector
46
+
47
+ Before rendering menu options, classify the repo (align with setup Phase 3):
48
+
49
+ - **Brownfield** when **either**:
50
+ - `PROJECT-DEFINITION` exists under `./xbrief/` or legacy `./vbrief/` (`PROJECT-DEFINITION.xbrief.json` or `PROJECT-DEFINITION.vbrief.json`), **or**
51
+ - any lifecycle folder (`proposed/`, `pending/`, `active/`, `completed/`, `cancelled/` under `xbrief/` or legacy `vbrief/`) has scope records
52
+ - **Greenfield** otherwise
53
+
54
+ ! The detector MUST run on every Chaining Gate presentation.
55
+ ⊗ Offer only the greenfield-framed **Proceed to specification** default on a brownfield repo.
56
+ ⊗ Treat brownfield as a full create path without an explicit Replace/scrap confirm.
57
+
45
58
  ### Options
46
59
 
47
- Present two groups sourced from the `Type` column in
48
- [strategies/README.md](./README.md#strategy-types):
60
+ ! Numbered Chaining Gate menus (greenfield and brownfield examples below) MUST end with `Discuss` and `Back` as the final two options per [deterministic-questions.md](../contracts/deterministic-questions.md).
61
+
62
+ Present groups sourced from the `Type` column in
63
+ [strategies/README.md](./README.md#strategy-types). The **default path** depends
64
+ on the brownfield detector:
65
+
66
+ **Default path (greenfield):**
67
+ 1. **Proceed to specification** (default) — continue to the [Sizing Gate](#sizing-gate) for a full create path
49
68
 
50
- **Default:**
51
- 1. **Proceed to specification** (default) — continue to the [Sizing Gate](#sizing-gate)
69
+ **Default path (brownfield) — create-vs-update menu:**
70
+ 1. **Add scope to this project** (default) — load existing project identity + preparatory artifacts; **skip** the greenfield "what are we building?" interview; gather only the new scope; emit **one** proposed scope record; apply the [Preparatory Guard](./artifact-guards.md#preparatory-guard-light) on write
71
+ 2. **Update project definition** — run a **delta** interview against existing identity; apply the [Spec-Generating Guard](./artifact-guards.md#spec-generating-guard-full); **merge** narratives (prefer enrich/merge when the user declines replace)
72
+ 3. **Replace specification (scrap)** — full recreate equivalent to greenfield Proceed; **only** after explicit affirmative (`yes` / `confirmed`); vague replies (`proceed`, `ok`, `go ahead`) are **not** acceptance
73
+ - ! On confirmed scrap, continue to the [Sizing Gate](#sizing-gate) as a full create path
74
+ - ⊗ Auto-delete PROJECT-DEFINITION without scrap confirm
75
+ - ⊗ Rename Proceed to "Update" without changing behavior
52
76
 
53
77
  **Preparatory strategies** (type: `preparatory` — loops back to this gate on completion):
54
78
  - Research — investigate the domain, find libraries, identify pitfalls
@@ -79,9 +103,28 @@ See `strategies/map.md` for standalone behavior.
79
103
  - ! Append all new artifact paths to the flat `artifacts` array
80
104
  - ! The next strategy and eventual spec generation MUST load all artifacts
81
105
  listed in `plan.vbrief.json`
106
+ - ! On brownfield **Add scope** or **Update project definition**, load existing
107
+ PROJECT-DEFINITION identity and preparatory artifacts before asking questions
108
+
109
+ ### Write Guards (interview)
110
+
111
+ ! Interview is a **spec-generating** strategy and MUST follow
112
+ [artifact-guards.md](./artifact-guards.md) before emission (Light or Full path).
113
+
114
+ - ! **Before writing** scope records to `proposed/`: [Preparatory Guard](./artifact-guards.md#preparatory-guard-light) (xbrief-first: `./xbrief/proposed/`, else legacy `./vbrief/proposed/`)
115
+ - ! **Before writing** or updating `PROJECT-DEFINITION`: [Spec-Generating Guard](./artifact-guards.md#spec-generating-guard-full) against the **single live** identity file only — `./xbrief/PROJECT-DEFINITION.xbrief.json` when present (or after `deft migrate:xbrief`); else, until migrated, the existing legacy `./vbrief/PROJECT-DEFINITION.vbrief.json`. ⊗ Dual-write both roots
116
+ - ! Prefer enrich/merge when the user declines replace
117
+ - ⊗ Silently overwrite PROJECT-DEFINITION or same-slug proposed scopes
118
+ - ⊗ Guard only the legacy `vbrief/` path while an `xbrief/` PROJECT-DEFINITION is the live identity
119
+ - ⊗ Rely on write-time guards alone without the brownfield Chaining Gate menu above
120
+
121
+ Surface this obligation **inside `interview.md`** so agents that load only this
122
+ file still hit the guards (#2925; incomplete closeout of #82/#387).
82
123
 
83
124
  ### Example Prompt
84
125
 
126
+ **Greenfield:**
127
+
85
128
  ```
86
129
  Ready to generate the specification. Before we proceed, would you like to:
87
130
 
@@ -96,8 +139,32 @@ Ready to generate the specification. Before we proceed, would you like to:
96
139
  --- Switch strategy ---
97
140
  6. Switch to yolo — auto-pilot picks all answers
98
141
  7. Switch to speckit — formal spec process with story readiness before implementation
99
-
100
142
  8. Other (specify)
143
+ 9. Discuss
144
+ 10. Back
145
+ ```
146
+
147
+ **Brownfield:**
148
+
149
+ ```
150
+ This repo already has a project definition and/or scopes. Before we proceed:
151
+
152
+ 1. Add scope to this project (default) — keep identity; add one new proposed scope
153
+ 2. Update project definition — delta interview; merge narratives (Spec-Generating Guard)
154
+ 3. Replace specification (scrap) — requires explicit yes/confirmed; full recreate
155
+
156
+ --- Preparatory (loops back) ---
157
+ 4. Run a research phase — investigate the domain, find libraries, identify pitfalls
158
+ 5. Run a discuss phase — lock key decisions using Feynman technique
159
+ 6. Run a probe phase — adversarially stress-test the plan; surface assumptions, edge cases, and risks
160
+ 7. Run a map phase — analyze existing codebase conventions
161
+
162
+ --- Switch strategy ---
163
+ 8. Switch to yolo — auto-pilot picks all answers
164
+ 9. Switch to speckit — formal spec process with story readiness before implementation
165
+ 10. Other (specify)
166
+ 11. Discuss
167
+ 12. Back
101
168
  ```
102
169
 
103
170
  ---
@@ -188,19 +255,22 @@ flowchart LR
188
255
 
189
256
  ## Light Path (small/medium projects)
190
257
 
191
- Interview → scope vBRIEFs (date-prefixed in proposed/) + PROJECT-DEFINITION.vbrief.json + rendered SPECIFICATION (v0.20 contract).
258
+ Interview → scope records (date-prefixed in proposed/) + live PROJECT-DEFINITION + rendered SPECIFICATION (v0.20 contract). Paths are **xbrief-first**: use `./xbrief/` when that root is the live project layout; else legacy `./vbrief/`.
192
259
 
193
260
  ### Flow
194
261
 
195
262
  1. Sizing gate selects Light
196
263
  2. Interview (rules above)
197
- 3. Write scope vBRIEF(s) to `./vbrief/proposed/YYYY-MM-DD-<slug>.vbrief.json` (date-prefixed per vbrief filename convention) with `status: proposed`
198
- 4. Run `task project:render` to create/update `./vbrief/PROJECT-DEFINITION.vbrief.json` (full project identity + items registry) and ensure all five lifecycle folders exist
264
+ 3. Write scope record(s) to the live lifecycle root — `./xbrief/proposed/YYYY-MM-DD-<slug>.xbrief.json` when `./xbrief/` is live; else legacy `./vbrief/proposed/YYYY-MM-DD-<slug>.vbrief.json` date-prefixed with `status: proposed`
265
+ 4. Run `task project:render` to create/update the **live** PROJECT-DEFINITION under the resolved layout root (`./xbrief/PROJECT-DEFINITION.xbrief.json`). Engine layout resolution requires `xbrief/` (#2112); if the repo is legacy-only `./vbrief/`, run `deft migrate:xbrief` first, then render. Ensure all five lifecycle folders exist under the live root. ⊗ Write a second PROJECT-DEFINITION under legacy `vbrief/` when `xbrief/` is live
199
266
  5. Summarize decisions, ask user to review
200
- 6. On approval, use `task scope:promote` (or equivalent) to move scope vBRIEF(s) to `./vbrief/pending/` with `status: pending` / `approved`
201
- 7. Run `task spec:render` (SPECIFICATION.md is a rendered derivative with deprecation sentinel; `specification.vbrief.json` is legacy and is NOT written by this strategy on the v0.20 path)
267
+ 6. On approval, use `task scope:promote` (or equivalent) to move scope record(s) to `pending/` under the **same** live root with `status: pending` / `approved`
268
+ 7. Run `task project:export-spec` / `task spec:render` as appropriate (SPECIFICATION.md is a rendered derivative with deprecation sentinel; `specification.vbrief.json` / `specification.xbrief.json` are legacy and are NOT written by this strategy on the v0.20 path)
202
269
 
203
- ! At the emission step (step 3 above), after writing the scope vBRIEF(s) to `./vbrief/proposed/`, surface the GitHub-issue tracking hint from [emit-hints.md](./emit-hints.md) name all three patterns (none / `--umbrella` / `--per-vbrief`).
270
+ ! **Before writing** scope records or updating `PROJECT-DEFINITION` on the Light path, follow [artifact-guards.md](./artifact-guards.md): Preparatory Guard for `proposed/` scopes; Spec-Generating Guard against the **live** identity file (xbrief-first). Prefer enrich/merge when the user declines replace.
271
+ ! ⊗ Write a second PROJECT-DEFINITION under legacy `vbrief/` when `./xbrief/PROJECT-DEFINITION.xbrief.json` is already the live identity.
272
+
273
+ ! At the emission step (step 3 above), after writing the scope record(s) to the live `proposed/`, surface the GitHub-issue tracking hint from [emit-hints.md](./emit-hints.md) — name all three patterns (none / `--umbrella` / `--per-vbrief`).
204
274
 
205
275
  ### SPECIFICATION Structure (Light)
206
276
 
@@ -252,20 +322,23 @@ How to ship it.
252
322
 
253
323
  ## Full Path (large/complex projects)
254
324
 
255
- Interview → PRD → scope vBRIEFs (date-prefixed in proposed/) + PROJECT-DEFINITION.vbrief.json + rendered SPECIFICATION (v0.20 contract).
325
+ Interview → PRD → scope records (date-prefixed in proposed/) + live PROJECT-DEFINITION + rendered SPECIFICATION (v0.20 contract). Paths are **xbrief-first**: use `./xbrief/` when that root is the live project layout; else legacy `./vbrief/`.
256
326
 
257
327
  ### Flow
258
328
 
259
329
  1. Sizing gate selects Full
260
330
  2. Interview (rules above)
261
331
  3. Generate `PRD.md` — user approval gate
262
- 4. Write scope vBRIEF(s) to `./vbrief/proposed/YYYY-MM-DD-<slug>.vbrief.json` (date-prefixed per vbrief filename convention) with `status: proposed`
263
- 5. Run `task project:render` to create/update `./vbrief/PROJECT-DEFINITION.vbrief.json` (full project identity + items registry) and ensure all five lifecycle folders exist
332
+ 4. Write scope record(s) to the live lifecycle root — `./xbrief/proposed/YYYY-MM-DD-<slug>.xbrief.json` when `./xbrief/` is live; else legacy `./vbrief/proposed/YYYY-MM-DD-<slug>.vbrief.json` date-prefixed with `status: proposed`
333
+ 5. Run `task project:render` to create/update the **live** PROJECT-DEFINITION under the resolved layout root (`./xbrief/PROJECT-DEFINITION.xbrief.json`). Engine layout resolution requires `xbrief/` (#2112); if the repo is legacy-only `./vbrief/`, run `deft migrate:xbrief` first, then render. Ensure all five lifecycle folders exist under the live root. ⊗ Write a second PROJECT-DEFINITION under legacy `vbrief/` when `xbrief/` is live
264
334
  6. Summarize decisions, ask user to review
265
- 7. On approval, use `task scope:promote` (or equivalent) to move scope vBRIEF(s) to `./vbrief/pending/` with `status: pending` / `approved`
266
- 8. Run `task spec:render` (SPECIFICATION.md is a rendered derivative with deprecation sentinel; `specification.vbrief.json` is legacy and is NOT written by this strategy on the v0.20 path)
335
+ 7. On approval, use `task scope:promote` (or equivalent) to move scope record(s) to `pending/` under the **same** live root with `status: pending` / `approved`
336
+ 8. Run `task project:export-spec` / `task spec:render` as appropriate (SPECIFICATION.md is a rendered derivative with deprecation sentinel; `specification.vbrief.json` / `specification.xbrief.json` are legacy and are NOT written by this strategy on the v0.20 path)
337
+
338
+ ! **Before writing** PRD, scope records, or updating `PROJECT-DEFINITION` on the Full path, follow [artifact-guards.md](./artifact-guards.md): Preparatory Guard for `proposed/` scopes; Spec-Generating Guard against the **live** identity file (xbrief-first). Prefer enrich/merge when the user declines replace.
339
+ ! ⊗ Write a second PROJECT-DEFINITION under legacy `vbrief/` when `./xbrief/PROJECT-DEFINITION.xbrief.json` is already the live identity.
267
340
 
268
- ! At the emission step (step 4 above), after writing the scope vBRIEF(s) to `./vbrief/proposed/`, surface the GitHub-issue tracking hint from [emit-hints.md](./emit-hints.md) — name all three patterns (none / `--umbrella` / `--per-vbrief`).
341
+ ! At the emission step (step 4 above), after writing the scope record(s) to the live `proposed/`, surface the GitHub-issue tracking hint from [emit-hints.md](./emit-hints.md) — name all three patterns (none / `--umbrella` / `--per-vbrief`).
269
342
 
270
343
  ### PRD Structure (Full path only)
271
344
 
@@ -417,9 +490,9 @@ Each task SHOULD include:
417
490
 
418
491
  - ! All requirements mapped to tasks
419
492
  - ! Dependencies form a valid DAG (no cycles)
420
- - ! Scope vBRIEF(s) exist in `./vbrief/proposed/` (with date-prefixed filenames and `status: "proposed"`) or promoted to pending/active (with `status: "approved" / "pending"`)
421
- - ! `./vbrief/PROJECT-DEFINITION.vbrief.json` is present (populated via `task project:render`)
422
- - ! `SPECIFICATION.md` has been rendered via `task spec:render`
493
+ - ! Scope records exist in the live `proposed/` root (`./xbrief/proposed/` when live; else legacy `./vbrief/proposed/`) with date-prefixed filenames and `status: "proposed"`, or promoted to pending/active (with `status: "approved" / "pending"`)
494
+ - ! Live PROJECT-DEFINITION is present (`./xbrief/PROJECT-DEFINITION.xbrief.json` when that is the identity; else legacy `./vbrief/PROJECT-DEFINITION.vbrief.json`) populated via `task project:render`
495
+ - ! `SPECIFICATION.md` has been rendered via export/`task spec:render`
423
496
  - ! Proceed to [Acceptance Gate](#acceptance-gate)
424
497
 
425
498
  ---
@@ -517,20 +590,20 @@ diff only on the second pass or when the user explicitly asks for it.
517
590
 
518
591
  | Artifact | Purpose | Created By |
519
592
  |----------|---------|------------|
520
- | `./vbrief/proposed/YYYY-MM-DD-*.vbrief.json` | Scope story vBRIEFs (date-prefixed, v0.20 contract) | Interview |
521
- | `./vbrief/PROJECT-DEFINITION.vbrief.json` | Project identity gestalt + items registry | `task project:render` (triggered by strategy) |
522
- | `SPECIFICATION.md` | Generated plan with embedded Requirements (rendered derivative; deprecation sentinel) | `task spec:render` |
523
- | (no `specification.vbrief.json`) | Legacy artifact — omitted on v0.20 path | — |
593
+ | `./xbrief/proposed/YYYY-MM-DD-*.xbrief.json` (else legacy `./vbrief/proposed/YYYY-MM-DD-*.vbrief.json`) | Scope story records (date-prefixed, v0.20 contract) | Interview |
594
+ | `./xbrief/PROJECT-DEFINITION.xbrief.json` (else legacy `./vbrief/PROJECT-DEFINITION.vbrief.json`) | Project identity gestalt + items registry | `task project:render` (triggered by strategy) |
595
+ | `SPECIFICATION.md` | Generated plan with embedded Requirements (rendered derivative; deprecation sentinel) | export / `task spec:render` |
596
+ | (no `specification.*.json`) | Legacy artifact — omitted on v0.20 path | — |
524
597
 
525
598
  **Full path:**
526
599
 
527
600
  | Artifact | Purpose | Created By |
528
601
  |----------|---------|------------|
529
602
  | `PRD.md` | What to build (approval gate) | Interview |
530
- | `./vbrief/proposed/YYYY-MM-DD-*.vbrief.json` | Scope story vBRIEFs (date-prefixed, v0.20 contract) | Post-PRD interview |
531
- | `./vbrief/PROJECT-DEFINITION.vbrief.json` | Project identity gestalt + items registry | `task project:render` (triggered by strategy) |
532
- | `SPECIFICATION.md` | Generated implementation plan (rendered derivative; deprecation sentinel) | `task spec:render` |
533
- | (no `specification.vbrief.json`) | Legacy artifact — omitted on v0.20 path | — |
603
+ | `./xbrief/proposed/YYYY-MM-DD-*.xbrief.json` (else legacy `./vbrief/proposed/YYYY-MM-DD-*.vbrief.json`) | Scope story records (date-prefixed, v0.20 contract) | Post-PRD interview |
604
+ | `./xbrief/PROJECT-DEFINITION.xbrief.json` (else legacy `./vbrief/PROJECT-DEFINITION.vbrief.json`) | Project identity gestalt + items registry | `task project:render` (triggered by strategy) |
605
+ | `SPECIFICATION.md` | Generated implementation plan (rendered derivative; deprecation sentinel) | export / `task spec:render` |
606
+ | (no `specification.*.json`) | Legacy artifact — omitted on v0.20 path | — |
534
607
 
535
608
  ## Invoking This Strategy
536
609
 
@@ -547,5 +620,5 @@ Use the interview strategy to plan [project].
547
620
  After completion:
548
621
 
549
622
  ```
550
- implement the scope vBRIEFs in ./vbrief/active/
623
+ implement the scope records in ./xbrief/active/ (or legacy ./vbrief/active/)
551
624
  ```
@@ -104,7 +104,7 @@ feature/component name if probing a sub-scope. Use the same value consistently
104
104
  throughout the session. Examples: `my-app-probe`, `auth-probe`.
105
105
 
106
106
  - ! Produce a `vbrief/proposed/{scope}-probe.vbrief.json` scope vBRIEF with three mandatory narratives:
107
- - `LockedDecisions` — what was resolved and why
107
+ - `LockedDecisions` — what was resolved and why (when the lock is an intentional under-build, include dual-path graduation fields: `now`, `later`, `graduationRef`, `trigger`, `status` — see [Graduation (Now+Later)](#graduation-nowlater-when-locking-an-under-build-2899))
108
108
  - `SurfacedRisks` — concerns raised, even if not fully resolved
109
109
  - `DeferredDecisions` — explicitly acknowledged items with justification
110
110
  - ! Each entry in a narrative includes: **question asked**, **answer given**, **status** (locked / deferred / risk-accepted)
@@ -114,6 +114,30 @@ throughout the session. Examples: `my-app-probe`, `auth-probe`.
114
114
 
115
115
  ! After emitting the probe scope vBRIEF to `vbrief/proposed/`, surface the GitHub-issue tracking hint from [emit-hints.md](./emit-hints.md) — name all three patterns (none / `--umbrella` / `--per-vbrief`).
116
116
 
117
+ ## Graduation (Now+Later) when locking an under-build (#2899)
118
+
119
+ Probe already separates **locked**, **deferred**, and **risk-accepted** branches. **Graduation** is a fourth case that must not collapse into `DeferredDecisions`:
120
+
121
+ | Concept | Probe role |
122
+ |---|---|
123
+ | `LockedDecisions` (permanent) | Approach is decided and is the end-product path |
124
+ | `DeferredDecisions` | *Undecided* open question with justification |
125
+ | **Graduation** on a lock | *Decided* weaker **Now** + *decided* end-product **Later** |
126
+ | rapid **graduate** | Strategy-level spike → fresh spec; not dual-path tracking here |
127
+
128
+ Glossary naming for Graduation is owned by sibling work (#2907); this section is the Wave A probe contract.
129
+
130
+ - ! When probe **locks** a temporary / weaker approach and the end-product approach is also decided, record the dual-path shape on that `LockedDecisions` entry (not as a `DeferredDecisions` open question):
131
+ - `now` — what ships in the near path
132
+ - `later` — end-product approach
133
+ - `graduationRef` — GitHub issue and/or scope xBRIEF path for Later work
134
+ - `trigger` — condition that makes Later required (free text)
135
+ - `status` — `open` | `shipped` | `cancelled` (cancel with justification)
136
+ - ! Permanent locks and true open questions do **not** gain graduation fields — do not ticket-spam every `DeferredDecisions` item into a graduation
137
+ - ! `task scope:complete` on a Now story MUST NOT close linked graduation work; Later stays open until shipped or explicitly cancelled
138
+ - ⊗ Accepting "we'll harden it later" as a locked under-build without dual-path fields, or filing it only as `DeferredDecisions`
139
+ - ⊗ Closing graduation work solely because the MVP / Now story completed
140
+
117
141
  ---
118
142
 
119
143
  ## Then: Chaining Gate
@@ -143,6 +167,8 @@ written, return to the [chaining gate](./interview.md#chaining-gate).
143
167
  ## Anti-Patterns
144
168
 
145
169
  - ⊗ Accepting "we'll figure it out later" without marking it as explicitly deferred
170
+ - ⊗ Recording a *decided* weaker-Now + stronger-Later path only as `DeferredDecisions` (or chat) instead of dual-path graduation fields on the lock (#2899)
171
+ - ⊗ Treating Now-story `scope:complete` as closure of linked graduation work (#2899)
146
172
  - ⊗ Asking generic checklist questions instead of following the decision tree
147
173
  - ⊗ Letting vague answers pass without pushing for concrete specifics
148
174
  - ⊗ Using codebase exploration as a substitute for asking the user about deliberate design choices
@@ -12,6 +12,29 @@
12
12
 
13
13
  const { spawnSync } = require("node:child_process");
14
14
 
15
+ /**
16
+ * cmd.exe command separators / metacharacters. Free-text DEFT_ENGINE_CMD_JSON
17
+ * tokens (release --summary text, CLI_ARGS, #2547) may legitimately contain
18
+ * these; double-quoting renders them literal to cmd.exe's parser so a token can
19
+ * never break out of its argv slot (subprocess-scm-01 / #2911).
20
+ */
21
+ const WIN32_CMD_METACHAR_RE = /[\s"&|<>^()%!]/;
22
+
23
+ /**
24
+ * Quote a single argument for `cmd.exe /d /s /c` so that shell metacharacters
25
+ * stay inside one argv token. Mirrors tasks/engine-pm-run.cjs quoteWin32Arg but
26
+ * also quotes cmd.exe separators (& | < > ^ ( ) % !) because engine-invoke
27
+ * forwards operator free-text, not an allowlisted command.
28
+ * @param {string} arg
29
+ */
30
+ function quoteWin32Arg(arg) {
31
+ const s = String(arg);
32
+ if (s.length > 0 && !WIN32_CMD_METACHAR_RE.test(s)) {
33
+ return s;
34
+ }
35
+ return `"${s.replace(/"/g, '""')}"`;
36
+ }
37
+
15
38
  /** Minimal POSIX-ish shell word splitter (double/single quotes, escapes). */
16
39
  function shellSplit(input) {
17
40
  const out = [];
@@ -75,15 +98,8 @@ function main() {
75
98
  process.exit(2);
76
99
  }
77
100
 
78
- let execPath;
79
- let execArgv;
80
- if (mode === "vendored") {
81
- execPath = process.execPath;
82
- execArgv = [target, ...argv];
83
- } else if (mode === "global") {
84
- execPath = target;
85
- execArgv = argv;
86
- } else {
101
+ const plan = buildSpawnPlan(mode, target, argv);
102
+ if (!plan) {
87
103
  console.error(`deft: engine-invoke unknown mode ${JSON.stringify(mode)}`);
88
104
  process.exit(2);
89
105
  }
@@ -97,11 +113,12 @@ function main() {
97
113
  // stdio inherit (not pipe): piped stdout/stderr deadlocks when the child emits
98
114
  // more than the OS pipe buffer before exit — observed as greenfield smoke
99
115
  // hanging then CI SIGTERM exit 143 with no output (#2554 / #2547).
100
- const result = spawnSync(execPath, execArgv, {
116
+ const result = spawnSync(plan.command, plan.args, {
101
117
  stdio: "inherit",
102
118
  env: childEnv,
103
- // Global deft/directive on Windows are .cmd shims; shell:false cannot spawn them (#2415).
104
- shell: mode === "global" && process.platform === "win32",
119
+ // Never shell:true even on win32 global (subprocess-scm-01 / #2911). The
120
+ // win32 .cmd shim is reached through a tightly quoted cmd.exe wrapper below.
121
+ shell: plan.shell,
105
122
  // CREATE_NO_WINDOW: hide console windows from Cursor Task / nested shells (#2563).
106
123
  windowsHide: true,
107
124
  });
@@ -109,8 +126,47 @@ function main() {
109
126
  process.exit(code === null ? 1 : code);
110
127
  }
111
128
 
129
+ /**
130
+ * Resolve the concrete spawn command/args for a mode+target without ever using
131
+ * shell:true. On the win32 global path the target is a `.cmd` shim that Node
132
+ * refuses to spawn with shell:false (CVE-2024-27980 / #2415); shell:true would
133
+ * let cmd.exe re-parse free-text DEFT_ENGINE_CMD_JSON tokens (subprocess-scm-01
134
+ * / #2911). Instead route through `cmd.exe /d /s /c` with every token tightly
135
+ * quoted so metacharacters stay inside a single argv token — aligned with
136
+ * tasks/engine-pm-run.cjs executeAllowlisted().
137
+ *
138
+ * @param {string} mode
139
+ * @param {string} target
140
+ * @param {string[]} argv
141
+ * @param {{ platform?: string, nodePath?: string }} [opts]
142
+ * @returns {{ command: string, args: string[], shell: false } | null}
143
+ */
144
+ function buildSpawnPlan(mode, target, argv, opts = {}) {
145
+ const platform = opts.platform || process.platform;
146
+ const nodePath = opts.nodePath || process.execPath;
147
+
148
+ let execPath;
149
+ let execArgv;
150
+ if (mode === "vendored") {
151
+ execPath = nodePath;
152
+ execArgv = [target, ...argv];
153
+ } else if (mode === "global") {
154
+ execPath = target;
155
+ execArgv = argv;
156
+ } else {
157
+ return null;
158
+ }
159
+
160
+ if (mode === "global" && platform === "win32") {
161
+ const commandLine = [execPath, ...execArgv].map(quoteWin32Arg).join(" ");
162
+ return { command: "cmd.exe", args: ["/d", "/s", "/c", commandLine], shell: false };
163
+ }
164
+
165
+ return { command: execPath, args: execArgv, shell: false };
166
+ }
167
+
112
168
  if (require.main === module) {
113
169
  main();
114
170
  }
115
171
 
116
- module.exports = { shellSplit };
172
+ module.exports = { shellSplit, quoteWin32Arg, buildSpawnPlan, WIN32_CMD_METACHAR_RE };