@sienklogic/plan-build-run 2.23.0 → 2.24.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.
Files changed (41) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dashboard/package.json +1 -1
  3. package/dashboard/src/repositories/planning.repository.js +1 -11
  4. package/dashboard/src/routes/pages.routes.js +117 -2
  5. package/dashboard/src/server.js +4 -0
  6. package/dashboard/src/services/audit.service.js +42 -0
  7. package/dashboard/src/services/dashboard.service.js +1 -12
  8. package/dashboard/src/services/roadmap.service.js +1 -11
  9. package/dashboard/src/utils/strip-bom.js +8 -0
  10. package/dashboard/src/views/audit-detail.ejs +5 -0
  11. package/dashboard/src/views/audits.ejs +5 -0
  12. package/dashboard/src/views/partials/audit-detail-content.ejs +12 -0
  13. package/dashboard/src/views/partials/audits-content.ejs +34 -0
  14. package/dashboard/src/views/partials/sidebar.ejs +8 -0
  15. package/dashboard/src/views/partials/todos-content.ejs +13 -3
  16. package/package.json +1 -1
  17. package/plugins/copilot-pbr/agents/integration-checker.agent.md +9 -2
  18. package/plugins/copilot-pbr/agents/planner.agent.md +19 -0
  19. package/plugins/copilot-pbr/agents/verifier.agent.md +22 -2
  20. package/plugins/copilot-pbr/plugin.json +1 -1
  21. package/plugins/copilot-pbr/references/plan-format.md +22 -0
  22. package/plugins/copilot-pbr/templates/INTEGRATION-REPORT.md.tmpl +18 -2
  23. package/plugins/copilot-pbr/templates/VERIFICATION-DETAIL.md.tmpl +2 -1
  24. package/plugins/cursor-pbr/.cursor-plugin/plugin.json +1 -1
  25. package/plugins/cursor-pbr/agents/integration-checker.md +9 -2
  26. package/plugins/cursor-pbr/agents/planner.md +19 -0
  27. package/plugins/cursor-pbr/agents/verifier.md +22 -2
  28. package/plugins/cursor-pbr/references/plan-format.md +22 -0
  29. package/plugins/cursor-pbr/templates/INTEGRATION-REPORT.md.tmpl +18 -2
  30. package/plugins/cursor-pbr/templates/VERIFICATION-DETAIL.md.tmpl +2 -1
  31. package/plugins/pbr/.claude-plugin/plugin.json +1 -1
  32. package/plugins/pbr/agents/integration-checker.md +9 -2
  33. package/plugins/pbr/agents/planner.md +19 -0
  34. package/plugins/pbr/agents/verifier.md +22 -2
  35. package/plugins/pbr/references/plan-format.md +22 -0
  36. package/plugins/pbr/scripts/check-plan-format.js +2 -2
  37. package/plugins/pbr/scripts/check-subagent-output.js +2 -2
  38. package/plugins/pbr/scripts/validate-task.js +1 -1
  39. package/plugins/pbr/templates/INTEGRATION-REPORT.md.tmpl +18 -2
  40. package/plugins/pbr/templates/VERIFICATION-DETAIL.md.tmpl +2 -1
  41. package/dashboard/src/views/coming-soon.ejs +0 -11
package/CHANGELOG.md CHANGED
@@ -5,6 +5,21 @@ 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.24.0](https://github.com/SienkLogic/plan-build-run/compare/plan-build-run-v2.23.0...plan-build-run-v2.24.0) (2026-02-24)
9
+
10
+
11
+ ### Features
12
+
13
+ * **35-05:** add Audit Reports view with /audits and /audits/:filename routes ([33dfae7](https://github.com/SienkLogic/plan-build-run/commit/33dfae7fff02cbba93e250211e54b26d565f4f76))
14
+ * **35-05:** GREEN - implement audit.service.js with listAuditReports and getAuditReport ([c79179a](https://github.com/SienkLogic/plan-build-run/commit/c79179a75a386096e74589f13fea4a56174b1870))
15
+ * **quick-003:** add data-flow to plan-format reference, verification template, and integration report template ([e73a31e](https://github.com/SienkLogic/plan-build-run/commit/e73a31e0c1eb9535014de4b60924bd98ced2f8cb))
16
+ * **quick-003:** add data-flow verification to planner, verifier, and integration-checker agents ([e37e192](https://github.com/SienkLogic/plan-build-run/commit/e37e19297e038c38aecd7d04e93c007928242f0f))
17
+
18
+
19
+ ### Bug Fixes
20
+
21
+ * **quick-003:** pass data.session_id to LLM operations instead of undefined ([df4d168](https://github.com/SienkLogic/plan-build-run/commit/df4d1682b50935b53ddcc665b8dcdc394b8b277e))
22
+
8
23
  ## [2.23.0](https://github.com/SienkLogic/plan-build-run/compare/plan-build-run-v2.22.2...plan-build-run-v2.23.0) (2026-02-24)
9
24
 
10
25
 
@@ -19,7 +19,6 @@
19
19
  ],
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
- "@rollup/rollup-win32-x64-msvc": "^4.57.1",
23
22
  "chokidar": "^5.0.0",
24
23
  "commander": "^12.1.0",
25
24
  "ejs": "^3.1.10",
@@ -30,6 +29,7 @@
30
29
  "sanitize-html": "^2.13.0"
31
30
  },
32
31
  "devDependencies": {
32
+ "@rollup/rollup-win32-x64-msvc": "^4.57.1",
33
33
  "@vitest/coverage-v8": "^4.0.18",
34
34
  "memfs": "^4.56.10",
35
35
  "supertest": "^7.2.2",
@@ -3,20 +3,10 @@ import { join, resolve, relative, normalize } from 'node:path';
3
3
  import matter from 'gray-matter';
4
4
  import { marked } from 'marked';
5
5
  import sanitizeHtml from 'sanitize-html';
6
+ import { stripBOM } from '../utils/strip-bom.js';
6
7
 
7
8
  marked.setOptions({ gfm: true, breaks: false });
8
9
 
9
- /**
10
- * Strip UTF-8 BOM (Byte Order Mark) if present.
11
- * Windows editors (Notepad, older VS Code) may prepend BOM to UTF-8 files.
12
- * gray-matter will fail to detect frontmatter delimiters if BOM is present.
13
- * @param {string} content - Raw file content
14
- * @returns {string} Content without BOM
15
- */
16
- function stripBOM(content) {
17
- return content.replace(/^\uFEFF/, '');
18
- }
19
-
20
10
  /**
21
11
  * Validate that a resolved path stays within the base directory.
22
12
  * Prevents path traversal attacks (e.g., ../../etc/passwd).
@@ -2,12 +2,13 @@ import { Router } from 'express';
2
2
  import { getPhaseDetail, getPhaseDocument } from '../services/phase.service.js';
3
3
  import { getRoadmapData, generateDependencyMermaid } from '../services/roadmap.service.js';
4
4
  import { parseStateFile, derivePhaseStatuses } from '../services/dashboard.service.js';
5
- import { listPendingTodos, getTodoDetail, createTodo, completeTodo } from '../services/todo.service.js';
5
+ import { listPendingTodos, getTodoDetail, createTodo, completeTodo, listDoneTodos } from '../services/todo.service.js';
6
6
  import { getAllMilestones, getMilestoneDetail } from '../services/milestone.service.js';
7
7
  import { getProjectAnalytics } from '../services/analytics.service.js';
8
8
  import { getLlmMetrics } from '../services/local-llm-metrics.service.js';
9
- import { listNotes } from '../services/notes.service.js';
9
+ import { listNotes, getNoteBySlug } from '../services/notes.service.js';
10
10
  import { listQuickTasks, getQuickTask } from '../services/quick.service.js';
11
+ import { listAuditReports, getAuditReport } from '../services/audit.service.js';
11
12
 
12
13
  const router = Router();
13
14
 
@@ -190,6 +191,27 @@ router.get('/todos/new', (req, res) => {
190
191
  }
191
192
  });
192
193
 
194
+ router.get('/todos/done', async (req, res) => {
195
+ const projectDir = req.app.locals.projectDir;
196
+ const todos = await listDoneTodos(projectDir);
197
+
198
+ const templateData = {
199
+ title: 'Completed Todos',
200
+ activePage: 'todos',
201
+ currentPath: '/todos/done',
202
+ breadcrumbs: [{ label: 'Todos', url: '/todos' }, { label: 'Completed' }],
203
+ todos
204
+ };
205
+
206
+ res.setHeader('Vary', 'HX-Request');
207
+
208
+ if (req.get('HX-Request') === 'true') {
209
+ res.render('partials/todos-done-content', templateData);
210
+ } else {
211
+ res.render('todos-done', templateData);
212
+ }
213
+ });
214
+
193
215
  router.get('/todos/:id', async (req, res) => {
194
216
  const { id } = req.params;
195
217
 
@@ -404,6 +426,42 @@ router.get('/notes', async (req, res) => {
404
426
  }
405
427
  });
406
428
 
429
+ router.get('/notes/:slug', async (req, res) => {
430
+ const { slug } = req.params;
431
+
432
+ // Validate slug: lowercase alphanumeric and dashes only
433
+ if (!/^[a-z0-9-]+$/.test(slug)) {
434
+ const err = new Error('Invalid note slug format');
435
+ err.status = 404;
436
+ throw err;
437
+ }
438
+
439
+ const projectDir = req.app.locals.projectDir;
440
+ const note = await getNoteBySlug(projectDir, slug);
441
+
442
+ if (!note) {
443
+ const err = new Error(`Note "${slug}" not found`);
444
+ err.status = 404;
445
+ throw err;
446
+ }
447
+
448
+ const templateData = {
449
+ title: note.title,
450
+ activePage: 'notes',
451
+ currentPath: '/notes/' + slug,
452
+ breadcrumbs: [{ label: 'Notes', url: '/notes' }, { label: note.title }],
453
+ ...note
454
+ };
455
+
456
+ res.setHeader('Vary', 'HX-Request');
457
+
458
+ if (req.get('HX-Request') === 'true') {
459
+ res.render('partials/note-detail-content', templateData);
460
+ } else {
461
+ res.render('note-detail', templateData);
462
+ }
463
+ });
464
+
407
465
  router.get('/roadmap', async (req, res) => {
408
466
  const projectDir = req.app.locals.projectDir;
409
467
  const [roadmapData, stateData] = await Promise.all([
@@ -486,4 +544,61 @@ router.get('/quick/:id', async (req, res) => {
486
544
  }
487
545
  });
488
546
 
547
+ router.get('/audits', async (req, res) => {
548
+ const projectDir = req.app.locals.projectDir;
549
+ const reports = await listAuditReports(projectDir);
550
+
551
+ const templateData = {
552
+ title: 'Audit Reports',
553
+ activePage: 'audits',
554
+ currentPath: '/audits',
555
+ breadcrumbs: [{ label: 'Audit Reports' }],
556
+ reports
557
+ };
558
+
559
+ res.setHeader('Vary', 'HX-Request');
560
+
561
+ if (req.get('HX-Request') === 'true') {
562
+ res.render('partials/audits-content', templateData);
563
+ } else {
564
+ res.render('audits', templateData);
565
+ }
566
+ });
567
+
568
+ router.get('/audits/:filename', async (req, res) => {
569
+ const { filename } = req.params;
570
+
571
+ // Validate filename: safe characters only, must end in .md
572
+ if (!/^[\w.-]+\.md$/.test(filename)) {
573
+ const err = new Error('Invalid audit report filename');
574
+ err.status = 404;
575
+ throw err;
576
+ }
577
+
578
+ const projectDir = req.app.locals.projectDir;
579
+ const report = await getAuditReport(projectDir, filename);
580
+
581
+ if (!report) {
582
+ const err = new Error(`Audit report "${filename}" not found`);
583
+ err.status = 404;
584
+ throw err;
585
+ }
586
+
587
+ const templateData = {
588
+ title: report.title,
589
+ activePage: 'audits',
590
+ currentPath: '/audits/' + filename,
591
+ breadcrumbs: [{ label: 'Audit Reports', url: '/audits' }, { label: report.title }],
592
+ ...report
593
+ };
594
+
595
+ res.setHeader('Vary', 'HX-Request');
596
+
597
+ if (req.get('HX-Request') === 'true') {
598
+ res.render('partials/audit-detail-content', templateData);
599
+ } else {
600
+ res.render('audit-detail', templateData);
601
+ }
602
+ });
603
+
489
604
  export default router;
@@ -1,6 +1,8 @@
1
1
  import { createApp } from './app.js';
2
2
  import { createWatcher } from './services/watcher.service.js';
3
3
  import { broadcast } from './services/sse.service.js';
4
+ import { cache as milestoneCache } from './services/milestone.service.js';
5
+ import { cache as analyticsCache } from './services/analytics.service.js';
4
6
 
5
7
  export function startServer(config) {
6
8
  const app = createApp(config);
@@ -8,6 +10,8 @@ export function startServer(config) {
8
10
 
9
11
  // Start file watcher for live updates
10
12
  const watcher = createWatcher(projectDir, (event) => {
13
+ milestoneCache.invalidateAll();
14
+ analyticsCache.invalidateAll();
11
15
  broadcast('file-change', event);
12
16
  });
13
17
 
@@ -0,0 +1,42 @@
1
+ import { readdir } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { readMarkdownFile } from '../repositories/planning.repository.js';
4
+
5
+ export async function listAuditReports(projectDir) {
6
+ const auditsDir = join(projectDir, '.planning', 'audits');
7
+ let entries;
8
+ try {
9
+ entries = await readdir(auditsDir);
10
+ } catch (err) {
11
+ if (err.code === 'ENOENT') return [];
12
+ throw err;
13
+ }
14
+ const mdFiles = entries.filter(f => f.endsWith('.md')).sort().reverse();
15
+ const reports = [];
16
+ for (const filename of mdFiles) {
17
+ const dateMatch = filename.match(/^(\d{4}-\d{2}-\d{2})-(.+)\.md$/);
18
+ const date = dateMatch ? dateMatch[1] : null;
19
+ const slug = dateMatch ? dateMatch[2] : filename.replace(/\.md$/, '');
20
+ const title = slug.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
21
+ reports.push({ filename, date, slug, title });
22
+ }
23
+ return reports;
24
+ }
25
+
26
+ export async function getAuditReport(projectDir, filename) {
27
+ if (!/^[\w.-]+\.md$/.test(filename)) return null;
28
+ if (filename.includes('/') || filename.includes('\\') || filename.includes('..')) return null;
29
+
30
+ const auditsDir = join(projectDir, '.planning', 'audits');
31
+ try {
32
+ const { frontmatter, html } = await readMarkdownFile(join(auditsDir, filename));
33
+ const dateMatch = filename.match(/^(\d{4}-\d{2}-\d{2})-(.+)\.md$/);
34
+ const date = dateMatch ? dateMatch[1] : null;
35
+ const slug = dateMatch ? dateMatch[2] : filename.replace(/\.md$/, '');
36
+ const title = slug.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
37
+ return { filename, date, slug, title, frontmatter, html };
38
+ } catch (err) {
39
+ if (err.code === 'ENOENT') return null;
40
+ throw err;
41
+ }
42
+ }
@@ -1,17 +1,6 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
-
4
- /**
5
- * Strip UTF-8 BOM from file content.
6
- * Duplicated from planning.repository.js intentionally --
7
- * this service reads raw text, not via the repository layer.
8
- *
9
- * @param {string} content - Raw file content
10
- * @returns {string} Content without BOM
11
- */
12
- function stripBOM(content) {
13
- return content.replace(/^\uFEFF/, '');
14
- }
3
+ import { stripBOM } from '../utils/strip-bom.js';
15
4
 
16
5
  /**
17
6
  * Parse STATE.md to extract project status information.
@@ -1,17 +1,7 @@
1
1
  import { readFile, readdir } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
3
  import { parseRoadmapFile } from './dashboard.service.js';
4
-
5
- /**
6
- * Strip UTF-8 BOM from file content.
7
- * Duplicated intentionally -- this service reads raw text, not via the repository layer.
8
- *
9
- * @param {string} content - Raw file content
10
- * @returns {string} Content without BOM
11
- */
12
- function stripBOM(content) {
13
- return content.replace(/^\uFEFF/, '');
14
- }
4
+ import { stripBOM } from '../utils/strip-bom.js';
15
5
 
16
6
  /**
17
7
  * Count the number of PLAN.md files in a phase directory.
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Strip UTF-8 BOM (Byte Order Mark) if present.
3
+ * @param {string} content
4
+ * @returns {string}
5
+ */
6
+ export function stripBOM(content) {
7
+ return content.replace(/^\uFEFF/, '');
8
+ }
@@ -0,0 +1,5 @@
1
+ <%- include('partials/layout-top', { title: title, activePage: 'audits' }) %>
2
+
3
+ <%- include('partials/audit-detail-content') %>
4
+
5
+ <%- include('partials/layout-bottom') %>
@@ -0,0 +1,5 @@
1
+ <%- include('partials/layout-top', { title: 'Audit Reports', activePage: 'audits' }) %>
2
+
3
+ <%- include('partials/audits-content') %>
4
+
5
+ <%- include('partials/layout-bottom') %>
@@ -0,0 +1,12 @@
1
+ <%- include('breadcrumbs', { breadcrumbs: typeof breadcrumbs !== 'undefined' ? breadcrumbs : [] }) %>
2
+ <h1><%= title %></h1>
3
+
4
+ <p><a href="/audits">&larr; Back to Audit Reports</a></p>
5
+
6
+ <% if (typeof date !== 'undefined' && date) { %>
7
+ <p><small>Date: <%= date %></small></p>
8
+ <% } %>
9
+
10
+ <article class="markdown-body">
11
+ <%- html %>
12
+ </article>
@@ -0,0 +1,34 @@
1
+ <%- include('breadcrumbs', { breadcrumbs: typeof breadcrumbs !== 'undefined' ? breadcrumbs : [] }) %>
2
+ <h1>Audit Reports</h1>
3
+
4
+ <% if (typeof reports !== 'undefined' && reports.length > 0) { %>
5
+ <article>
6
+ <div class="table-wrap">
7
+ <table>
8
+ <thead>
9
+ <tr>
10
+ <th scope="col">Date</th>
11
+ <th scope="col">Report</th>
12
+ </tr>
13
+ </thead>
14
+ <tbody>
15
+ <% reports.forEach(function(report) { %>
16
+ <tr>
17
+ <td><%= report.date || '—' %></td>
18
+ <td>
19
+ <a href="/audits/<%= report.filename %>"
20
+ hx-get="/audits/<%= report.filename %>"
21
+ hx-target="#main-content"
22
+ hx-push-url="true">
23
+ <%= report.title %>
24
+ </a>
25
+ </td>
26
+ </tr>
27
+ <% }); %>
28
+ </tbody>
29
+ </table>
30
+ </div>
31
+ </article>
32
+ <% } else { %>
33
+ <%- include('empty-state', { icon: '🔍', title: 'No audit reports found', action: 'Run /pbr:audit to generate a session audit report.' }) %>
34
+ <% } %>
@@ -87,6 +87,14 @@
87
87
  Quick Tasks
88
88
  </a>
89
89
  </li>
90
+ <li>
91
+ <a href="/audits"
92
+ hx-get="/audits"
93
+ hx-target="#main-content"
94
+ hx-push-url="true"<%= typeof activePage !== 'undefined' && activePage === 'audits' ? ' aria-current="page"' : '' %>>
95
+ Audit Reports
96
+ </a>
97
+ </li>
90
98
  </ul>
91
99
  </details>
92
100
 
@@ -1,10 +1,17 @@
1
1
  <%- include('breadcrumbs', { breadcrumbs: typeof breadcrumbs !== 'undefined' ? breadcrumbs : [] }) %>
2
2
  <h1>Todos</h1>
3
3
 
4
- <p><a href="/todos/new" role="button"
4
+ <p>
5
+ <a href="/todos/new" role="button"
5
6
  hx-get="/todos/new"
6
7
  hx-target="#main-content"
7
- hx-push-url="true">Create Todo</a></p>
8
+ hx-push-url="true">Create Todo</a>
9
+ &nbsp;
10
+ <a href="/todos/done"
11
+ hx-get="/todos/done"
12
+ hx-target="#main-content"
13
+ hx-push-url="true">View Completed Todos</a>
14
+ </p>
8
15
 
9
16
  <% const f = typeof filters !== 'undefined' ? filters : { priority: '', status: '', q: '' }; %>
10
17
  <article>
@@ -70,7 +77,10 @@
70
77
  <tr>
71
78
  <td><%= todo.id %></td>
72
79
  <td>
73
- <a href="/todos/<%= todo.id %>">
80
+ <a href="/todos/<%= todo.id %>"
81
+ hx-get="/todos/<%= todo.id %>"
82
+ hx-target="#main-content"
83
+ hx-push-url="true">
74
84
  <%= todo.title %>
75
85
  </a>
76
86
  </td>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sienklogic/plan-build-run",
3
- "version": "2.23.0",
3
+ "version": "2.24.0",
4
4
  "description": "Plan it, Build it, Run it — structured development workflow for Claude Code",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -35,6 +35,7 @@ You MUST perform all applicable categories (skip only if zero items exist for th
35
35
  3. **Auth Protection** — Every non-public route must have auth middleware. Frontend route guards must match backend protection.
36
36
  4. **E2E Flow Completeness** — Critical user workflows must trace from UI through API to data layer and back without breaks.
37
37
  5. **Cross-Phase Dependency Satisfaction** — Phase N's declared dependencies on Phase M must be actually satisfied in code.
38
+ 6. **Data-Flow Propagation** — Values originating at one boundary (hook stdin fields, API request params, env vars) must propagate correctly through the call chain to their destination (log entries, database records, API responses). A connected pipeline with missing data is a broken integration.
38
39
 
39
40
  > **First-phase edge case**: If no completed phases exist yet, focus on verifying the current phase's internal consistency — exports match imports within the phase, API contracts are self-consistent. Cross-phase checks are not applicable and should be skipped.
40
41
 
@@ -47,14 +48,19 @@ Read `references/agent-contracts.md` to validate agent-to-agent handoffs. Verify
47
48
  - **Write access for output artifact only** — you have Write access for your output artifact only. You CANNOT fix source code — you REPORT issues.
48
49
  - **Cross-phase scope** — unlike verifier (single phase), you check across phases.
49
50
 
50
- ## 6-Step Verification Process
51
+ ## 7-Step Verification Process
51
52
 
52
53
  1. **Build Export/Import Map**: Read each completed phase's SUMMARY.md frontmatter (`requires`, `provides`, `affects`). Grep actual exports/imports in source. Cross-reference declared vs actual — flag mismatches.
53
54
  2. **Verify Export Usage**: For each `provides` item: locate actual export (missing = `MISSING_EXPORT` ERROR), find consumers (none = `ORPHANED` WARNING), verify usage not just import (`IMPORTED_UNUSED` WARNING), check signature compatibility (`MISMATCHED` ERROR). Status `CONSUMED` = OK.
54
55
  3. **Verify API Coverage**: Discover routes, find frontend callers, match by method+path+body/params. Produce coverage table. See `references/integration-patterns.md` for framework-specific patterns.
55
56
  4. **Verify Auth Protection**: Identify auth mechanism, list all routes, classify (public vs protected), check frontend guards. Flag UNPROTECTED routes.
56
57
  5. **Verify E2E Flows**: Trace critical workflows step-by-step — verify each step exists and connects to the next (import/call/redirect). Record evidence (file:line). Flow status: COMPLETE | BROKEN | PARTIAL | UNTRACEABLE. See `references/integration-patterns.md` for flow templates.
57
- 6. **Compile Integration Report**: Produce final report with all findings by category.
58
+ 6. **Verify Data-Flow Propagation**: For each cross-boundary data field identified in plans or SUMMARY.md, trace the value from source through intermediate functions to destination. Verify the value is actually passed (not `undefined`/`null`/hardcoded) at each step.
59
+ - **Source examples**: hook stdin (`data.session_id`), API request params, environment variables, config fields
60
+ - **Destination examples**: log entries, database records, API responses, metric files
61
+ - **Method**: Grep each intermediate call site and inspect arguments. Flag `DATA_DROPPED` when a value available in scope is replaced by `undefined` or a placeholder.
62
+ - **Status**: `PROPAGATED` (value flows correctly) | `DATA_DROPPED` (value lost at some step) | `UNTRACEABLE` (cannot determine flow)
63
+ 7. **Compile Integration Report**: Produce final report with all findings by category.
58
64
 
59
65
  ## Output Format
60
66
 
@@ -119,3 +125,4 @@ See `references/integration-patterns.md` for grep/search patterns by framework.
119
125
  - "File exists" is not "component is integrated"
120
126
  - Auth middleware existing somewhere does not mean routes are protected
121
127
  - Always check error handling paths, not just happy paths
128
+ - Structural connectivity is not data-flow correctness — a connected pipeline can still drop data at any step
@@ -66,6 +66,23 @@ Each must-have maps to one or more tasks. Every task exists to make a must-have
66
66
 
67
67
  ---
68
68
 
69
+ ## Data Contracts for Cross-Boundary Parameters
70
+
71
+ When a function signature includes parameters that flow across module boundaries — session IDs from hook stdin, config objects from disk, auth tokens from environment — the plan **MUST** specify the **source** for each argument, not just the type.
72
+
73
+ For every cross-boundary call in a task's `<action>`, document:
74
+
75
+ | Parameter | Source | Context | Fallback |
76
+ |-----------|--------|---------|----------|
77
+ | `sessionId` | `data.session_id` (hook stdin) | Hook scripts only | `undefined` (CLI context) |
78
+ | `config` | `configLoad(planningDir)` | All callers | `resolveConfig(undefined)` |
79
+
80
+ **When to apply:** Any function call where the caller and callee live in different modules AND at least one argument originates from an external boundary (stdin, env, disk, network). Internal helper calls within the same module do not need contracts.
81
+
82
+ **Why this matters:** Without explicit source mapping, executors will use the type-correct but value-wrong default (e.g., `undefined` instead of `data.session_id`). The plan is the single source of truth for how data flows — if the plan says `undefined`, the executor will faithfully implement `undefined`.
83
+
84
+ ---
85
+
69
86
  ## Plan Structure
70
87
 
71
88
  Read `references/plan-format.md` for the complete plan file specification including:
@@ -165,6 +182,7 @@ When CONTEXT.md or RESEARCH-SUMMARY.md contains `[NEEDS DECISION]` flags from th
165
182
  - [ ] Dependencies are acyclic, no file conflicts within same wave
166
183
  - [ ] Locked decisions honored, no deferred ideas included
167
184
  - [ ] Verify commands are actually executable
185
+ - [ ] Cross-boundary parameters have documented sources (data contracts)
168
186
 
169
187
  ---
170
188
 
@@ -238,3 +256,4 @@ One-line task descriptions in `<name>`. File paths in `<files>`, not explanation
238
256
  9. DO NOT plan for features outside the current phase goal
239
257
  10. DO NOT assume research is done — check discovery level
240
258
  11. DO NOT leave done conditions vague — they must be observable
259
+ 12. DO NOT specify literal `undefined` for parameters that have a known source in the calling context — use data contracts to map sources
@@ -95,10 +95,29 @@ Verify the artifact is imported AND used by other parts of the system (functions
95
95
  | Yes | Yes | No | UNWIRED |
96
96
  | Yes | Yes | Yes | PASSED |
97
97
 
98
+ > **Note:** WIRED status (Level 3) requires correct arguments, not just correct function names. A call that passes `undefined` for a parameter available in scope is `ARGS_WRONG`, not `WIRED`.
99
+
98
100
  ### Step 6: Verify Key Links (Always)
99
101
 
100
102
  For each key_link: identify source and target components, verify the import path resolves, verify the imported symbol is actually called/used, and verify call signatures match. Watch for: wrong import paths, imported-but-never-called symbols, defined-but-never-applied middleware, registered-but-never-triggered event handlers.
101
103
 
104
+ ### Step 6b: Argument-Level Spot Checks (Always)
105
+
106
+ Beyond verifying that calls exist, spot-check that **arguments passed to cross-boundary calls carry the correct values**. A call with the right function but wrong arguments is effectively UNWIRED.
107
+
108
+ **Focus on:** IDs (session, user, request), config objects, auth tokens, and context data that originate from external boundaries (stdin, env, disk).
109
+
110
+ **Method:**
111
+ 1. For each key_link verified in Step 6, grep the call site and inspect the arguments
112
+ 2. Compare each argument against the data source available in the calling scope
113
+ 3. Flag any argument that passes `undefined`, `null`, or a hardcoded placeholder when the calling scope has the real value available (e.g., `data.session_id` is in scope but `undefined` is passed)
114
+
115
+ **Classification:**
116
+ - `WIRED` requires both correct function AND correct arguments
117
+ - `ARGS_WRONG` = correct function called but one or more arguments are incorrect/missing — this is a key link gap
118
+
119
+ **Example:** A hook script receives `data` from stdin containing `session_id`. If it calls `logMetric(planningDir, { session_id: undefined })` instead of `logMetric(planningDir, { session_id: data.session_id })`, that is an `ARGS_WRONG` gap even though the call itself exists.
120
+
102
121
  ### Step 7: Check Requirements Coverage (Always)
103
122
 
104
123
  Cross-reference all must-haves against verification results in a table:
@@ -107,8 +126,8 @@ Cross-reference all must-haves against verification results in a table:
107
126
  | # | Must-Have | Type | L1 (Exists) | L2 (Substantive) | L3 (Wired) | Status |
108
127
  |---|----------|------|-------------|-------------------|------------|--------|
109
128
  | 1 | {description} | truth | - | - | - | VERIFIED/FAILED |
110
- | 2 | {description} | artifact | YES/NO | YES/STUB/PARTIAL | WIRED/ORPHANED | PASS/FAIL |
111
- | 3 | {description} | key_link | - | - | YES/NO | PASS/FAIL |
129
+ | 2 | {description} | artifact | YES/NO | YES/STUB/PARTIAL | WIRED/ORPHANED/ARGS_WRONG | PASS/FAIL |
130
+ | 3 | {description} | key_link | - | - | YES/NO/ARGS_WRONG | PASS/FAIL |
112
131
  ```
113
132
 
114
133
  ### Step 8: Scan for Anti-Patterns (Full Verification Only)
@@ -226,3 +245,4 @@ Read `references/stub-patterns.md` for stub detection patterns by technology. Re
226
245
  9. DO NOT give PASSED status if ANY must-have fails at ANY level
227
246
  10. DO NOT count deferred items as gaps — they are intentionally not implemented
228
247
  11. DO NOT be lenient — your job is to find problems, not to be encouraging
248
+ 12. DO NOT mark a call as WIRED if it passes hardcoded `undefined`/`null` for parameters that have a known source in scope — check arguments, not just function names
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pbr",
3
3
  "displayName": "Plan-Build-Run",
4
- "version": "2.23.0",
4
+ "version": "2.24.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",
@@ -71,6 +71,28 @@ requirement_ids:
71
71
  | `consumes` | NO | array | What this plan needs from prior plans. Format: `"Thing (from plan XX-YY)"` |
72
72
  | `requirement_ids` | NO | array | Requirement IDs from REQUIREMENTS.md or ROADMAP.md goal IDs that this plan addresses. Enables bidirectional traceability between plans and requirements/goals. |
73
73
  | `dependency_fingerprints` | NO | object | Hashes of dependency phase SUMMARY.md files at plan-creation time. Used to detect stale plans. |
74
+ | `data_contracts` | NO | array | Cross-boundary parameter mappings for calls where arguments originate from external boundaries. Format: `"param: source (context) [fallback]"` |
75
+
76
+ ### Data Contracts
77
+
78
+ When a task's `<action>` includes calls across module boundaries where arguments come from external sources (hook stdin, env vars, API params, config files), document the parameter-to-source mapping in `data_contracts` frontmatter and in the `<action>` step itself.
79
+
80
+ Example frontmatter:
81
+
82
+ ```yaml
83
+ data_contracts:
84
+ - "sessionId: data.session_id (hook stdin) [undefined in CLI context]"
85
+ - "config: configLoad(planningDir) (disk) [resolveConfig(undefined)]"
86
+ ```
87
+
88
+ Example in `<action>`:
89
+
90
+ ```
91
+ 3. Call classifyArtifact(llmConfig, planningDir, content, fileType, data.session_id)
92
+ Data contract: sessionId ← data.session_id from hook stdin (undefined in CLI context)
93
+ ```
94
+
95
+ **When to apply:** Any call where caller and callee are in different modules AND at least one argument originates from an external boundary. Internal helper calls within the same module do not need contracts.
74
96
 
75
97
  ---
76
98
 
@@ -112,7 +112,22 @@ Phase 03 (Core) ──provides──→ Phase 04 (Frontend)
112
112
  ### Flow 2: {Flow Name} - {STATUS}
113
113
  ...
114
114
 
115
- ## 5. Integration Issues Summary
115
+ ## 5. Data-Flow Propagation
116
+
117
+ ### Cross-Boundary Data Flows
118
+
119
+ | Data Field | Source | Intermediate Steps | Destination | Status |
120
+ |------------|--------|-------------------|-------------|--------|
121
+ | {field name} | {origin, e.g., hook stdin `data.session_id`} | {module1:L12 → module2:L45} | {dest, e.g., metrics.jsonl `session_id`} | PROPAGATED |
122
+ | {field name} | {origin} | {module1:L12 → module2:L45} | {dest} | DATA_DROPPED |
123
+
124
+ ### Data-Flow Issues
125
+
126
+ | Field | Dropped At | Available In Scope | Passed Instead | Fix |
127
+ |-------|-----------|-------------------|----------------|-----|
128
+ | {field} | {file:line} | `data.session_id` | `undefined` | Pass `data.session_id` |
129
+
130
+ ## 6. Integration Issues Summary
116
131
 
117
132
  ### Critical Issues (system cannot function)
118
133
 
@@ -131,7 +146,7 @@ Phase 03 (Core) ──provides──→ Phase 04 (Frontend)
131
146
  1. **{Issue}**: {description}
132
147
  - Fix: {recommended action}
133
148
 
134
- ## 6. Integration Score
149
+ ## 7. Integration Score
135
150
 
136
151
  | Category | Items Checked | Passed | Failed | Score |
137
152
  |----------|--------------|--------|--------|-------|
@@ -139,6 +154,7 @@ Phase 03 (Core) ──provides──→ Phase 04 (Frontend)
139
154
  | API coverage | {n} | {n} | {n} | {%} |
140
155
  | Auth protection | {n} | {n} | {n} | {%} |
141
156
  | E2E flows | {n} | {n} | {n} | {%} |
157
+ | Data-flow propagation | {n} | {n} | {n} | {%} |
142
158
  | **Overall** | {n} | {n} | {n} | **{%}** |
143
159
 
144
160
  ## Recommendations
@@ -54,8 +54,9 @@ anti_patterns:
54
54
 
55
55
  | # | Link Description | Source | Target | Status | Evidence |
56
56
  |---|-----------------|--------|--------|--------|----------|
57
- | 1 | {what connects to what} | `{source_file}` | `{target_file}` | WIRED | Import at L12, called at L45 |
57
+ | 1 | {what connects to what} | `{source_file}` | `{target_file}` | WIRED | Import at L12, called at L45, args correct |
58
58
  | 2 | {what connects to what} | `{source_file}` | `{target_file}` | BROKEN | Imported but never called |
59
+ | 3 | {what connects to what} | `{source_file}` | `{target_file}` | ARGS_WRONG | Called at L45 but passes undefined for sessionId (data.session_id in scope) |
59
60
 
60
61
  ## Gaps Found
61
62
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pbr",
3
3
  "displayName": "Plan-Build-Run",
4
- "version": "2.23.0",
4
+ "version": "2.24.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",
@@ -34,6 +34,7 @@ You MUST perform all applicable categories (skip only if zero items exist for th
34
34
  3. **Auth Protection** — Every non-public route must have auth middleware. Frontend route guards must match backend protection.
35
35
  4. **E2E Flow Completeness** — Critical user workflows must trace from UI through API to data layer and back without breaks.
36
36
  5. **Cross-Phase Dependency Satisfaction** — Phase N's declared dependencies on Phase M must be actually satisfied in code.
37
+ 6. **Data-Flow Propagation** — Values originating at one boundary (hook stdin fields, API request params, env vars) must propagate correctly through the call chain to their destination (log entries, database records, API responses). A connected pipeline with missing data is a broken integration.
37
38
 
38
39
  > **First-phase edge case**: If no completed phases exist yet, focus on verifying the current phase's internal consistency — exports match imports within the phase, API contracts are self-consistent. Cross-phase checks are not applicable and should be skipped.
39
40
 
@@ -46,14 +47,19 @@ Read `references/agent-contracts.md` to validate agent-to-agent handoffs. Verify
46
47
  - **Write access for output artifact only** — you have Write access for your output artifact only. You CANNOT fix source code — you REPORT issues.
47
48
  - **Cross-phase scope** — unlike verifier (single phase), you check across phases.
48
49
 
49
- ## 6-Step Verification Process
50
+ ## 7-Step Verification Process
50
51
 
51
52
  1. **Build Export/Import Map**: Read each completed phase's SUMMARY.md frontmatter (`requires`, `provides`, `affects`). Grep actual exports/imports in source. Cross-reference declared vs actual — flag mismatches.
52
53
  2. **Verify Export Usage**: For each `provides` item: locate actual export (missing = `MISSING_EXPORT` ERROR), find consumers (none = `ORPHANED` WARNING), verify usage not just import (`IMPORTED_UNUSED` WARNING), check signature compatibility (`MISMATCHED` ERROR). Status `CONSUMED` = OK.
53
54
  3. **Verify API Coverage**: Discover routes, find frontend callers, match by method+path+body/params. Produce coverage table. See `references/integration-patterns.md` for framework-specific patterns.
54
55
  4. **Verify Auth Protection**: Identify auth mechanism, list all routes, classify (public vs protected), check frontend guards. Flag UNPROTECTED routes.
55
56
  5. **Verify E2E Flows**: Trace critical workflows step-by-step — verify each step exists and connects to the next (import/call/redirect). Record evidence (file:line). Flow status: COMPLETE | BROKEN | PARTIAL | UNTRACEABLE. See `references/integration-patterns.md` for flow templates.
56
- 6. **Compile Integration Report**: Produce final report with all findings by category.
57
+ 6. **Verify Data-Flow Propagation**: For each cross-boundary data field identified in plans or SUMMARY.md, trace the value from source through intermediate functions to destination. Verify the value is actually passed (not `undefined`/`null`/hardcoded) at each step.
58
+ - **Source examples**: hook stdin (`data.session_id`), API request params, environment variables, config fields
59
+ - **Destination examples**: log entries, database records, API responses, metric files
60
+ - **Method**: Grep each intermediate call site and inspect arguments. Flag `DATA_DROPPED` when a value available in scope is replaced by `undefined` or a placeholder.
61
+ - **Status**: `PROPAGATED` (value flows correctly) | `DATA_DROPPED` (value lost at some step) | `UNTRACEABLE` (cannot determine flow)
62
+ 7. **Compile Integration Report**: Produce final report with all findings by category.
57
63
 
58
64
  ## Output Format
59
65
 
@@ -118,3 +124,4 @@ See `references/integration-patterns.md` for grep/search patterns by framework.
118
124
  - "File exists" is not "component is integrated"
119
125
  - Auth middleware existing somewhere does not mean routes are protected
120
126
  - Always check error handling paths, not just happy paths
127
+ - Structural connectivity is not data-flow correctness — a connected pipeline can still drop data at any step
@@ -65,6 +65,23 @@ Each must-have maps to one or more tasks. Every task exists to make a must-have
65
65
 
66
66
  ---
67
67
 
68
+ ## Data Contracts for Cross-Boundary Parameters
69
+
70
+ When a function signature includes parameters that flow across module boundaries — session IDs from hook stdin, config objects from disk, auth tokens from environment — the plan **MUST** specify the **source** for each argument, not just the type.
71
+
72
+ For every cross-boundary call in a task's `<action>`, document:
73
+
74
+ | Parameter | Source | Context | Fallback |
75
+ |-----------|--------|---------|----------|
76
+ | `sessionId` | `data.session_id` (hook stdin) | Hook scripts only | `undefined` (CLI context) |
77
+ | `config` | `configLoad(planningDir)` | All callers | `resolveConfig(undefined)` |
78
+
79
+ **When to apply:** Any function call where the caller and callee live in different modules AND at least one argument originates from an external boundary (stdin, env, disk, network). Internal helper calls within the same module do not need contracts.
80
+
81
+ **Why this matters:** Without explicit source mapping, executors will use the type-correct but value-wrong default (e.g., `undefined` instead of `data.session_id`). The plan is the single source of truth for how data flows — if the plan says `undefined`, the executor will faithfully implement `undefined`.
82
+
83
+ ---
84
+
68
85
  ## Plan Structure
69
86
 
70
87
  Read `references/plan-format.md` for the complete plan file specification including:
@@ -164,6 +181,7 @@ When CONTEXT.md or RESEARCH-SUMMARY.md contains `[NEEDS DECISION]` flags from th
164
181
  - [ ] Dependencies are acyclic, no file conflicts within same wave
165
182
  - [ ] Locked decisions honored, no deferred ideas included
166
183
  - [ ] Verify commands are actually executable
184
+ - [ ] Cross-boundary parameters have documented sources (data contracts)
167
185
 
168
186
  ---
169
187
 
@@ -237,3 +255,4 @@ One-line task descriptions in `<name>`. File paths in `<files>`, not explanation
237
255
  9. DO NOT plan for features outside the current phase goal
238
256
  10. DO NOT assume research is done — check discovery level
239
257
  11. DO NOT leave done conditions vague — they must be observable
258
+ 12. DO NOT specify literal `undefined` for parameters that have a known source in the calling context — use data contracts to map sources
@@ -94,10 +94,29 @@ Verify the artifact is imported AND used by other parts of the system (functions
94
94
  | Yes | Yes | No | UNWIRED |
95
95
  | Yes | Yes | Yes | PASSED |
96
96
 
97
+ > **Note:** WIRED status (Level 3) requires correct arguments, not just correct function names. A call that passes `undefined` for a parameter available in scope is `ARGS_WRONG`, not `WIRED`.
98
+
97
99
  ### Step 6: Verify Key Links (Always)
98
100
 
99
101
  For each key_link: identify source and target components, verify the import path resolves, verify the imported symbol is actually called/used, and verify call signatures match. Watch for: wrong import paths, imported-but-never-called symbols, defined-but-never-applied middleware, registered-but-never-triggered event handlers.
100
102
 
103
+ ### Step 6b: Argument-Level Spot Checks (Always)
104
+
105
+ Beyond verifying that calls exist, spot-check that **arguments passed to cross-boundary calls carry the correct values**. A call with the right function but wrong arguments is effectively UNWIRED.
106
+
107
+ **Focus on:** IDs (session, user, request), config objects, auth tokens, and context data that originate from external boundaries (stdin, env, disk).
108
+
109
+ **Method:**
110
+ 1. For each key_link verified in Step 6, grep the call site and inspect the arguments
111
+ 2. Compare each argument against the data source available in the calling scope
112
+ 3. Flag any argument that passes `undefined`, `null`, or a hardcoded placeholder when the calling scope has the real value available (e.g., `data.session_id` is in scope but `undefined` is passed)
113
+
114
+ **Classification:**
115
+ - `WIRED` requires both correct function AND correct arguments
116
+ - `ARGS_WRONG` = correct function called but one or more arguments are incorrect/missing — this is a key link gap
117
+
118
+ **Example:** A hook script receives `data` from stdin containing `session_id`. If it calls `logMetric(planningDir, { session_id: undefined })` instead of `logMetric(planningDir, { session_id: data.session_id })`, that is an `ARGS_WRONG` gap even though the call itself exists.
119
+
101
120
  ### Step 7: Check Requirements Coverage (Always)
102
121
 
103
122
  Cross-reference all must-haves against verification results in a table:
@@ -106,8 +125,8 @@ Cross-reference all must-haves against verification results in a table:
106
125
  | # | Must-Have | Type | L1 (Exists) | L2 (Substantive) | L3 (Wired) | Status |
107
126
  |---|----------|------|-------------|-------------------|------------|--------|
108
127
  | 1 | {description} | truth | - | - | - | VERIFIED/FAILED |
109
- | 2 | {description} | artifact | YES/NO | YES/STUB/PARTIAL | WIRED/ORPHANED | PASS/FAIL |
110
- | 3 | {description} | key_link | - | - | YES/NO | PASS/FAIL |
128
+ | 2 | {description} | artifact | YES/NO | YES/STUB/PARTIAL | WIRED/ORPHANED/ARGS_WRONG | PASS/FAIL |
129
+ | 3 | {description} | key_link | - | - | YES/NO/ARGS_WRONG | PASS/FAIL |
111
130
  ```
112
131
 
113
132
  ### Step 8: Scan for Anti-Patterns (Full Verification Only)
@@ -225,3 +244,4 @@ Read `references/stub-patterns.md` for stub detection patterns by technology. Re
225
244
  9. DO NOT give PASSED status if ANY must-have fails at ANY level
226
245
  10. DO NOT count deferred items as gaps — they are intentionally not implemented
227
246
  11. DO NOT be lenient — your job is to find problems, not to be encouraging
247
+ 12. DO NOT mark a call as WIRED if it passes hardcoded `undefined`/`null` for parameters that have a known source in scope — check arguments, not just function names
@@ -71,6 +71,28 @@ requirement_ids:
71
71
  | `consumes` | NO | array | What this plan needs from prior plans. Format: `"Thing (from plan XX-YY)"` |
72
72
  | `requirement_ids` | NO | array | Requirement IDs from REQUIREMENTS.md or ROADMAP.md goal IDs that this plan addresses. Enables bidirectional traceability between plans and requirements/goals. |
73
73
  | `dependency_fingerprints` | NO | object | Hashes of dependency phase SUMMARY.md files at plan-creation time. Used to detect stale plans. |
74
+ | `data_contracts` | NO | array | Cross-boundary parameter mappings for calls where arguments originate from external boundaries. Format: `"param: source (context) [fallback]"` |
75
+
76
+ ### Data Contracts
77
+
78
+ When a task's `<action>` includes calls across module boundaries where arguments come from external sources (hook stdin, env vars, API params, config files), document the parameter-to-source mapping in `data_contracts` frontmatter and in the `<action>` step itself.
79
+
80
+ Example frontmatter:
81
+
82
+ ```yaml
83
+ data_contracts:
84
+ - "sessionId: data.session_id (hook stdin) [undefined in CLI context]"
85
+ - "config: configLoad(planningDir) (disk) [resolveConfig(undefined)]"
86
+ ```
87
+
88
+ Example in `<action>`:
89
+
90
+ ```
91
+ 3. Call classifyArtifact(llmConfig, planningDir, content, fileType, data.session_id)
92
+ Data contract: sessionId ← data.session_id from hook stdin (undefined in CLI context)
93
+ ```
94
+
95
+ **When to apply:** Any call where caller and callee are in different modules AND at least one argument originates from an external boundary. Internal helper calls within the same module do not need contracts.
74
96
 
75
97
  ---
76
98
 
@@ -112,7 +112,22 @@ Phase 03 (Core) ──provides──→ Phase 04 (Frontend)
112
112
  ### Flow 2: {Flow Name} - {STATUS}
113
113
  ...
114
114
 
115
- ## 5. Integration Issues Summary
115
+ ## 5. Data-Flow Propagation
116
+
117
+ ### Cross-Boundary Data Flows
118
+
119
+ | Data Field | Source | Intermediate Steps | Destination | Status |
120
+ |------------|--------|-------------------|-------------|--------|
121
+ | {field name} | {origin, e.g., hook stdin `data.session_id`} | {module1:L12 → module2:L45} | {dest, e.g., metrics.jsonl `session_id`} | PROPAGATED |
122
+ | {field name} | {origin} | {module1:L12 → module2:L45} | {dest} | DATA_DROPPED |
123
+
124
+ ### Data-Flow Issues
125
+
126
+ | Field | Dropped At | Available In Scope | Passed Instead | Fix |
127
+ |-------|-----------|-------------------|----------------|-----|
128
+ | {field} | {file:line} | `data.session_id` | `undefined` | Pass `data.session_id` |
129
+
130
+ ## 6. Integration Issues Summary
116
131
 
117
132
  ### Critical Issues (system cannot function)
118
133
 
@@ -131,7 +146,7 @@ Phase 03 (Core) ──provides──→ Phase 04 (Frontend)
131
146
  1. **{Issue}**: {description}
132
147
  - Fix: {recommended action}
133
148
 
134
- ## 6. Integration Score
149
+ ## 7. Integration Score
135
150
 
136
151
  | Category | Items Checked | Passed | Failed | Score |
137
152
  |----------|--------------|--------|--------|-------|
@@ -139,6 +154,7 @@ Phase 03 (Core) ──provides──→ Phase 04 (Frontend)
139
154
  | API coverage | {n} | {n} | {n} | {%} |
140
155
  | Auth protection | {n} | {n} | {n} | {%} |
141
156
  | E2E flows | {n} | {n} | {n} | {%} |
157
+ | Data-flow propagation | {n} | {n} | {n} | {%} |
142
158
  | **Overall** | {n} | {n} | {n} | **{%}** |
143
159
 
144
160
  ## Recommendations
@@ -54,8 +54,9 @@ anti_patterns:
54
54
 
55
55
  | # | Link Description | Source | Target | Status | Evidence |
56
56
  |---|-----------------|--------|--------|--------|----------|
57
- | 1 | {what connects to what} | `{source_file}` | `{target_file}` | WIRED | Import at L12, called at L45 |
57
+ | 1 | {what connects to what} | `{source_file}` | `{target_file}` | WIRED | Import at L12, called at L45, args correct |
58
58
  | 2 | {what connects to what} | `{source_file}` | `{target_file}` | BROKEN | Imported but never called |
59
+ | 3 | {what connects to what} | `{source_file}` | `{target_file}` | ARGS_WRONG | Called at L45 but passes undefined for sessionId (data.session_id in scope) |
59
60
 
60
61
  ## Gaps Found
61
62
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pbr",
3
- "version": "2.23.0",
3
+ "version": "2.24.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",
@@ -40,6 +40,7 @@ You MUST perform all applicable categories (skip only if zero items exist for th
40
40
  3. **Auth Protection** — Every non-public route must have auth middleware. Frontend route guards must match backend protection.
41
41
  4. **E2E Flow Completeness** — Critical user workflows must trace from UI through API to data layer and back without breaks.
42
42
  5. **Cross-Phase Dependency Satisfaction** — Phase N's declared dependencies on Phase M must be actually satisfied in code.
43
+ 6. **Data-Flow Propagation** — Values originating at one boundary (hook stdin fields, API request params, env vars) must propagate correctly through the call chain to their destination (log entries, database records, API responses). A connected pipeline with missing data is a broken integration.
43
44
 
44
45
  > **First-phase edge case**: If no completed phases exist yet, focus on verifying the current phase's internal consistency — exports match imports within the phase, API contracts are self-consistent. Cross-phase checks are not applicable and should be skipped.
45
46
 
@@ -52,14 +53,19 @@ Read `references/agent-contracts.md` to validate agent-to-agent handoffs. Verify
52
53
  - **Write access for output artifact only** — you have Write access for your output artifact only. You CANNOT fix source code — you REPORT issues.
53
54
  - **Cross-phase scope** — unlike verifier (single phase), you check across phases.
54
55
 
55
- ## 6-Step Verification Process
56
+ ## 7-Step Verification Process
56
57
 
57
58
  1. **Build Export/Import Map**: Read each completed phase's SUMMARY.md frontmatter (`requires`, `provides`, `affects`). Grep actual exports/imports in source. Cross-reference declared vs actual — flag mismatches.
58
59
  2. **Verify Export Usage**: For each `provides` item: locate actual export (missing = `MISSING_EXPORT` ERROR), find consumers (none = `ORPHANED` WARNING), verify usage not just import (`IMPORTED_UNUSED` WARNING), check signature compatibility (`MISMATCHED` ERROR). Status `CONSUMED` = OK.
59
60
  3. **Verify API Coverage**: Discover routes, find frontend callers, match by method+path+body/params. Produce coverage table. See `references/integration-patterns.md` for framework-specific patterns.
60
61
  4. **Verify Auth Protection**: Identify auth mechanism, list all routes, classify (public vs protected), check frontend guards. Flag UNPROTECTED routes.
61
62
  5. **Verify E2E Flows**: Trace critical workflows step-by-step — verify each step exists and connects to the next (import/call/redirect). Record evidence (file:line). Flow status: COMPLETE | BROKEN | PARTIAL | UNTRACEABLE. See `references/integration-patterns.md` for flow templates.
62
- 6. **Compile Integration Report**: Produce final report with all findings by category.
63
+ 6. **Verify Data-Flow Propagation**: For each cross-boundary data field identified in plans or SUMMARY.md, trace the value from source through intermediate functions to destination. Verify the value is actually passed (not `undefined`/`null`/hardcoded) at each step.
64
+ - **Source examples**: hook stdin (`data.session_id`), API request params, environment variables, config fields
65
+ - **Destination examples**: log entries, database records, API responses, metric files
66
+ - **Method**: Grep each intermediate call site and inspect arguments. Flag `DATA_DROPPED` when a value available in scope is replaced by `undefined` or a placeholder.
67
+ - **Status**: `PROPAGATED` (value flows correctly) | `DATA_DROPPED` (value lost at some step) | `UNTRACEABLE` (cannot determine flow)
68
+ 7. **Compile Integration Report**: Produce final report with all findings by category.
63
69
 
64
70
  ## Output Format
65
71
 
@@ -124,3 +130,4 @@ See `references/integration-patterns.md` for grep/search patterns by framework.
124
130
  - "File exists" is not "component is integrated"
125
131
  - Auth middleware existing somewhere does not mean routes are protected
126
132
  - Always check error handling paths, not just happy paths
133
+ - Structural connectivity is not data-flow correctness — a connected pipeline can still drop data at any step
@@ -73,6 +73,23 @@ Each must-have maps to one or more tasks. Every task exists to make a must-have
73
73
 
74
74
  ---
75
75
 
76
+ ## Data Contracts for Cross-Boundary Parameters
77
+
78
+ When a function signature includes parameters that flow across module boundaries — session IDs from hook stdin, config objects from disk, auth tokens from environment — the plan **MUST** specify the **source** for each argument, not just the type.
79
+
80
+ For every cross-boundary call in a task's `<action>`, document:
81
+
82
+ | Parameter | Source | Context | Fallback |
83
+ |-----------|--------|---------|----------|
84
+ | `sessionId` | `data.session_id` (hook stdin) | Hook scripts only | `undefined` (CLI context) |
85
+ | `config` | `configLoad(planningDir)` | All callers | `resolveConfig(undefined)` |
86
+
87
+ **When to apply:** Any function call where the caller and callee live in different modules AND at least one argument originates from an external boundary (stdin, env, disk, network). Internal helper calls within the same module do not need contracts.
88
+
89
+ **Why this matters:** Without explicit source mapping, executors will use the type-correct but value-wrong default (e.g., `undefined` instead of `data.session_id`). The plan is the single source of truth for how data flows — if the plan says `undefined`, the executor will faithfully implement `undefined`.
90
+
91
+ ---
92
+
76
93
  ## Plan Structure
77
94
 
78
95
  Read `references/plan-format.md` for the complete plan file specification including:
@@ -172,6 +189,7 @@ When CONTEXT.md or RESEARCH-SUMMARY.md contains `[NEEDS DECISION]` flags from th
172
189
  - [ ] Dependencies are acyclic, no file conflicts within same wave
173
190
  - [ ] Locked decisions honored, no deferred ideas included
174
191
  - [ ] Verify commands are actually executable
192
+ - [ ] Cross-boundary parameters have documented sources (data contracts)
175
193
 
176
194
  ---
177
195
 
@@ -245,3 +263,4 @@ One-line task descriptions in `<name>`. File paths in `<files>`, not explanation
245
263
  9. DO NOT plan for features outside the current phase goal
246
264
  10. DO NOT assume research is done — check discovery level
247
265
  11. DO NOT leave done conditions vague — they must be observable
266
+ 12. DO NOT specify literal `undefined` for parameters that have a known source in the calling context — use data contracts to map sources
@@ -101,10 +101,29 @@ Verify the artifact is imported AND used by other parts of the system (functions
101
101
  | Yes | Yes | No | UNWIRED |
102
102
  | Yes | Yes | Yes | PASSED |
103
103
 
104
+ > **Note:** WIRED status (Level 3) requires correct arguments, not just correct function names. A call that passes `undefined` for a parameter available in scope is `ARGS_WRONG`, not `WIRED`.
105
+
104
106
  ### Step 6: Verify Key Links (Always)
105
107
 
106
108
  For each key_link: identify source and target components, verify the import path resolves, verify the imported symbol is actually called/used, and verify call signatures match. Watch for: wrong import paths, imported-but-never-called symbols, defined-but-never-applied middleware, registered-but-never-triggered event handlers.
107
109
 
110
+ ### Step 6b: Argument-Level Spot Checks (Always)
111
+
112
+ Beyond verifying that calls exist, spot-check that **arguments passed to cross-boundary calls carry the correct values**. A call with the right function but wrong arguments is effectively UNWIRED.
113
+
114
+ **Focus on:** IDs (session, user, request), config objects, auth tokens, and context data that originate from external boundaries (stdin, env, disk).
115
+
116
+ **Method:**
117
+ 1. For each key_link verified in Step 6, grep the call site and inspect the arguments
118
+ 2. Compare each argument against the data source available in the calling scope
119
+ 3. Flag any argument that passes `undefined`, `null`, or a hardcoded placeholder when the calling scope has the real value available (e.g., `data.session_id` is in scope but `undefined` is passed)
120
+
121
+ **Classification:**
122
+ - `WIRED` requires both correct function AND correct arguments
123
+ - `ARGS_WRONG` = correct function called but one or more arguments are incorrect/missing — this is a key link gap
124
+
125
+ **Example:** A hook script receives `data` from stdin containing `session_id`. If it calls `logMetric(planningDir, { session_id: undefined })` instead of `logMetric(planningDir, { session_id: data.session_id })`, that is an `ARGS_WRONG` gap even though the call itself exists.
126
+
108
127
  ### Step 7: Check Requirements Coverage (Always)
109
128
 
110
129
  Cross-reference all must-haves against verification results in a table:
@@ -113,8 +132,8 @@ Cross-reference all must-haves against verification results in a table:
113
132
  | # | Must-Have | Type | L1 (Exists) | L2 (Substantive) | L3 (Wired) | Status |
114
133
  |---|----------|------|-------------|-------------------|------------|--------|
115
134
  | 1 | {description} | truth | - | - | - | VERIFIED/FAILED |
116
- | 2 | {description} | artifact | YES/NO | YES/STUB/PARTIAL | WIRED/ORPHANED | PASS/FAIL |
117
- | 3 | {description} | key_link | - | - | YES/NO | PASS/FAIL |
135
+ | 2 | {description} | artifact | YES/NO | YES/STUB/PARTIAL | WIRED/ORPHANED/ARGS_WRONG | PASS/FAIL |
136
+ | 3 | {description} | key_link | - | - | YES/NO/ARGS_WRONG | PASS/FAIL |
118
137
  ```
119
138
 
120
139
  ### Step 8: Scan for Anti-Patterns (Full Verification Only)
@@ -232,3 +251,4 @@ Read `references/stub-patterns.md` for stub detection patterns by technology. Re
232
251
  9. DO NOT give PASSED status if ANY must-have fails at ANY level
233
252
  10. DO NOT count deferred items as gaps — they are intentionally not implemented
234
253
  11. DO NOT be lenient — your job is to find problems, not to be encouraging
254
+ 12. DO NOT mark a call as WIRED if it passes hardcoded `undefined`/`null` for parameters that have a known source in scope — check arguments, not just function names
@@ -70,6 +70,28 @@ requirement_ids:
70
70
  | `consumes` | NO | array | What this plan needs from prior plans. Format: `"Thing (from plan XX-YY)"` |
71
71
  | `requirement_ids` | NO | array | Requirement IDs from REQUIREMENTS.md or ROADMAP.md goal IDs that this plan addresses. Enables bidirectional traceability between plans and requirements/goals. |
72
72
  | `dependency_fingerprints` | NO | object | Hashes of dependency phase SUMMARY.md files at plan-creation time. Used to detect stale plans. |
73
+ | `data_contracts` | NO | array | Cross-boundary parameter mappings for calls where arguments originate from external boundaries. Format: `"param: source (context) [fallback]"` |
74
+
75
+ ### Data Contracts
76
+
77
+ When a task's `<action>` includes calls across module boundaries where arguments come from external sources (hook stdin, env vars, API params, config files), document the parameter-to-source mapping in `data_contracts` frontmatter and in the `<action>` step itself.
78
+
79
+ Example frontmatter:
80
+
81
+ ```yaml
82
+ data_contracts:
83
+ - "sessionId: data.session_id (hook stdin) [undefined in CLI context]"
84
+ - "config: configLoad(planningDir) (disk) [resolveConfig(undefined)]"
85
+ ```
86
+
87
+ Example in `<action>`:
88
+
89
+ ```
90
+ 3. Call classifyArtifact(llmConfig, planningDir, content, fileType, data.session_id)
91
+ Data contract: sessionId ← data.session_id from hook stdin (undefined in CLI context)
92
+ ```
93
+
94
+ **When to apply:** Any call where caller and callee are in different modules AND at least one argument originates from an external boundary. Internal helper calls within the same module do not need contracts.
73
95
 
74
96
  ---
75
97
 
@@ -84,7 +84,7 @@ async function main() {
84
84
  const llmConfig = loadLocalLlmConfig();
85
85
  const planningDir = path.join(process.cwd(), '.planning');
86
86
  const fileType = isPlan ? 'PLAN' : 'SUMMARY';
87
- const llmResult = await classifyArtifact(llmConfig, planningDir, content, fileType, undefined);
87
+ const llmResult = await classifyArtifact(llmConfig, planningDir, content, fileType, data.session_id);
88
88
  if (llmResult && llmResult.classification) {
89
89
  const llmNote = `Local LLM: ${fileType} classified as "${llmResult.classification}" (confidence: ${(llmResult.confidence * 100).toFixed(0)}%)${llmResult.reason ? ' — ' + llmResult.reason : ''}`;
90
90
  result.warnings.push(llmNote);
@@ -287,7 +287,7 @@ async function checkPlanWrite(data) {
287
287
  const llmConfig = loadLocalLlmConfig();
288
288
  const planningDir = path.join(process.cwd(), '.planning');
289
289
  const fileType = isPlan ? 'PLAN' : 'SUMMARY';
290
- const llmResult = await classifyArtifact(llmConfig, planningDir, content, fileType, undefined);
290
+ const llmResult = await classifyArtifact(llmConfig, planningDir, content, fileType, data.session_id);
291
291
  if (llmResult && llmResult.classification) {
292
292
  const llmNote = `Local LLM: ${fileType} classified as "${llmResult.classification}" (confidence: ${(llmResult.confidence * 100).toFixed(0)}%)${llmResult.reason ? ' — ' + llmResult.reason : ''}`;
293
293
  result.warnings.push(llmNote);
@@ -444,7 +444,7 @@ async function main() {
444
444
  const llmConfig = loadLocalLlmConfig(cwd);
445
445
  const errorText = (data.tool_output || '').substring(0, 500);
446
446
  if (errorText) {
447
- const llmResult = await classifyError(llmConfig, planningDir, errorText, agentType, undefined);
447
+ const llmResult = await classifyError(llmConfig, planningDir, errorText, agentType, data.session_id);
448
448
  if (llmResult && llmResult.category) {
449
449
  llmCategoryNote = `\nLLM error category: ${llmResult.category} (confidence: ${(llmResult.confidence * 100).toFixed(0)}%)`;
450
450
  }
@@ -467,7 +467,7 @@ async function main() {
467
467
  const llmConfig = loadLocalLlmConfig(cwd);
468
468
  const errorText = (data.tool_output || '').substring(0, 500);
469
469
  if (errorText) {
470
- const llmResult = await classifyError(llmConfig, planningDir, errorText, agentType, undefined);
470
+ const llmResult = await classifyError(llmConfig, planningDir, errorText, agentType, data.session_id);
471
471
  if (llmResult && llmResult.category) {
472
472
  llmCategoryNote = `\nLLM error category: ${llmResult.category} (confidence: ${(llmResult.confidence * 100).toFixed(0)}%)`;
473
473
  }
@@ -805,7 +805,7 @@ function main() {
805
805
  try {
806
806
  const llmConfig = loadLocalLlmConfig(process.cwd());
807
807
  const planningDir = path.join(process.cwd(), '.planning');
808
- const llmResult = await llmValidateTask(llmConfig, planningDir, data.tool_input || {}, undefined);
808
+ const llmResult = await llmValidateTask(llmConfig, planningDir, data.tool_input || {}, data.session_id);
809
809
  if (llmResult && !llmResult.coherent) {
810
810
  warnings.push('LLM task coherence advisory: ' + (llmResult.issue || 'Task description may not match intended operation.') + ' (confidence: ' + (llmResult.confidence * 100).toFixed(0) + '%)');
811
811
  }
@@ -111,7 +111,22 @@ Phase 03 (Core) ──provides──→ Phase 04 (Frontend)
111
111
  ### Flow 2: {Flow Name} - {STATUS}
112
112
  ...
113
113
 
114
- ## 5. Integration Issues Summary
114
+ ## 5. Data-Flow Propagation
115
+
116
+ ### Cross-Boundary Data Flows
117
+
118
+ | Data Field | Source | Intermediate Steps | Destination | Status |
119
+ |------------|--------|-------------------|-------------|--------|
120
+ | {field name} | {origin, e.g., hook stdin `data.session_id`} | {module1:L12 → module2:L45} | {dest, e.g., metrics.jsonl `session_id`} | PROPAGATED |
121
+ | {field name} | {origin} | {module1:L12 → module2:L45} | {dest} | DATA_DROPPED |
122
+
123
+ ### Data-Flow Issues
124
+
125
+ | Field | Dropped At | Available In Scope | Passed Instead | Fix |
126
+ |-------|-----------|-------------------|----------------|-----|
127
+ | {field} | {file:line} | `data.session_id` | `undefined` | Pass `data.session_id` |
128
+
129
+ ## 6. Integration Issues Summary
115
130
 
116
131
  ### Critical Issues (system cannot function)
117
132
 
@@ -130,7 +145,7 @@ Phase 03 (Core) ──provides──→ Phase 04 (Frontend)
130
145
  1. **{Issue}**: {description}
131
146
  - Fix: {recommended action}
132
147
 
133
- ## 6. Integration Score
148
+ ## 7. Integration Score
134
149
 
135
150
  | Category | Items Checked | Passed | Failed | Score |
136
151
  |----------|--------------|--------|--------|-------|
@@ -138,6 +153,7 @@ Phase 03 (Core) ──provides──→ Phase 04 (Frontend)
138
153
  | API coverage | {n} | {n} | {n} | {%} |
139
154
  | Auth protection | {n} | {n} | {n} | {%} |
140
155
  | E2E flows | {n} | {n} | {n} | {%} |
156
+ | Data-flow propagation | {n} | {n} | {n} | {%} |
141
157
  | **Overall** | {n} | {n} | {n} | **{%}** |
142
158
 
143
159
  ## Recommendations
@@ -53,8 +53,9 @@ anti_patterns:
53
53
 
54
54
  | # | Link Description | Source | Target | Status | Evidence |
55
55
  |---|-----------------|--------|--------|--------|----------|
56
- | 1 | {what connects to what} | `{source_file}` | `{target_file}` | WIRED | Import at L12, called at L45 |
56
+ | 1 | {what connects to what} | `{source_file}` | `{target_file}` | WIRED | Import at L12, called at L45, args correct |
57
57
  | 2 | {what connects to what} | `{source_file}` | `{target_file}` | BROKEN | Imported but never called |
58
+ | 3 | {what connects to what} | `{source_file}` | `{target_file}` | ARGS_WRONG | Called at L45 but passes undefined for sessionId (data.session_id in scope) |
58
59
 
59
60
  ## Gaps Found
60
61
 
@@ -1,11 +0,0 @@
1
- <%- include('partials/layout-top', { title: title, activePage: activePage }) %>
2
-
3
- <h1><%= featureName %></h1>
4
- <p>This feature is coming soon.</p>
5
- <p>
6
- The <strong><%= featureName %></strong> view is planned but not yet implemented.
7
- Check back in a future phase.
8
- </p>
9
- <p><a href="/">Back to Dashboard</a></p>
10
-
11
- <%- include('partials/layout-bottom') %>