wdi-method 0.6.19 → 0.6.24
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 +74 -0
- package/LICENSE +21 -21
- package/NOTICE +28 -0
- package/README.id.md +190 -0
- package/README.ja.md +188 -0
- package/README.md +115 -463
- package/README.zh.md +188 -0
- package/bin/wdi-method.js +2199 -2112
- package/kit/.constitution/method/branch-guide.md +87 -0
- package/kit/.constitution/method/ci-guide.md +23 -4
- package/kit/.constitution/method/constitution.md +1 -0
- package/kit/.constitution/method/scripts/lifecycle.py +416 -0
- package/kit/.constitution/method/scripts/validate.py +126 -9
- package/kit/skills/wdi-autopilot/SKILL.md +36 -15
- package/kit/skills/wdi-build/SKILL.md +10 -4
- package/kit/skills/wdi-daily-autopilot/SKILL.md +138 -0
- package/kit/skills/wdi-daily-what-to-build/SKILL.md +155 -0
- package/kit/skills/wdi-daily-what-to-test/SKILL.md +127 -0
- package/kit/skills/wdi-explain-to-me/SKILL.md +1 -1
- package/kit/skills/wdi-help/SKILL.md +21 -7
- package/kit/skills/wdi-init/SKILL.md +16 -0
- package/kit/skills/wdi-prune-or-archive/SKILL.md +76 -0
- package/kit/skills/wdi-review/SKILL.md +4 -1
- package/kit-overlay/AGENTS.md +36 -4
- package/kit-overlay/constitution.md +1 -0
- package/lib/identity.mjs +246 -117
- package/package.json +8 -4
- package/scaffold/.control/custom-dispatch.yaml.example +65 -0
- package/scaffold/.control/registry/index.yaml +9 -0
- package/scaffold/.control/test-targets/desktop.md +15 -0
- package/scaffold/.control/test-targets/mobile.md +6 -0
- package/scaffold/.control/test-targets/web.md +6 -0
package/lib/identity.mjs
CHANGED
|
@@ -1,117 +1,246 @@
|
|
|
1
|
-
// index.yaml editing. Every regex here is anchored on `\n`, and a Windows checkout hands us `\r\n` —
|
|
2
|
-
// so every function normalises before matching and restores the file's own ending before writing.
|
|
3
|
-
//
|
|
4
|
-
// Skipping that is not a cosmetic bug. It silently returns "" for a name that IS set, which makes
|
|
5
|
-
// `identityIsPlaceholder` true, which makes `setProductIdentity` return early: the installer reports
|
|
6
|
-
// nothing and writes nothing, on every CRLF repo. Found 2026-08-18 on the first real install into a
|
|
7
|
-
// Windows product repo, where `policy.doc_language` printed empty while the file plainly said `id`.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
export function
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
`
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
1
|
+
// index.yaml editing. Every regex here is anchored on `\n`, and a Windows checkout hands us `\r\n` —
|
|
2
|
+
// so every function normalises before matching and restores the file's own ending before writing.
|
|
3
|
+
//
|
|
4
|
+
// Skipping that is not a cosmetic bug. It silently returns "" for a name that IS set, which makes
|
|
5
|
+
// `identityIsPlaceholder` true, which makes `setProductIdentity` return early: the installer reports
|
|
6
|
+
// nothing and writes nothing, on every CRLF repo. Found 2026-08-18 on the first real install into a
|
|
7
|
+
// Windows product repo, where `policy.doc_language` printed empty while the file plainly said `id`.
|
|
8
|
+
|
|
9
|
+
// FREE TEXT, not an enum. The consumer is a model, and a model does not need a list: "English",
|
|
10
|
+
// "Bahasa Indonesia", "id", "Indonesia" all read the same to it. Fencing this into two values would
|
|
11
|
+
// force the owner to translate their intent into the installer's vocabulary first, and nothing is
|
|
12
|
+
// bought with that.
|
|
13
|
+
export const DEFAULT_DOC_LANGUAGE = "English";
|
|
14
|
+
|
|
15
|
+
/** LF view of the text, plus how to put the original endings back. */
|
|
16
|
+
function lf(text) {
|
|
17
|
+
const crlf = text.includes("\r\n");
|
|
18
|
+
return { body: crlf ? text.replaceAll("\r\n", "\n") : text, crlf };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function restore(body, crlf) {
|
|
22
|
+
return crlf ? body.replaceAll("\n", "\r\n") : body;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function yamlQuote(value) {
|
|
26
|
+
return `"${String(value).replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Generic block-level key upsert that preserves comments, indentations, and existing sibling keys.
|
|
31
|
+
*/
|
|
32
|
+
export function upsertBlockKeys(body, blockName, pairs) {
|
|
33
|
+
const entries = Array.isArray(pairs) ? pairs : Object.entries(pairs);
|
|
34
|
+
const validEntries = entries.filter(([_, v]) => v !== undefined);
|
|
35
|
+
if (validEntries.length === 0) {
|
|
36
|
+
return body;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const lines = body.split("\n");
|
|
40
|
+
// Header must start at column 0 to prevent matching nested keys
|
|
41
|
+
const headerRegex = new RegExp(`^${blockName}:[ \\t]*(?:#.*)?$`);
|
|
42
|
+
const headerIndex = lines.findIndex((l) => headerRegex.test(l));
|
|
43
|
+
|
|
44
|
+
if (headerIndex === -1) {
|
|
45
|
+
const blockContent = validEntries.map(([k, v]) => ` ${k}: ${v}`).join("\n");
|
|
46
|
+
const newBlockLines = [`${blockName}:`, ...validEntries.map(([k, v]) => ` ${k}: ${v}`)];
|
|
47
|
+
|
|
48
|
+
// If adding policy and product exists, place policy right after product block
|
|
49
|
+
if (blockName === "policy") {
|
|
50
|
+
const productHeaderRegex = /^product:[ \t]*(?:#.*)?$/;
|
|
51
|
+
const productHeaderIndex = lines.findIndex((l) => productHeaderRegex.test(l));
|
|
52
|
+
if (productHeaderIndex !== -1) {
|
|
53
|
+
let nextIndex = lines.length;
|
|
54
|
+
for (let i = productHeaderIndex + 1; i < lines.length; i++) {
|
|
55
|
+
if (/^[a-zA-Z0-9_-]+:|^---/.test(lines[i])) {
|
|
56
|
+
nextIndex = i;
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
let lastIndented = productHeaderIndex;
|
|
61
|
+
for (let i = nextIndex - 1; i > productHeaderIndex; i--) {
|
|
62
|
+
if (/^\s/.test(lines[i])) {
|
|
63
|
+
lastIndented = i;
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (lines[lastIndented + 1] === "") {
|
|
68
|
+
const insertLines = nextIndex < lines.length ? [...newBlockLines, ""] : newBlockLines;
|
|
69
|
+
lines.splice(lastIndented + 2, 0, ...insertLines);
|
|
70
|
+
} else {
|
|
71
|
+
const insertLines = nextIndex < lines.length ? ["", ...newBlockLines, ""] : ["", ...newBlockLines];
|
|
72
|
+
lines.splice(lastIndented + 1, 0, ...insertLines);
|
|
73
|
+
}
|
|
74
|
+
return lines.join("\n");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const cleanBody = body.replace(/^\uFEFF/, "");
|
|
79
|
+
return cleanBody ? `${blockName}:\n${blockContent}\n\n${cleanBody}` : `${blockName}:\n${blockContent}\n`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// End of block is reached at the next top-level mapping key or document separator
|
|
83
|
+
let nextBlockIndex = lines.length;
|
|
84
|
+
for (let i = headerIndex + 1; i < lines.length; i++) {
|
|
85
|
+
if (/^[a-zA-Z0-9_-]+:|^---/.test(lines[i])) {
|
|
86
|
+
nextBlockIndex = i;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let lastIndentedIndex = headerIndex;
|
|
92
|
+
for (let i = nextBlockIndex - 1; i > headerIndex; i--) {
|
|
93
|
+
if (/^\s/.test(lines[i])) {
|
|
94
|
+
lastIndentedIndex = i;
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const toAppend = [];
|
|
100
|
+
for (const [key, value] of validEntries) {
|
|
101
|
+
// Only match direct children at 2 spaces indentation
|
|
102
|
+
const keyRegex = new RegExp(`^ ${key}:[ \\t]*`);
|
|
103
|
+
let foundIndex = -1;
|
|
104
|
+
for (let i = headerIndex + 1; i < nextBlockIndex; i++) {
|
|
105
|
+
if (keyRegex.test(lines[i])) {
|
|
106
|
+
foundIndex = i;
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (foundIndex !== -1) {
|
|
112
|
+
// Preserve any trailing inline comment
|
|
113
|
+
const commentMatch = lines[foundIndex].match(/(?:[ \t]+#.*)$/);
|
|
114
|
+
const inlineComment = commentMatch ? commentMatch[0] : "";
|
|
115
|
+
lines[foundIndex] = ` ${key}: ${value}${inlineComment}`;
|
|
116
|
+
} else {
|
|
117
|
+
toAppend.push(` ${key}: ${value}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (toAppend.length > 0) {
|
|
122
|
+
lines.splice(lastIndentedIndex + 1, 0, ...toAppend);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return lines.join("\n");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function readBlockValue(body, blockName, key) {
|
|
129
|
+
const lines = body.split("\n");
|
|
130
|
+
const headerRegex = new RegExp(`^${blockName}:[ \\t]*(?:#.*)?$`);
|
|
131
|
+
const headerIndex = lines.findIndex((l) => headerRegex.test(l));
|
|
132
|
+
if (headerIndex === -1) return "";
|
|
133
|
+
|
|
134
|
+
let nextBlockIndex = lines.length;
|
|
135
|
+
for (let i = headerIndex + 1; i < lines.length; i++) {
|
|
136
|
+
if (/^[a-zA-Z0-9_-]+:|^---/.test(lines[i])) {
|
|
137
|
+
nextBlockIndex = i;
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const doubleQuotedRegex = new RegExp(`^ ${key}:[ \\t]*"((?:[^"\\\\]|\\\\.)*)"`);
|
|
143
|
+
const singleQuotedRegex = new RegExp(`^ ${key}:[ \\t]*'((?:[^'\\\\]|\\\\.)*)'`);
|
|
144
|
+
const bareRegex = new RegExp(`^ ${key}:[ \\t]*([^\\n#]*)`);
|
|
145
|
+
|
|
146
|
+
for (let i = headerIndex + 1; i < nextBlockIndex; i++) {
|
|
147
|
+
const dQuoted = lines[i].match(doubleQuotedRegex);
|
|
148
|
+
if (dQuoted) {
|
|
149
|
+
return dQuoted[1].replaceAll('\\"', '"').replaceAll('\\\\', '\\').trim();
|
|
150
|
+
}
|
|
151
|
+
const sQuoted = lines[i].match(singleQuotedRegex);
|
|
152
|
+
if (sQuoted) {
|
|
153
|
+
return sQuoted[1].replaceAll("''", "'").trim();
|
|
154
|
+
}
|
|
155
|
+
const bare = lines[i].match(bareRegex);
|
|
156
|
+
if (bare) return bare[1].trim();
|
|
157
|
+
}
|
|
158
|
+
return "";
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function readProductIdentity(text) {
|
|
162
|
+
const { body } = lf(text);
|
|
163
|
+
return {
|
|
164
|
+
name: readBlockValue(body, "product", "name"),
|
|
165
|
+
client: readBlockValue(body, "product", "client"),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function writeProductIdentity(text, { name, client }) {
|
|
170
|
+
const { body, crlf } = lf(text);
|
|
171
|
+
const pairs = [
|
|
172
|
+
["name", yamlQuote(name)],
|
|
173
|
+
["client", yamlQuote(client ?? "")],
|
|
174
|
+
];
|
|
175
|
+
const nextBody = upsertBlockKeys(body, "product", pairs);
|
|
176
|
+
return restore(nextBody, crlf);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function identityIsPlaceholder(name) {
|
|
180
|
+
return !name || name === "{product}";
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ---------------------------------------------------------------------- policy (language & branches)
|
|
184
|
+
//
|
|
185
|
+
// Language settings:
|
|
186
|
+
// doc_language the PROSE of working documents in .what/ .how/ .control/
|
|
187
|
+
// doc_filename_language the SLUG part of a document filename
|
|
188
|
+
//
|
|
189
|
+
// Branch settings:
|
|
190
|
+
// primary_branch trunk / production branch (defaults to "main")
|
|
191
|
+
// development_branch active development branch (defaults to "main")
|
|
192
|
+
|
|
193
|
+
function readPolicyValue(body, key) {
|
|
194
|
+
return readBlockValue(body, "policy", key);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function readLanguagePolicy(text) {
|
|
198
|
+
const { body } = lf(text);
|
|
199
|
+
return {
|
|
200
|
+
docLanguage: readPolicyValue(body, "doc_language"),
|
|
201
|
+
docFilenameLanguage: readPolicyValue(body, "doc_filename_language"),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function writeLanguagePolicy(text, { docLanguage, docFilenameLanguage }) {
|
|
206
|
+
const { body, crlf } = lf(text);
|
|
207
|
+
const pairs = [];
|
|
208
|
+
if (docLanguage !== undefined) pairs.push(["doc_language", yamlQuote(docLanguage)]);
|
|
209
|
+
if (docFilenameLanguage !== undefined) pairs.push(["doc_filename_language", yamlQuote(docFilenameLanguage)]);
|
|
210
|
+
const nextBody = upsertBlockKeys(body, "policy", pairs);
|
|
211
|
+
return restore(nextBody, crlf);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function readBranchPolicy(text) {
|
|
215
|
+
const { body } = lf(text);
|
|
216
|
+
const primary = readPolicyValue(body, "primary_branch");
|
|
217
|
+
const development = readPolicyValue(body, "development_branch");
|
|
218
|
+
return {
|
|
219
|
+
primaryBranch: primary || "main",
|
|
220
|
+
developmentBranch: development || "main",
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function writeBranchPolicy(text, { primaryBranch, developmentBranch }) {
|
|
225
|
+
const { body, crlf } = lf(text);
|
|
226
|
+
const pairs = [];
|
|
227
|
+
if (primaryBranch !== undefined) pairs.push(["primary_branch", yamlQuote(primaryBranch)]);
|
|
228
|
+
if (developmentBranch !== undefined) pairs.push(["development_branch", yamlQuote(developmentBranch)]);
|
|
229
|
+
const nextBody = upsertBlockKeys(body, "policy", pairs);
|
|
230
|
+
return restore(nextBody, crlf);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// A first install has nowhere to read a product name from, so the folder is the best guess available —
|
|
234
|
+
// and a guess the owner can accept with Enter beats a field they must type. `acme-billing-portal`
|
|
235
|
+
// becomes `Acme Billing Portal`; camelCase splits too. It is a SUGGESTION: G1 confirms the real name.
|
|
236
|
+
export function humaniseFolderName(name) {
|
|
237
|
+
return String(name || "")
|
|
238
|
+
.replace(/[._-]+/g, " ")
|
|
239
|
+
.replace(/([a-z\d])([A-Z])/g, "$1 $2")
|
|
240
|
+
.replace(/\s+/g, " ")
|
|
241
|
+
.trim()
|
|
242
|
+
.split(" ")
|
|
243
|
+
.filter(Boolean)
|
|
244
|
+
.map((w) => (w === w.toUpperCase() && w.length <= 4 ? w : w[0].toUpperCase() + w.slice(1)))
|
|
245
|
+
.join(" ");
|
|
246
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wdi-method",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.24",
|
|
4
4
|
"description": "WDI Method — software delivery method that wraps BMad",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -13,8 +13,12 @@
|
|
|
13
13
|
"kit-overlay/",
|
|
14
14
|
"scaffold/",
|
|
15
15
|
"README.md",
|
|
16
|
+
"README.id.md",
|
|
17
|
+
"README.ja.md",
|
|
18
|
+
"README.zh.md",
|
|
16
19
|
"CHANGELOG.md",
|
|
17
20
|
"LICENSE",
|
|
21
|
+
"NOTICE",
|
|
18
22
|
"!**/__pycache__",
|
|
19
23
|
"!**/*.pyc"
|
|
20
24
|
],
|
|
@@ -35,10 +39,10 @@
|
|
|
35
39
|
"license": "MIT",
|
|
36
40
|
"repository": {
|
|
37
41
|
"type": "git",
|
|
38
|
-
"url": "git+https://github.com/
|
|
42
|
+
"url": "git+https://github.com/wiradeltaid/wdi-method.git"
|
|
39
43
|
},
|
|
40
44
|
"bugs": {
|
|
41
|
-
"url": "https://github.com/
|
|
45
|
+
"url": "https://github.com/wiradeltaid/wdi-method/issues"
|
|
42
46
|
},
|
|
43
|
-
"homepage": "https://github.com/
|
|
47
|
+
"homepage": "https://github.com/wiradeltaid/wdi-method#readme"
|
|
44
48
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# .control/custom-dispatch.yaml.example
|
|
2
|
+
# Optional machine-local runner and dispatch configuration for autonomous daily tier skills.
|
|
3
|
+
# When seeded to `.control/custom-dispatch.yaml`, it remains local and gitignored.
|
|
4
|
+
schema: 1
|
|
5
|
+
|
|
6
|
+
runners: {}
|
|
7
|
+
# Uncomment and customize runners on this machine:
|
|
8
|
+
#
|
|
9
|
+
# Auto runners (dispatches in-session if model is reachable from active session;
|
|
10
|
+
# otherwise executes shell-out command):
|
|
11
|
+
# sonnet:
|
|
12
|
+
# type: auto
|
|
13
|
+
# model: sonnet
|
|
14
|
+
# command: 'claude --model claude-sonnet-5 --effort high --dangerously-skip-permissions -p'
|
|
15
|
+
#
|
|
16
|
+
# opus:
|
|
17
|
+
# type: auto
|
|
18
|
+
# model: opus
|
|
19
|
+
# command: 'claude --model claude-opus-5 --effort high --dangerously-skip-permissions -p'
|
|
20
|
+
#
|
|
21
|
+
# External CLI runners (always shell-out):
|
|
22
|
+
# NOTE: For roles.reviewer, runner commands MUST enforce read-only execution where supported
|
|
23
|
+
# (e.g. `--trust-tools=fs_read` for kiro-cli, `--mode plan` for cursor-agent).
|
|
24
|
+
#
|
|
25
|
+
# terra:
|
|
26
|
+
# type: shell-out
|
|
27
|
+
# command: 'kiro-cli chat --model gpt-5.6-terra --effort high --trust-tools=fs_read --no-interactive'
|
|
28
|
+
# timeout_s: 600 # Hard wall-clock timeout in seconds for backgrounded dispatch (default: 600s)
|
|
29
|
+
#
|
|
30
|
+
# composer:
|
|
31
|
+
# type: shell-out
|
|
32
|
+
# command: 'cursor-agent --mode plan --model "composer-2.5"'
|
|
33
|
+
# timeout_s: 600
|
|
34
|
+
#
|
|
35
|
+
# sol:
|
|
36
|
+
# type: shell-out
|
|
37
|
+
# command: 'cursor-agent --mode plan --model "gpt-5.6-sol-high"'
|
|
38
|
+
# timeout_s: 600
|
|
39
|
+
|
|
40
|
+
roles:
|
|
41
|
+
# Builder configuration:
|
|
42
|
+
# builder is fixed to 'coordinator' (mandatory). The coordinating session implements code
|
|
43
|
+
# directly in the active run worktree (tight TDD cycles, direct verification). Coding delegation
|
|
44
|
+
# (in-session subagent or external builder runner) is prohibited in the daily routine to eliminate
|
|
45
|
+
# handoff latency and delegation hallucinations.
|
|
46
|
+
# Guardrail: builder: coordinator MUST NOT be paired with reviewer: none unless all touched
|
|
47
|
+
# components have risk_accepted: low and the decision is recorded. A builder cannot be its own sole judge.
|
|
48
|
+
builder: coordinator
|
|
49
|
+
|
|
50
|
+
# Reviewer options:
|
|
51
|
+
# - default: Resolves peer review via caller's active session / global agent rules
|
|
52
|
+
# - <runner-id>: Dispatches peer review to the named runner
|
|
53
|
+
# - none: Disables peer review pass (coordinator self-review only)
|
|
54
|
+
# NOTE: Reviewer runners in daily triage (wdi-daily-what-to-build) operate strictly as advisory /
|
|
55
|
+
# read-only critics. The coordinator alone applies edits to the spec/tickets and writes registry traces.
|
|
56
|
+
reviewer: default
|
|
57
|
+
|
|
58
|
+
# Deep analyst options (for architecture spine, complex debugging, or hard decisions):
|
|
59
|
+
# - default: Uses reviewer runner or coordinator for deep analysis
|
|
60
|
+
# - <runner-id>: Dispatches deep analysis to a dedicated high-reasoning runner
|
|
61
|
+
# - none: Disables separate deep analyst dispatch (handled by reviewer/coordinator)
|
|
62
|
+
deep_analyst: default
|
|
63
|
+
|
|
64
|
+
review_policy:
|
|
65
|
+
peer_review: true # Set to false (or set reviewer: none) to bypass peer review pass
|
|
@@ -9,6 +9,15 @@ product:
|
|
|
9
9
|
name: "{product}"
|
|
10
10
|
client: ""
|
|
11
11
|
|
|
12
|
+
# POLICY SETTINGS. Language and branching rules for the repository.
|
|
13
|
+
# Branch settings default to `main` if omitted. Set `development_branch` to match
|
|
14
|
+
# `primary_branch` for single-branch workflows.
|
|
15
|
+
policy:
|
|
16
|
+
doc_language: "English"
|
|
17
|
+
doc_filename_language: "English"
|
|
18
|
+
# primary_branch: main
|
|
19
|
+
# development_branch: development # Set equal to primary_branch for single-branch workflow
|
|
20
|
+
|
|
12
21
|
# GLOBAL DEPTH. The only document-depth knob, and it MUST NOT also govern review intensity —
|
|
13
22
|
# that is `risk_accepted` per component in components.yaml.
|
|
14
23
|
# catalog UC list + three inventories + C4; zero extra files per component. G4 is skipped
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Desktop Smoke Test Target
|
|
2
|
+
|
|
3
|
+
## Target Configuration
|
|
4
|
+
# Optional build and process settings for this repository:
|
|
5
|
+
# build_command: <build command from codebase-stack-guide.md>
|
|
6
|
+
# profile: debug | release (default: release for visual/performance, debug for fast logic loops)
|
|
7
|
+
# artifact_path: <path to compiled binary artifact>
|
|
8
|
+
# process_name: <application process name>
|
|
9
|
+
|
|
10
|
+
## Verification Checklist Template
|
|
11
|
+
|
|
12
|
+
- [ ] Application launches cleanly without crashes or uncaught runtime panics
|
|
13
|
+
- [ ] Primary window displays expected initial view and layout
|
|
14
|
+
- [ ] User input, navigation, and window controls respond as expected
|
|
15
|
+
- [ ] Application logs are free from unexpected warnings or errors
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Mobile Smoke Test Checklist Template
|
|
2
|
+
|
|
3
|
+
- [ ] Application installs and launches on target device or emulator
|
|
4
|
+
- [ ] Initial view displays cleanly without crashing
|
|
5
|
+
- [ ] Touch gestures and primary navigation respond as expected
|
|
6
|
+
- [ ] Device runtime log displays no unhandled exceptions
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Web Smoke Test Checklist Template
|
|
2
|
+
|
|
3
|
+
- [ ] Application server starts and responds to HTTP requests
|
|
4
|
+
- [ ] Initial route renders without 404 or 500 status codes
|
|
5
|
+
- [ ] Interactive UI controls respond to user interaction
|
|
6
|
+
- [ ] Browser console log shows no uncaught exceptions
|