@sienklogic/plan-build-run 2.21.0 → 2.21.1
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 +24 -0
- package/CLAUDE.md +2 -2
- package/package.json +1 -1
- package/plugins/copilot-pbr/agents/codebase-mapper.agent.md +1 -1
- package/plugins/copilot-pbr/agents/debugger.agent.md +2 -0
- package/plugins/copilot-pbr/agents/executor.agent.md +3 -1
- package/plugins/copilot-pbr/agents/general.agent.md +2 -1
- package/plugins/copilot-pbr/agents/integration-checker.agent.md +2 -0
- package/plugins/copilot-pbr/agents/plan-checker.agent.md +1 -1
- package/plugins/copilot-pbr/agents/planner.agent.md +3 -1
- package/plugins/copilot-pbr/agents/researcher.agent.md +5 -3
- package/plugins/copilot-pbr/agents/synthesizer.agent.md +1 -1
- package/plugins/copilot-pbr/agents/verifier.agent.md +3 -3
- package/plugins/copilot-pbr/hooks/hooks.json +13 -13
- package/plugins/copilot-pbr/plugin.json +1 -1
- package/plugins/copilot-pbr/skills/audit/SKILL.md +1 -1
- package/plugins/copilot-pbr/skills/build/SKILL.md +4 -5
- package/plugins/copilot-pbr/skills/config/SKILL.md +1 -1
- package/plugins/copilot-pbr/skills/debug/SKILL.md +1 -1
- package/plugins/copilot-pbr/skills/import/SKILL.md +1 -1
- package/plugins/copilot-pbr/skills/plan/SKILL.md +1 -1
- package/plugins/copilot-pbr/skills/review/SKILL.md +4 -4
- package/plugins/copilot-pbr/skills/scan/SKILL.md +4 -4
- package/plugins/copilot-pbr/skills/shared/config-loading.md +1 -1
- package/plugins/copilot-pbr/skills/shared/context-budget.md +3 -3
- package/plugins/copilot-pbr/skills/shared/state-loading.md +1 -1
- package/plugins/copilot-pbr/skills/shared/state-update.md +12 -4
- package/plugins/copilot-pbr/skills/shared/universal-anti-patterns.md +1 -1
- package/plugins/copilot-pbr/templates/ROADMAP.md.tmpl +7 -0
- package/plugins/cursor-pbr/.cursor-plugin/plugin.json +1 -1
- package/plugins/cursor-pbr/agents/codebase-mapper.md +1 -1
- package/plugins/cursor-pbr/agents/debugger.md +2 -0
- package/plugins/cursor-pbr/agents/executor.md +3 -1
- package/plugins/cursor-pbr/agents/general.md +2 -1
- package/plugins/cursor-pbr/agents/integration-checker.md +2 -0
- package/plugins/cursor-pbr/agents/plan-checker.md +1 -1
- package/plugins/cursor-pbr/agents/planner.md +3 -1
- package/plugins/cursor-pbr/agents/researcher.md +5 -3
- package/plugins/cursor-pbr/agents/synthesizer.md +1 -1
- package/plugins/cursor-pbr/agents/verifier.md +3 -3
- package/plugins/cursor-pbr/skills/audit/SKILL.md +1 -1
- package/plugins/cursor-pbr/skills/build/SKILL.md +4 -5
- package/plugins/cursor-pbr/skills/config/SKILL.md +1 -1
- package/plugins/cursor-pbr/skills/debug/SKILL.md +1 -1
- package/plugins/cursor-pbr/skills/import/SKILL.md +1 -1
- package/plugins/cursor-pbr/skills/plan/SKILL.md +1 -1
- package/plugins/cursor-pbr/skills/review/SKILL.md +4 -4
- package/plugins/cursor-pbr/skills/scan/SKILL.md +4 -4
- package/plugins/cursor-pbr/skills/shared/config-loading.md +1 -1
- package/plugins/cursor-pbr/skills/shared/context-budget.md +3 -3
- package/plugins/cursor-pbr/skills/shared/state-loading.md +1 -1
- package/plugins/cursor-pbr/skills/shared/state-update.md +12 -4
- package/plugins/cursor-pbr/skills/shared/universal-anti-patterns.md +1 -1
- package/plugins/cursor-pbr/templates/ROADMAP.md.tmpl +7 -0
- package/plugins/pbr/.claude-plugin/plugin.json +1 -1
- package/plugins/pbr/agents/codebase-mapper.md +1 -1
- package/plugins/pbr/agents/debugger.md +2 -0
- package/plugins/pbr/agents/executor.md +3 -1
- package/plugins/pbr/agents/general.md +2 -1
- package/plugins/pbr/agents/integration-checker.md +2 -0
- package/plugins/pbr/agents/plan-checker.md +0 -1
- package/plugins/pbr/agents/planner.md +2 -1
- package/plugins/pbr/agents/researcher.md +2 -0
- package/plugins/pbr/agents/synthesizer.md +0 -1
- package/plugins/pbr/agents/verifier.md +1 -1
- package/plugins/pbr/commands/do.md +5 -0
- package/plugins/pbr/scripts/check-phase-boundary.js +2 -8
- package/plugins/pbr/scripts/check-plan-format.js +78 -2
- package/plugins/pbr/scripts/check-skill-workflow.js +3 -11
- package/plugins/pbr/scripts/check-state-sync.js +18 -8
- package/plugins/pbr/scripts/check-subagent-output.js +78 -6
- package/plugins/pbr/scripts/log-tool-failure.js +1 -4
- package/plugins/pbr/scripts/pre-write-dispatch.js +0 -1
- package/plugins/pbr/scripts/status-line.js +44 -11
- package/plugins/pbr/scripts/validate-commit.js +8 -7
- package/plugins/pbr/scripts/validate-skill-args.js +2 -1
- package/plugins/pbr/scripts/validate-task.js +0 -5
- package/plugins/pbr/skills/build/SKILL.md +4 -5
- package/plugins/pbr/skills/health/SKILL.md +0 -2
- package/plugins/pbr/skills/plan/SKILL.md +1 -1
- package/plugins/pbr/skills/review/SKILL.md +4 -4
- package/plugins/pbr/skills/shared/state-update.md +10 -2
- package/plugins/pbr/templates/ROADMAP.md.tmpl +2 -0
|
@@ -152,21 +152,76 @@ const AGENT_OUTPUTS = {
|
|
|
152
152
|
}
|
|
153
153
|
};
|
|
154
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Extract current phase number from STATE.md, preferring frontmatter over body.
|
|
157
|
+
* @param {string} stateContent - Full STATE.md content
|
|
158
|
+
* @returns {string|null} Phase number string or null
|
|
159
|
+
*/
|
|
160
|
+
function getCurrentPhase(stateContent) {
|
|
161
|
+
// Prefer frontmatter (always up-to-date)
|
|
162
|
+
const fmMatch = stateContent.match(/^current_phase:\s*(\d+)/m);
|
|
163
|
+
if (fmMatch) return fmMatch[1];
|
|
164
|
+
// Fall back to body text
|
|
165
|
+
const bodyMatch = stateContent.match(/Phase:\s*(\d+)\s+of\s+\d+/);
|
|
166
|
+
return bodyMatch ? bodyMatch[1] : null;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Check if ROADMAP.md is stale after executor/verifier completion.
|
|
171
|
+
* Detects: (1) no Progress table for current milestone, (2) table exists but
|
|
172
|
+
* phase row is out of date vs. phase artifacts on disk.
|
|
173
|
+
*
|
|
174
|
+
* @param {string} planningDir - Path to .planning/
|
|
175
|
+
* @returns {string|null} Warning message or null if in sync
|
|
176
|
+
*/
|
|
177
|
+
function checkRoadmapStaleness(planningDir) {
|
|
178
|
+
const roadmapPath = path.join(planningDir, 'ROADMAP.md');
|
|
179
|
+
if (!fs.existsSync(roadmapPath)) return null;
|
|
180
|
+
|
|
181
|
+
try {
|
|
182
|
+
const content = fs.readFileSync(roadmapPath, 'utf8');
|
|
183
|
+
|
|
184
|
+
// Check if there's a Progress table at all
|
|
185
|
+
const hasProgressTable = /Plans\s*Complete/i.test(content);
|
|
186
|
+
if (!hasProgressTable) {
|
|
187
|
+
return 'ROADMAP.md has no Progress table for the current milestone. The orchestrator should add a Progress table with columns: Phase | Plans Complete | Status | Completed. See skills/shared/state-update.md for format.';
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// If table exists, check if current phase row is present
|
|
191
|
+
const stateFile = path.join(planningDir, 'STATE.md');
|
|
192
|
+
if (fs.existsSync(stateFile)) {
|
|
193
|
+
const stateContent = fs.readFileSync(stateFile, 'utf8');
|
|
194
|
+
const currentPhase = getCurrentPhase(stateContent);
|
|
195
|
+
if (currentPhase) {
|
|
196
|
+
const paddedPhase = currentPhase.padStart(2, '0');
|
|
197
|
+
const phaseInTable = new RegExp(`\\|\\s*${paddedPhase}\\.`).test(content) ||
|
|
198
|
+
new RegExp(`\\|\\s*${parseInt(currentPhase, 10)}\\.`).test(content);
|
|
199
|
+
if (!phaseInTable) {
|
|
200
|
+
return `ROADMAP.md Progress table exists but has no row for Phase ${currentPhase}. Add a row for the current phase.`;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
} catch (_e) {
|
|
205
|
+
// best-effort
|
|
206
|
+
}
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
|
|
155
210
|
function findInPhaseDir(planningDir, pattern) {
|
|
156
211
|
const matches = [];
|
|
157
212
|
const phasesDir = path.join(planningDir, 'phases');
|
|
158
213
|
if (!fs.existsSync(phasesDir)) return matches;
|
|
159
214
|
|
|
160
215
|
try {
|
|
161
|
-
// Find the active phase from STATE.md
|
|
216
|
+
// Find the active phase from STATE.md (prefer frontmatter over body)
|
|
162
217
|
const stateFile = path.join(planningDir, 'STATE.md');
|
|
163
218
|
if (!fs.existsSync(stateFile)) return matches;
|
|
164
219
|
|
|
165
220
|
const stateContent = fs.readFileSync(stateFile, 'utf8');
|
|
166
|
-
const
|
|
167
|
-
if (!
|
|
221
|
+
const phaseNum = getCurrentPhase(stateContent);
|
|
222
|
+
if (!phaseNum) return matches;
|
|
168
223
|
|
|
169
|
-
const currentPhase =
|
|
224
|
+
const currentPhase = phaseNum.padStart(2, '0');
|
|
170
225
|
const dirs = fs.readdirSync(phasesDir).filter(d => d.startsWith(currentPhase));
|
|
171
226
|
if (dirs.length === 0) return matches;
|
|
172
227
|
|
|
@@ -288,6 +343,23 @@ function main() {
|
|
|
288
343
|
// Skill-specific post-completion validation
|
|
289
344
|
const skillWarnings = [];
|
|
290
345
|
|
|
346
|
+
// ACTIVE-SKILL ENFORCEMENT: Warn when no .active-skill file exists.
|
|
347
|
+
// Skills are instructed (with CRITICAL markers) to write this file, but LLMs
|
|
348
|
+
// skip it under cognitive load. This warning reminds the orchestrator.
|
|
349
|
+
if (!activeSkill && agentType !== 'pbr:general' && agentType !== 'pbr:plan-checker' && agentType !== 'pbr:integration-checker') {
|
|
350
|
+
skillWarnings.push('.active-skill file is missing — the orchestrating skill never wrote it. This means skill-workflow guards were inactive for this entire operation. CRITICAL: Write the skill name to .planning/.active-skill BEFORE spawning agents.');
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// ROADMAP.md SYNC: After executor or verifier completes, check if ROADMAP.md
|
|
354
|
+
// needs updating. Subagent writes (SUMMARY/VERIFICATION) trigger check-state-sync
|
|
355
|
+
// in the subagent context, but the main context ROADMAP.md may still be stale.
|
|
356
|
+
if (agentType === 'pbr:executor' || agentType === 'pbr:verifier') {
|
|
357
|
+
const roadmapWarning = checkRoadmapStaleness(planningDir);
|
|
358
|
+
if (roadmapWarning) {
|
|
359
|
+
skillWarnings.push(roadmapWarning);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
291
363
|
// Mtime-based recency check for researcher and synthesizer
|
|
292
364
|
if (found._stale && (agentType === 'pbr:researcher' || agentType === 'pbr:synthesizer')) {
|
|
293
365
|
const label = agentType === 'pbr:researcher' ? 'Researcher' : 'Synthesizer';
|
|
@@ -333,7 +405,7 @@ function main() {
|
|
|
333
405
|
const verFiles = findInPhaseDir(planningDir, /^VERIFICATION\.md$/i);
|
|
334
406
|
for (const vf of verFiles) {
|
|
335
407
|
try {
|
|
336
|
-
const content = fs.readFileSync(vf, 'utf8');
|
|
408
|
+
const content = fs.readFileSync(path.join(planningDir, vf), 'utf8');
|
|
337
409
|
const statusMatch = content.match(/^status:\s*(\S+)/mi);
|
|
338
410
|
if (statusMatch && statusMatch[1] === 'gaps_found') {
|
|
339
411
|
skillWarnings.push('Review verifier: VERIFICATION.md has status "gaps_found" — ensure gaps are surfaced to the user.');
|
|
@@ -386,5 +458,5 @@ function main() {
|
|
|
386
458
|
process.exit(0);
|
|
387
459
|
}
|
|
388
460
|
|
|
389
|
-
module.exports = { AGENT_OUTPUTS, findInPhaseDir, findInQuickDir, checkSummaryCommits, isRecent };
|
|
461
|
+
module.exports = { AGENT_OUTPUTS, findInPhaseDir, findInQuickDir, checkSummaryCommits, isRecent, getCurrentPhase, checkRoadmapStaleness };
|
|
390
462
|
if (require.main === module || process.argv[1] === __filename) { main(); }
|
|
@@ -55,10 +55,7 @@ async function main() {
|
|
|
55
55
|
// Provide recovery hints for Bash failures (most common actionable failure)
|
|
56
56
|
if (toolName === 'Bash' && !isInterrupt) {
|
|
57
57
|
const output = {
|
|
58
|
-
|
|
59
|
-
hookEventName: 'PostToolUseFailure',
|
|
60
|
-
additionalContext: 'Bash command failed. If this is a recurring issue, consider using /pbr:debug for systematic investigation.'
|
|
61
|
-
}
|
|
58
|
+
additionalContext: 'Bash command failed. If this is a recurring issue, consider using /pbr:debug for systematic investigation.'
|
|
62
59
|
};
|
|
63
60
|
process.stdout.write(JSON.stringify(output));
|
|
64
61
|
}
|
|
@@ -115,7 +115,6 @@ function main() {
|
|
|
115
115
|
const currentPhase = stateContent.match(/Phase:\s*(\d+)\s+of/);
|
|
116
116
|
if (currentPhase && parseInt(phaseMatch[1], 10) !== parseInt(currentPhase[1], 10)) {
|
|
117
117
|
process.stdout.write(JSON.stringify({
|
|
118
|
-
decision: 'allow',
|
|
119
118
|
additionalContext: `[pbr] Advisory: writing to phase ${phaseMatch[1]} but current phase is ${currentPhase[1]}. Ensure this cross-phase write is intentional.`
|
|
120
119
|
}));
|
|
121
120
|
process.exit(0);
|
|
@@ -197,6 +197,23 @@ function main() {
|
|
|
197
197
|
process.exit(0);
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
+
/**
|
|
201
|
+
* Parse YAML frontmatter from STATE.md content.
|
|
202
|
+
* Returns an object with frontmatter fields, or null if no frontmatter.
|
|
203
|
+
*/
|
|
204
|
+
function parseFrontmatter(content) {
|
|
205
|
+
if (!content.startsWith('---')) return null;
|
|
206
|
+
const endIdx = content.indexOf('---', 3);
|
|
207
|
+
if (endIdx === -1) return null;
|
|
208
|
+
const fm = content.substring(3, endIdx);
|
|
209
|
+
const result = {};
|
|
210
|
+
for (const line of fm.split(/\r?\n/)) {
|
|
211
|
+
const m = line.match(/^(\w[\w_]*):\s*"?([^"]*)"?\s*$/);
|
|
212
|
+
if (m) result[m[1]] = m[2];
|
|
213
|
+
}
|
|
214
|
+
return result;
|
|
215
|
+
}
|
|
216
|
+
|
|
200
217
|
function buildStatusLine(content, ctxPercent, cfg, stdinData) {
|
|
201
218
|
const config = cfg || DEFAULTS;
|
|
202
219
|
const sections = config.sections || DEFAULTS.sections;
|
|
@@ -205,15 +222,26 @@ function buildStatusLine(content, ctxPercent, cfg, stdinData) {
|
|
|
205
222
|
const barCfg = config.context_bar || DEFAULTS.context_bar;
|
|
206
223
|
const sd = stdinData || {};
|
|
207
224
|
|
|
225
|
+
// Prefer frontmatter (always up-to-date) over body text (may be stale)
|
|
226
|
+
const fm = parseFrontmatter(content);
|
|
227
|
+
|
|
208
228
|
const parts = [];
|
|
209
229
|
|
|
210
230
|
// Phase section (always includes brand text)
|
|
211
231
|
if (sections.includes('phase')) {
|
|
232
|
+
const fmPhase = fm && fm.current_phase;
|
|
233
|
+
const fmTotal = fm && fm.total_phases;
|
|
234
|
+
const fmName = fm && fm.phase_name;
|
|
212
235
|
const phaseMatch = content.match(/Phase:\s*(\d+)\s*of\s*(\d+)\s*(?:\(([^)]+)\))?/);
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
236
|
+
|
|
237
|
+
const phaseNum = fmPhase || (phaseMatch && phaseMatch[1]);
|
|
238
|
+
const phaseTotal = fmTotal || (phaseMatch && phaseMatch[2]);
|
|
239
|
+
const phaseName = fmName || (phaseMatch && phaseMatch[3]);
|
|
240
|
+
|
|
241
|
+
if (phaseNum && phaseTotal) {
|
|
242
|
+
parts.push(`${c.boldCyan}${brandText}${c.reset} ${c.bold}Phase ${phaseNum}/${phaseTotal}${c.reset}`);
|
|
243
|
+
if (phaseName) {
|
|
244
|
+
parts.push(`${c.magenta}${phaseName}${c.reset}`);
|
|
217
245
|
}
|
|
218
246
|
} else {
|
|
219
247
|
parts.push(`${c.boldCyan}${brandText}${c.reset}`);
|
|
@@ -222,10 +250,14 @@ function buildStatusLine(content, ctxPercent, cfg, stdinData) {
|
|
|
222
250
|
|
|
223
251
|
// Plan section
|
|
224
252
|
if (sections.includes('plan')) {
|
|
253
|
+
const fmComplete = fm && fm.plans_complete;
|
|
254
|
+
const fmTotal = fm && fm.plans_total;
|
|
225
255
|
const planMatch = content.match(/Plan:\s*(\d+)\s*of\s*(\d+)/);
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
256
|
+
|
|
257
|
+
const done = fmComplete != null ? parseInt(fmComplete, 10) : (planMatch ? parseInt(planMatch[1], 10) : null);
|
|
258
|
+
const total = fmTotal != null ? parseInt(fmTotal, 10) : (planMatch ? parseInt(planMatch[2], 10) : null);
|
|
259
|
+
|
|
260
|
+
if (done != null && total != null && total > 0) {
|
|
229
261
|
const planColor = done === total ? c.green : c.white;
|
|
230
262
|
parts.push(`${planColor}Plan ${done}/${total}${c.reset}`);
|
|
231
263
|
}
|
|
@@ -233,9 +265,10 @@ function buildStatusLine(content, ctxPercent, cfg, stdinData) {
|
|
|
233
265
|
|
|
234
266
|
// Status section
|
|
235
267
|
if (sections.includes('status')) {
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
268
|
+
const fmStatus = fm && fm.status;
|
|
269
|
+
const statusMatch = content.match(/^Status:\s*(.+)/m);
|
|
270
|
+
const text = fmStatus || (statusMatch && statusMatch[1].trim());
|
|
271
|
+
if (text) {
|
|
239
272
|
const short = text.length > maxLen ? text.slice(0, maxLen - 3) + '...' : text;
|
|
240
273
|
parts.push(`${statusColor(text)}${short}${c.reset}`);
|
|
241
274
|
}
|
|
@@ -285,4 +318,4 @@ function buildStatusLine(content, ctxPercent, cfg, stdinData) {
|
|
|
285
318
|
}
|
|
286
319
|
|
|
287
320
|
if (require.main === module || process.argv[1] === __filename) { main(); }
|
|
288
|
-
module.exports = { buildStatusLine, buildContextBar, getContextPercent, getGitInfo, formatDuration, loadStatusLineConfig, DEFAULTS };
|
|
321
|
+
module.exports = { buildStatusLine, buildContextBar, getContextPercent, getGitInfo, formatDuration, loadStatusLineConfig, parseFrontmatter, DEFAULTS };
|
|
@@ -178,6 +178,14 @@ function isGitCommit(command) {
|
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
function extractCommitMessage(command) {
|
|
181
|
+
// Try heredoc first: -m "$(cat <<'EOF'\n...\nEOF\n)" or -m "$(cat <<EOF\n...\nEOF\n)"
|
|
182
|
+
// Must check before generic -m patterns to avoid capturing heredoc syntax as the message
|
|
183
|
+
const heredocMatch = command.match(/<<'?EOF'?\s*\n([\s\S]*?)\nEOF/);
|
|
184
|
+
if (heredocMatch) {
|
|
185
|
+
// First line of heredoc is the commit message
|
|
186
|
+
return heredocMatch[1].trim().split('\n')[0].trim();
|
|
187
|
+
}
|
|
188
|
+
|
|
181
189
|
// Try -m "message" or -m 'message'
|
|
182
190
|
const mFlagMatch = command.match(/-m\s+["']([^"']+)["']/);
|
|
183
191
|
if (mFlagMatch) return mFlagMatch[1];
|
|
@@ -186,13 +194,6 @@ function extractCommitMessage(command) {
|
|
|
186
194
|
const mFlagMatch2 = command.match(/-m\s+"([^"]+)"/);
|
|
187
195
|
if (mFlagMatch2) return mFlagMatch2[1];
|
|
188
196
|
|
|
189
|
-
// Try heredoc: -m "$(cat <<'EOF'\n...\nEOF\n)"
|
|
190
|
-
const heredocMatch = command.match(/<<'?EOF'?\s*\n([\s\S]*?)\nEOF/);
|
|
191
|
-
if (heredocMatch) {
|
|
192
|
-
// First line of heredoc is the commit message
|
|
193
|
-
return heredocMatch[1].trim().split('\n')[0].trim();
|
|
194
|
-
}
|
|
195
|
-
|
|
196
197
|
return null;
|
|
197
198
|
}
|
|
198
199
|
|
|
@@ -101,7 +101,8 @@ function checkSkillArgs(data) {
|
|
|
101
101
|
|
|
102
102
|
return {
|
|
103
103
|
output: {
|
|
104
|
-
|
|
104
|
+
decision: 'block',
|
|
105
|
+
reason: [
|
|
105
106
|
'BLOCKED: /pbr:plan received freeform text instead of a phase number.',
|
|
106
107
|
'',
|
|
107
108
|
'The arguments "' + args.substring(0, 80) + (args.length > 80 ? '...' : '') + '" do not match any valid pattern.',
|
|
@@ -567,11 +567,6 @@ function checkBuildDependencyGate(data) {
|
|
|
567
567
|
return null;
|
|
568
568
|
}
|
|
569
569
|
|
|
570
|
-
/**
|
|
571
|
-
* Advisory check: when active skill is "build" and an executor is being
|
|
572
|
-
* spawned, warn if .checkpoint-manifest.json is missing in the phase dir.
|
|
573
|
-
* Returns a warning string or null.
|
|
574
|
-
*/
|
|
575
570
|
/**
|
|
576
571
|
* Parse VERIFICATION.md frontmatter to extract status field.
|
|
577
572
|
* Returns the status string or 'unknown' if not parseable.
|
|
@@ -720,11 +720,10 @@ These return `{ success, old_status, new_status }` or `{ success, old_plans, new
|
|
|
720
720
|
|
|
721
721
|
**CRITICAL: Update STATE.md NOW with phase completion status. Do NOT skip this step.**
|
|
722
722
|
|
|
723
|
-
**8b. Update STATE.md:**
|
|
724
|
-
-
|
|
725
|
-
- Plan
|
|
726
|
-
-
|
|
727
|
-
- Progress bar
|
|
723
|
+
**8b. Update STATE.md (CRITICAL — update BOTH frontmatter AND body):**
|
|
724
|
+
- Frontmatter: `status`, `plans_complete`, `last_activity`, `progress_percent`, `last_command`
|
|
725
|
+
- Body `## Current Position`: `Phase:` line, `Plan:` line, `Status:` line, `Last activity:` line, `Progress:` bar
|
|
726
|
+
- These MUST stay in sync — the status line reads frontmatter, humans read the body
|
|
728
727
|
|
|
729
728
|
**8c. Commit planning docs (if configured):**
|
|
730
729
|
Reference: `skills/shared/commit-planning-docs.md` for the standard commit pattern.
|
|
@@ -209,8 +209,6 @@ This ensures the user can recover the original STATE.md if the fix produces inco
|
|
|
209
209
|
|
|
210
210
|
4. If "Skip": Do nothing, continue to the rest of the output.
|
|
211
211
|
|
|
212
|
-
**Note:** When auto-fix is active, the health skill is no longer strictly read-only. The `allowed-tools` frontmatter must include `Write` and `AskUserQuestion` for auto-fix to work. Update the frontmatter accordingly.
|
|
213
|
-
|
|
214
212
|
---
|
|
215
213
|
|
|
216
214
|
## Bonus: Recent Decisions
|
|
@@ -492,7 +492,7 @@ Use AskUserQuestion (pattern: approve-revise-abort from `skills/shared/gate-prom
|
|
|
492
492
|
4. Update the `Plans Complete` column to `0/{N}` where N = number of plan files just created
|
|
493
493
|
5. Update the `Status` column to `planned`
|
|
494
494
|
6. Save the file — do NOT skip this step
|
|
495
|
-
- Update STATE.md
|
|
495
|
+
- Update STATE.md **(CRITICAL — update BOTH frontmatter AND body)**: set `status: "planned"`, `plans_total`, `last_command` in frontmatter AND update `Status:`, `Plan:` lines in body `## Current Position`
|
|
496
496
|
- **If `features.auto_advance` is `true` AND `mode` is `autonomous`:** Chain directly to build: `Skill({ skill: "pbr:build", args: "{N}" })`. This continues the build→review→plan→build cycle automatically.
|
|
497
497
|
- **Otherwise:** Suggest next action: `/pbr:build {N}`
|
|
498
498
|
|
|
@@ -331,10 +331,10 @@ If all automated checks and UAT items passed:
|
|
|
331
331
|
4. Update the `Status` column to `verified`
|
|
332
332
|
5. Update the `Completed` column to the current date (YYYY-MM-DD)
|
|
333
333
|
6. Save the file — do NOT skip this step
|
|
334
|
-
2. Update `.planning/STATE.md
|
|
335
|
-
-
|
|
336
|
-
- Progress
|
|
337
|
-
-
|
|
334
|
+
2. Update `.planning/STATE.md` **(CRITICAL — update BOTH frontmatter AND body):**
|
|
335
|
+
- Frontmatter: `status: "verified"`, `progress_percent`, `last_activity`, `last_command`
|
|
336
|
+
- Body `## Current Position`: `Status:` line, `Last activity:` line, `Progress:` bar
|
|
337
|
+
- These MUST stay in sync — see `skills/shared/state-update.md`
|
|
338
338
|
- **STATE.md size limit:** Follow size limit enforcement rules in `skills/shared/state-update.md` (150 lines max).
|
|
339
339
|
3. Update VERIFICATION.md with UAT results (append UAT section)
|
|
340
340
|
3. Present completion:
|
|
@@ -4,12 +4,17 @@ Standard pattern for updating `.planning/STATE.md`. Include this fragment in ski
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
**CRITICAL: STATE.md has TWO representations — YAML frontmatter AND markdown body. You MUST update BOTH when changing state. The status line reads frontmatter; humans and hooks read the body. If you only update frontmatter, the body goes stale and the status line shows wrong data. Do NOT skip body updates under any circumstances.**
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
7
11
|
## When to Update STATE.md
|
|
8
12
|
|
|
9
13
|
| Event | What to Update |
|
|
10
14
|
|-------|---------------|
|
|
11
|
-
| Phase status changes (planned, building, verified) | Current Position section |
|
|
12
|
-
| Plan completes or fails | Plan counter, status, last activity |
|
|
15
|
+
| Phase status changes (planned, building, verified) | Frontmatter fields AND Current Position section |
|
|
16
|
+
| Plan completes or fails | Frontmatter fields AND Plan counter, status, last activity |
|
|
17
|
+
| Phase advances to next phase | Frontmatter fields AND Phase line, Status, Last activity, Progress bar |
|
|
13
18
|
| New decision made | Accumulated Context > Decisions |
|
|
14
19
|
| Blocker discovered or resolved | Accumulated Context > Blockers/Concerns |
|
|
15
20
|
| Session starts or ends | Session Continuity section |
|
|
@@ -31,6 +36,9 @@ See: .planning/PROJECT.md (updated {date})
|
|
|
31
36
|
Update `Current focus` when phase changes.
|
|
32
37
|
|
|
33
38
|
### 2. Current Position (lines 9-14)
|
|
39
|
+
|
|
40
|
+
**CRITICAL: This section MUST match the frontmatter fields above it. When you update `current_phase` or `status` in frontmatter, you MUST also update the corresponding lines below. A hook will auto-fix drift, but do not rely on it.**
|
|
41
|
+
|
|
34
42
|
```
|
|
35
43
|
## Current Position
|
|
36
44
|
Phase: {N} of {total} ({Phase name})
|