wdi-method 0.6.25 → 0.6.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -10,6 +10,33 @@ version contains every fix below it.
10
10
 
11
11
  ---
12
12
 
13
+ ## [0.6.27] - 2026-09-20
14
+
15
+ ### Added
16
+
17
+ - **Validation Rule `scratch-hygiene` in `validate.py`:** Added strict structural hygiene enforcement for the specification workspace root (`.scratch/`). Active specs live exclusively inside registered `.scratch/<spec-id>-<slug>/` directories. Loose files directly under `.scratch/` root (e.g. `prompt-*.txt`, `output-*.txt`, logs, transcripts) and unregistered child directories immediately fail validation with finding `scratch-hygiene`. Additionally, verifies that `.work/` contains no `SPEC.md` files.
18
+ - **Defensive Scratch Dumps `.gitignore` Scaffolding in `bin/wdi-method.js`:** Expanded `ensureGitignoreScratchDumps()` during install and update to automatically add defensive ignore rules to consumer `.gitignore`: `.scratch/*.txt`, `.scratch/*.log`, `.scratch/*-review-output.md`, `.scratch/*-second-opinion.md`, and `.scratch/tmp/`, preventing accidental execution transport dumps from polluting git status.
19
+
20
+ ### Changed
21
+
22
+ - **Spec Workspace vs Execution Scratch Boundaries in `repo-guide.md` & `AGENTS.md`:** Clarified that `.scratch/` is strictly a registered spec workspace (authoritative and git-tracked), NOT an informal scratchpad. Loose files and ad-hoc folders are forbidden. All subagent transport packets, raw CLI redirection dumps, and review transcripts MUST be written to `.work/<skill>/` and deleted upon completion.
23
+ - **Skill Transport Discipline in `wdi-build`:** Explicitly added transport guardrails forbidding placing execution scratch, prompt handoffs, or tool logs in `.scratch/`.
24
+
25
+ **What a repo that already has the method installed does about it.** Run `npx wdi-method@latest update`. It updates `validate.py`, the build skills, and automatically appends defensive ignore patterns to `.gitignore`. Any loose files in `.scratch/` should be deleted or moved to `.work/`.
26
+
27
+ ## [0.6.26] - 2026-09-20
28
+
29
+ ### Added
30
+
31
+ - **Validation Rule `spec-folder-location` in `validate.py`:** Added upfront validation that every spec in `specs.yaml` has a valid `spec_folder`. For active specs, `spec_folder` MUST be within `.scratch/<spec-id>-<slug>/` (or legacy `_bmad-output/specs/`). Specs placed inside `.work/` or other unauthorized roots (such as `docs/` or repo root) fail immediately with finding `spec-folder-location`, preventing misplaced specs from progressing through implementation before failing during lifecycle archival.
32
+
33
+ ### Changed
34
+
35
+ - **Non-Authoritative Execution Scratch Taxonomy in `repo-guide.md`:** Clarified section heading to `## .work/ — non-authoritative execution scratch` and added explicit normative rules: `.work/` is execution scratch only and MUST NOT contain `SPEC.md`, ticket files, or any `spec_folder` target. Active specifications and ticket efforts MUST live under `.scratch/<spec-id>-<slug>/`.
36
+ - **Deterministic Spec Location in `wdi-daily-what-to-build` & `wdi-build`:** Added explicit target contracts in `wdi-daily-what-to-build` (Step 3) and `wdi-build` (Phase 1 & Phase 2), mandating `.scratch/<spec-id>-<slug>/` and forbidding `.work/` or `docs/`. Preflight check instructs immediate correction if `spec-folder-location` fails.
37
+
38
+ **What a repo that already has the method installed does about it.** Run `npx wdi-method@latest update`. It updates `validate.py`, the repo guide, and the build skills. Active specs mistakenly authored in `.work/` should be moved to `.scratch/<spec-id>-<slug>/` with `spec_folder` updated in `specs.yaml`.
39
+
13
40
  ## [0.6.25] - 2026-09-19
14
41
 
15
42
  ### Changed
package/README.id.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # WDI Method
2
2
 
3
- [English](README.md) | [Bahasa Indonesia](README.id.md) | [日本語](README.ja.md) | [简体中文](README.zh.md)
3
+ [English](README.md) | [Bahasa Indonesia](README.id.md) | [日本語](README.ja.md) | [简体中文](README.zh.md)
4
+ [Changelog](CHANGELOG.md) | [Contributing](CONTRIBUTING.md) | [License](LICENSE) | [Security](SECURITY.md) | [Privacy](PRIVACY.md)
4
5
 
5
6
  **Lapisan tinjauan yang melengkapi BMad — spesifikasi terverifikasi yang dibaca manusia untuk memeriksa keputusan teknis sebelum kode ditulis, disesuaikan dengan skala perubahan nyata.**
6
7
 
package/README.ja.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # WDI Method
2
2
 
3
- [English](README.md) | [Bahasa Indonesia](README.id.md) | [日本語](README.ja.md) | [简体中文](README.zh.md)
3
+ [English](README.md) | [Bahasa Indonesia](README.id.md) | [日本語](README.ja.md) | [简体中文](README.zh.md)
4
+ [Changelog](CHANGELOG.md) | [Contributing](CONTRIBUTING.md) | [License](LICENSE) | [Security](SECURITY.md) | [Privacy](PRIVACY.md)
4
5
 
5
6
  **BMadが薄く残したレビュー層 — コードを書く前に技術的な決定を人間が検証するための仕様書フレームワーク。変更規模に応じて適切な粒度を提供します。**
6
7
 
package/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # WDI Method
2
2
 
3
- [English](README.md) | [Bahasa Indonesia](README.id.md) | [日本語](README.ja.md) | [简体中文](README.zh.md)
3
+ [English](README.md) | [Bahasa Indonesia](README.id.md) | [日本語](README.ja.md) | [简体中文](README.zh.md)
4
+ [Changelog](CHANGELOG.md) | [Contributing](CONTRIBUTING.md) | [License](LICENSE) | [Security](SECURITY.md) | [Privacy](PRIVACY.md)
4
5
 
5
6
  **The review layer BMad leaves thin — verifiable specifications a human reads to check technical decisions before code is written, sized to what the change actually deserves.**
6
7
 
package/README.zh.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # WDI Method
2
2
 
3
- [English](README.md) | [Bahasa Indonesia](README.id.md) | [日本語](README.ja.md) | [简体中文](README.zh.md)
3
+ [English](README.md) | [Bahasa Indonesia](README.id.md) | [日本語](README.ja.md) | [简体中文](README.zh.md)
4
+ [Changelog](CHANGELOG.md) | [Contributing](CONTRIBUTING.md) | [License](LICENSE) | [Security](SECURITY.md) | [Privacy](PRIVACY.md)
4
5
 
5
6
  **BMad 所精简保留的审查层 —— 供人类在编写代码前验证技术决策的规范说明框架,根据实际变更规模匹配相应的文档粒度。**
6
7
 
package/bin/wdi-method.js CHANGED
@@ -919,7 +919,16 @@ function ensureGitignoreSmoke(target) {
919
919
 
920
920
  function ensureGitignoreScratchDumps(target) {
921
921
  const gitignorePath = path.join(target, ".gitignore");
922
- const rules = [".work/*.txt", ".work/*.log", ".work/tmp/"];
922
+ const rules = [
923
+ ".work/*.txt",
924
+ ".work/*.log",
925
+ ".work/tmp/",
926
+ ".scratch/*.txt",
927
+ ".scratch/*.log",
928
+ ".scratch/*-review-output.md",
929
+ ".scratch/*-second-opinion.md",
930
+ ".scratch/tmp/",
931
+ ];
923
932
  if (fs.existsSync(gitignorePath)) {
924
933
  const content = fs.readFileSync(gitignorePath, "utf8");
925
934
  const lines = content.split(/\r?\n/).map((l) => l.trim());
@@ -40,10 +40,11 @@ fact and MUST NOT state the commercial one. *"Retention is 90 days"* is a techni
40
40
  *"Retention is 90 days because the client would not pay for more"* is a commercial one wearing a
41
41
  technical coat.
42
42
 
43
- ## `.work/` — scratch that is committed
43
+ ## `.work/` — non-authoritative execution scratch
44
44
 
45
- `.work/` holds work in progress that has no home yet: notes while reading an unfamiliar system,
46
- drafts, exploratory output, a working paper for a change spanning several sessions.
45
+ `.work/` holds execution scratch and work in progress that has no home yet: notes while reading
46
+ an unfamiliar system, exploratory output, disposable reviewer transport, a working paper for a change
47
+ spanning several sessions.
47
48
 
48
49
  It is **committed**, so that a session picked up on another machine finds it, and so a reviewer can
49
50
  see what a change was actually reasoning about.
@@ -55,6 +56,9 @@ pattern that drops the folder is not, because it leaves the material on one mach
55
56
 
56
57
  It is **ephemeral**, and the two together are what make its rules matter:
57
58
 
59
+ - `.work/` is execution scratch only. It MUST NOT contain `SPEC.md`, ticket files, or any `spec_folder`
60
+ target. Active specification workspaces MUST live at `.scratch/<spec-id>-<slug>/` where `wdi-build`
61
+ and the registry track them.
58
62
  - Any durable outcome MUST be moved out before the task closes — to the corpus if it is truth, to
59
63
  `_bmad-output/` if it is a run's byproduct.
60
64
  - Obsolete scratch MUST be deleted when its task closes. `.work/` that only grows stops being
@@ -64,9 +68,15 @@ It is **ephemeral**, and the two together are what make its rules matter:
64
68
  - Nothing MUST be read from `.work/` as authority. If something there is right, it belongs
65
69
  somewhere with an owner.
66
70
 
67
- `.work/` MUST NOT be confused with `_bmad-output/`. That folder holds the output of skill runs, is
68
- never curated, and is cited by path. `.work/` holds what a human or agent wrote by hand while
69
- working, and is meant to empty out.
71
+ `.work/` MUST NOT be confused with `_bmad-output/` or `.scratch/`. `_bmad-output/` holds the output of
72
+ skill runs, is never curated, and is cited by path. `.scratch/` holds active specifications and tickets
73
+ tracked in `specs.yaml`. `.work/` holds disposable execution scratch written by hand or during triage
74
+ (such as `.work/wdi-daily-what-to-build/`), and is meant to empty out.
75
+
76
+ `.scratch/` is **strictly for registered spec workspaces**. It MUST NOT be used as an informal scratchpad:
77
+ - Loose files (such as prompt dumps, tool stdout/stderr, review packets, script logs) MUST NOT be written directly under `.scratch/`.
78
+ - Unregistered directories MUST NOT be created under `.scratch/`. Every child directory MUST match an active or unarchived `spec_folder` in `.control/registry/specs.yaml`.
79
+ - All temporary subagent transport files, raw reviewer outputs, and intermediate scratch MUST be written under `.work/<skill>/` and deleted once folded into the spec. `scratch-hygiene` enforces these boundaries.
70
80
 
71
81
  ## Referring to things outside this repository
72
82
 
@@ -973,6 +973,113 @@ def archived_spec_closed(c: Corpus, r: Result) -> None:
973
973
  "only closed specs may be archived")
974
974
 
975
975
 
976
+ def spec_folder_location(c: Corpus, r: Result) -> None:
977
+ """A spec's `spec_folder` MUST be within an allowed root.
978
+
979
+ Active specs MUST live at `.scratch/<spec-id>-<slug>/` (or legacy `_bmad-output/specs/`).
980
+ Closed specs may remain in `.scratch/` or be archived under `.archive/specs/<spec-id>-<slug>/`.
981
+ `.work/` is execution scratch only and MUST NOT be used for `spec_folder`.
982
+ """
983
+ for spec in c.spec_list:
984
+ sid = str(spec.get("id") or "").strip()
985
+ folder = str(spec.get("spec_folder") or "").strip()
986
+ if not folder:
987
+ for t in spec.get("tickets") or []:
988
+ if isinstance(t, dict) and str(t.get("spec_folder") or "").strip():
989
+ folder = str(t.get("spec_folder")).strip()
990
+ break
991
+ if not folder:
992
+ continue
993
+ clean = folder.replace("\\", "/").strip()
994
+ while clean.startswith("./"):
995
+ clean = clean[2:]
996
+ if clean.startswith("/"):
997
+ clean = clean.lstrip("/")
998
+ clean = clean.rstrip("/")
999
+ norm = os.path.normpath(clean).replace("\\", "/") if clean else ""
1000
+
1001
+ # Check for forbidden .work/ root
1002
+ if clean == ".work" or clean.startswith(".work/") or norm == ".work" or norm.startswith(".work/"):
1003
+ r.fail("spec-folder-location", sid,
1004
+ f"spec_folder `{folder}` is inside `.work/` — `.work/` is execution scratch only; "
1005
+ f"active specs MUST live at `.scratch/{sid}-<slug>/`")
1006
+ continue
1007
+
1008
+ status = str(spec.get("status") or "").strip()
1009
+ is_closed = status == "closed"
1010
+
1011
+ allowed_roots = [".scratch", "_bmad-output/specs"]
1012
+ if is_closed:
1013
+ allowed_roots.append(".archive/specs")
1014
+
1015
+ is_allowed = any(clean == a or clean.startswith(f"{a}/") or norm == a or norm.startswith(f"{a}/")
1016
+ for a in allowed_roots)
1017
+ if not is_allowed:
1018
+ allowed_desc = ", ".join(f"`{a}/`" for a in allowed_roots)
1019
+ r.fail("spec-folder-location", sid,
1020
+ f"spec_folder `{folder}` is outside allowed roots ({allowed_desc})")
1021
+
1022
+
1023
+ def scratch_workspace_hygiene(c: Corpus, r: Result) -> None:
1024
+ """The spec workspace root (.scratch/) MUST NOT contain loose files or unregistered directories.
1025
+
1026
+ Active specs live at `.scratch/<spec-id>-<slug>/` where tickets and SPEC.md are tracked.
1027
+ Ephemeral execution scratch, tool dumps, and prompt/review logs MUST live in `.work/`
1028
+ and MUST NOT be placed in `.scratch/`.
1029
+ """
1030
+ scratch_dir = c.root / ".scratch"
1031
+ if scratch_dir.is_dir():
1032
+ registered_names: set[str] = set()
1033
+ for spec in c.spec_list:
1034
+ folder = str(spec.get("spec_folder") or "").strip()
1035
+ if not folder:
1036
+ for t in spec.get("tickets") or []:
1037
+ if isinstance(t, dict) and str(t.get("spec_folder") or "").strip():
1038
+ folder = str(t.get("spec_folder")).strip()
1039
+ break
1040
+ if not folder:
1041
+ continue
1042
+ clean = folder.replace("\\", "/").strip()
1043
+ while clean.startswith("./"):
1044
+ clean = clean[2:]
1045
+ clean = clean.strip("/")
1046
+ if clean == ".scratch" or clean.startswith(".scratch/"):
1047
+ parts = clean.split("/")
1048
+ if len(parts) >= 2:
1049
+ registered_names.add(parts[1])
1050
+
1051
+ allowable_files = {".gitkeep", "wdi-probe.md"}
1052
+
1053
+ try:
1054
+ entries = sorted(scratch_dir.iterdir(), key=lambda p: p.name)
1055
+ except OSError:
1056
+ entries = []
1057
+
1058
+ for entry in entries:
1059
+ if entry.is_file():
1060
+ if entry.name in allowable_files:
1061
+ continue
1062
+ r.fail("scratch-hygiene", f".scratch/{entry.name}",
1063
+ f"loose file `{entry.name}` in `.scratch/` root — execution scratch MUST live in "
1064
+ f"`.work/<skill>/` and MUST NOT be placed in `.scratch/`")
1065
+ elif entry.is_dir():
1066
+ if entry.name not in registered_names:
1067
+ r.fail("scratch-hygiene", f".scratch/{entry.name}",
1068
+ f"directory `.scratch/{entry.name}` is not registered as a `spec_folder` in "
1069
+ f"`.control/registry/specs.yaml` — execution scratch MUST live in `.work/<skill>/`")
1070
+
1071
+ work_dir = c.root / ".work"
1072
+ if work_dir.is_dir():
1073
+ for spec_file in work_dir.rglob("SPEC.md"):
1074
+ try:
1075
+ rel = spec_file.relative_to(c.root).as_posix()
1076
+ except ValueError:
1077
+ rel = str(spec_file)
1078
+ r.fail("scratch-hygiene", rel,
1079
+ f"found `{rel}` inside `.work/` — `.work/` is execution scratch only; "
1080
+ f"specification files MUST NOT be placed in `.work/`")
1081
+
1082
+
976
1083
  PLATFORM = "_platform"
977
1084
  CROSS_CUTTING = ".how/_platform/cross-cutting.md"
978
1085
  # The section heading entity-one-writer looks for. A heading a SCRIPT matches is a machine-facing key, and
@@ -1904,7 +2011,7 @@ def run_checks(c: Corpus, asof: dt.date) -> Result:
1904
2011
  # no two copies left to compare.
1905
2012
  # V19 is REPEALED. It checked one line item — an `RTR-` file in .control/reports/ — and the
1906
2013
  # retrospective it archived was the only thing spec size `L` ever decided. Both went together.
1907
- for fn in (goal_has_fr, fr_has_uc, uc_scheduled, ticket_has_test, nfr_has_enforcer, refs_resolve, no_cycles, applied_dec_touches, locked_gate_passed, parallel_tickets_blocked, lc_registered, review_trace, chain_links, memlog_home, spec_names_release_prd, ticket_status_one_home, archived_spec_closed, defect_root_cause, entity_one_writer, spec_after_g4, high_risk_named, mandate_accept, cites_resolve, container_built, custom_room_declared, corpus_in_git, engines_invocable, withdrawn_recorded, id_allocated_once):
2014
+ for fn in (goal_has_fr, fr_has_uc, uc_scheduled, ticket_has_test, nfr_has_enforcer, refs_resolve, no_cycles, applied_dec_touches, locked_gate_passed, parallel_tickets_blocked, lc_registered, review_trace, chain_links, memlog_home, spec_names_release_prd, ticket_status_one_home, archived_spec_closed, spec_folder_location, scratch_workspace_hygiene, defect_root_cause, entity_one_writer, spec_after_g4, high_risk_named, mandate_accept, cites_resolve, container_built, custom_room_declared, corpus_in_git, engines_invocable, withdrawn_recorded, id_allocated_once):
1908
2015
  fn(c, r)
1909
2016
  plan_dates(c, r, asof)
1910
2017
  return r
@@ -85,7 +85,7 @@ that is where they are born.
85
85
  | `fr` | The `FR` this spec satisfies. Ideally one — an `FR` is human-testable from birth |
86
86
  | `size` | `S` · `M` · `L`. MAY be raised mid-flight; MUST NOT be lowered |
87
87
  | `depends_on` | At **spec** level. A spec declaring none runs in parallel with its neighbours |
88
- | `spec_folder` | One per spec, not one per spec × component |
88
+ | `spec_folder` | MUST be `.scratch/<spec-id>-<slug>/` (one per spec, not one per spec × component). MUST NOT use `.work/` or `docs/` |
89
89
  | `tickets` | Flat, one row per ticket: `id` · `component` · `satisfies: [UC]` · `blocked_by` · `touches` · test names |
90
90
  | a ticket `id` | `<spec-id>-<NN>` — `SPEC-3-01`. The engine numbers its files from `01` per feature, which is unique only inside one spec; the RTM needs a key that is unique across the corpus |
91
91
 
@@ -139,11 +139,16 @@ is here and name the prior art.
139
139
  sequenced expand → migrate in batches → contract; `delivery-flow-guide.md` owns that rule.
140
140
  - **Ticket files land under `spec_folder`, and `spec_folder` is `.scratch/<spec-id>-<slug>/`.** Their
141
141
  **shape** is the engine's — one file per ticket, numbered in dependency order, blocking edges declared
142
- — and the location is ours, written in `docs/agents/issue-tracker.md` where the engines read it. The
142
+ — and the location is ours, written in `docs/agents/issue-tracker.md` where the engines read it. MUST
143
+ NOT use `.work/` or `docs/` (`spec-folder-location` enforces this). The
143
144
  id in front of the slug is not decoration: four live repos wrote that leaf four different ways, one of
144
145
  them all four inside a single repo, and a folder nothing can trace back to a row in `specs.yaml` is
145
146
  how a spec goes missing. A ticket at the repo root, or under `docs/`, or in a `.scratch/` directory
146
147
  with no row in `specs.yaml`, is drift.
148
+ - **Execution scratch, prompt handoffs, and tool logs MUST NOT be placed in `.scratch/`.** Any raw model
149
+ output, multi-agent dispatch transcripts, or debug dumps MUST be written to `.work/<skill>/` and
150
+ deleted after distillation. The root of `.scratch/` MUST NOT contain loose files, and unregistered
151
+ directories MUST NOT be created there (`scratch-hygiene` enforces this).
147
152
 
148
153
  `SPEC.md` and ticket files **are not read by humans.** Both are machine contracts, and no review burden MAY be
149
154
  moved onto them. `wdi-review` MAY still be dispatched over the contract; its trace lands on the spec in
@@ -50,6 +50,13 @@ Invoke `wdi-build` Phase 1 (open spec and author tickets via `to-spec` / `to-tic
50
50
  active development branch (`policy.development_branch`, default `main`), per the Delivery Flow standing
51
51
  exception.
52
52
 
53
+ ### Spec Location & Target Contract (`spec-folder-location`)
54
+ The specification and its ticket files MUST land under `.scratch/<spec-id>-<slug>/` and `spec_folder`
55
+ in `.control/registry/specs.yaml` MUST name that exact path.
56
+ - MUST NOT place `SPEC.md` or ticket files in `.work/` or `docs/`.
57
+ - `.work/` is reserved strictly for ephemeral execution scratch (such as `.work/wdi-daily-what-to-build/`);
58
+ a spec in `.work/` violates `spec-folder-location` and fails lifecycle archiving.
59
+
53
60
  ### Ticket Dependency Contract (Upfront `parallel-tickets-blocked`)
54
61
  Author `touches` and `blocked_by` together; do NOT defer dependency relationships until validation.
55
62
  For every pair of tickets in the same spec whose `touches` lists intersect, there MUST be a directed
@@ -61,9 +68,11 @@ Before leaving Step 3, run validator preflight:
61
68
  ```bash
62
69
  uv run .constitution/method/scripts/validate.py --check --baseline
63
70
  ```
64
- If `parallel-tickets-blocked` reports unsequenced tickets, add the missing `blocked_by` edge to an upstream
65
- ticket immediately. Findings already matching `.github/validate-baseline.txt` are pre-existing debt, not new
66
- blockers; only new RED findings must be repaired before dispatching review.
71
+ If `spec-folder-location` reports an invalid `spec_folder` (e.g. under `.work/`), move the folder to
72
+ `.scratch/<spec-id>-<slug>/` and update `specs.yaml` immediately. If `parallel-tickets-blocked` reports
73
+ unsequenced tickets, add the missing `blocked_by` edge to an upstream ticket immediately. Findings already
74
+ matching `.github/validate-baseline.txt` are pre-existing debt, not new blockers; only new RED findings must
75
+ be repaired before dispatching review.
67
76
 
68
77
  **Stop at the boundary of Phase 1.** Once the spec and ticket files exist on disk, do NOT proceed into
69
78
  Phase 2 (worktree isolation and ticket implementation) or Phase 3 (closing the spec) — each of those is
@@ -99,9 +99,10 @@ Read this instead of reasoning about what `.what/` and `.how/` mean.
99
99
  | The explanation of a rule, never a rule itself | `.constitution/method/why/` |
100
100
  | A decision, an open question, a registry, a structure map, minutes | `.control/` |
101
101
  | The brief, a PRD, a use case, a business rule — what is promised | `.what/` |
102
- | The spine, C4, an inventory, an SDD, a contract — how it is built | `.how/` |
102
+ | A spine, C4, an inventory, an SDD, a contract — how it is built | `.how/` |
103
+ | Active spec workspaces and tickets tracked in `specs.yaml` | `.scratch/<spec-id>-<slug>/` |
103
104
  | A skill run's working output, and documents that predate the method | `_bmad-output/` |
104
- | Scratch that empties when the task closes | `.work/` |
105
+ | Scratch that empties when the task closes (prompt packets, tool dumps) | `.work/` |
105
106
  | The application | named under `## Code` below |
106
107
 
107
108
  ## Layer boundaries
@@ -184,6 +185,9 @@ verifies the result, and lands the memlog.
184
185
  `.control/generated/` (`status.yaml`) instead.
185
186
  - `.scratch/` MUST NOT be searched with broad or recursive wildcard patterns (`*` or `**`) to discover
186
187
  specs — inspect only the candidate spec's folder using `spec_folder:` from `status.yaml` or `specs.yaml`.
188
+ - Loose files (prompt dumps, tool outputs, transcripts, script logs) MUST NOT be written into `.scratch/`
189
+ root, and unregistered folders MUST NOT be created there. All execution scratch MUST live in
190
+ `.work/<skill>/` and MUST be cleaned up before committing (`scratch-hygiene` enforces this).
187
191
 
188
192
  ## Routing — load a guide when the task matches
189
193
 
@@ -40,10 +40,11 @@ fact and MUST NOT state the commercial one. *"Retention is 90 days"* is a techni
40
40
  *"Retention is 90 days because the client would not pay for more"* is a commercial one wearing a
41
41
  technical coat.
42
42
 
43
- ## `.work/` — scratch that is committed
43
+ ## `.work/` — non-authoritative execution scratch
44
44
 
45
- `.work/` holds work in progress that has no home yet: notes while reading an unfamiliar system,
46
- drafts, exploratory output, a working paper for a change spanning several sessions.
45
+ `.work/` holds execution scratch and work in progress that has no home yet: notes while reading
46
+ an unfamiliar system, exploratory output, disposable reviewer transport, a working paper for a change
47
+ spanning several sessions.
47
48
 
48
49
  It is **committed**, so that a session picked up on another machine finds it, and so a reviewer can
49
50
  see what a change was actually reasoning about.
@@ -55,6 +56,9 @@ pattern that drops the folder is not, because it leaves the material on one mach
55
56
 
56
57
  It is **ephemeral**, and the two together are what make its rules matter:
57
58
 
59
+ - `.work/` is execution scratch only. It MUST NOT contain `SPEC.md`, ticket files, or any `spec_folder`
60
+ target. Active specification workspaces MUST live at `.scratch/<spec-id>-<slug>/` where `wdi-build`
61
+ and the registry track them.
58
62
  - Any durable outcome MUST be moved out before the task closes — to the corpus if it is truth, to
59
63
  `_bmad-output/` if it is a run's byproduct.
60
64
  - Obsolete scratch MUST be deleted when its task closes. `.work/` that only grows stops being
@@ -64,9 +68,15 @@ It is **ephemeral**, and the two together are what make its rules matter:
64
68
  - Nothing MUST be read from `.work/` as authority. If something there is right, it belongs
65
69
  somewhere with an owner.
66
70
 
67
- `.work/` MUST NOT be confused with `_bmad-output/`. That folder holds the output of skill runs, is
68
- never curated, and is cited by path. `.work/` holds what a human or agent wrote by hand while
69
- working, and is meant to empty out.
71
+ `.work/` MUST NOT be confused with `_bmad-output/` or `.scratch/`. `_bmad-output/` holds the output of
72
+ skill runs, is never curated, and is cited by path. `.scratch/` holds active specifications and tickets
73
+ tracked in `specs.yaml`. `.work/` holds disposable execution scratch written by hand or during triage
74
+ (such as `.work/wdi-daily-what-to-build/`), and is meant to empty out.
75
+
76
+ `.scratch/` is **strictly for registered spec workspaces**. It MUST NOT be used as an informal scratchpad:
77
+ - Loose files (such as prompt dumps, tool stdout/stderr, review packets, script logs) MUST NOT be written directly under `.scratch/`.
78
+ - Unregistered directories MUST NOT be created under `.scratch/`. Every child directory MUST match an active or unarchived `spec_folder` in `.control/registry/specs.yaml`.
79
+ - All temporary subagent transport files, raw reviewer outputs, and intermediate scratch MUST be written under `.work/<skill>/` and deleted once folded into the spec. `scratch-hygiene` enforces these boundaries.
70
80
 
71
81
  ## Referring to things outside this repository
72
82
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wdi-method",
3
- "version": "0.6.25",
3
+ "version": "0.6.27",
4
4
  "description": "WDI Method — software delivery method that wraps BMad",
5
5
  "type": "module",
6
6
  "bin": {