@sienklogic/plan-build-run 2.19.0 → 2.19.2
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 +1287 -303
- package/CLAUDE.md +74 -39
- package/LICENSE +2 -1
- package/README.md +412 -177
- package/bin/install.js +2752 -0
- package/dashboard/bin/cli.cjs +96 -0
- package/dashboard/bin/stop.cjs +129 -0
- package/dashboard/eslint.config.js +37 -0
- package/dashboard/index.html +20 -0
- package/dashboard/package.json +27 -25
- package/dashboard/server/index.js +151 -0
- package/dashboard/server/lib/frontmatter.js +92 -0
- package/dashboard/server/middleware/static.js +35 -0
- package/dashboard/server/package.json +16 -0
- package/dashboard/server/routes/agents.js +234 -0
- package/dashboard/server/routes/config.js +64 -0
- package/dashboard/server/routes/health.js +98 -0
- package/dashboard/server/routes/incidents.js +78 -0
- package/dashboard/server/routes/intel.js +69 -0
- package/dashboard/server/routes/memory.js +107 -0
- package/dashboard/server/routes/planning.js +234 -0
- package/dashboard/server/routes/progress.js +77 -0
- package/dashboard/server/routes/projects.js +36 -0
- package/dashboard/server/routes/requirements.js +40 -0
- package/dashboard/server/routes/roadmap.js +69 -0
- package/dashboard/server/routes/sessions.js +70 -0
- package/dashboard/server/routes/status.js +25 -0
- package/dashboard/server/routes/telemetry.js +233 -0
- package/dashboard/server/services/file-watcher.js +105 -0
- package/dashboard/server/services/planning-reader.js +727 -0
- package/dashboard/server/test/cli.test.js +34 -0
- package/dashboard/server/test/frontmatter.test.js +104 -0
- package/dashboard/server/test/isolation.test.js +32 -0
- package/dashboard/server/test/planning-reader.test.js +151 -0
- package/dashboard/server/test/routes.test.js +91 -0
- package/dashboard/server/test/ws.test.js +81 -0
- package/dashboard/server/ws.js +96 -0
- package/dashboard/src/App.jsx +165 -0
- package/dashboard/src/components/charts/BudgetBars.jsx +42 -0
- package/dashboard/src/components/charts/ContextRadar.jsx +34 -0
- package/dashboard/src/components/charts/PhaseDonut.jsx +66 -0
- package/dashboard/src/components/charts/SuccessTrend.jsx +45 -0
- package/dashboard/src/components/charts/TokenChart.jsx +55 -0
- package/dashboard/src/components/charts/index.js +5 -0
- package/dashboard/src/components/config/CfgSection.jsx +93 -0
- package/dashboard/src/components/layout/Header.jsx +89 -0
- package/dashboard/src/components/layout/ProjectSwitcher.jsx +160 -0
- package/dashboard/src/components/layout/Sidebar.jsx +161 -0
- package/dashboard/src/components/ui/AutoModeBanner.jsx +138 -0
- package/dashboard/src/components/ui/BackButton.jsx +27 -0
- package/dashboard/src/components/ui/Badge.jsx +27 -0
- package/dashboard/src/components/ui/Card.jsx +23 -0
- package/dashboard/src/components/ui/ChartTooltip.jsx +48 -0
- package/dashboard/src/components/ui/CheckpointBox.jsx +110 -0
- package/dashboard/src/components/ui/CodeBlock.jsx +27 -0
- package/dashboard/src/components/ui/ConfidenceBadge.jsx +20 -0
- package/dashboard/src/components/ui/ConfirmModal.jsx +161 -0
- package/dashboard/src/components/ui/ConnectionBanner.jsx +60 -0
- package/dashboard/src/components/ui/ErrorBoundary.jsx +106 -0
- package/dashboard/src/components/ui/ErrorBox.jsx +107 -0
- package/dashboard/src/components/ui/KeyValue.jsx +33 -0
- package/dashboard/src/components/ui/LoadingSkeleton.jsx +84 -0
- package/dashboard/src/components/ui/MetricCard.jsx +58 -0
- package/dashboard/src/components/ui/NextUpBlock.jsx +92 -0
- package/dashboard/src/components/ui/NumberInput.jsx +44 -0
- package/dashboard/src/components/ui/PBRBanner.jsx +47 -0
- package/dashboard/src/components/ui/PipelineView.jsx +130 -0
- package/dashboard/src/components/ui/ProgressBar.jsx +28 -0
- package/dashboard/src/components/ui/ProgressDisplay.jsx +47 -0
- package/dashboard/src/components/ui/QualityGateBadge.jsx +15 -0
- package/dashboard/src/components/ui/SectionTitle.jsx +35 -0
- package/dashboard/src/components/ui/SelectInput.jsx +45 -0
- package/dashboard/src/components/ui/StatusDot.jsx +51 -0
- package/dashboard/src/components/ui/StatusSymbol.jsx +49 -0
- package/dashboard/src/components/ui/TabBar.jsx +41 -0
- package/dashboard/src/components/ui/TextInput.jsx +42 -0
- package/dashboard/src/components/ui/Toast.jsx +117 -0
- package/dashboard/src/components/ui/Toggle.jsx +70 -0
- package/dashboard/src/components/ui/index.js +29 -0
- package/dashboard/src/hooks/useDocumentTitle.js +16 -0
- package/dashboard/src/hooks/useFetch.js +50 -0
- package/dashboard/src/hooks/useToast.jsx +43 -0
- package/dashboard/src/hooks/useWebSocket.js +103 -0
- package/dashboard/src/lib/api.js +112 -0
- package/dashboard/src/lib/configSchema.js +189 -0
- package/dashboard/src/lib/constants.js +22 -0
- package/dashboard/src/main.jsx +15 -0
- package/dashboard/src/pages/AgentsPage.jsx +191 -0
- package/dashboard/src/pages/ConfigPage.jsx +298 -0
- package/dashboard/src/pages/HooksPage.jsx +412 -0
- package/dashboard/src/pages/IncidentsPage.jsx +135 -0
- package/dashboard/src/pages/IntelPage.jsx +193 -0
- package/dashboard/src/pages/LiveFeed.jsx +274 -0
- package/dashboard/src/pages/MemoryPage.jsx +107 -0
- package/dashboard/src/pages/OnboardingPage.jsx +117 -0
- package/dashboard/src/pages/Overview.jsx +360 -0
- package/dashboard/src/pages/PhaseDetailView.jsx +216 -0
- package/dashboard/src/pages/PlanningPage.jsx +181 -0
- package/dashboard/src/pages/ProgressPage.jsx +249 -0
- package/dashboard/src/pages/ResearchPage.jsx +129 -0
- package/dashboard/src/pages/RoadmapPage.jsx +251 -0
- package/dashboard/src/pages/SessionsPage.jsx +117 -0
- package/dashboard/src/pages/Telemetry.jsx +166 -0
- package/dashboard/src/pages/planning/DecisionsTab.jsx +153 -0
- package/dashboard/src/pages/planning/FilesTab.jsx +420 -0
- package/dashboard/src/pages/planning/MilestoneDetail.jsx +319 -0
- package/dashboard/src/pages/planning/MilestonesTab.jsx +151 -0
- package/dashboard/src/pages/planning/NotesTab.jsx +251 -0
- package/dashboard/src/pages/planning/PhasesTab.jsx +218 -0
- package/dashboard/src/pages/planning/QuickTab.jsx +50 -0
- package/dashboard/src/pages/planning/ResearchTab.jsx +103 -0
- package/dashboard/src/pages/planning/TodosTab.jsx +297 -0
- package/dashboard/src/theme/ThemeProvider.jsx +38 -0
- package/dashboard/src/theme/tokens.js +17 -0
- package/dashboard/tests/components/ConfirmModal.test.jsx +179 -0
- package/dashboard/tests/components/ConnectionBanner.test.jsx +37 -0
- package/dashboard/tests/components/ErrorBoundary.test.jsx +59 -0
- package/dashboard/tests/components/LoadingSkeleton.test.jsx +46 -0
- package/dashboard/tests/components/ToastContainer.test.jsx +47 -0
- package/dashboard/tests/components/Toggle.test.jsx +61 -0
- package/dashboard/tests/hooks/useFetch.test.jsx +77 -0
- package/dashboard/tests/hooks/useToast.test.jsx +78 -0
- package/dashboard/tests/hooks/useWebSocket.test.jsx +128 -0
- package/dashboard/tests/pages/ConfigPage.test.jsx +199 -0
- package/dashboard/tests/pages/PlanningPage.test.jsx +119 -0
- package/dashboard/tests/pages/planning/FilesTab.test.jsx +198 -0
- package/dashboard/tests/pages/planning/NotesTab.test.jsx +178 -0
- package/dashboard/tests/pages/planning/TodosTab.test.jsx +188 -0
- package/dashboard/tests/performance.test.jsx +46 -0
- package/dashboard/tests/routes/config.test.js +98 -0
- package/dashboard/tests/routes/health.test.js +40 -0
- package/dashboard/tests/routes/planning.test.js +112 -0
- package/dashboard/tests/routes/roadmap.test.js +91 -0
- package/dashboard/tests/routes/status.test.js +131 -0
- package/dashboard/tests/server/planning-reader.test.js +153 -0
- package/dashboard/tests/setup.js +7 -0
- package/dashboard/vite.config.js +41 -0
- package/package.json +55 -40
- package/plan-build-run/bin/config-schema.json +1298 -0
- package/plugins/pbr/.claude-plugin/plugin.json +1 -1
- package/plugins/pbr/CLAUDE.md +19 -0
- package/plugins/pbr/UI-CONSISTENCY-GAPS.md +1 -1
- package/plugins/pbr/agents/advisor-researcher.md +101 -0
- package/plugins/pbr/agents/audit.md +205 -89
- package/plugins/pbr/agents/codebase-mapper.md +158 -23
- package/plugins/pbr/agents/debugger.md +212 -34
- package/plugins/pbr/agents/dev-sync.md +206 -0
- package/plugins/pbr/agents/executor.md +717 -39
- package/plugins/pbr/agents/general.md +71 -6
- package/plugins/pbr/agents/integration-checker.md +146 -30
- package/plugins/pbr/agents/intel-updater.md +332 -0
- package/plugins/pbr/agents/nyquist-auditor.md +253 -0
- package/plugins/pbr/agents/plan-checker.md +265 -65
- package/plugins/pbr/agents/planner.md +440 -42
- package/plugins/pbr/agents/researcher.md +219 -36
- package/plugins/pbr/agents/roadmapper.md +397 -0
- package/plugins/pbr/agents/synthesizer.md +166 -26
- package/plugins/pbr/agents/ui-checker.md +203 -0
- package/plugins/pbr/agents/ui-researcher.md +224 -0
- package/plugins/pbr/agents/verifier.md +495 -47
- package/plugins/pbr/commands/add-phase.md +75 -0
- package/plugins/pbr/commands/add-todo.md +8 -0
- package/plugins/pbr/commands/audit-fix.md +5 -0
- package/plugins/pbr/commands/audit-milestone.md +8 -0
- package/plugins/pbr/commands/autonomous.md +5 -0
- package/plugins/pbr/commands/backlog.md +6 -0
- package/plugins/pbr/commands/check-todos.md +8 -0
- package/plugins/pbr/commands/complete-milestone.md +8 -0
- package/plugins/pbr/commands/config.md +1 -1
- package/plugins/pbr/commands/discuss-phase.md +6 -0
- package/plugins/pbr/commands/do.md +5 -0
- package/plugins/pbr/commands/execute-phase.md +6 -0
- package/plugins/pbr/commands/fast.md +6 -0
- package/plugins/pbr/commands/forensics.md +6 -0
- package/plugins/pbr/commands/import.md +1 -1
- package/plugins/pbr/commands/insert-phase.md +65 -0
- package/plugins/pbr/commands/intel.md +5 -0
- package/plugins/pbr/commands/join-discord.md +11 -0
- package/plugins/pbr/commands/list-phase-assumptions.md +5 -0
- package/plugins/pbr/commands/map-codebase.md +6 -0
- package/plugins/pbr/commands/milestone-summary.md +6 -0
- package/plugins/pbr/commands/new-milestone.md +8 -0
- package/plugins/pbr/commands/new-project.md +6 -0
- package/plugins/pbr/commands/pause-work.md +5 -0
- package/plugins/pbr/commands/plan-milestone-gaps.md +7 -0
- package/plugins/pbr/commands/plan-phase.md +6 -0
- package/plugins/pbr/commands/plant-seed.md +6 -0
- package/plugins/pbr/commands/profile-user.md +5 -0
- package/plugins/pbr/commands/profile.md +5 -0
- package/plugins/pbr/commands/progress.md +6 -0
- package/plugins/pbr/commands/quick.md +1 -1
- package/plugins/pbr/commands/reapply-patches.md +47 -0
- package/plugins/pbr/commands/release.md +6 -0
- package/plugins/pbr/commands/remove-phase.md +66 -0
- package/plugins/pbr/commands/research-phase.md +59 -0
- package/plugins/pbr/commands/resume-work.md +5 -0
- package/plugins/pbr/commands/seed.md +6 -0
- package/plugins/pbr/commands/session-report.md +5 -0
- package/plugins/pbr/commands/set-profile.md +6 -0
- package/plugins/pbr/commands/settings.md +5 -0
- package/plugins/pbr/commands/setup.md +1 -1
- package/plugins/pbr/commands/ship.md +5 -0
- package/plugins/pbr/commands/stats.md +6 -0
- package/plugins/pbr/commands/test.md +5 -0
- package/plugins/pbr/commands/thread.md +6 -0
- package/plugins/pbr/commands/todo.md +1 -1
- package/plugins/pbr/commands/ui-phase.md +5 -0
- package/plugins/pbr/commands/ui-review.md +5 -0
- package/plugins/pbr/commands/undo.md +5 -0
- package/plugins/pbr/commands/update.md +37 -0
- package/plugins/pbr/commands/validate-phase.md +5 -0
- package/plugins/pbr/commands/verify-work.md +6 -0
- package/plugins/pbr/dashboard/package-lock.json +6 -0
- package/plugins/pbr/dist/architecture-guard.js +59 -0
- package/plugins/pbr/dist/audit-dimensions.js +556 -0
- package/plugins/pbr/dist/auto-continue.js +277 -0
- package/plugins/pbr/dist/block-skill-self-read.js +124 -0
- package/plugins/pbr/dist/check-agent-state-write.js +63 -0
- package/plugins/pbr/dist/check-config-change.js +155 -0
- package/plugins/pbr/dist/check-cross-plugin-sync.js +93 -0
- package/plugins/pbr/dist/check-dangerous-commands.js +193 -0
- package/plugins/pbr/dist/check-direct-state-write.js +37 -0
- package/plugins/pbr/dist/check-doc-sprawl.js +102 -0
- package/plugins/pbr/dist/check-phase-boundary.js +191 -0
- package/plugins/pbr/dist/check-plan-format.js +209 -0
- package/plugins/pbr/dist/check-read-first.js +345 -0
- package/plugins/pbr/dist/check-roadmap-sync.js +507 -0
- package/plugins/pbr/dist/check-skill-workflow.js +354 -0
- package/plugins/pbr/dist/check-state-sync.js +658 -0
- package/plugins/pbr/dist/check-subagent-output.js +396 -0
- package/plugins/pbr/dist/check-summary-gate.js +188 -0
- package/plugins/pbr/dist/context-bridge.js +425 -0
- package/plugins/pbr/dist/context-budget-check.js +442 -0
- package/plugins/pbr/dist/context-quality.js +271 -0
- package/plugins/pbr/dist/enforce-context-budget.js +138 -0
- package/plugins/pbr/dist/enforce-pbr-workflow.js +277 -0
- package/plugins/pbr/dist/event-handler.js +202 -0
- package/plugins/pbr/dist/event-logger.js +125 -0
- package/plugins/pbr/dist/feedback-loop.js +155 -0
- package/plugins/pbr/dist/graph-update.js +422 -0
- package/plugins/pbr/dist/hook-logger.js +114 -0
- package/plugins/pbr/dist/hook-server-client.js +361 -0
- package/plugins/pbr/dist/hook-server.js +658 -0
- package/plugins/pbr/dist/hooks-schema.json +87 -0
- package/plugins/pbr/dist/instructions-loaded.js +173 -0
- package/plugins/pbr/dist/intercept-plan-mode.js +81 -0
- package/plugins/pbr/dist/log-notification.js +131 -0
- package/plugins/pbr/dist/log-subagent.js +349 -0
- package/plugins/pbr/dist/log-tool-failure.js +140 -0
- package/plugins/pbr/dist/milestone-learnings.js +519 -0
- package/plugins/pbr/dist/pbr-tools.js +1961 -0
- package/plugins/pbr/dist/post-bash-triage.js +96 -0
- package/plugins/pbr/dist/post-compact.js +135 -0
- package/plugins/pbr/dist/post-hoc.js +237 -0
- package/plugins/pbr/dist/post-write-dispatch.js +243 -0
- package/plugins/pbr/dist/post-write-quality.js +208 -0
- package/plugins/pbr/dist/pre-bash-dispatch.js +212 -0
- package/plugins/pbr/dist/pre-skill-dispatch.js +114 -0
- package/plugins/pbr/dist/pre-task-dispatch.js +269 -0
- package/plugins/pbr/dist/pre-write-dispatch.js +234 -0
- package/plugins/pbr/dist/progress-tracker.js +173 -0
- package/plugins/pbr/dist/prompt-guard.js +114 -0
- package/plugins/pbr/dist/prompt-routing.js +209 -0
- package/plugins/pbr/dist/quick-status.js +179 -0
- package/plugins/pbr/dist/record-incident.js +37 -0
- package/plugins/pbr/dist/run-hook.js +144 -0
- package/plugins/pbr/dist/session-cleanup.js +653 -0
- package/plugins/pbr/dist/session-tracker.js +124 -0
- package/plugins/pbr/dist/status-line.js +849 -0
- package/plugins/pbr/dist/suggest-compact.js +307 -0
- package/plugins/pbr/dist/sync-context-to-claude.js +100 -0
- package/plugins/pbr/dist/task-completed.js +206 -0
- package/plugins/pbr/dist/track-context-budget.js +432 -0
- package/plugins/pbr/dist/track-user-gates.js +88 -0
- package/plugins/pbr/dist/trust-tracker.js +193 -0
- package/plugins/pbr/dist/validate-commit.js +215 -0
- package/plugins/pbr/dist/validate-skill-args.js +222 -0
- package/plugins/pbr/dist/validate-task.js +271 -0
- package/plugins/pbr/dist/worktree-create.js +144 -0
- package/plugins/pbr/dist/worktree-remove.js +147 -0
- package/plugins/pbr/hooks/hooks.json +143 -60
- package/plugins/pbr/references/agent-contracts.md +39 -8
- package/plugins/pbr/references/agent-teams.md +3 -3
- package/plugins/pbr/references/archive/checkpoints.md +189 -0
- package/plugins/pbr/references/archive/context-quality-tiers.md +45 -0
- package/plugins/pbr/references/archive/hook-ordering.md +89 -0
- package/plugins/pbr/references/archive/limitations.md +106 -0
- package/plugins/pbr/references/archive/pbr-rules.md +194 -0
- package/plugins/pbr/references/archive/pbr-tools-cli.md +415 -0
- package/plugins/pbr/references/archive/pretooluse-jsonl-behavior.md +58 -0
- package/plugins/pbr/references/archive/signal-files.md +41 -0
- package/plugins/pbr/references/archive/tmux-setup.md +288 -0
- package/plugins/pbr/references/archive/verification-matrix.md +34 -0
- package/plugins/pbr/references/archive/verification-patterns.md +277 -0
- package/plugins/pbr/references/archive/worktree-sparse-checkout.md +86 -0
- package/plugins/pbr/references/checkpoints.md +723 -104
- package/plugins/pbr/references/config-reference.md +376 -10
- package/plugins/pbr/references/continuation-format.md +1 -0
- package/plugins/pbr/references/decimal-phase-calculation.md +65 -0
- package/plugins/pbr/references/deviation-rules.md +12 -0
- package/plugins/pbr/references/git-integration.md +110 -27
- package/plugins/pbr/references/git-planning-commit.md +35 -0
- package/plugins/pbr/references/model-profile-resolution.md +34 -0
- package/plugins/pbr/references/model-profiles.md +90 -7
- package/plugins/pbr/references/model-selection.md +1 -1
- package/plugins/pbr/references/node-repair.md +48 -0
- package/plugins/pbr/references/plan-authoring.md +65 -0
- package/plugins/pbr/references/plan-format.md +161 -10
- package/plugins/pbr/references/questioning.md +138 -49
- package/plugins/pbr/references/reading-verification.md +4 -4
- package/plugins/pbr/references/tdd.md +263 -0
- package/plugins/pbr/references/ui-brand.md +449 -0
- package/plugins/pbr/references/verification-overrides.md +39 -0
- package/plugins/pbr/references/verification-patterns.md +529 -113
- package/plugins/pbr/scripts/architecture-guard.js +59 -0
- package/plugins/pbr/scripts/audit-checks/behavioral-compliance.js +2098 -0
- package/plugins/pbr/scripts/audit-checks/error-analysis.js +989 -0
- package/plugins/pbr/scripts/audit-checks/feature-verification.js +723 -0
- package/plugins/pbr/scripts/audit-checks/index.js +433 -0
- package/plugins/pbr/scripts/audit-checks/infrastructure.js +816 -0
- package/plugins/pbr/scripts/audit-checks/quality-metrics.js +452 -0
- package/plugins/pbr/scripts/audit-checks/session-quality.js +980 -0
- package/plugins/pbr/scripts/audit-checks/si-agent-hook-config-checks.js +396 -0
- package/plugins/pbr/scripts/audit-checks/si-cross-cutting-checks.js +272 -0
- package/plugins/pbr/scripts/audit-checks/si-skill-checks.js +424 -0
- package/plugins/pbr/scripts/audit-checks/workflow-compliance.js +1175 -0
- package/plugins/pbr/scripts/audit-dimensions.js +556 -0
- package/plugins/pbr/scripts/auto-continue.js +192 -31
- package/plugins/pbr/scripts/block-skill-self-read.js +124 -0
- package/plugins/pbr/scripts/check-agent-state-write.js +63 -0
- package/plugins/pbr/scripts/check-config-change.js +155 -0
- package/plugins/pbr/scripts/check-cross-plugin-sync.js +93 -0
- package/plugins/pbr/scripts/check-dangerous-commands.js +18 -5
- package/plugins/pbr/scripts/check-direct-state-write.js +37 -0
- package/plugins/pbr/scripts/check-phase-boundary.js +3 -8
- package/plugins/pbr/scripts/check-plan-format.js +135 -278
- package/plugins/pbr/scripts/check-read-first.js +345 -0
- package/plugins/pbr/scripts/check-roadmap-sync.js +182 -21
- package/plugins/pbr/scripts/check-skill-workflow.js +24 -27
- package/plugins/pbr/scripts/check-state-sync.js +339 -215
- package/plugins/pbr/scripts/check-subagent-output.js +281 -275
- package/plugins/pbr/scripts/check-summary-gate.js +5 -15
- package/plugins/pbr/scripts/config-schema.json +1134 -95
- package/plugins/pbr/scripts/context-bridge.js +425 -0
- package/plugins/pbr/scripts/context-budget-check.js +169 -14
- package/plugins/pbr/scripts/context-quality.js +271 -0
- package/plugins/pbr/scripts/enforce-context-budget.js +138 -0
- package/plugins/pbr/scripts/enforce-pbr-workflow.js +277 -0
- package/plugins/pbr/scripts/event-handler.js +127 -87
- package/plugins/pbr/scripts/event-logger.js +58 -25
- package/plugins/pbr/scripts/feedback-loop.js +155 -0
- package/plugins/pbr/scripts/graph-update.js +422 -0
- package/plugins/pbr/scripts/hook-logger.js +69 -35
- package/plugins/pbr/scripts/hook-server-client.js +361 -0
- package/plugins/pbr/scripts/hook-server.js +658 -0
- package/plugins/pbr/scripts/hooks-schema.json +13 -5
- package/plugins/pbr/scripts/instructions-loaded.js +173 -0
- package/plugins/pbr/scripts/intent-router.cjs +147 -0
- package/plugins/pbr/scripts/intercept-plan-mode.js +52 -18
- package/plugins/pbr/scripts/lib/alternatives.js +203 -0
- package/plugins/pbr/scripts/lib/audit.js +65 -0
- package/plugins/pbr/scripts/lib/auto-cleanup.js +221 -0
- package/plugins/pbr/scripts/lib/auto-verify.js +103 -0
- package/plugins/pbr/scripts/lib/build.js +719 -0
- package/plugins/pbr/scripts/lib/ci-fix-loop.js +228 -0
- package/plugins/pbr/scripts/lib/commands.js +483 -0
- package/plugins/pbr/scripts/lib/compound.js +216 -0
- package/plugins/pbr/scripts/lib/config.js +1308 -0
- package/plugins/pbr/scripts/lib/context.js +254 -0
- package/plugins/pbr/scripts/lib/contextual-help.js +183 -0
- package/plugins/pbr/scripts/lib/convention-detector.js +413 -0
- package/plugins/pbr/scripts/lib/core.js +1569 -0
- package/plugins/pbr/scripts/lib/dashboard-launch.js +364 -0
- package/plugins/pbr/scripts/lib/data-hygiene.js +179 -0
- package/plugins/pbr/scripts/lib/decision-extraction.js +183 -0
- package/plugins/pbr/scripts/lib/decisions.js +194 -0
- package/plugins/pbr/scripts/lib/dependency-break.js +147 -0
- package/plugins/pbr/scripts/lib/format-validators.js +1025 -0
- package/plugins/pbr/scripts/lib/frontmatter.js +302 -0
- package/plugins/pbr/scripts/lib/gates/advisories.js +129 -0
- package/plugins/pbr/scripts/lib/gates/build-dependency.js +115 -0
- package/plugins/pbr/scripts/lib/gates/build-executor.js +104 -0
- package/plugins/pbr/scripts/lib/gates/doc-existence.js +46 -0
- package/plugins/pbr/scripts/lib/gates/helpers.js +93 -0
- package/plugins/pbr/scripts/lib/gates/inline-execution.js +185 -0
- package/plugins/pbr/scripts/lib/gates/milestone-complete.js +136 -0
- package/plugins/pbr/scripts/lib/gates/milestone-summary.js +119 -0
- package/plugins/pbr/scripts/lib/gates/multi-phase-loader.js +147 -0
- package/plugins/pbr/scripts/lib/gates/plan-executor.js +36 -0
- package/plugins/pbr/scripts/lib/gates/plan-validation.js +114 -0
- package/plugins/pbr/scripts/lib/gates/quick-executor.js +76 -0
- package/plugins/pbr/scripts/lib/gates/review-planner.js +61 -0
- package/plugins/pbr/scripts/lib/gates/review-verifier.js +69 -0
- package/plugins/pbr/scripts/lib/gates/rich-agent-context.js +137 -0
- package/plugins/pbr/scripts/lib/gates/user-confirmation.js +93 -0
- package/plugins/pbr/scripts/lib/graph-cli.js +89 -0
- package/plugins/pbr/scripts/lib/graph.js +553 -0
- package/plugins/pbr/scripts/lib/health-checks.js +107 -0
- package/plugins/pbr/scripts/lib/health-phase06.js +120 -0
- package/plugins/pbr/scripts/lib/health.js +132 -0
- package/plugins/pbr/scripts/lib/help.js +100 -0
- package/plugins/pbr/scripts/lib/history.js +150 -0
- package/plugins/pbr/scripts/lib/impact-analysis.js +319 -0
- package/plugins/pbr/scripts/lib/incidents.js +190 -0
- package/plugins/pbr/scripts/lib/init.js +643 -0
- package/plugins/pbr/scripts/lib/insights-parser.js +320 -0
- package/plugins/pbr/scripts/lib/intel.js +653 -0
- package/plugins/pbr/scripts/lib/learnings.js +511 -0
- package/plugins/pbr/scripts/lib/migrate.js +298 -0
- package/plugins/pbr/scripts/lib/milestone.js +306 -0
- package/plugins/pbr/scripts/lib/negative-knowledge.js +194 -0
- package/plugins/pbr/scripts/lib/notification-throttle.js +141 -0
- package/plugins/pbr/scripts/lib/onboarding-generator.js +288 -0
- package/plugins/pbr/scripts/lib/parse-args.js +134 -0
- package/plugins/pbr/scripts/lib/pattern-routing.js +55 -0
- package/plugins/pbr/scripts/lib/patterns.js +272 -0
- package/plugins/pbr/scripts/lib/perf.js +190 -0
- package/plugins/pbr/scripts/lib/phase.js +1025 -0
- package/plugins/pbr/scripts/lib/pid-lock.js +154 -0
- package/plugins/pbr/scripts/lib/post-hoc.js +160 -0
- package/plugins/pbr/scripts/lib/pre-commit-checks.js +220 -0
- package/plugins/pbr/scripts/lib/pre-research.js +126 -0
- package/plugins/pbr/scripts/lib/preview.js +174 -0
- package/plugins/pbr/scripts/lib/progress-visualization.js +296 -0
- package/plugins/pbr/scripts/lib/quick-init.js +131 -0
- package/plugins/pbr/scripts/lib/reference.js +236 -0
- package/plugins/pbr/scripts/lib/requirements.js +153 -0
- package/plugins/pbr/scripts/lib/resolve-root.js +66 -0
- package/plugins/pbr/scripts/lib/reverse-spec.js +259 -0
- package/plugins/pbr/scripts/lib/roadmap.js +1089 -0
- package/plugins/pbr/scripts/lib/security-scan.js +200 -0
- package/plugins/pbr/scripts/lib/session-briefing.js +895 -0
- package/plugins/pbr/scripts/lib/skill-section.js +99 -0
- package/plugins/pbr/scripts/lib/smart-next-task.js +198 -0
- package/plugins/pbr/scripts/lib/snapshot-manager.js +232 -0
- package/plugins/pbr/scripts/lib/spec-diff.js +209 -0
- package/plugins/pbr/scripts/lib/spec-engine.js +189 -0
- package/plugins/pbr/scripts/lib/spot-check.js +539 -0
- package/plugins/pbr/scripts/lib/state-queue.js +171 -0
- package/plugins/pbr/scripts/lib/state.js +1082 -0
- package/plugins/pbr/scripts/lib/status-render.js +511 -0
- package/plugins/pbr/scripts/lib/step-verify.js +149 -0
- package/plugins/pbr/scripts/lib/subagent-validators.js +1059 -0
- package/plugins/pbr/scripts/lib/suggest-next.js +435 -0
- package/plugins/pbr/scripts/lib/tech-debt-scanner.js +116 -0
- package/plugins/pbr/scripts/lib/templates.js +362 -0
- package/plugins/pbr/scripts/lib/test-selection.js +163 -0
- package/plugins/pbr/scripts/lib/todo.js +300 -0
- package/plugins/pbr/scripts/lib/verify.js +1483 -0
- package/plugins/pbr/scripts/log-notification.js +131 -0
- package/plugins/pbr/scripts/log-subagent.js +203 -18
- package/plugins/pbr/scripts/log-tool-failure.js +60 -8
- package/plugins/pbr/scripts/milestone-learnings.js +519 -0
- package/plugins/pbr/scripts/package.json +1 -1
- package/plugins/pbr/scripts/pbr-tools.js +1754 -1171
- package/plugins/pbr/scripts/post-bash-triage.js +96 -0
- package/plugins/pbr/scripts/post-compact.js +135 -0
- package/plugins/pbr/scripts/post-hoc.js +237 -0
- package/plugins/pbr/scripts/post-write-dispatch.js +201 -31
- package/plugins/pbr/scripts/post-write-quality.js +4 -3
- package/plugins/pbr/scripts/pre-bash-dispatch.js +147 -51
- package/plugins/pbr/scripts/pre-skill-dispatch.js +114 -0
- package/plugins/pbr/scripts/pre-task-dispatch.js +269 -0
- package/plugins/pbr/scripts/pre-write-dispatch.js +170 -73
- package/plugins/pbr/scripts/progress-tracker.js +122 -310
- package/plugins/pbr/scripts/prompt-guard.js +114 -0
- package/plugins/pbr/scripts/prompt-routing.js +209 -0
- package/plugins/pbr/scripts/quick-status.js +179 -0
- package/plugins/pbr/scripts/record-incident.js +37 -0
- package/plugins/pbr/scripts/risk-classifier.cjs +123 -0
- package/plugins/pbr/scripts/run-hook.js +62 -10
- package/plugins/pbr/scripts/session-cleanup.js +428 -29
- package/plugins/pbr/scripts/session-tracker.js +124 -0
- package/plugins/pbr/scripts/status-line.js +593 -32
- package/plugins/pbr/scripts/suggest-compact.js +201 -13
- package/plugins/pbr/scripts/sync-context-to-claude.js +100 -0
- package/plugins/pbr/scripts/task-completed.js +165 -4
- package/plugins/pbr/scripts/test/config.test.js +126 -0
- package/plugins/pbr/scripts/test/cross-platform.test.js +131 -0
- package/plugins/pbr/scripts/test/fixtures/config.json +20 -0
- package/plugins/pbr/scripts/test/fixtures/plan.md +54 -0
- package/plugins/pbr/scripts/test/fixtures/project.md +30 -0
- package/plugins/pbr/scripts/test/fixtures/roadmap.md +55 -0
- package/plugins/pbr/scripts/test/fixtures/state.md +60 -0
- package/plugins/pbr/scripts/test/fixtures/summary.md +35 -0
- package/plugins/pbr/scripts/test/fixtures.test.js +184 -0
- package/plugins/pbr/scripts/test/phase.test.js +142 -0
- package/plugins/pbr/scripts/test/roadmap.test.js +96 -0
- package/plugins/pbr/scripts/test/state.test.js +155 -0
- package/plugins/pbr/scripts/track-context-budget.js +368 -99
- package/plugins/pbr/scripts/track-user-gates.js +88 -0
- package/plugins/pbr/scripts/trust-tracker.js +193 -0
- package/plugins/pbr/scripts/validate-commit.js +41 -26
- package/plugins/pbr/scripts/validate-skill-args.js +87 -15
- package/plugins/pbr/scripts/validate-task.js +83 -627
- package/plugins/pbr/scripts/worktree-create.js +144 -0
- package/plugins/pbr/scripts/worktree-remove.js +147 -0
- package/plugins/pbr/skills/audit/SKILL.md +195 -24
- package/plugins/pbr/skills/audit-fix/SKILL.md +326 -0
- package/plugins/pbr/skills/autonomous/SKILL.md +545 -0
- package/plugins/pbr/skills/backlog/SKILL.md +56 -0
- package/plugins/pbr/skills/begin/SKILL.md +508 -153
- package/plugins/pbr/skills/begin/templates/STATE.md.tmpl +1 -2
- package/plugins/pbr/skills/begin/templates/config.json.tmpl +411 -36
- package/plugins/pbr/skills/begin/templates/researcher-prompt.md.tmpl +28 -0
- package/plugins/pbr/skills/begin/templates/roadmap-prompt.md.tmpl +28 -3
- package/plugins/pbr/skills/begin/templates/synthesis-prompt.md.tmpl +33 -5
- package/plugins/pbr/skills/build/SKILL.md +1040 -354
- package/plugins/pbr/skills/build/templates/continuation-prompt.md.tmpl +26 -0
- package/plugins/pbr/skills/build/templates/executor-prompt.md.tmpl +77 -0
- package/plugins/pbr/skills/build/templates/inline-verifier-prompt.md.tmpl +33 -0
- package/plugins/pbr/skills/config/SKILL.md +112 -9
- package/plugins/pbr/skills/continue/SKILL.md +113 -33
- package/plugins/pbr/skills/dashboard/SKILL.md +21 -9
- package/plugins/pbr/skills/debug/SKILL.md +70 -12
- package/plugins/pbr/skills/debug/templates/continuation-prompt.md.tmpl +12 -1
- package/plugins/pbr/skills/debug/templates/initial-investigation-prompt.md.tmpl +12 -5
- package/plugins/pbr/skills/discuss/SKILL.md +206 -25
- package/plugins/pbr/skills/discuss/templates/CONTEXT.md.tmpl +21 -1
- package/plugins/pbr/skills/do/SKILL.md +119 -24
- package/plugins/pbr/skills/explore/SKILL.md +95 -20
- package/plugins/pbr/skills/fast/SKILL.md +94 -0
- package/plugins/pbr/skills/forensics/SKILL.md +144 -0
- package/plugins/pbr/skills/health/SKILL.md +35 -117
- package/plugins/pbr/skills/help/SKILL.md +84 -101
- package/plugins/pbr/skills/import/SKILL.md +332 -13
- package/plugins/pbr/skills/intel/SKILL.md +131 -0
- package/plugins/pbr/skills/list-phase-assumptions/SKILL.md +231 -0
- package/plugins/pbr/skills/milestone/SKILL.md +421 -263
- package/plugins/pbr/skills/milestone/templates/audit-output.md.tmpl +76 -0
- package/plugins/pbr/skills/milestone/templates/complete-output.md.tmpl +32 -0
- package/plugins/pbr/skills/milestone/templates/edge-cases.md +54 -0
- package/plugins/pbr/skills/milestone/templates/gaps-output.md.tmpl +25 -0
- package/plugins/pbr/skills/milestone/templates/integration-checker-prompt.md.tmpl +25 -0
- package/plugins/pbr/skills/milestone/templates/new-output.md.tmpl +29 -0
- package/plugins/pbr/skills/milestone-summary/SKILL.md +86 -0
- package/plugins/pbr/skills/note/SKILL.md +20 -4
- package/plugins/pbr/skills/pause/SKILL.md +54 -14
- package/plugins/pbr/skills/pause/templates/continue-here.md.tmpl +33 -52
- package/plugins/pbr/skills/plan/SKILL.md +526 -280
- package/plugins/pbr/skills/plan/templates/checker-prompt.md.tmpl +5 -2
- package/plugins/pbr/skills/plan/templates/completion-output.md.tmpl +27 -0
- package/plugins/pbr/skills/plan/templates/planner-prompt.md.tmpl +27 -1
- package/plugins/pbr/skills/plan/templates/revision-prompt.md.tmpl +21 -5
- package/plugins/pbr/skills/profile/SKILL.md +185 -0
- package/plugins/pbr/skills/profile-user/SKILL.md +227 -0
- package/plugins/pbr/skills/quick/SKILL.md +435 -100
- package/plugins/pbr/skills/release/SKILL.md +206 -0
- package/plugins/pbr/skills/resume/SKILL.md +170 -46
- package/plugins/pbr/skills/review/SKILL.md +217 -164
- package/plugins/pbr/skills/review/templates/verifier-prompt.md.tmpl +7 -0
- package/plugins/pbr/skills/scan/SKILL.md +152 -106
- package/plugins/pbr/skills/scan/templates/mapper-prompt.md.tmpl +5 -56
- package/plugins/pbr/skills/seed/SKILL.md +87 -0
- package/plugins/pbr/skills/session-report/SKILL.md +130 -0
- package/plugins/pbr/skills/setup/SKILL.md +150 -202
- package/plugins/pbr/skills/shared/agent-context-enrichment.md +21 -0
- package/plugins/pbr/skills/shared/agent-type-resolution.md +32 -0
- package/plugins/pbr/skills/shared/commit-planning-docs.md +8 -0
- package/plugins/pbr/skills/shared/context-budget.md +66 -1
- package/plugins/pbr/skills/shared/context-loader-task.md +18 -11
- package/plugins/pbr/skills/shared/digest-select.md +2 -2
- package/plugins/pbr/skills/shared/domain-probes.md +1 -1
- package/plugins/pbr/skills/shared/error-reporting.md +38 -60
- package/plugins/pbr/skills/shared/gate-prompts.md +4 -2
- package/plugins/pbr/skills/shared/memory-capture.md +48 -0
- package/plugins/pbr/skills/shared/phase-argument-parsing.md +4 -4
- package/plugins/pbr/skills/shared/revision-loop.md +24 -6
- package/plugins/pbr/skills/shared/state-update.md +49 -56
- package/plugins/pbr/skills/shared/universal-anti-patterns.md +27 -4
- package/plugins/pbr/skills/ship/SKILL.md +155 -0
- package/plugins/pbr/skills/stats/SKILL.md +80 -0
- package/plugins/pbr/skills/status/SKILL.md +185 -119
- package/plugins/pbr/skills/test/SKILL.md +254 -0
- package/plugins/pbr/skills/thread/SKILL.md +73 -0
- package/plugins/pbr/skills/todo/SKILL.md +28 -72
- package/plugins/pbr/skills/ui-phase/SKILL.md +180 -0
- package/plugins/pbr/skills/ui-review/SKILL.md +206 -0
- package/plugins/pbr/skills/undo/SKILL.md +221 -0
- package/plugins/pbr/skills/validate-phase/SKILL.md +362 -0
- package/plugins/pbr/templates/CONTEXT.md.tmpl +45 -20
- package/plugins/pbr/templates/DISCOVERY.md.tmpl +29 -0
- package/plugins/pbr/templates/DISCUSSION-LOG.md.tmpl +49 -0
- package/plugins/pbr/templates/HANDOFF.json.tmpl +30 -0
- package/plugins/pbr/templates/INTEGRATION-REPORT.md.tmpl +18 -2
- package/plugins/pbr/templates/MILESTONE-AUDIT.md.tmpl +44 -0
- package/plugins/pbr/templates/PROJECT.md.tmpl +126 -0
- package/plugins/pbr/templates/REQUIREMENTS.md.tmpl +96 -0
- package/plugins/pbr/templates/RETROSPECTIVE.md.tmpl +43 -0
- package/plugins/pbr/templates/ROADMAP.md.tmpl +108 -14
- package/plugins/pbr/templates/SUMMARY-complex.md.tmpl +133 -0
- package/plugins/pbr/templates/SUMMARY-minimal.md.tmpl +55 -0
- package/plugins/pbr/templates/SUMMARY.md.tmpl +21 -0
- package/plugins/pbr/templates/UAT.md.tmpl +94 -0
- package/plugins/pbr/templates/UI-SPEC.md.tmpl +144 -0
- package/plugins/pbr/templates/VALIDATION.md.tmpl +94 -0
- package/plugins/pbr/templates/VERIFICATION-DETAIL.md.tmpl +49 -13
- package/plugins/pbr/templates/project-CONTEXT.md.tmpl +59 -0
- package/plugins/pbr/templates/research-outputs/ARCHITECTURE.md.tmpl +91 -0
- package/plugins/pbr/templates/research-outputs/FEATURES.md.tmpl +64 -0
- package/plugins/pbr/templates/research-outputs/PITFALLS.md.tmpl +50 -0
- package/plugins/pbr/templates/research-outputs/STACK.md.tmpl +63 -0
- package/plugins/pbr/templates/research-outputs/SUMMARY.md.tmpl +98 -0
- package/scripts/build-hooks.js +61 -0
- package/scripts/check-ci.js +100 -0
- package/scripts/clean-changelog.js +364 -0
- package/scripts/generate-derivatives.js +581 -0
- package/scripts/posttest.js +93 -0
- package/scripts/release.js +262 -0
- package/scripts/run-tests.cjs +29 -0
- package/scripts/test-wrapper.js +43 -0
- package/dashboard/bin/cli.js +0 -25
- package/dashboard/public/css/layout.css +0 -704
- package/dashboard/public/css/status-colors.css +0 -98
- package/dashboard/public/css/tokens.css +0 -59
- package/dashboard/public/js/htmx-title.js +0 -5
- package/dashboard/public/js/sidebar-toggle.js +0 -34
- package/dashboard/public/js/sse-client.js +0 -100
- package/dashboard/public/js/theme-toggle.js +0 -46
- package/dashboard/src/app.js +0 -91
- package/dashboard/src/middleware/current-phase.js +0 -24
- package/dashboard/src/middleware/errorHandler.js +0 -52
- package/dashboard/src/middleware/notFoundHandler.js +0 -9
- package/dashboard/src/repositories/planning.repository.js +0 -130
- package/dashboard/src/routes/events.routes.js +0 -45
- package/dashboard/src/routes/index.routes.js +0 -35
- package/dashboard/src/routes/pages.routes.js +0 -426
- package/dashboard/src/server.js +0 -42
- package/dashboard/src/services/analytics.service.js +0 -141
- package/dashboard/src/services/dashboard.service.js +0 -309
- package/dashboard/src/services/milestone.service.js +0 -222
- package/dashboard/src/services/notes.service.js +0 -50
- package/dashboard/src/services/phase.service.js +0 -232
- package/dashboard/src/services/project.service.js +0 -57
- package/dashboard/src/services/roadmap.service.js +0 -258
- package/dashboard/src/services/sse.service.js +0 -58
- package/dashboard/src/services/todo.service.js +0 -272
- package/dashboard/src/services/watcher.service.js +0 -48
- package/dashboard/src/utils/cache.js +0 -55
- package/dashboard/src/views/analytics.ejs +0 -5
- package/dashboard/src/views/coming-soon.ejs +0 -11
- package/dashboard/src/views/dependencies.ejs +0 -5
- package/dashboard/src/views/error.ejs +0 -20
- package/dashboard/src/views/index.ejs +0 -5
- package/dashboard/src/views/milestone-detail.ejs +0 -5
- package/dashboard/src/views/milestones.ejs +0 -5
- package/dashboard/src/views/notes.ejs +0 -5
- package/dashboard/src/views/partials/analytics-content.ejs +0 -90
- package/dashboard/src/views/partials/breadcrumbs.ejs +0 -14
- package/dashboard/src/views/partials/dashboard-content.ejs +0 -84
- package/dashboard/src/views/partials/dependencies-content.ejs +0 -48
- package/dashboard/src/views/partials/empty-state.ejs +0 -7
- package/dashboard/src/views/partials/footer.ejs +0 -3
- package/dashboard/src/views/partials/head.ejs +0 -30
- package/dashboard/src/views/partials/header.ejs +0 -21
- package/dashboard/src/views/partials/layout-bottom.ejs +0 -43
- package/dashboard/src/views/partials/layout-top.ejs +0 -16
- package/dashboard/src/views/partials/milestone-detail-content.ejs +0 -20
- package/dashboard/src/views/partials/milestones-content.ejs +0 -88
- package/dashboard/src/views/partials/notes-content.ejs +0 -23
- package/dashboard/src/views/partials/phase-content.ejs +0 -193
- package/dashboard/src/views/partials/phase-doc-content.ejs +0 -38
- package/dashboard/src/views/partials/phases-content.ejs +0 -124
- package/dashboard/src/views/partials/roadmap-content.ejs +0 -180
- package/dashboard/src/views/partials/sidebar.ejs +0 -99
- package/dashboard/src/views/partials/todo-create-content.ejs +0 -54
- package/dashboard/src/views/partials/todo-detail-content.ejs +0 -42
- package/dashboard/src/views/partials/todos-content.ejs +0 -97
- package/dashboard/src/views/phase-detail.ejs +0 -5
- package/dashboard/src/views/phase-doc.ejs +0 -5
- package/dashboard/src/views/phases.ejs +0 -5
- package/dashboard/src/views/roadmap.ejs +0 -5
- package/dashboard/src/views/todo-create.ejs +0 -5
- package/dashboard/src/views/todo-detail.ejs +0 -5
- package/dashboard/src/views/todos.ejs +0 -5
- package/plugins/copilot-pbr/CHANGELOG.md +0 -19
- package/plugins/copilot-pbr/README.md +0 -139
- package/plugins/copilot-pbr/agents/audit.agent.md +0 -113
- package/plugins/copilot-pbr/agents/codebase-mapper.agent.md +0 -151
- package/plugins/copilot-pbr/agents/debugger.agent.md +0 -182
- package/plugins/copilot-pbr/agents/executor.agent.md +0 -267
- package/plugins/copilot-pbr/agents/general.agent.md +0 -88
- package/plugins/copilot-pbr/agents/integration-checker.agent.md +0 -119
- package/plugins/copilot-pbr/agents/plan-checker.agent.md +0 -208
- package/plugins/copilot-pbr/agents/planner.agent.md +0 -238
- package/plugins/copilot-pbr/agents/researcher.agent.md +0 -186
- package/plugins/copilot-pbr/agents/synthesizer.agent.md +0 -126
- package/plugins/copilot-pbr/agents/verifier.agent.md +0 -228
- package/plugins/copilot-pbr/hooks/hooks.json +0 -156
- package/plugins/copilot-pbr/plugin.json +0 -30
- package/plugins/copilot-pbr/references/agent-anti-patterns.md +0 -25
- package/plugins/copilot-pbr/references/agent-contracts.md +0 -297
- package/plugins/copilot-pbr/references/agent-interactions.md +0 -135
- package/plugins/copilot-pbr/references/agent-teams.md +0 -55
- package/plugins/copilot-pbr/references/checkpoints.md +0 -158
- package/plugins/copilot-pbr/references/common-bug-patterns.md +0 -14
- package/plugins/copilot-pbr/references/config-reference.md +0 -442
- package/plugins/copilot-pbr/references/continuation-format.md +0 -213
- package/plugins/copilot-pbr/references/deviation-rules.md +0 -113
- package/plugins/copilot-pbr/references/git-integration.md +0 -227
- package/plugins/copilot-pbr/references/integration-patterns.md +0 -118
- package/plugins/copilot-pbr/references/model-profiles.md +0 -100
- package/plugins/copilot-pbr/references/model-selection.md +0 -32
- package/plugins/copilot-pbr/references/pbr-rules.md +0 -195
- package/plugins/copilot-pbr/references/pbr-tools-cli.md +0 -285
- package/plugins/copilot-pbr/references/plan-authoring.md +0 -182
- package/plugins/copilot-pbr/references/plan-format.md +0 -288
- package/plugins/copilot-pbr/references/planning-config.md +0 -214
- package/plugins/copilot-pbr/references/questioning.md +0 -215
- package/plugins/copilot-pbr/references/reading-verification.md +0 -128
- package/plugins/copilot-pbr/references/stub-patterns.md +0 -161
- package/plugins/copilot-pbr/references/subagent-coordination.md +0 -120
- package/plugins/copilot-pbr/references/ui-formatting.md +0 -444
- package/plugins/copilot-pbr/references/verification-patterns.md +0 -199
- package/plugins/copilot-pbr/references/wave-execution.md +0 -96
- package/plugins/copilot-pbr/rules/pbr-workflow.mdc +0 -48
- package/plugins/copilot-pbr/setup.ps1 +0 -93
- package/plugins/copilot-pbr/setup.sh +0 -93
- package/plugins/copilot-pbr/skills/audit/SKILL.md +0 -330
- package/plugins/copilot-pbr/skills/begin/SKILL.md +0 -589
- package/plugins/copilot-pbr/skills/begin/templates/PROJECT.md.tmpl +0 -34
- package/plugins/copilot-pbr/skills/begin/templates/REQUIREMENTS.md.tmpl +0 -19
- package/plugins/copilot-pbr/skills/begin/templates/STATE.md.tmpl +0 -50
- package/plugins/copilot-pbr/skills/begin/templates/config.json.tmpl +0 -64
- package/plugins/copilot-pbr/skills/begin/templates/researcher-prompt.md.tmpl +0 -20
- package/plugins/copilot-pbr/skills/begin/templates/roadmap-prompt.md.tmpl +0 -31
- package/plugins/copilot-pbr/skills/begin/templates/synthesis-prompt.md.tmpl +0 -17
- package/plugins/copilot-pbr/skills/build/SKILL.md +0 -960
- package/plugins/copilot-pbr/skills/config/SKILL.md +0 -250
- package/plugins/copilot-pbr/skills/continue/SKILL.md +0 -159
- package/plugins/copilot-pbr/skills/dashboard/SKILL.md +0 -43
- package/plugins/copilot-pbr/skills/debug/SKILL.md +0 -508
- package/plugins/copilot-pbr/skills/debug/templates/continuation-prompt.md.tmpl +0 -17
- package/plugins/copilot-pbr/skills/debug/templates/initial-investigation-prompt.md.tmpl +0 -28
- package/plugins/copilot-pbr/skills/discuss/SKILL.md +0 -353
- package/plugins/copilot-pbr/skills/discuss/templates/CONTEXT.md.tmpl +0 -62
- package/plugins/copilot-pbr/skills/discuss/templates/decision-categories.md +0 -10
- package/plugins/copilot-pbr/skills/do/SKILL.md +0 -66
- package/plugins/copilot-pbr/skills/explore/SKILL.md +0 -373
- package/plugins/copilot-pbr/skills/health/SKILL.md +0 -283
- package/plugins/copilot-pbr/skills/health/templates/check-pattern.md.tmpl +0 -31
- package/plugins/copilot-pbr/skills/health/templates/output-format.md.tmpl +0 -64
- package/plugins/copilot-pbr/skills/help/SKILL.md +0 -170
- package/plugins/copilot-pbr/skills/import/SKILL.md +0 -502
- package/plugins/copilot-pbr/skills/milestone/SKILL.md +0 -745
- package/plugins/copilot-pbr/skills/milestone/templates/audit-report.md.tmpl +0 -49
- package/plugins/copilot-pbr/skills/milestone/templates/stats-file.md.tmpl +0 -31
- package/plugins/copilot-pbr/skills/note/SKILL.md +0 -213
- package/plugins/copilot-pbr/skills/pause/SKILL.md +0 -247
- package/plugins/copilot-pbr/skills/pause/templates/continue-here.md.tmpl +0 -72
- package/plugins/copilot-pbr/skills/plan/SKILL.md +0 -662
- package/plugins/copilot-pbr/skills/plan/templates/checker-prompt.md.tmpl +0 -22
- package/plugins/copilot-pbr/skills/plan/templates/gap-closure-prompt.md.tmpl +0 -33
- package/plugins/copilot-pbr/skills/plan/templates/planner-prompt.md.tmpl +0 -39
- package/plugins/copilot-pbr/skills/plan/templates/researcher-prompt.md.tmpl +0 -20
- package/plugins/copilot-pbr/skills/plan/templates/revision-prompt.md.tmpl +0 -24
- package/plugins/copilot-pbr/skills/quick/SKILL.md +0 -376
- package/plugins/copilot-pbr/skills/resume/SKILL.md +0 -399
- package/plugins/copilot-pbr/skills/review/SKILL.md +0 -653
- package/plugins/copilot-pbr/skills/review/templates/debugger-prompt.md.tmpl +0 -61
- package/plugins/copilot-pbr/skills/review/templates/gap-planner-prompt.md.tmpl +0 -41
- package/plugins/copilot-pbr/skills/review/templates/verifier-prompt.md.tmpl +0 -116
- package/plugins/copilot-pbr/skills/scan/SKILL.md +0 -299
- package/plugins/copilot-pbr/skills/scan/templates/mapper-prompt.md.tmpl +0 -202
- package/plugins/copilot-pbr/skills/setup/SKILL.md +0 -296
- package/plugins/copilot-pbr/skills/shared/commit-planning-docs.md +0 -36
- package/plugins/copilot-pbr/skills/shared/config-loading.md +0 -103
- package/plugins/copilot-pbr/skills/shared/context-budget.md +0 -41
- package/plugins/copilot-pbr/skills/shared/context-loader-task.md +0 -87
- package/plugins/copilot-pbr/skills/shared/digest-select.md +0 -80
- package/plugins/copilot-pbr/skills/shared/domain-probes.md +0 -126
- package/plugins/copilot-pbr/skills/shared/error-reporting.md +0 -81
- package/plugins/copilot-pbr/skills/shared/gate-prompts.md +0 -389
- package/plugins/copilot-pbr/skills/shared/phase-argument-parsing.md +0 -46
- package/plugins/copilot-pbr/skills/shared/progress-display.md +0 -53
- package/plugins/copilot-pbr/skills/shared/revision-loop.md +0 -82
- package/plugins/copilot-pbr/skills/shared/state-loading.md +0 -63
- package/plugins/copilot-pbr/skills/shared/state-update.md +0 -162
- package/plugins/copilot-pbr/skills/shared/universal-anti-patterns.md +0 -38
- package/plugins/copilot-pbr/skills/status/SKILL.md +0 -362
- package/plugins/copilot-pbr/skills/statusline/SKILL.md +0 -149
- package/plugins/copilot-pbr/skills/todo/SKILL.md +0 -279
- package/plugins/copilot-pbr/templates/CONTEXT.md.tmpl +0 -53
- package/plugins/copilot-pbr/templates/INTEGRATION-REPORT.md.tmpl +0 -152
- package/plugins/copilot-pbr/templates/RESEARCH-SUMMARY.md.tmpl +0 -98
- package/plugins/copilot-pbr/templates/ROADMAP.md.tmpl +0 -41
- package/plugins/copilot-pbr/templates/SUMMARY.md.tmpl +0 -82
- package/plugins/copilot-pbr/templates/VERIFICATION-DETAIL.md.tmpl +0 -117
- package/plugins/copilot-pbr/templates/codebase/ARCHITECTURE.md.tmpl +0 -98
- package/plugins/copilot-pbr/templates/codebase/CONCERNS.md.tmpl +0 -93
- package/plugins/copilot-pbr/templates/codebase/CONVENTIONS.md.tmpl +0 -104
- package/plugins/copilot-pbr/templates/codebase/INTEGRATIONS.md.tmpl +0 -78
- package/plugins/copilot-pbr/templates/codebase/STACK.md.tmpl +0 -78
- package/plugins/copilot-pbr/templates/codebase/STRUCTURE.md.tmpl +0 -80
- package/plugins/copilot-pbr/templates/codebase/TESTING.md.tmpl +0 -107
- package/plugins/copilot-pbr/templates/continue-here.md.tmpl +0 -74
- package/plugins/copilot-pbr/templates/prompt-partials/phase-project-context.md.tmpl +0 -38
- package/plugins/copilot-pbr/templates/research/ARCHITECTURE.md.tmpl +0 -124
- package/plugins/copilot-pbr/templates/research/STACK.md.tmpl +0 -71
- package/plugins/copilot-pbr/templates/research/SUMMARY.md.tmpl +0 -112
- package/plugins/copilot-pbr/templates/research-outputs/phase-research.md.tmpl +0 -81
- package/plugins/copilot-pbr/templates/research-outputs/project-research.md.tmpl +0 -99
- package/plugins/copilot-pbr/templates/research-outputs/synthesis.md.tmpl +0 -36
- package/plugins/cursor-pbr/.cursor-plugin/plugin.json +0 -32
- package/plugins/cursor-pbr/CHANGELOG.md +0 -15
- package/plugins/cursor-pbr/README.md +0 -123
- package/plugins/cursor-pbr/agents/audit.md +0 -178
- package/plugins/cursor-pbr/agents/codebase-mapper.md +0 -150
- package/plugins/cursor-pbr/agents/debugger.md +0 -181
- package/plugins/cursor-pbr/agents/executor.md +0 -266
- package/plugins/cursor-pbr/agents/general.md +0 -87
- package/plugins/cursor-pbr/agents/integration-checker.md +0 -118
- package/plugins/cursor-pbr/agents/plan-checker.md +0 -207
- package/plugins/cursor-pbr/agents/planner.md +0 -237
- package/plugins/cursor-pbr/agents/researcher.md +0 -185
- package/plugins/cursor-pbr/agents/synthesizer.md +0 -125
- package/plugins/cursor-pbr/agents/verifier.md +0 -227
- package/plugins/cursor-pbr/assets/.gitkeep +0 -0
- package/plugins/cursor-pbr/assets/logo.svg +0 -21
- package/plugins/cursor-pbr/hooks/hooks.json +0 -213
- package/plugins/cursor-pbr/references/agent-anti-patterns.md +0 -25
- package/plugins/cursor-pbr/references/agent-contracts.md +0 -297
- package/plugins/cursor-pbr/references/agent-interactions.md +0 -135
- package/plugins/cursor-pbr/references/agent-teams.md +0 -55
- package/plugins/cursor-pbr/references/checkpoints.md +0 -158
- package/plugins/cursor-pbr/references/common-bug-patterns.md +0 -14
- package/plugins/cursor-pbr/references/config-reference.md +0 -442
- package/plugins/cursor-pbr/references/continuation-format.md +0 -213
- package/plugins/cursor-pbr/references/deviation-rules.md +0 -113
- package/plugins/cursor-pbr/references/git-integration.md +0 -227
- package/plugins/cursor-pbr/references/integration-patterns.md +0 -118
- package/plugins/cursor-pbr/references/model-profiles.md +0 -100
- package/plugins/cursor-pbr/references/model-selection.md +0 -32
- package/plugins/cursor-pbr/references/pbr-rules.md +0 -195
- package/plugins/cursor-pbr/references/pbr-tools-cli.md +0 -285
- package/plugins/cursor-pbr/references/plan-authoring.md +0 -182
- package/plugins/cursor-pbr/references/plan-format.md +0 -288
- package/plugins/cursor-pbr/references/planning-config.md +0 -214
- package/plugins/cursor-pbr/references/questioning.md +0 -215
- package/plugins/cursor-pbr/references/reading-verification.md +0 -128
- package/plugins/cursor-pbr/references/stub-patterns.md +0 -161
- package/plugins/cursor-pbr/references/subagent-coordination.md +0 -120
- package/plugins/cursor-pbr/references/ui-formatting.md +0 -444
- package/plugins/cursor-pbr/references/verification-patterns.md +0 -199
- package/plugins/cursor-pbr/references/wave-execution.md +0 -96
- package/plugins/cursor-pbr/rules/pbr-workflow.mdc +0 -48
- package/plugins/cursor-pbr/setup.ps1 +0 -78
- package/plugins/cursor-pbr/setup.sh +0 -83
- package/plugins/cursor-pbr/skills/audit/SKILL.md +0 -331
- package/plugins/cursor-pbr/skills/begin/SKILL.md +0 -589
- package/plugins/cursor-pbr/skills/begin/templates/PROJECT.md.tmpl +0 -34
- package/plugins/cursor-pbr/skills/begin/templates/REQUIREMENTS.md.tmpl +0 -19
- package/plugins/cursor-pbr/skills/begin/templates/STATE.md.tmpl +0 -50
- package/plugins/cursor-pbr/skills/begin/templates/config.json.tmpl +0 -64
- package/plugins/cursor-pbr/skills/begin/templates/researcher-prompt.md.tmpl +0 -20
- package/plugins/cursor-pbr/skills/begin/templates/roadmap-prompt.md.tmpl +0 -31
- package/plugins/cursor-pbr/skills/begin/templates/synthesis-prompt.md.tmpl +0 -17
- package/plugins/cursor-pbr/skills/build/SKILL.md +0 -961
- package/plugins/cursor-pbr/skills/config/SKILL.md +0 -252
- package/plugins/cursor-pbr/skills/continue/SKILL.md +0 -159
- package/plugins/cursor-pbr/skills/dashboard/SKILL.md +0 -44
- package/plugins/cursor-pbr/skills/debug/SKILL.md +0 -512
- package/plugins/cursor-pbr/skills/debug/templates/continuation-prompt.md.tmpl +0 -17
- package/plugins/cursor-pbr/skills/debug/templates/initial-investigation-prompt.md.tmpl +0 -28
- package/plugins/cursor-pbr/skills/discuss/SKILL.md +0 -354
- package/plugins/cursor-pbr/skills/discuss/templates/CONTEXT.md.tmpl +0 -62
- package/plugins/cursor-pbr/skills/discuss/templates/decision-categories.md +0 -10
- package/plugins/cursor-pbr/skills/do/SKILL.md +0 -67
- package/plugins/cursor-pbr/skills/explore/SKILL.md +0 -376
- package/plugins/cursor-pbr/skills/health/SKILL.md +0 -283
- package/plugins/cursor-pbr/skills/health/templates/check-pattern.md.tmpl +0 -31
- package/plugins/cursor-pbr/skills/health/templates/output-format.md.tmpl +0 -64
- package/plugins/cursor-pbr/skills/help/SKILL.md +0 -170
- package/plugins/cursor-pbr/skills/import/SKILL.md +0 -505
- package/plugins/cursor-pbr/skills/milestone/SKILL.md +0 -746
- package/plugins/cursor-pbr/skills/milestone/templates/audit-report.md.tmpl +0 -49
- package/plugins/cursor-pbr/skills/milestone/templates/stats-file.md.tmpl +0 -31
- package/plugins/cursor-pbr/skills/note/SKILL.md +0 -214
- package/plugins/cursor-pbr/skills/pause/SKILL.md +0 -248
- package/plugins/cursor-pbr/skills/pause/templates/continue-here.md.tmpl +0 -72
- package/plugins/cursor-pbr/skills/plan/SKILL.md +0 -663
- package/plugins/cursor-pbr/skills/plan/templates/checker-prompt.md.tmpl +0 -22
- package/plugins/cursor-pbr/skills/plan/templates/gap-closure-prompt.md.tmpl +0 -33
- package/plugins/cursor-pbr/skills/plan/templates/planner-prompt.md.tmpl +0 -39
- package/plugins/cursor-pbr/skills/plan/templates/researcher-prompt.md.tmpl +0 -20
- package/plugins/cursor-pbr/skills/plan/templates/revision-prompt.md.tmpl +0 -24
- package/plugins/cursor-pbr/skills/quick/SKILL.md +0 -376
- package/plugins/cursor-pbr/skills/resume/SKILL.md +0 -399
- package/plugins/cursor-pbr/skills/review/SKILL.md +0 -654
- package/plugins/cursor-pbr/skills/review/templates/debugger-prompt.md.tmpl +0 -61
- package/plugins/cursor-pbr/skills/review/templates/gap-planner-prompt.md.tmpl +0 -41
- package/plugins/cursor-pbr/skills/review/templates/verifier-prompt.md.tmpl +0 -116
- package/plugins/cursor-pbr/skills/scan/SKILL.md +0 -300
- package/plugins/cursor-pbr/skills/scan/templates/mapper-prompt.md.tmpl +0 -202
- package/plugins/cursor-pbr/skills/setup/SKILL.md +0 -296
- package/plugins/cursor-pbr/skills/shared/commit-planning-docs.md +0 -36
- package/plugins/cursor-pbr/skills/shared/config-loading.md +0 -103
- package/plugins/cursor-pbr/skills/shared/context-budget.md +0 -41
- package/plugins/cursor-pbr/skills/shared/context-loader-task.md +0 -87
- package/plugins/cursor-pbr/skills/shared/digest-select.md +0 -80
- package/plugins/cursor-pbr/skills/shared/domain-probes.md +0 -126
- package/plugins/cursor-pbr/skills/shared/error-reporting.md +0 -81
- package/plugins/cursor-pbr/skills/shared/gate-prompts.md +0 -389
- package/plugins/cursor-pbr/skills/shared/phase-argument-parsing.md +0 -46
- package/plugins/cursor-pbr/skills/shared/progress-display.md +0 -53
- package/plugins/cursor-pbr/skills/shared/revision-loop.md +0 -82
- package/plugins/cursor-pbr/skills/shared/state-loading.md +0 -63
- package/plugins/cursor-pbr/skills/shared/state-update.md +0 -162
- package/plugins/cursor-pbr/skills/shared/universal-anti-patterns.md +0 -38
- package/plugins/cursor-pbr/skills/status/SKILL.md +0 -362
- package/plugins/cursor-pbr/skills/statusline/SKILL.md +0 -150
- package/plugins/cursor-pbr/skills/todo/SKILL.md +0 -280
- package/plugins/cursor-pbr/templates/CONTEXT.md.tmpl +0 -53
- package/plugins/cursor-pbr/templates/INTEGRATION-REPORT.md.tmpl +0 -152
- package/plugins/cursor-pbr/templates/RESEARCH-SUMMARY.md.tmpl +0 -98
- package/plugins/cursor-pbr/templates/ROADMAP.md.tmpl +0 -41
- package/plugins/cursor-pbr/templates/SUMMARY.md.tmpl +0 -82
- package/plugins/cursor-pbr/templates/VERIFICATION-DETAIL.md.tmpl +0 -117
- package/plugins/cursor-pbr/templates/codebase/ARCHITECTURE.md.tmpl +0 -98
- package/plugins/cursor-pbr/templates/codebase/CONCERNS.md.tmpl +0 -93
- package/plugins/cursor-pbr/templates/codebase/CONVENTIONS.md.tmpl +0 -104
- package/plugins/cursor-pbr/templates/codebase/INTEGRATIONS.md.tmpl +0 -78
- package/plugins/cursor-pbr/templates/codebase/STACK.md.tmpl +0 -78
- package/plugins/cursor-pbr/templates/codebase/STRUCTURE.md.tmpl +0 -80
- package/plugins/cursor-pbr/templates/codebase/TESTING.md.tmpl +0 -107
- package/plugins/cursor-pbr/templates/continue-here.md.tmpl +0 -74
- package/plugins/cursor-pbr/templates/prompt-partials/phase-project-context.md.tmpl +0 -38
- package/plugins/cursor-pbr/templates/research/ARCHITECTURE.md.tmpl +0 -124
- package/plugins/cursor-pbr/templates/research/STACK.md.tmpl +0 -71
- package/plugins/cursor-pbr/templates/research/SUMMARY.md.tmpl +0 -112
- package/plugins/cursor-pbr/templates/research-outputs/phase-research.md.tmpl +0 -81
- package/plugins/cursor-pbr/templates/research-outputs/project-research.md.tmpl +0 -99
- package/plugins/cursor-pbr/templates/research-outputs/synthesis.md.tmpl +0 -36
- package/plugins/pbr/references/agent-interactions.md +0 -134
- package/plugins/pbr/references/pbr-rules.md +0 -194
- package/plugins/pbr/references/pbr-tools-cli.md +0 -285
- package/plugins/pbr/references/planning-config.md +0 -213
- package/plugins/pbr/references/subagent-coordination.md +0 -119
- package/plugins/pbr/references/ui-formatting.md +0 -444
- package/plugins/pbr/scripts/validate-plugin-structure.js +0 -183
- package/plugins/pbr/skills/milestone/templates/audit-report.md.tmpl +0 -48
- package/plugins/pbr/skills/shared/progress-display.md +0 -53
- package/plugins/pbr/skills/shared/state-loading.md +0 -62
- package/plugins/pbr/templates/RESEARCH-SUMMARY.md.tmpl +0 -97
- package/plugins/pbr/templates/research/ARCHITECTURE.md.tmpl +0 -124
- package/plugins/pbr/templates/research/STACK.md.tmpl +0 -71
- package/plugins/pbr/templates/research/SUMMARY.md.tmpl +0 -112
- package/plugins/pbr/templates/research-outputs/phase-research.md.tmpl +0 -81
- package/plugins/pbr/templates/research-outputs/project-research.md.tmpl +0 -99
- package/plugins/pbr/templates/research-outputs/synthesis.md.tmpl +0 -36
- /package/plugins/pbr/references/{agent-anti-patterns.md → archive/agent-anti-patterns.md} +0 -0
|
@@ -0,0 +1,989 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Error & Failure Analysis Check Module
|
|
5
|
+
*
|
|
6
|
+
* Implements all 7 EF error and failure analysis dimensions
|
|
7
|
+
* for the PBR audit system. Each check returns a structured result:
|
|
8
|
+
* { dimension, status, message, evidence }.
|
|
9
|
+
*
|
|
10
|
+
* Checks:
|
|
11
|
+
* EF-01: Tool failure rate analysis (PostToolUseFailure events by tool type)
|
|
12
|
+
* EF-02: Agent failure/timeout detection (missing completion markers, null durations)
|
|
13
|
+
* EF-03: Hook false positive detection (blocks on legitimate writes)
|
|
14
|
+
* EF-04: Hook false negative detection (bad actions not blocked)
|
|
15
|
+
* EF-05: Retry/repetition pattern detection (same tool called 3+ times consecutively)
|
|
16
|
+
* EF-06: Cross-session interference detection (overlapping sessions, .active-skill races)
|
|
17
|
+
* EF-07: Session cleanup verification (SessionEnd cleanup and stale file removal)
|
|
18
|
+
*
|
|
19
|
+
* Config dependencies:
|
|
20
|
+
* - config.audit.thresholds.tool_failure_rate_warn (EF-01, default 0.10)
|
|
21
|
+
* - config.audit.thresholds.agent_timeout_ms (EF-02, default 600000)
|
|
22
|
+
* - config.audit.thresholds.retry_pattern_min_count (EF-05, default 3)
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const fs = require('fs');
|
|
26
|
+
const path = require('path');
|
|
27
|
+
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Result helper
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Build a structured check result.
|
|
34
|
+
* @param {string} dimCode - Dimension code (e.g. "EF-01")
|
|
35
|
+
* @param {'pass'|'warn'|'fail'} status
|
|
36
|
+
* @param {string} message
|
|
37
|
+
* @param {string[]} [evidence]
|
|
38
|
+
* @returns {{ dimension: string, status: string, message: string, evidence: string[] }}
|
|
39
|
+
*/
|
|
40
|
+
function result(dimCode, status, message, evidence) {
|
|
41
|
+
return {
|
|
42
|
+
dimension: dimCode,
|
|
43
|
+
status,
|
|
44
|
+
message,
|
|
45
|
+
evidence: evidence || [],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Shared helpers
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Get the logs directory path from a planningDir.
|
|
55
|
+
* @param {string} planningDir - Path to .planning directory
|
|
56
|
+
* @returns {string}
|
|
57
|
+
*/
|
|
58
|
+
function getLogsDir(planningDir) {
|
|
59
|
+
return path.join(planningDir, 'logs');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Read all JSONL files matching a prefix from a logs directory.
|
|
64
|
+
* E.g., readJsonlFiles(logsDir, 'hooks') reads all hooks-*.jsonl files.
|
|
65
|
+
* @param {string} logsDir - Path to logs directory
|
|
66
|
+
* @param {string} prefix - File prefix (e.g. 'hooks', 'events')
|
|
67
|
+
* @returns {object[]} Array of parsed JSON entries
|
|
68
|
+
*/
|
|
69
|
+
function readJsonlFiles(logsDir, prefix) {
|
|
70
|
+
const entries = [];
|
|
71
|
+
if (!fs.existsSync(logsDir)) return entries;
|
|
72
|
+
|
|
73
|
+
let files;
|
|
74
|
+
try {
|
|
75
|
+
files = fs.readdirSync(logsDir);
|
|
76
|
+
} catch (_e) {
|
|
77
|
+
return entries;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const pattern = new RegExp(`^${prefix}-.*\\.jsonl$`);
|
|
81
|
+
for (const file of files) {
|
|
82
|
+
if (!pattern.test(file)) continue;
|
|
83
|
+
try {
|
|
84
|
+
const content = fs.readFileSync(path.join(logsDir, file), 'utf8');
|
|
85
|
+
const lines = content.split('\n');
|
|
86
|
+
for (const line of lines) {
|
|
87
|
+
const trimmed = line.trim();
|
|
88
|
+
if (!trimmed) continue;
|
|
89
|
+
try {
|
|
90
|
+
entries.push(JSON.parse(trimmed));
|
|
91
|
+
} catch (_e) {
|
|
92
|
+
// Skip malformed lines
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
} catch (_e) {
|
|
96
|
+
// Skip unreadable files
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return entries;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// Shared: test-source entry filter
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Return true if the entry appears to originate from a test run.
|
|
109
|
+
* Test entries have a "cat" field that is a temp directory path
|
|
110
|
+
* (e.g. C:\Users\...\Temp\... or /tmp/...).
|
|
111
|
+
* @param {object} entry - Parsed JSON log entry
|
|
112
|
+
* @returns {boolean}
|
|
113
|
+
*/
|
|
114
|
+
function isTestSourced(entry) {
|
|
115
|
+
const cat = entry.cat || '';
|
|
116
|
+
if (typeof cat !== 'string') return false;
|
|
117
|
+
return /[/\\]temp[/\\]|[/\\]tmp[/\\]/i.test(cat);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
// EF-01: Tool Failure Rate Analysis
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Analyze tool failure rates from event logs only (single source of truth).
|
|
126
|
+
* Groups failures by tool type, calculates rates, caps at 100%.
|
|
127
|
+
* Filters out test-sourced entries before analysis.
|
|
128
|
+
*
|
|
129
|
+
* @param {string} planningDir - Path to .planning directory
|
|
130
|
+
* @param {object} config - Config object (may have audit.thresholds.tool_failure_rate_warn)
|
|
131
|
+
* @returns {{ dimension: string, status: string, message: string, evidence: string[] }}
|
|
132
|
+
*/
|
|
133
|
+
function checkToolFailureRate(planningDir, config) {
|
|
134
|
+
const logsDir = getLogsDir(planningDir);
|
|
135
|
+
const threshold = config?.audit?.thresholds?.tool_failure_rate_warn ?? 0.10;
|
|
136
|
+
|
|
137
|
+
// Use ONLY event logs as the single source of truth (no hook log merging)
|
|
138
|
+
const eventEntries = readJsonlFiles(logsDir, 'events')
|
|
139
|
+
.filter(e => !isTestSourced(e));
|
|
140
|
+
|
|
141
|
+
// Only count PostToolUseFailure events as real tool failures —
|
|
142
|
+
// PreToolUse blocks are intentional enforcement, not tool execution failures
|
|
143
|
+
const eventFailures = eventEntries.filter(
|
|
144
|
+
e => e.cat === 'tool' && e.event === 'failure' && e.type !== 'PreToolUse'
|
|
145
|
+
);
|
|
146
|
+
const totalToolEvents = eventEntries.filter(e => e.cat === 'tool');
|
|
147
|
+
|
|
148
|
+
// Count failures per tool
|
|
149
|
+
const failuresByTool = {};
|
|
150
|
+
for (const entry of eventFailures) {
|
|
151
|
+
const tool = entry.tool || entry.data?.tool || 'unknown';
|
|
152
|
+
failuresByTool[tool] = (failuresByTool[tool] || 0) + 1;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Count total calls per tool
|
|
156
|
+
const totalByTool = {};
|
|
157
|
+
for (const entry of totalToolEvents) {
|
|
158
|
+
const tool = entry.tool || entry.data?.tool || 'unknown';
|
|
159
|
+
totalByTool[tool] = (totalByTool[tool] || 0) + 1;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Separately tally PreToolUse enforcement blocks (informational only)
|
|
163
|
+
const hookEntries = readJsonlFiles(logsDir, 'hooks')
|
|
164
|
+
.filter(e => !isTestSourced(e));
|
|
165
|
+
const enforcementBlocks = hookEntries.filter(
|
|
166
|
+
e => (e.action === 'block' || e.decision === 'block')
|
|
167
|
+
);
|
|
168
|
+
const enforcementByTool = {};
|
|
169
|
+
for (const entry of enforcementBlocks) {
|
|
170
|
+
const tool = entry.details?.tool || entry.data?.tool || entry.tool || 'unknown';
|
|
171
|
+
enforcementByTool[tool] = (enforcementByTool[tool] || 0) + 1;
|
|
172
|
+
}
|
|
173
|
+
const totalEnforcement = Object.values(enforcementByTool).reduce((a, b) => a + b, 0);
|
|
174
|
+
|
|
175
|
+
const totalFailures = Object.values(failuresByTool).reduce((a, b) => a + b, 0);
|
|
176
|
+
if (totalFailures === 0 && totalEnforcement === 0) {
|
|
177
|
+
return result('EF-01', 'pass', 'No tool failures detected in logs', []);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Build evidence and determine status
|
|
181
|
+
const evidence = [];
|
|
182
|
+
let anyAboveThreshold = false;
|
|
183
|
+
|
|
184
|
+
// Detect if we only have failure events (no success events logged)
|
|
185
|
+
const onlyFailureEventsLogged = totalToolEvents.length === eventFailures.length;
|
|
186
|
+
if (onlyFailureEventsLogged && totalToolEvents.length > 0) {
|
|
187
|
+
evidence.push('Tool success events not logged — failure rates are raw counts, not percentages');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
for (const [tool, rawCount] of Object.entries(failuresByTool)) {
|
|
191
|
+
const total = totalByTool[tool];
|
|
192
|
+
if (total && total > 0 && total > rawCount) {
|
|
193
|
+
// We have more total events than failures — rate is meaningful
|
|
194
|
+
const count = Math.min(rawCount, total);
|
|
195
|
+
const rate = count / total;
|
|
196
|
+
const pct = (rate * 100).toFixed(1);
|
|
197
|
+
evidence.push(`${tool}: ${count} failures (${pct}% rate of ${total} calls)`);
|
|
198
|
+
if (rate > threshold) anyAboveThreshold = true;
|
|
199
|
+
} else if (total && total === rawCount) {
|
|
200
|
+
// Total equals failures — only failure events tracked, rate not calculable
|
|
201
|
+
evidence.push(`${tool}: ${rawCount} failure(s) (total calls not tracked — rate not calculable)`);
|
|
202
|
+
// Do NOT flag as above-threshold since rate is unknown
|
|
203
|
+
} else {
|
|
204
|
+
evidence.push(`${tool}: ${rawCount} failures (total calls unknown)`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Report enforcement blocks as informational (not counted toward failure rate)
|
|
209
|
+
if (totalEnforcement > 0) {
|
|
210
|
+
const blockDetails = Object.entries(enforcementByTool)
|
|
211
|
+
.map(([tool, count]) => `${tool}: ${count}`)
|
|
212
|
+
.join(', ');
|
|
213
|
+
evidence.push(`Enforcement blocks (informational, not failures): ${blockDetails}`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (totalFailures === 0) {
|
|
217
|
+
return result('EF-01', 'pass',
|
|
218
|
+
`No real tool failures (${totalEnforcement} enforcement block(s) excluded)`,
|
|
219
|
+
evidence);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const status = anyAboveThreshold ? 'fail' : 'warn';
|
|
223
|
+
const message = anyAboveThreshold
|
|
224
|
+
? `Tool failure rate exceeds ${(threshold * 100).toFixed(0)}% threshold for some tools`
|
|
225
|
+
: `${totalFailures} tool failure(s) detected but within threshold`;
|
|
226
|
+
|
|
227
|
+
return result('EF-01', status, message, evidence);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// ---------------------------------------------------------------------------
|
|
231
|
+
// EF-02: Agent Failure/Timeout Detection
|
|
232
|
+
// ---------------------------------------------------------------------------
|
|
233
|
+
|
|
234
|
+
/** Known completion markers that agents should output. */
|
|
235
|
+
const COMPLETION_MARKERS = [
|
|
236
|
+
'## EXECUTION COMPLETE',
|
|
237
|
+
'## PLANNING COMPLETE',
|
|
238
|
+
'## VERIFICATION COMPLETE',
|
|
239
|
+
'## PLAN COMPLETE',
|
|
240
|
+
'## PLAN FAILED',
|
|
241
|
+
'## CHECKPOINT:',
|
|
242
|
+
];
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Detect agents that failed, timed out, or are missing completion markers.
|
|
246
|
+
*
|
|
247
|
+
* Examines event logs for agent spawn/complete lifecycle events, and hook logs
|
|
248
|
+
* for check-subagent-output warnings about missing artifacts.
|
|
249
|
+
*
|
|
250
|
+
* @param {string} planningDir - Path to .planning directory
|
|
251
|
+
* @param {object} config - Config object (may have audit.thresholds.agent_timeout_ms)
|
|
252
|
+
* @returns {{ dimension: string, status: string, message: string, evidence: string[] }}
|
|
253
|
+
*/
|
|
254
|
+
function checkAgentFailureTimeout(planningDir, config) {
|
|
255
|
+
const logsDir = getLogsDir(planningDir);
|
|
256
|
+
const timeoutMs = config?.audit?.thresholds?.agent_timeout_ms ?? 600000;
|
|
257
|
+
|
|
258
|
+
const eventEntries = readJsonlFiles(logsDir, 'events');
|
|
259
|
+
const hookEntries = readJsonlFiles(logsDir, 'hooks');
|
|
260
|
+
|
|
261
|
+
// Collect agent spawn and complete events from event logs
|
|
262
|
+
const spawns = eventEntries.filter(e => e.cat === 'agent' && e.event === 'spawn');
|
|
263
|
+
const completions = eventEntries.filter(e => e.cat === 'agent' && e.event === 'complete');
|
|
264
|
+
|
|
265
|
+
// Also collect from hook logs (log-subagent entries)
|
|
266
|
+
const hookSpawns = hookEntries.filter(e => e.hook === 'log-subagent' && e.action === 'spawned');
|
|
267
|
+
const hookCompletions = hookEntries.filter(e => e.hook === 'log-subagent' && e.action === 'completed');
|
|
268
|
+
|
|
269
|
+
// Build a map of agent_id -> spawn info
|
|
270
|
+
const agentMap = new Map();
|
|
271
|
+
for (const s of [...spawns, ...hookSpawns]) {
|
|
272
|
+
const id = s.agent_id || s.details?.agent_id || null;
|
|
273
|
+
if (!id) continue;
|
|
274
|
+
if (agentMap.has(id)) continue; // first seen wins
|
|
275
|
+
agentMap.set(id, {
|
|
276
|
+
type: s.agent_type || s.details?.agent_type || 'unknown',
|
|
277
|
+
ts: s.ts || null,
|
|
278
|
+
completed: false,
|
|
279
|
+
duration_ms: null,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Mark completions
|
|
284
|
+
for (const c of [...completions, ...hookCompletions]) {
|
|
285
|
+
const id = c.agent_id || c.details?.agent_id || null;
|
|
286
|
+
if (!id || !agentMap.has(id)) continue;
|
|
287
|
+
const agent = agentMap.get(id);
|
|
288
|
+
agent.completed = true;
|
|
289
|
+
agent.duration_ms = c.duration_ms || c.details?.duration_ms || null;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Check for check-subagent-output warnings (agents that completed without expected artifacts)
|
|
293
|
+
const subagentWarnings = hookEntries.filter(
|
|
294
|
+
e => e.hook === 'check-subagent-output' && e.action === 'warned'
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
const evidence = [];
|
|
298
|
+
let failCount = 0;
|
|
299
|
+
let warnCount = 0;
|
|
300
|
+
|
|
301
|
+
// Check each agent for failure conditions
|
|
302
|
+
for (const [id, agent] of agentMap) {
|
|
303
|
+
const label = `${agent.type} agent (${id})`;
|
|
304
|
+
const timeStr = agent.ts ? ` (started ${agent.ts.substring(11, 16)})` : '';
|
|
305
|
+
|
|
306
|
+
if (!agent.completed) {
|
|
307
|
+
evidence.push(`${label}: no completion event found${timeStr}`);
|
|
308
|
+
failCount++;
|
|
309
|
+
} else if (agent.duration_ms === null || agent.duration_ms === undefined) {
|
|
310
|
+
evidence.push(`${label}: completed with null duration${timeStr}`);
|
|
311
|
+
warnCount++;
|
|
312
|
+
} else if (agent.duration_ms > timeoutMs) {
|
|
313
|
+
const mins = (agent.duration_ms / 60000).toFixed(1);
|
|
314
|
+
evidence.push(`${label}: duration ${mins}min exceeds ${(timeoutMs / 60000).toFixed(0)}min timeout${timeStr}`);
|
|
315
|
+
failCount++;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Add warnings from check-subagent-output
|
|
320
|
+
for (const w of subagentWarnings) {
|
|
321
|
+
const desc = w.details?.description || w.details?.message || 'missing expected artifact';
|
|
322
|
+
const agentType = w.details?.agent_type || 'unknown';
|
|
323
|
+
evidence.push(`${agentType} agent: ${desc}`);
|
|
324
|
+
warnCount++;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (failCount === 0 && warnCount === 0) {
|
|
328
|
+
return result('EF-02', 'pass', 'All agents completed successfully', []);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const status = failCount > 0 ? 'fail' : 'warn';
|
|
332
|
+
const message = failCount > 0
|
|
333
|
+
? `${failCount} agent(s) failed or timed out`
|
|
334
|
+
: `${warnCount} agent warning(s) detected but all completed`;
|
|
335
|
+
|
|
336
|
+
return result('EF-02', status, message, evidence);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// ---------------------------------------------------------------------------
|
|
340
|
+
// EF-05: Retry/Repetition Pattern Detection
|
|
341
|
+
// ---------------------------------------------------------------------------
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Detect consecutive same-tool call patterns that suggest retries or repetition.
|
|
345
|
+
*
|
|
346
|
+
* Scans event logs for tool-use events sorted by timestamp. A "repetition" is
|
|
347
|
+
* when the same tool appears N+ times consecutively. If failures occurred in
|
|
348
|
+
* the sequence, it's classified as a "retry" pattern.
|
|
349
|
+
*
|
|
350
|
+
* @param {string} planningDir - Path to .planning directory
|
|
351
|
+
* @param {object} config - Config object (may have audit.thresholds.retry_pattern_min_count)
|
|
352
|
+
* @returns {{ dimension: string, status: string, message: string, evidence: string[] }}
|
|
353
|
+
*/
|
|
354
|
+
function checkRetryRepetitionPattern(planningDir, config) {
|
|
355
|
+
const logsDir = getLogsDir(planningDir);
|
|
356
|
+
const minCount = config?.audit?.thresholds?.retry_pattern_min_count ?? 3;
|
|
357
|
+
|
|
358
|
+
const eventEntries = readJsonlFiles(logsDir, 'events')
|
|
359
|
+
.filter(e => !isTestSourced(e));
|
|
360
|
+
|
|
361
|
+
// Filter for tool-use events and sort by timestamp
|
|
362
|
+
const toolEvents = eventEntries
|
|
363
|
+
.filter(e => e.cat === 'tool')
|
|
364
|
+
.sort((a, b) => (a.ts || '').localeCompare(b.ts || ''));
|
|
365
|
+
|
|
366
|
+
if (toolEvents.length === 0) {
|
|
367
|
+
return result('EF-05', 'pass', 'No tool events found in logs', []);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Scan for consecutive runs of the same tool
|
|
371
|
+
const patterns = [];
|
|
372
|
+
let currentTool = null;
|
|
373
|
+
let runStart = 0;
|
|
374
|
+
let runCount = 0;
|
|
375
|
+
let runFailures = 0;
|
|
376
|
+
|
|
377
|
+
for (let i = 0; i <= toolEvents.length; i++) {
|
|
378
|
+
const entry = i < toolEvents.length ? toolEvents[i] : null;
|
|
379
|
+
const tool = entry ? (entry.tool || entry.data?.tool || 'unknown') : null;
|
|
380
|
+
|
|
381
|
+
if (tool === currentTool && i < toolEvents.length) {
|
|
382
|
+
runCount++;
|
|
383
|
+
if (entry.event === 'failure') runFailures++;
|
|
384
|
+
} else {
|
|
385
|
+
// End of a run — check if it meets the threshold
|
|
386
|
+
if (currentTool && runCount >= minCount) {
|
|
387
|
+
const startTs = toolEvents[runStart]?.ts || '';
|
|
388
|
+
const endTs = toolEvents[Math.min(runStart + runCount - 1, toolEvents.length - 1)]?.ts || '';
|
|
389
|
+
const startTime = startTs.substring(11, 16) || '??:??';
|
|
390
|
+
const endTime = endTs.substring(11, 16) || '??:??';
|
|
391
|
+
const patternType = runFailures > 0 ? 'retry' : 'repetition';
|
|
392
|
+
|
|
393
|
+
patterns.push({
|
|
394
|
+
tool: currentTool,
|
|
395
|
+
count: runCount,
|
|
396
|
+
failures: runFailures,
|
|
397
|
+
type: patternType,
|
|
398
|
+
timeWindow: `${startTime}-${endTime}`,
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Start new run
|
|
403
|
+
currentTool = tool;
|
|
404
|
+
runStart = i;
|
|
405
|
+
runCount = 1;
|
|
406
|
+
runFailures = (entry && entry.event === 'failure') ? 1 : 0;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (patterns.length === 0) {
|
|
411
|
+
return result('EF-05', 'pass', `No consecutive same-tool patterns (${minCount}+) detected`, []);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const evidence = patterns.map(p => {
|
|
415
|
+
const failStr = p.failures > 0 ? ` (${p.failures} failures)` : '';
|
|
416
|
+
return `${p.tool} called ${p.count} times consecutively${failStr} -- ${p.type} pattern at ${p.timeWindow}`;
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
const hasRetries = patterns.some(p => p.type === 'retry');
|
|
420
|
+
const status = hasRetries ? 'warn' : 'pass';
|
|
421
|
+
const message = hasRetries
|
|
422
|
+
? `${patterns.filter(p => p.type === 'retry').length} retry pattern(s) detected`
|
|
423
|
+
: `${patterns.length} repetition pattern(s) detected (no failures, likely normal batch work)`;
|
|
424
|
+
|
|
425
|
+
return result('EF-05', status, message, evidence);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// ---------------------------------------------------------------------------
|
|
429
|
+
// EF-03: Hook False Positive Detection
|
|
430
|
+
// ---------------------------------------------------------------------------
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Analyze PreToolUse block decisions for potential false positives.
|
|
434
|
+
*
|
|
435
|
+
* A false positive is a block that was later overridden (same file allowed
|
|
436
|
+
* afterward), or a block on a file that belongs to the plan's files_modified
|
|
437
|
+
* list, or a block from check-skill-workflow / check-doc-sprawl on a
|
|
438
|
+
* legitimate PBR artifact path.
|
|
439
|
+
*
|
|
440
|
+
* @param {string} planningDir - Path to .planning directory
|
|
441
|
+
* @param {object} config - Config object
|
|
442
|
+
* @returns {{ dimension: string, status: string, message: string, evidence: string[] }}
|
|
443
|
+
*/
|
|
444
|
+
function checkHookFalsePositive(planningDir, config) {
|
|
445
|
+
const logsDir = getLogsDir(planningDir);
|
|
446
|
+
const hookEntries = readJsonlFiles(logsDir, 'hooks');
|
|
447
|
+
|
|
448
|
+
// Collect block and allow decisions from PreToolUse hooks
|
|
449
|
+
const blocks = [];
|
|
450
|
+
const allows = [];
|
|
451
|
+
|
|
452
|
+
for (const entry of hookEntries) {
|
|
453
|
+
const action = entry.action || entry.decision || '';
|
|
454
|
+
const file = entry.details?.file || entry.data?.file || entry.file || '';
|
|
455
|
+
const hook = entry.hook || '';
|
|
456
|
+
const reason = entry.reason || entry.details?.reason || '';
|
|
457
|
+
const ts = entry.ts || '';
|
|
458
|
+
|
|
459
|
+
if (action === 'block') {
|
|
460
|
+
blocks.push({ hook, file, reason, ts });
|
|
461
|
+
} else if (action === 'allow') {
|
|
462
|
+
allows.push({ file, ts });
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if (blocks.length === 0) {
|
|
467
|
+
return result('EF-03', 'pass', 'No hook blocks found in logs', []);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Build a set of allowed files for quick lookup
|
|
471
|
+
const allowedFiles = new Map();
|
|
472
|
+
for (const a of allows) {
|
|
473
|
+
if (!a.file) continue;
|
|
474
|
+
const existing = allowedFiles.get(a.file);
|
|
475
|
+
if (!existing || a.ts > existing) {
|
|
476
|
+
allowedFiles.set(a.file, a.ts);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Known PBR artifact path patterns that are legitimate write targets
|
|
481
|
+
const pbrArtifactPatterns = [
|
|
482
|
+
/\.planning\//,
|
|
483
|
+
/PLAN.*\.md$/i,
|
|
484
|
+
/SUMMARY.*\.md$/i,
|
|
485
|
+
/VERIFICATION.*\.md$/i,
|
|
486
|
+
/STATE\.md$/i,
|
|
487
|
+
/ROADMAP\.md$/i,
|
|
488
|
+
/CONTEXT\.md$/i,
|
|
489
|
+
];
|
|
490
|
+
|
|
491
|
+
const evidence = [];
|
|
492
|
+
|
|
493
|
+
for (const block of blocks) {
|
|
494
|
+
const reasons = [];
|
|
495
|
+
|
|
496
|
+
// Check if same file was allowed later (block was retried and succeeded)
|
|
497
|
+
if (block.file && allowedFiles.has(block.file)) {
|
|
498
|
+
const allowTs = allowedFiles.get(block.file);
|
|
499
|
+
if (allowTs > block.ts) {
|
|
500
|
+
reasons.push('allowed later');
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// Check if block was from check-skill-workflow or check-doc-sprawl on a PBR artifact
|
|
505
|
+
const isFalsePositiveHook = block.hook === 'check-skill-workflow' ||
|
|
506
|
+
block.hook === 'check-doc-sprawl' ||
|
|
507
|
+
block.hook === 'pre-write-dispatch';
|
|
508
|
+
if (isFalsePositiveHook && block.file) {
|
|
509
|
+
const normalized = block.file.replace(/\\/g, '/');
|
|
510
|
+
const isPbrArtifact = pbrArtifactPatterns.some(p => p.test(normalized));
|
|
511
|
+
if (isPbrArtifact) {
|
|
512
|
+
reasons.push('block on legitimate PBR artifact path');
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
if (reasons.length > 0) {
|
|
517
|
+
const fileStr = block.file ? ` on ${block.file}` : '';
|
|
518
|
+
const timeStr = block.ts ? ` at ${block.ts.substring(11, 16)}` : '';
|
|
519
|
+
evidence.push(
|
|
520
|
+
`${block.hook} blocked${fileStr}${timeStr} — possible false positive (${reasons.join(', ')})`
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
if (evidence.length === 0) {
|
|
526
|
+
return result('EF-03', 'pass', `${blocks.length} block(s) reviewed, no false positives detected`, []);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
return result('EF-03', 'warn',
|
|
530
|
+
`${evidence.length} potential false positive(s) in ${blocks.length} total blocks`,
|
|
531
|
+
evidence
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// ---------------------------------------------------------------------------
|
|
536
|
+
// EF-04: Hook False Negative Detection
|
|
537
|
+
// ---------------------------------------------------------------------------
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Detect bad actions that should have been blocked but weren't.
|
|
541
|
+
*
|
|
542
|
+
* Scans for: non-conventional commits that passed validation, writes to files
|
|
543
|
+
* outside files_modified, and destructive git commands that were allowed.
|
|
544
|
+
*
|
|
545
|
+
* @param {string} planningDir - Path to .planning directory
|
|
546
|
+
* @param {object} config - Config object
|
|
547
|
+
* @returns {{ dimension: string, status: string, message: string, evidence: string[] }}
|
|
548
|
+
*/
|
|
549
|
+
function checkHookFalseNegative(planningDir, config) {
|
|
550
|
+
const logsDir = getLogsDir(planningDir);
|
|
551
|
+
const hookEntries = readJsonlFiles(logsDir, 'hooks');
|
|
552
|
+
const eventEntries = readJsonlFiles(logsDir, 'events');
|
|
553
|
+
|
|
554
|
+
const evidence = [];
|
|
555
|
+
|
|
556
|
+
// 1. Check for commits with non-conventional format that passed validation
|
|
557
|
+
const conventionalPattern = /^(feat|fix|refactor|test|docs|chore|wip|revert|perf|ci|build)\(.+\):\s.+/;
|
|
558
|
+
const commitEvents = eventEntries.filter(
|
|
559
|
+
e => (e.event === 'commit-validated' && e.status === 'allow') ||
|
|
560
|
+
(e.cat === 'tool' && e.tool === 'Bash' && e.data?.command && /git commit/.test(e.data.command))
|
|
561
|
+
);
|
|
562
|
+
|
|
563
|
+
for (const ce of commitEvents) {
|
|
564
|
+
const msg = ce.data?.message || ce.details?.message || '';
|
|
565
|
+
if (msg && !conventionalPattern.test(msg)) {
|
|
566
|
+
evidence.push(
|
|
567
|
+
`Non-conventional commit passed validation: "${msg.substring(0, 60)}"`
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// 2. Check for destructive git commands that were allowed
|
|
573
|
+
const destructivePatterns = [
|
|
574
|
+
/git\s+push\s+--force/,
|
|
575
|
+
/git\s+push\s+-f\b/,
|
|
576
|
+
/git\s+reset\s+--hard/,
|
|
577
|
+
/git\s+checkout\s+--\s/,
|
|
578
|
+
/git\s+clean\s+-f/,
|
|
579
|
+
/git\s+branch\s+-D/,
|
|
580
|
+
];
|
|
581
|
+
|
|
582
|
+
const bashEvents = hookEntries.filter(
|
|
583
|
+
e => (e.hook === 'pre-bash-dispatch' || e.hook === 'check-dangerous-commands') &&
|
|
584
|
+
(e.action === 'allow' || e.decision === 'allow')
|
|
585
|
+
);
|
|
586
|
+
|
|
587
|
+
for (const be of bashEvents) {
|
|
588
|
+
const cmd = be.details?.cmd || be.data?.cmd || be.data?.command || '';
|
|
589
|
+
for (const dp of destructivePatterns) {
|
|
590
|
+
if (dp.test(cmd)) {
|
|
591
|
+
const timeStr = be.ts ? ` at ${be.ts.substring(11, 16)}` : '';
|
|
592
|
+
evidence.push(
|
|
593
|
+
`Destructive command allowed${timeStr}: "${cmd.substring(0, 80)}"`
|
|
594
|
+
);
|
|
595
|
+
break;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// 3. Check for writes outside files_modified that were allowed
|
|
601
|
+
// Look for allow decisions on writes, cross-reference with plan frontmatter if available
|
|
602
|
+
const writeAllows = hookEntries.filter(
|
|
603
|
+
e => e.hook === 'pre-write-dispatch' && (e.action === 'allow' || e.decision === 'allow')
|
|
604
|
+
);
|
|
605
|
+
|
|
606
|
+
// Try to read current plan's files_modified from any available PLAN file
|
|
607
|
+
let filesModified = null;
|
|
608
|
+
try {
|
|
609
|
+
const phasesDir = path.join(planningDir, 'phases');
|
|
610
|
+
if (fs.existsSync(phasesDir)) {
|
|
611
|
+
const phaseDirs = fs.readdirSync(phasesDir);
|
|
612
|
+
for (const pd of phaseDirs) {
|
|
613
|
+
const planFiles = fs.readdirSync(path.join(phasesDir, pd)).filter(f => /^PLAN.*\.md$/i.test(f));
|
|
614
|
+
for (const pf of planFiles) {
|
|
615
|
+
try {
|
|
616
|
+
const content = fs.readFileSync(path.join(phasesDir, pd, pf), 'utf8');
|
|
617
|
+
const fmMatch = content.match(/files_modified:\s*\n((?:\s+-\s+"[^"]+"\n?)+)/);
|
|
618
|
+
if (fmMatch) {
|
|
619
|
+
const files = fmMatch[1].match(/"([^"]+)"/g);
|
|
620
|
+
if (files) {
|
|
621
|
+
filesModified = files.map(f => f.replace(/"/g, ''));
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
} catch (_e) { /* skip unreadable */ }
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
} catch (_e) { /* no plan files available */ }
|
|
629
|
+
|
|
630
|
+
if (filesModified && filesModified.length > 0) {
|
|
631
|
+
for (const wa of writeAllows) {
|
|
632
|
+
const file = wa.details?.file || wa.data?.file || '';
|
|
633
|
+
if (!file) continue;
|
|
634
|
+
const normalized = file.replace(/\\/g, '/');
|
|
635
|
+
// Skip .planning writes — those are always allowed
|
|
636
|
+
if (normalized.includes('.planning/')) continue;
|
|
637
|
+
const isInPlan = filesModified.some(fm => normalized.endsWith(fm) || normalized.includes(fm));
|
|
638
|
+
if (!isInPlan) {
|
|
639
|
+
evidence.push(
|
|
640
|
+
`Write allowed outside files_modified: "${normalized}"`
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
if (evidence.length === 0) {
|
|
647
|
+
return result('EF-04', 'pass', 'No false negatives detected in hook enforcement', []);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
return result('EF-04', 'warn',
|
|
651
|
+
`${evidence.length} potential false negative(s) in hook enforcement`,
|
|
652
|
+
evidence
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// ---------------------------------------------------------------------------
|
|
657
|
+
// EF-06: Cross-Session Interference Detection
|
|
658
|
+
// ---------------------------------------------------------------------------
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* Detect cross-session interference: overlapping sessions, .active-skill
|
|
662
|
+
* claim races, and stale lock files.
|
|
663
|
+
*
|
|
664
|
+
* @param {string} planningDir - Path to .planning directory
|
|
665
|
+
* @param {object} config - Config object
|
|
666
|
+
* @returns {{ dimension: string, status: string, message: string, evidence: string[] }}
|
|
667
|
+
*/
|
|
668
|
+
function checkCrossSessionInterference(planningDir, config) {
|
|
669
|
+
const logsDir = getLogsDir(planningDir);
|
|
670
|
+
const hookEntries = readJsonlFiles(logsDir, 'hooks');
|
|
671
|
+
const eventEntries = readJsonlFiles(logsDir, 'events');
|
|
672
|
+
|
|
673
|
+
const evidence = [];
|
|
674
|
+
let hasFail = false;
|
|
675
|
+
|
|
676
|
+
// 1. Extract session IDs and detect overlapping sessions
|
|
677
|
+
const sessionTimestamps = new Map(); // sessionId -> [timestamps]
|
|
678
|
+
|
|
679
|
+
for (const entry of [...hookEntries, ...eventEntries]) {
|
|
680
|
+
const sessionId = entry.session_id || entry.sessionId || entry.details?.session_id || null;
|
|
681
|
+
const ts = entry.ts || '';
|
|
682
|
+
if (!sessionId || !ts) continue;
|
|
683
|
+
|
|
684
|
+
if (!sessionTimestamps.has(sessionId)) {
|
|
685
|
+
sessionTimestamps.set(sessionId, []);
|
|
686
|
+
}
|
|
687
|
+
sessionTimestamps.get(sessionId).push(ts);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// Check for overlapping sessions: two sessions with interleaved timestamps
|
|
691
|
+
const sessions = Array.from(sessionTimestamps.entries());
|
|
692
|
+
for (let i = 0; i < sessions.length; i++) {
|
|
693
|
+
for (let j = i + 1; j < sessions.length; j++) {
|
|
694
|
+
const [idA, tsA] = sessions[i];
|
|
695
|
+
const [idB, tsB] = sessions[j];
|
|
696
|
+
if (tsA.length === 0 || tsB.length === 0) continue;
|
|
697
|
+
|
|
698
|
+
const minA = tsA.reduce((a, b) => a < b ? a : b);
|
|
699
|
+
const maxA = tsA.reduce((a, b) => a > b ? a : b);
|
|
700
|
+
const minB = tsB.reduce((a, b) => a < b ? a : b);
|
|
701
|
+
const maxB = tsB.reduce((a, b) => a > b ? a : b);
|
|
702
|
+
|
|
703
|
+
// Overlap: session A's range intersects session B's range
|
|
704
|
+
if (minA <= maxB && minB <= maxA) {
|
|
705
|
+
const overlapStart = minA > minB ? minA : minB;
|
|
706
|
+
evidence.push(
|
|
707
|
+
`Two sessions active simultaneously at ${overlapStart.substring(11, 16)} (session ${idA.substring(0, 8)} and ${idB.substring(0, 8)})`
|
|
708
|
+
);
|
|
709
|
+
hasFail = true;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// 2. Look for .active-skill conflict entries in hook logs
|
|
715
|
+
const activeSkillConflicts = hookEntries.filter(e => {
|
|
716
|
+
const details = JSON.stringify(e.details || e.data || {}).toLowerCase();
|
|
717
|
+
return details.includes('active-skill') || details.includes('active_skill');
|
|
718
|
+
}).filter(e => {
|
|
719
|
+
const action = e.action || e.decision || '';
|
|
720
|
+
return action === 'block' || action === 'warn' || action === 'warned';
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
for (const conflict of activeSkillConflicts) {
|
|
724
|
+
const hook = conflict.hook || 'unknown';
|
|
725
|
+
const action = conflict.action || conflict.decision || '';
|
|
726
|
+
const ts = conflict.ts ? ` at ${conflict.ts.substring(11, 16)}` : '';
|
|
727
|
+
evidence.push(
|
|
728
|
+
`${hook} ${action} on active-skill conflict${ts}`
|
|
729
|
+
);
|
|
730
|
+
if (action === 'block') hasFail = true;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// 3. Check if stale session files exist (leftover from crashed session)
|
|
734
|
+
const staleSessionFiles = [
|
|
735
|
+
{ file: '.active-skill', desc: '.active-skill' },
|
|
736
|
+
{ file: '.session.json', desc: '.session.json (consolidated session state)' },
|
|
737
|
+
];
|
|
738
|
+
|
|
739
|
+
for (const sf of staleSessionFiles) {
|
|
740
|
+
const filePath = path.join(planningDir, sf.file);
|
|
741
|
+
try {
|
|
742
|
+
if (fs.existsSync(filePath)) {
|
|
743
|
+
const stat = fs.statSync(filePath);
|
|
744
|
+
const ageMs = Date.now() - stat.mtimeMs;
|
|
745
|
+
const ageMin = Math.round(ageMs / 60000);
|
|
746
|
+
evidence.push(
|
|
747
|
+
`${sf.desc} exists (${ageMin}min old) — may be stale from crashed session`
|
|
748
|
+
);
|
|
749
|
+
}
|
|
750
|
+
} catch (_e) { /* best-effort */ }
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
if (evidence.length === 0) {
|
|
754
|
+
return result('EF-06', 'pass', 'No cross-session interference detected', []);
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
const status = hasFail ? 'fail' : 'warn';
|
|
758
|
+
const message = hasFail
|
|
759
|
+
? `Cross-session interference detected: ${evidence.length} issue(s)`
|
|
760
|
+
: `${evidence.length} potential cross-session issue(s) found`;
|
|
761
|
+
|
|
762
|
+
return result('EF-06', status, message, evidence);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// ---------------------------------------------------------------------------
|
|
766
|
+
// EF-07: Session Cleanup Verification
|
|
767
|
+
// ---------------------------------------------------------------------------
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* Verify that session-cleanup.js fires at SessionEnd and removes stale files.
|
|
771
|
+
*
|
|
772
|
+
* Checks for: cleanup log entries matching SessionEnd events, orphaned
|
|
773
|
+
* artifacts (.active-skill, .active-operation, .auto-next, .context-tracker,
|
|
774
|
+
* stale .checkpoint-manifest.json), and old hook log files.
|
|
775
|
+
*
|
|
776
|
+
* @param {string} planningDir - Path to .planning directory
|
|
777
|
+
* @param {object} config - Config object
|
|
778
|
+
* @returns {{ dimension: string, status: string, message: string, evidence: string[] }}
|
|
779
|
+
*/
|
|
780
|
+
function checkSessionCleanupVerification(planningDir, config) {
|
|
781
|
+
const logsDir = getLogsDir(planningDir);
|
|
782
|
+
const hookEntries = readJsonlFiles(logsDir, 'hooks');
|
|
783
|
+
const eventEntries = readJsonlFiles(logsDir, 'events');
|
|
784
|
+
|
|
785
|
+
const evidence = [];
|
|
786
|
+
|
|
787
|
+
// 1. Find SessionEnd events and verify matching cleanup entries
|
|
788
|
+
const sessionEndEvents = eventEntries.filter(
|
|
789
|
+
e => e.event === 'SessionEnd' || (e.cat === 'lifecycle' && e.event === 'session-end')
|
|
790
|
+
);
|
|
791
|
+
|
|
792
|
+
const cleanupEntries = hookEntries.filter(
|
|
793
|
+
e => e.hook === 'session-cleanup' && (e.action === 'cleaned' || e.decision === 'cleaned' || e.action === 'nothing')
|
|
794
|
+
);
|
|
795
|
+
|
|
796
|
+
// For each SessionEnd, check if there's a cleanup entry near the same time
|
|
797
|
+
for (const se of sessionEndEvents) {
|
|
798
|
+
const seTs = se.ts || '';
|
|
799
|
+
if (!seTs) continue;
|
|
800
|
+
|
|
801
|
+
const hasMatchingCleanup = cleanupEntries.some(ce => {
|
|
802
|
+
const ceTs = ce.ts || '';
|
|
803
|
+
if (!ceTs) return false;
|
|
804
|
+
// Within 60 seconds of each other
|
|
805
|
+
try {
|
|
806
|
+
const diff = Math.abs(new Date(ceTs).getTime() - new Date(seTs).getTime());
|
|
807
|
+
return diff < 60000;
|
|
808
|
+
} catch (_e) {
|
|
809
|
+
return false;
|
|
810
|
+
}
|
|
811
|
+
});
|
|
812
|
+
|
|
813
|
+
if (!hasMatchingCleanup) {
|
|
814
|
+
evidence.push(
|
|
815
|
+
`SessionEnd at ${seTs.substring(11, 16)} has no matching cleanup entry`
|
|
816
|
+
);
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
// 2. Check for orphaned artifacts that cleanup should have removed
|
|
821
|
+
const orphanChecks = [
|
|
822
|
+
{ file: '.active-skill', desc: '.active-skill (should not exist between sessions)' },
|
|
823
|
+
{ file: '.active-operation', desc: '.active-operation (should not exist between sessions)' },
|
|
824
|
+
{ file: '.active-plan', desc: '.active-plan (should not exist between sessions)' },
|
|
825
|
+
{ file: '.session.json', desc: '.session.json (consolidated session state)' },
|
|
826
|
+
{ file: '.auto-next', desc: '.auto-next (transient signal file)' },
|
|
827
|
+
{ file: '.context-tracker', desc: '.context-tracker (session-scoped)' },
|
|
828
|
+
];
|
|
829
|
+
|
|
830
|
+
for (const check of orphanChecks) {
|
|
831
|
+
const filePath = path.join(planningDir, check.file);
|
|
832
|
+
try {
|
|
833
|
+
if (fs.existsSync(filePath)) {
|
|
834
|
+
evidence.push(`${check.desc} exists — cleanup may have missed it`);
|
|
835
|
+
}
|
|
836
|
+
} catch (_e) { /* best-effort */ }
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
// 3. Check for stale .checkpoint-manifest.json (>24 hours old)
|
|
840
|
+
const STALE_MS = 24 * 60 * 60 * 1000;
|
|
841
|
+
try {
|
|
842
|
+
const phasesDir = path.join(planningDir, 'phases');
|
|
843
|
+
if (fs.existsSync(phasesDir)) {
|
|
844
|
+
const dirs = fs.readdirSync(phasesDir);
|
|
845
|
+
for (const dir of dirs) {
|
|
846
|
+
const manifestPath = path.join(phasesDir, dir, '.checkpoint-manifest.json');
|
|
847
|
+
try {
|
|
848
|
+
if (fs.existsSync(manifestPath)) {
|
|
849
|
+
const stat = fs.statSync(manifestPath);
|
|
850
|
+
const ageMs = Date.now() - stat.mtimeMs;
|
|
851
|
+
if (ageMs > STALE_MS) {
|
|
852
|
+
const ageHrs = Math.round(ageMs / 3600000);
|
|
853
|
+
evidence.push(
|
|
854
|
+
`.checkpoint-manifest.json in ${dir} is ${ageHrs}h old — cleanup should have removed it`
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
} catch (_e) { /* skip */ }
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
} catch (_e) { /* best-effort */ }
|
|
862
|
+
|
|
863
|
+
// 4. Check hook log rotation — if hooks-*.jsonl has files older than 30 days
|
|
864
|
+
const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000;
|
|
865
|
+
try {
|
|
866
|
+
if (fs.existsSync(logsDir)) {
|
|
867
|
+
const logFiles = fs.readdirSync(logsDir).filter(f => /^hooks-.*\.jsonl/.test(f));
|
|
868
|
+
for (const lf of logFiles) {
|
|
869
|
+
const stat = fs.statSync(path.join(logsDir, lf));
|
|
870
|
+
const ageMs = Date.now() - stat.mtimeMs;
|
|
871
|
+
if (ageMs > THIRTY_DAYS_MS) {
|
|
872
|
+
const ageDays = Math.round(ageMs / (24 * 60 * 60 * 1000));
|
|
873
|
+
evidence.push(
|
|
874
|
+
`${lf} is ${ageDays} days old — log rotation may not be running`
|
|
875
|
+
);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
} catch (_e) { /* best-effort */ }
|
|
880
|
+
|
|
881
|
+
if (evidence.length === 0) {
|
|
882
|
+
const cleanupCount = cleanupEntries.length;
|
|
883
|
+
return result('EF-07', 'pass',
|
|
884
|
+
`Session cleanup verified (${cleanupCount} cleanup event(s) found)`, []);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
return result('EF-07', 'warn',
|
|
888
|
+
`${evidence.length} session cleanup gap(s) found`,
|
|
889
|
+
evidence
|
|
890
|
+
);
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
// ---------------------------------------------------------------------------
|
|
894
|
+
// EF-08: Incident Pattern Analysis
|
|
895
|
+
// ---------------------------------------------------------------------------
|
|
896
|
+
|
|
897
|
+
/**
|
|
898
|
+
* Analyze incident journal for recurring patterns, high-severity clusters,
|
|
899
|
+
* and auto-fix effectiveness.
|
|
900
|
+
*
|
|
901
|
+
* @param {string} planningDir - Path to .planning directory
|
|
902
|
+
* @param {object} _config - Config object (unused, kept for signature consistency)
|
|
903
|
+
* @returns {{ dimension: string, status: string, message: string, evidence: string[] }}
|
|
904
|
+
*/
|
|
905
|
+
function checkIncidentPatterns(planningDir, _config) {
|
|
906
|
+
let _incidents;
|
|
907
|
+
try {
|
|
908
|
+
_incidents = require('../lib/incidents');
|
|
909
|
+
} catch (_e) {
|
|
910
|
+
return result('EF-08', 'pass', 'Incident journal module not available — skipping', []);
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
let summaryData;
|
|
914
|
+
try {
|
|
915
|
+
summaryData = _incidents.summary({ planningDir });
|
|
916
|
+
} catch (_e) {
|
|
917
|
+
return result('EF-08', 'pass', 'Could not read incident summary — skipping', []);
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
const evidence = [];
|
|
921
|
+
|
|
922
|
+
// Total count
|
|
923
|
+
evidence.push(`Total incidents: ${summaryData.total}`);
|
|
924
|
+
|
|
925
|
+
// Top 3 types by frequency
|
|
926
|
+
const typeEntries = Object.entries(summaryData.by_type || {})
|
|
927
|
+
.sort((a, b) => b[1] - a[1])
|
|
928
|
+
.slice(0, 3);
|
|
929
|
+
if (typeEntries.length > 0) {
|
|
930
|
+
evidence.push(`Top types: ${typeEntries.map(([t, c]) => `${t}(${c})`).join(', ')}`);
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
// Auto-fix rate
|
|
934
|
+
let autoFixCount = 0;
|
|
935
|
+
try {
|
|
936
|
+
const all = _incidents.list({ planningDir, limit: Infinity, reverse: false });
|
|
937
|
+
autoFixCount = all.filter(e => e.auto_fixed).length;
|
|
938
|
+
const rate = summaryData.total > 0
|
|
939
|
+
? ((autoFixCount / summaryData.total) * 100).toFixed(1)
|
|
940
|
+
: '0.0';
|
|
941
|
+
evidence.push(`Auto-fix rate: ${autoFixCount}/${summaryData.total} (${rate}%)`);
|
|
942
|
+
} catch (_e) {
|
|
943
|
+
// best-effort
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
// Query error-severity incidents
|
|
947
|
+
let errorIncidents = [];
|
|
948
|
+
try {
|
|
949
|
+
errorIncidents = _incidents.query({ severity: 'error' }, { planningDir, limit: Infinity });
|
|
950
|
+
} catch (_e) {
|
|
951
|
+
// best-effort
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
const errorCount = errorIncidents.length;
|
|
955
|
+
|
|
956
|
+
// Determine status
|
|
957
|
+
if (summaryData.total < 50 && errorCount === 0) {
|
|
958
|
+
return result('EF-08', 'pass',
|
|
959
|
+
`Incident journal healthy: ${summaryData.total} total, 0 errors`, evidence);
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
if (errorCount >= 10) {
|
|
963
|
+
evidence.push(`Error-severity incidents: ${errorCount} (threshold: 10)`);
|
|
964
|
+
return result('EF-08', 'fail',
|
|
965
|
+
`High error-severity incident count: ${errorCount}`, evidence);
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
// warn: total > 50 or error incidents exist but < 10
|
|
969
|
+
if (errorCount > 0) {
|
|
970
|
+
evidence.push(`Error-severity incidents: ${errorCount}`);
|
|
971
|
+
}
|
|
972
|
+
return result('EF-08', 'warn',
|
|
973
|
+
`Incident journal needs attention: ${summaryData.total} total, ${errorCount} errors`, evidence);
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
// ---------------------------------------------------------------------------
|
|
977
|
+
// Exports
|
|
978
|
+
// ---------------------------------------------------------------------------
|
|
979
|
+
|
|
980
|
+
module.exports = {
|
|
981
|
+
checkToolFailureRate,
|
|
982
|
+
checkAgentFailureTimeout,
|
|
983
|
+
checkRetryRepetitionPattern,
|
|
984
|
+
checkHookFalsePositive,
|
|
985
|
+
checkHookFalseNegative,
|
|
986
|
+
checkCrossSessionInterference,
|
|
987
|
+
checkSessionCleanupVerification,
|
|
988
|
+
checkIncidentPatterns,
|
|
989
|
+
};
|