@sienklogic/plan-build-run 2.11.0 → 2.12.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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,20 @@ All notable changes to Plan-Build-Run will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.12.0](https://github.com/SienkLogic/plan-build-run/compare/plan-build-run-v2.11.0...plan-build-run-v2.12.0) (2026-02-21)
9
+
10
+
11
+ ### Features
12
+
13
+ * **tools:** add review verifier post-check and stale active-skill detection ([dbd2eb8](https://github.com/SienkLogic/plan-build-run/commit/dbd2eb899c2b15cc2ac61913b29ca1aaab6b0b1f))
14
+ * **tools:** add scan mapper area validation and stale Building status detection ([8d8a438](https://github.com/SienkLogic/plan-build-run/commit/8d8a43809095722ae9d00242f90f29d32bef4d1f))
15
+
16
+
17
+ ### Bug Fixes
18
+
19
+ * **tools:** extend executor commit check to quick skill and add .catch() to log-tool-failure ([197efc7](https://github.com/SienkLogic/plan-build-run/commit/197efc70cb5e2a36f014ee2c5c7d653b4b1898f4))
20
+ * **tools:** warn on context budget tracker reset and roadmap sync parse failures ([f5aef28](https://github.com/SienkLogic/plan-build-run/commit/f5aef2804e42a934d3e7a480e3045ac6c0e0fc9b))
21
+
8
22
  ## [2.11.0](https://github.com/SienkLogic/plan-build-run/compare/plan-build-run-v2.10.0...plan-build-run-v2.11.0) (2026-02-21)
9
23
 
10
24
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sienklogic/plan-build-run",
3
- "version": "2.11.0",
3
+ "version": "2.12.0",
4
4
  "description": "Plan it, Build it, Run it — structured development workflow for Claude Code",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pbr",
3
3
  "displayName": "Plan-Build-Run",
4
- "version": "2.11.0",
4
+ "version": "2.12.0",
5
5
  "description": "Plan-Build-Run — Structured development workflow for GitHub Copilot CLI. Solves context rot through disciplined agent delegation, structured planning, atomic execution, and goal-backward verification.",
6
6
  "author": {
7
7
  "name": "SienkLogic",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pbr",
3
3
  "displayName": "Plan-Build-Run",
4
- "version": "2.11.0",
4
+ "version": "2.12.0",
5
5
  "description": "Plan-Build-Run — Structured development workflow for Cursor. Solves context rot through disciplined subagent delegation, structured planning, atomic execution, and goal-backward verification.",
6
6
  "author": {
7
7
  "name": "SienkLogic",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pbr",
3
- "version": "2.11.0",
3
+ "version": "2.12.0",
4
4
  "description": "Plan-Build-Run — Structured development workflow for Claude Code. Solves context rot through disciplined subagent delegation, structured planning, atomic execution, and goal-backward verification.",
5
5
  "author": {
6
6
  "name": "SienkLogic",
@@ -48,6 +48,10 @@ function main() {
48
48
  const stateInfo = parseState(stateContent);
49
49
  if (!stateInfo || !stateInfo.phase || !stateInfo.status) {
50
50
  logHook('check-roadmap-sync', 'PostToolUse', 'skip', { reason: 'could not parse STATE.md' });
51
+ const output = {
52
+ additionalContext: '[Roadmap Sync] Could not parse phase/status from STATE.md — ensure it contains **Phase**: and **Status**: fields.'
53
+ };
54
+ process.stdout.write(JSON.stringify(output));
51
55
  process.exit(0);
52
56
  }
53
57
 
@@ -220,7 +224,11 @@ function checkSync(data) {
220
224
  const stateInfo = parseState(stateContent);
221
225
  if (!stateInfo || !stateInfo.phase || !stateInfo.status) {
222
226
  logHook('check-roadmap-sync', 'PostToolUse', 'skip', { reason: 'could not parse STATE.md' });
223
- return null;
227
+ return {
228
+ output: {
229
+ additionalContext: '[Roadmap Sync] Could not parse phase/status from STATE.md — ensure it contains **Phase**: and **Status**: fields.'
230
+ }
231
+ };
224
232
  }
225
233
 
226
234
  if (!LIFECYCLE_STATUSES.includes(stateInfo.status)) {
@@ -312,8 +312,38 @@ function main() {
312
312
  }
313
313
  }
314
314
 
315
- // GAP-06: Build executor SUMMARY should have commits
316
- if (activeSkill === 'build' && agentType === 'pbr:executor') {
315
+ // GAP-08: Scan codebase-mapper should produce all 4 focus areas
316
+ if (activeSkill === 'scan' && agentType === 'pbr:codebase-mapper') {
317
+ const expectedAreas = ['tech', 'arch', 'quality', 'concerns'];
318
+ const codebaseDir = path.join(planningDir, 'codebase');
319
+ if (fs.existsSync(codebaseDir)) {
320
+ try {
321
+ const files = fs.readdirSync(codebaseDir).map(f => f.toLowerCase());
322
+ for (const area of expectedAreas) {
323
+ if (!files.some(f => f.includes(area))) {
324
+ skillWarnings.push(`Scan mapper: No output file containing "${area}" found in .planning/codebase/. One of the 4 mappers may have failed.`);
325
+ }
326
+ }
327
+ } catch (_e) { /* best-effort */ }
328
+ }
329
+ }
330
+
331
+ // GAP-07: Review verifier should produce meaningful VERIFICATION.md status
332
+ if (activeSkill === 'review' && agentType === 'pbr:verifier') {
333
+ const verFiles = findInPhaseDir(planningDir, /^VERIFICATION\.md$/i);
334
+ for (const vf of verFiles) {
335
+ try {
336
+ const content = fs.readFileSync(vf, 'utf8');
337
+ const statusMatch = content.match(/^status:\s*(\S+)/mi);
338
+ if (statusMatch && statusMatch[1] === 'gaps_found') {
339
+ skillWarnings.push('Review verifier: VERIFICATION.md has status "gaps_found" — ensure gaps are surfaced to the user.');
340
+ }
341
+ } catch (_e) { /* best-effort */ }
342
+ }
343
+ }
344
+
345
+ // GAP-06: Build/quick executor SUMMARY should have commits
346
+ if ((activeSkill === 'build' || activeSkill === 'quick') && agentType === 'pbr:executor') {
317
347
  checkSummaryCommits(planningDir, found, skillWarnings);
318
348
  }
319
349
 
@@ -85,4 +85,4 @@ function summarizeInput(toolName, toolInput) {
85
85
  }
86
86
  }
87
87
 
88
- main();
88
+ main().catch(() => {});
@@ -91,6 +91,20 @@ function buildContext(planningDir, stateFile) {
91
91
  if (continuity) {
92
92
  parts.push(`\nLast Session:\n${continuity}`);
93
93
  }
94
+
95
+ // Detect stale "Building" status — likely a crashed executor
96
+ const statusMatch = state.match(/\*{0,2}(?:Phase\s+)?Status\*{0,2}:\s*["']?(\w+)["']?/i);
97
+ if (statusMatch && statusMatch[1].toLowerCase() === 'building') {
98
+ try {
99
+ const stateStat = fs.statSync(stateFile);
100
+ const ageMs = Date.now() - stateStat.mtimeMs;
101
+ const ageMinutes = Math.round(ageMs / 60000);
102
+ if (ageMinutes > 30) {
103
+ parts.push(`\nWarning: STATE.md shows status "Building" but was last modified ${ageMinutes} minutes ago. This may indicate a crashed executor. Run /pbr:health to diagnose.`);
104
+ logHook('progress-tracker', 'SessionStart', 'stale-building', { ageMinutes });
105
+ }
106
+ } catch (_e) { /* best-effort */ }
107
+ }
94
108
  } else {
95
109
  parts.push('\nNo STATE.md found. Run /pbr:begin to initialize or /pbr:status to check.');
96
110
  }
@@ -67,8 +67,17 @@ function main() {
67
67
  const currentSkill = readFileSafe(skillPath);
68
68
  let tracker = loadTracker(trackerPath);
69
69
 
70
- if (tracker.skill !== currentSkill || tracker.files.length > 200) {
70
+ if (tracker.skill !== currentSkill) {
71
71
  tracker = { skill: currentSkill, reads: 0, total_chars: 0, files: [] };
72
+ } else if (tracker.files.length > 200) {
73
+ logHook('track-context-budget', 'PostToolUse', 'warn', {
74
+ reason: 'tracker reset at 200 files',
75
+ reads: tracker.reads,
76
+ total_chars: tracker.total_chars,
77
+ unique_files: tracker.files.length,
78
+ });
79
+ const prevCharsTotal = tracker.total_chars;
80
+ tracker = { skill: currentSkill, reads: 0, total_chars: prevCharsTotal, files: [] };
72
81
  }
73
82
 
74
83
  // Update tracker
@@ -677,6 +677,18 @@ function checkActiveSkillIntegrity(data) {
677
677
  return 'Active-skill integrity: .planning/.active-skill not found. Skill-specific enforcement is disabled. The invoking skill should write this file. To fix: Wait for the current skill to finish, or delete .planning/.active-skill if stale.';
678
678
  }
679
679
 
680
+ // Stale lock detection: warn if .active-skill is older than 2 hours
681
+ try {
682
+ const stat = fs.statSync(activeSkillFile);
683
+ const ageMs = Date.now() - stat.mtimeMs;
684
+ const TWO_HOURS = 2 * 60 * 60 * 1000;
685
+ if (ageMs > TWO_HOURS) {
686
+ const ageHours = Math.round(ageMs / (60 * 60 * 1000));
687
+ const skill = fs.readFileSync(activeSkillFile, 'utf8').trim();
688
+ return `Active-skill integrity: .planning/.active-skill is ${ageHours}h old (skill: "${skill}"). This may be a stale lock from a crashed session. Run /pbr:health to diagnose, or delete .planning/.active-skill if the previous session is no longer running.`;
689
+ }
690
+ } catch (_e) { /* best-effort */ }
691
+
680
692
  return null;
681
693
  }
682
694