wdi-method 0.6.26 → 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 +14 -0
- package/bin/wdi-method.js +10 -1
- package/kit/.constitution/method/repo-guide.md +5 -0
- package/kit/.constitution/method/scripts/validate.py +61 -1
- package/kit/skills/wdi-build/SKILL.md +4 -0
- package/kit-overlay/AGENTS.md +6 -2
- package/kit-overlay/repo-guide.md +5 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,20 @@ 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
|
+
|
|
13
27
|
## [0.6.26] - 2026-09-20
|
|
14
28
|
|
|
15
29
|
### Added
|
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 = [
|
|
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());
|
|
@@ -73,6 +73,11 @@ skill runs, is never curated, and is cited by path. `.scratch/` holds active spe
|
|
|
73
73
|
tracked in `specs.yaml`. `.work/` holds disposable execution scratch written by hand or during triage
|
|
74
74
|
(such as `.work/wdi-daily-what-to-build/`), and is meant to empty out.
|
|
75
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.
|
|
80
|
+
|
|
76
81
|
## Referring to things outside this repository
|
|
77
82
|
|
|
78
83
|
Engagement context — who the client is, what was agreed, what is due — lives elsewhere. This repo
|
|
@@ -1020,6 +1020,66 @@ def spec_folder_location(c: Corpus, r: Result) -> None:
|
|
|
1020
1020
|
f"spec_folder `{folder}` is outside allowed roots ({allowed_desc})")
|
|
1021
1021
|
|
|
1022
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
|
+
|
|
1023
1083
|
PLATFORM = "_platform"
|
|
1024
1084
|
CROSS_CUTTING = ".how/_platform/cross-cutting.md"
|
|
1025
1085
|
# The section heading entity-one-writer looks for. A heading a SCRIPT matches is a machine-facing key, and
|
|
@@ -1951,7 +2011,7 @@ def run_checks(c: Corpus, asof: dt.date) -> Result:
|
|
|
1951
2011
|
# no two copies left to compare.
|
|
1952
2012
|
# V19 is REPEALED. It checked one line item — an `RTR-` file in .control/reports/ — and the
|
|
1953
2013
|
# retrospective it archived was the only thing spec size `L` ever decided. Both went together.
|
|
1954
|
-
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, 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):
|
|
1955
2015
|
fn(c, r)
|
|
1956
2016
|
plan_dates(c, r, asof)
|
|
1957
2017
|
return r
|
|
@@ -145,6 +145,10 @@ is here and name the prior art.
|
|
|
145
145
|
them all four inside a single repo, and a folder nothing can trace back to a row in `specs.yaml` is
|
|
146
146
|
how a spec goes missing. A ticket at the repo root, or under `docs/`, or in a `.scratch/` directory
|
|
147
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).
|
|
148
152
|
|
|
149
153
|
`SPEC.md` and ticket files **are not read by humans.** Both are machine contracts, and no review burden MAY be
|
|
150
154
|
moved onto them. `wdi-review` MAY still be dispatched over the contract; its trace lands on the spec in
|
package/kit-overlay/AGENTS.md
CHANGED
|
@@ -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
|
-
|
|
|
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
|
|
|
@@ -73,6 +73,11 @@ skill runs, is never curated, and is cited by path. `.scratch/` holds active spe
|
|
|
73
73
|
tracked in `specs.yaml`. `.work/` holds disposable execution scratch written by hand or during triage
|
|
74
74
|
(such as `.work/wdi-daily-what-to-build/`), and is meant to empty out.
|
|
75
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.
|
|
80
|
+
|
|
76
81
|
## Referring to things outside this repository
|
|
77
82
|
|
|
78
83
|
Engagement context — who the client is, what was agreed, what is due — lives elsewhere. This repo
|