peaks-loop 4.0.54 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/README-en.md +16 -1
- package/README.md +15 -1
- package/config/eslint/.peaks-rules.cjs +158 -13
- package/config/eslint/tsconfig.lint.json +47 -0
- package/dist/cli/cli-envelope.d.ts +114 -0
- package/dist/cli/cli-envelope.js +85 -0
- package/dist/cli/cli-helpers.d.ts +1 -1
- package/dist/cli/cli-helpers.js +8 -4
- package/dist/cli/commands/_cli-error-envelope.js +1 -1
- package/dist/cli/commands/_register.js +85 -38
- package/dist/cli/commands/_super.js +40 -7
- package/dist/cli/commands/adapter-commands-s2a.js +8 -7
- package/dist/cli/commands/adapter-commands.d.ts +2 -2
- package/dist/cli/commands/adapter-commands.js +7 -8
- package/dist/cli/commands/api-diff-commands.js +5 -4
- package/dist/cli/commands/asset-commands.d.ts +2 -2
- package/dist/cli/commands/asset-commands.js +135 -132
- package/dist/cli/commands/audit-commands.d.ts +1 -1
- package/dist/cli/commands/audit-commands.js +75 -32
- package/dist/cli/commands/autonomous-swarm-commands.d.ts +1 -1
- package/dist/cli/commands/autonomous-swarm-commands.js +14 -8
- package/dist/cli/commands/baseline-commands.js +34 -9
- package/dist/cli/commands/bee-commands.d.ts +1 -1
- package/dist/cli/commands/bee-commands.js +18 -21
- package/dist/cli/commands/best-practice-scan-command.js +6 -34
- package/dist/cli/commands/best-practice-scan-helpers.d.ts +12 -0
- package/dist/cli/commands/best-practice-scan-helpers.js +37 -0
- package/dist/cli/commands/capability-commands.d.ts +1 -1
- package/dist/cli/commands/capability-commands.js +14 -4
- package/dist/cli/commands/capability-worker-config-sc-commands.d.ts +1 -1
- package/dist/cli/commands/changeset-commands.js +21 -18
- package/dist/cli/commands/code-gate-command.js +9 -2
- package/dist/cli/commands/code-job-shape-commands-read-job-shape.d.ts +15 -0
- package/dist/cli/commands/code-job-shape-commands-read-job-shape.js +70 -0
- package/dist/cli/commands/code-job-shape-commands.js +21 -45
- package/dist/cli/commands/code-mode-gate-commands.d.ts +15 -1
- package/dist/cli/commands/code-mode-gate-commands.js +19 -199
- package/dist/cli/commands/code-mode-gate-plan-command.d.ts +14 -0
- package/dist/cli/commands/code-mode-gate-plan-command.js +23 -0
- package/dist/cli/commands/code-mode-gate-should-pause-command.d.ts +24 -0
- package/dist/cli/commands/code-mode-gate-should-pause-command.js +137 -0
- package/dist/cli/commands/code-mode-gate-should-pause-envelope.d.ts +59 -0
- package/dist/cli/commands/code-mode-gate-should-pause-envelope.js +144 -0
- package/dist/cli/commands/code-orchestrator-can-do.js +4 -4
- package/dist/cli/commands/code-review-commands.js +18 -3
- package/dist/cli/commands/code-run-command.js +44 -7
- package/dist/cli/commands/code-runtime-commands.d.ts +17 -0
- package/dist/cli/commands/code-runtime-commands.js +105 -73
- package/dist/cli/commands/codegraph-commands.js +11 -6
- package/dist/cli/commands/codegraph-status-command.js +6 -2
- package/dist/cli/commands/compact-command.js +52 -26
- package/dist/cli/commands/complexity-commands.js +6 -7
- package/dist/cli/commands/config-commands.d.ts +1 -1
- package/dist/cli/commands/config-commands.js +33 -9
- package/dist/cli/commands/container-commands.js +39 -15
- package/dist/cli/commands/context-commands.d.ts +1 -1
- package/dist/cli/commands/context-commands.js +40 -13
- package/dist/cli/commands/core/artifacts-command.js +11 -3
- package/dist/cli/commands/core/binding-commands.js +9 -6
- package/dist/cli/commands/core/doctor-command.js +7 -4
- package/dist/cli/commands/core/memory-command.js +47 -21
- package/dist/cli/commands/core/proxy-command.js +3 -1
- package/dist/cli/commands/core/session-command.js +16 -8
- package/dist/cli/commands/core/skill-command.js +31 -16
- package/dist/cli/commands/core/standards-command.js +55 -13
- package/dist/cli/commands/cron-commands-actions.d.ts +43 -0
- package/dist/cli/commands/cron-commands-actions.js +128 -0
- package/dist/cli/commands/cron-commands-schedule.d.ts +86 -0
- package/dist/cli/commands/cron-commands-schedule.js +151 -0
- package/dist/cli/commands/cron-commands.d.ts +35 -32
- package/dist/cli/commands/cron-commands.js +167 -211
- package/dist/cli/commands/cron-scheduler-commands.js +40 -20
- package/dist/cli/commands/cron-scheduler-persist.d.ts +110 -0
- package/dist/cli/commands/cron-scheduler-persist.js +171 -0
- package/dist/cli/commands/dashboard-long-run.js +45 -14
- package/dist/cli/commands/dashboard-summary.js +15 -5
- package/dist/cli/commands/dispatch-commands.js +65 -32
- package/dist/cli/commands/dispatch-from-dag.js +9 -10
- package/dist/cli/commands/doc-commands.js +8 -9
- package/dist/cli/commands/doctor/invoke-from-code.js +1 -1
- package/dist/cli/commands/e2e-verify-env.d.ts +19 -0
- package/dist/cli/commands/e2e-verify-env.js +19 -0
- package/dist/cli/commands/e2e-verify.d.ts +3 -14
- package/dist/cli/commands/e2e-verify.js +49 -29
- package/dist/cli/commands/ecc-commands.d.ts +1 -1
- package/dist/cli/commands/ecc-commands.js +8 -17
- package/dist/cli/commands/evidence-commands.js +10 -6
- package/dist/cli/commands/evolution-commands.d.ts +2 -2
- package/dist/cli/commands/evolution-commands.js +109 -115
- package/dist/cli/commands/feedback-commands-helpers.d.ts +22 -0
- package/dist/cli/commands/feedback-commands-helpers.js +33 -0
- package/dist/cli/commands/feedback-commands.js +15 -38
- package/dist/cli/commands/final-review-commands.d.ts +1 -1
- package/dist/cli/commands/final-review-commands.js +24 -22
- package/dist/cli/commands/fixture-commands.d.ts +1 -1
- package/dist/cli/commands/fixture-commands.js +17 -6
- package/dist/cli/commands/fork-commands-sync-verify.d.ts +12 -0
- package/dist/cli/commands/fork-commands-sync-verify.js +72 -0
- package/dist/cli/commands/fork-commands.js +19 -51
- package/dist/cli/commands/fresh-context-commands.js +3 -1
- package/dist/cli/commands/gate-commands-enforce.d.ts +82 -0
- package/dist/cli/commands/gate-commands-enforce.js +210 -0
- package/dist/cli/commands/gate-commands.d.ts +1 -1
- package/dist/cli/commands/gate-commands.js +85 -205
- package/dist/cli/commands/governance-classify-contract-commands.d.ts +1 -1
- package/dist/cli/commands/governance-classify-contract-commands.js +28 -17
- package/dist/cli/commands/heartbeat-commands-action.d.ts +7 -0
- package/dist/cli/commands/heartbeat-commands-action.js +192 -0
- package/dist/cli/commands/heartbeat-commands-validation.d.ts +57 -0
- package/dist/cli/commands/heartbeat-commands-validation.js +120 -0
- package/dist/cli/commands/heartbeat-commands.d.ts +27 -6
- package/dist/cli/commands/heartbeat-commands.js +19 -198
- package/dist/cli/commands/hook-handle.d.ts +1 -1
- package/dist/cli/commands/hook-handle.js +46 -13
- package/dist/cli/commands/hooks-commands.d.ts +1 -1
- package/dist/cli/commands/hooks-commands.js +18 -6
- package/dist/cli/commands/ide-commands.d.ts +1 -1
- package/dist/cli/commands/ide-commands.js +5 -3
- package/dist/cli/commands/impact-commands.js +8 -2
- package/dist/cli/commands/job-add-slice-command.d.ts +43 -0
- package/dist/cli/commands/job-add-slice-command.js +151 -0
- package/dist/cli/commands/job-commands.d.ts +64 -0
- package/dist/cli/commands/job-commands.js +134 -63
- package/dist/cli/commands/lease-metrics-commands-support.d.ts +73 -0
- package/dist/cli/commands/lease-metrics-commands-support.js +67 -0
- package/dist/cli/commands/lease-metrics-commands.d.ts +3 -44
- package/dist/cli/commands/lease-metrics-commands.js +21 -61
- package/dist/cli/commands/lease-stats-commands.d.ts +1 -1
- package/dist/cli/commands/lease-stats-commands.js +9 -6
- package/dist/cli/commands/lint-commands.d.ts +9 -3
- package/dist/cli/commands/lint-commands.js +15 -8
- package/dist/cli/commands/log-commands.d.ts +1 -1
- package/dist/cli/commands/log-commands.js +8 -6
- package/dist/cli/commands/loop-commands.d.ts +1 -1
- package/dist/cli/commands/loop-commands.js +50 -50
- package/dist/cli/commands/loop-eval-commands.d.ts +1 -1
- package/dist/cli/commands/loop-eval-commands.js +101 -30
- package/dist/cli/commands/memory-commands.js +21 -13
- package/dist/cli/commands/migrate-1-4-1-command.js +1 -1
- package/dist/cli/commands/migrate-v2-10-to-v2-11-command.js +2 -2
- package/dist/cli/commands/mut-commands-helpers.d.ts +30 -0
- package/dist/cli/commands/mut-commands-helpers.js +43 -0
- package/dist/cli/commands/mut-commands.js +16 -58
- package/dist/cli/commands/observability-commands-queries.d.ts +37 -0
- package/dist/cli/commands/observability-commands-queries.js +113 -0
- package/dist/cli/commands/observability-commands.d.ts +1 -1
- package/dist/cli/commands/observability-commands.js +7 -92
- package/dist/cli/commands/openspec-commands.d.ts +1 -1
- package/dist/cli/commands/openspec-commands.js +20 -8
- package/dist/cli/commands/outer-cache-commands.js +18 -7
- package/dist/cli/commands/perf-audit-commands.d.ts +1 -1
- package/dist/cli/commands/perf-audit-commands.js +12 -7
- package/dist/cli/commands/perf-commands.d.ts +1 -1
- package/dist/cli/commands/perf-commands.js +5 -5
- package/dist/cli/commands/playwright-commands.d.ts +1 -1
- package/dist/cli/commands/playwright-commands.js +20 -8
- package/dist/cli/commands/polyrepo-commands.js +21 -19
- package/dist/cli/commands/prd-blocks-commands.js +1 -3
- package/dist/cli/commands/prd-commands.d.ts +1 -1
- package/dist/cli/commands/prd-commands.js +8 -4
- package/dist/cli/commands/preferences-commands.js +13 -12
- package/dist/cli/commands/primer-command.js +5 -5
- package/dist/cli/commands/project-commands.d.ts +1 -1
- package/dist/cli/commands/project-commands.js +33 -9
- package/dist/cli/commands/qa-business-review-commands.js +16 -12
- package/dist/cli/commands/qa-commands.d.ts +1 -1
- package/dist/cli/commands/qa-commands.js +3 -5
- package/dist/cli/commands/release-commands.d.ts +3 -1
- package/dist/cli/commands/release-commands.js +33 -22
- package/dist/cli/commands/request-commands.js +47 -30
- package/dist/cli/commands/request-format-helpers.js +1 -1
- package/dist/cli/commands/retrospective-commands.d.ts +18 -11
- package/dist/cli/commands/retrospective-commands.js +24 -173
- package/dist/cli/commands/retrospective-index-command.d.ts +26 -0
- package/dist/cli/commands/retrospective-index-command.js +99 -0
- package/dist/cli/commands/retrospective-project-root.d.ts +9 -0
- package/dist/cli/commands/retrospective-project-root.js +15 -0
- package/dist/cli/commands/retrospective-search-command.d.ts +27 -0
- package/dist/cli/commands/retrospective-search-command.js +97 -0
- package/dist/cli/commands/retrospective-show-command.d.ts +23 -0
- package/dist/cli/commands/retrospective-show-command.js +80 -0
- package/dist/cli/commands/reviewer-commands.d.ts +1 -1
- package/dist/cli/commands/role-commands.js +14 -9
- package/dist/cli/commands/runtime-commands.js +14 -7
- package/dist/cli/commands/sc-commands.d.ts +1 -1
- package/dist/cli/commands/sc-commands.js +39 -6
- package/dist/cli/commands/scan-commands.js +36 -13
- package/dist/cli/commands/security-audit-commands.d.ts +1 -1
- package/dist/cli/commands/security-audit-commands.js +12 -7
- package/dist/cli/commands/sediment-commands.d.ts +2 -2
- package/dist/cli/commands/sediment-commands.js +146 -139
- package/dist/cli/commands/session-24h-mode.js +15 -2
- package/dist/cli/commands/session-checkpoint-command.js +1 -1
- package/dist/cli/commands/session-migrate-skill-name.js +1 -1
- package/dist/cli/commands/shadcn-commands.d.ts +1 -1
- package/dist/cli/commands/shadcn-commands.js +3 -1
- package/dist/cli/commands/share-commands.js +38 -39
- package/dist/cli/commands/skill-conformance-commands.d.ts +1 -1
- package/dist/cli/commands/skill-conformance-commands.js +1 -1
- package/dist/cli/commands/skill-loop-engineering-readiness-commands.js +16 -17
- package/dist/cli/commands/skill-visibility.js +2 -2
- package/dist/cli/commands/slice-commands.d.ts +1 -1
- package/dist/cli/commands/slice-commands.js +36 -12
- package/dist/cli/commands/slice-integrate-commands.js +6 -7
- package/dist/cli/commands/slice-review-commands-helpers.d.ts +26 -0
- package/dist/cli/commands/slice-review-commands-helpers.js +26 -0
- package/dist/cli/commands/slice-review-commands.js +16 -24
- package/dist/cli/commands/smoke-commands-add-path.d.ts +14 -0
- package/dist/cli/commands/smoke-commands-add-path.js +39 -0
- package/dist/cli/commands/smoke-commands-define.d.ts +18 -0
- package/dist/cli/commands/smoke-commands-define.js +97 -0
- package/dist/cli/commands/smoke-commands-run.d.ts +18 -0
- package/dist/cli/commands/smoke-commands-run.js +119 -0
- package/dist/cli/commands/smoke-commands.d.ts +10 -1
- package/dist/cli/commands/smoke-commands.js +17 -179
- package/dist/cli/commands/sop-commands.d.ts +1 -1
- package/dist/cli/commands/sop-commands.js +38 -10
- package/dist/cli/commands/statusline-commands.d.ts +1 -1
- package/dist/cli/commands/statusline-commands.js +21 -10
- package/dist/cli/commands/sub-agent/detached.js +3 -3
- package/dist/cli/commands/sub-agent-dispatch-guard.d.ts +1 -1
- package/dist/cli/commands/sub-agent-shared.js +1 -1
- package/dist/cli/commands/sub-agent-shutdown-commands.js +17 -9
- package/dist/cli/commands/swarm-commands.d.ts +1 -1
- package/dist/cli/commands/swarm-commands.js +13 -6
- package/dist/cli/commands/tech-commands-helpers.d.ts +37 -0
- package/dist/cli/commands/tech-commands-helpers.js +24 -0
- package/dist/cli/commands/tech-commands.d.ts +2 -29
- package/dist/cli/commands/tech-commands.js +15 -28
- package/dist/cli/commands/test-commands.js +27 -7
- package/dist/cli/commands/upgrade-commands.d.ts +1 -1
- package/dist/cli/commands/user-touchpoint-commands.js +1 -3
- package/dist/cli/commands/verdict-aggregate-command.d.ts +1 -1
- package/dist/cli/commands/verdict-aggregate-command.js +20 -7
- package/dist/cli/commands/vm-commands.js +48 -19
- package/dist/cli/commands/wave-plan-commands.js +5 -2
- package/dist/cli/commands/web-commands.js +14 -4
- package/dist/cli/commands/web-lifecycle-commands.js +5 -3
- package/dist/cli/commands/workflow-commands.d.ts +1 -1
- package/dist/cli/commands/workflow-commands.js +64 -29
- package/dist/cli/commands/workflow-lifecycle-commands.js +19 -17
- package/dist/cli/commands/workflow-plan-commands-actions.d.ts +20 -0
- package/dist/cli/commands/workflow-plan-commands-actions.js +115 -0
- package/dist/cli/commands/workflow-plan-commands-session.d.ts +17 -0
- package/dist/cli/commands/workflow-plan-commands-session.js +59 -0
- package/dist/cli/commands/workflow-plan-commands.d.ts +10 -21
- package/dist/cli/commands/workflow-plan-commands.js +15 -141
- package/dist/cli/commands/workspace/clean-command.js +4 -1
- package/dist/cli/commands/workspace/helpers.js +2 -1
- package/dist/cli/commands/workspace/init-command.js +80 -20
- package/dist/cli/commands/workspace/reconcile-command.js +3 -1
- package/dist/cli/commands/workspace-commands.d.ts +1 -1
- package/dist/cli/commands/workspace-commands.js +1 -1
- package/dist/cli/commands/worktree-auth-commands.d.ts +1 -1
- package/dist/cli/commands/worktree-auth-commands.js +16 -169
- package/dist/cli/commands/worktree-auth-grant-command.d.ts +14 -0
- package/dist/cli/commands/worktree-auth-grant-command.js +118 -0
- package/dist/cli/commands/worktree-auth-manage-commands.d.ts +17 -0
- package/dist/cli/commands/worktree-auth-manage-commands.js +128 -0
- package/dist/cli/commands/worktree-lease-commands.d.ts +1 -1
- package/dist/cli/commands/worktree-lease-commands.js +65 -37
- package/dist/cli/index.js +6 -2
- package/dist/cli/program.js +6 -3
- package/dist/hooks/pre-tool-use-sub-agent.js +1 -6
- package/dist/reporters/bdd-reporter.js +9 -3
- package/dist/services/24h-mode/auto-engage.d.ts +19 -0
- package/dist/services/24h-mode/auto-engage.js +35 -0
- package/dist/services/24h-mode/decider.js +5 -1
- package/dist/services/24h-mode/store.js +4 -2
- package/dist/services/adapter/adapter-registry.js +20 -8
- package/dist/services/adapter/adapter.d.ts +2 -2
- package/dist/services/adapter/auto-adapter.d.ts +2 -2
- package/dist/services/adapter/auto-adapter.js +1 -1
- package/dist/services/adapter/claude-adapter.d.ts +1 -1
- package/dist/services/adapter/claude-adapter.js +8 -8
- package/dist/services/adapter/codex-adapter.d.ts +2 -2
- package/dist/services/adapter/codex-adapter.js +20 -8
- package/dist/services/adapter/copilot-adapter.d.ts +2 -2
- package/dist/services/adapter/copilot-adapter.js +20 -8
- package/dist/services/artifacts/artifact-prerequisites.js +10 -8
- package/dist/services/artifacts/artifact-service.js +3 -1
- package/dist/services/artifacts/artifact-template-bodies.d.ts +12 -0
- package/dist/services/artifacts/artifact-template-bodies.js +165 -0
- package/dist/services/artifacts/artifact-templates.js +26 -168
- package/dist/services/artifacts/request-artifact-service.js +14 -7
- package/dist/services/artifacts/request-artifact-state-helpers.d.ts +3 -2
- package/dist/services/artifacts/request-artifact-state-helpers.js +3 -5
- package/dist/services/artifacts/workspace-artifact-helpers.d.ts +28 -0
- package/dist/services/artifacts/workspace-artifact-helpers.js +54 -0
- package/dist/services/artifacts/workspace-service.d.ts +3 -22
- package/dist/services/artifacts/workspace-service.js +10 -60
- package/dist/services/audit/artifact-writer.js +21 -21
- package/dist/services/audit/audit-goal-service.js +2 -2
- package/dist/services/audit/backing-detector.js +7 -4
- package/dist/services/audit/classifier.js +3 -3
- package/dist/services/audit/decision-writer.js +13 -9
- package/dist/services/audit/enforcers/active-skill-resolver.js +15 -7
- package/dist/services/audit/enforcers/design-draft-confirm.js +4 -4
- package/dist/services/audit/enforcers/lint-audit-regression.js +10 -5
- package/dist/services/audit/enforcers/lint-bee-runtime-contract.js +16 -8
- package/dist/services/audit/enforcers/lint-catalog-governance.js +8 -4
- package/dist/services/audit/enforcers/lint-output-style.js +4 -2
- package/dist/services/audit/enforcers/lint-peaks-code-runtime.js +8 -3
- package/dist/services/audit/enforcers/lint-peaks-skill-runtime.js +8 -3
- package/dist/services/audit/enforcers/lint-peaks-ui-sc-txt-runtime.js +20 -20
- package/dist/services/audit/enforcers/lint-prd-artifact-handoff.js +5 -5
- package/dist/services/audit/enforcers/lint-prd-source-snapshot.d.ts +1 -1
- package/dist/services/audit/enforcers/lint-prd-source-snapshot.js +5 -5
- package/dist/services/audit/enforcers/lint-qa-gateguard-and-runtime.js +10 -10
- package/dist/services/audit/enforcers/lint-rd-handoff-coverage.js +10 -8
- package/dist/services/audit/enforcers/lint-reference-shape.js +50 -18
- package/dist/services/audit/enforcers/lint-skill-presence-mandatory.js +5 -5
- package/dist/services/audit/enforcers/lint-style.js +44 -11
- package/dist/services/audit/enforcers/lint-workflow-shape.js +4 -2
- package/dist/services/audit/enforcers/login-gate.js +2 -6
- package/dist/services/audit/enforcers/mock-placement.js +2 -2
- package/dist/services/audit/enforcers/no-root-pollution.js +38 -9
- package/dist/services/audit/enforcers/pre-rd-scan.js +1 -1
- package/dist/services/audit/enforcers/prototype-fidelity.js +1 -1
- package/dist/services/audit/enforcers/resume-detection.js +5 -4
- package/dist/services/audit/prose-ratio-calculator.js +1 -1
- package/dist/services/audit/red-line-catalog-p2-a-bee-runtime.d.ts +39 -0
- package/dist/services/audit/red-line-catalog-p2-a-bee-runtime.js +205 -0
- package/dist/services/audit/red-line-catalog-p2-a-lint-style.d.ts +52 -0
- package/dist/services/audit/red-line-catalog-p2-a-lint-style.js +205 -0
- package/dist/services/audit/red-line-catalog-p2-a.js +3 -374
- package/dist/services/audit/red-line-catalog-p2-b.js +26 -26
- package/dist/services/audit/red-line-catalog.js +26 -38
- package/dist/services/audit/red-lines-service.js +64 -49
- package/dist/services/audit/scanners/openspec-scanner.js +2 -2
- package/dist/services/audit/scanners/rules-tree-scanner.js +2 -2
- package/dist/services/audit/scanners/skills-tree-scanner.js +2 -2
- package/dist/services/audit/static-service.js +3 -3
- package/dist/services/audit-independent/index.d.ts +2 -2
- package/dist/services/audit-independent/index.js +2 -2
- package/dist/services/audit-independent/perf-audit-service.js +19 -8
- package/dist/services/audit-independent/security-audit-service.js +31 -16
- package/dist/services/autonomous-swarm/autonomous-swarm-service.js +75 -46
- package/dist/services/best-practice/output-formatter.js +7 -1
- package/dist/services/capability-audit-service/cross-check.js +11 -5
- package/dist/services/capability-audit-service/runner.js +15 -7
- package/dist/services/capability-baseline/store.js +16 -4
- package/dist/services/capability-baseline/types.js +15 -3
- package/dist/services/capability-baseline/validator.js +43 -7
- package/dist/services/capability-guard-runner/contracts/J02.js +36 -6
- package/dist/services/capability-guard-runner/contracts/J03.js +20 -11
- package/dist/services/capability-guard-runner/contracts/J04.js +24 -15
- package/dist/services/capability-guard-runner/contracts/J05.js +26 -10
- package/dist/services/capability-guard-runner/contracts/J07.js +8 -2
- package/dist/services/capability-guard-runner/contracts/J08.js +22 -5
- package/dist/services/capability-guard-runner/contracts/J10.js +6 -2
- package/dist/services/capability-guard-runner/contracts/J11.d.ts +5 -0
- package/dist/services/capability-guard-runner/contracts/J11.js +14 -2
- package/dist/services/capability-guard-runner/contracts/J12.js +12 -3
- package/dist/services/capability-guard-runner/diff.js +1 -5
- package/dist/services/changeset/changeset-check-service.js +1 -3
- package/dist/services/classify/classify-service.js +20 -8
- package/dist/services/classify/classify-types.js +22 -9
- package/dist/services/code/auto-compact-lifecycle.js +62 -39
- package/dist/services/code/auto-compact-modes.js +2 -2
- package/dist/services/code/auto-compact-orchestrator.js +14 -9
- package/dist/services/code/batch-heartbeat-poller.d.ts +10 -0
- package/dist/services/code/batch-heartbeat-poller.js +17 -1
- package/dist/services/code/compact-event-settle-types.d.ts +80 -0
- package/dist/services/code/compact-event-settle-types.js +20 -0
- package/dist/services/code/compact-event-settle.d.ts +3 -68
- package/dist/services/code/compact-event-settle.js +14 -10
- package/dist/services/code/dag-orchestrator.js +32 -26
- package/dist/services/code/job-shape-decision.js +3 -1
- package/dist/services/code/mode-gate-types.d.ts +57 -0
- package/dist/services/code/mode-gate-types.js +67 -0
- package/dist/services/code/mode-gate.d.ts +4 -58
- package/dist/services/code/mode-gate.js +24 -60
- package/dist/services/code/orchestrator-can-do-advisories.d.ts +30 -0
- package/dist/services/code/orchestrator-can-do-advisories.js +81 -0
- package/dist/services/code/orchestrator-can-do-probes.d.ts +30 -0
- package/dist/services/code/orchestrator-can-do-probes.js +77 -0
- package/dist/services/code/orchestrator-can-do.d.ts +11 -22
- package/dist/services/code/orchestrator-can-do.js +37 -137
- package/dist/services/code/post-compact-checkpoint.d.ts +28 -0
- package/dist/services/code/post-compact-checkpoint.js +57 -0
- package/dist/services/code/post-compact-detector.js +16 -48
- package/dist/services/code/step-08-gate.d.ts +4 -3
- package/dist/services/code/step-08-gate.js +21 -9
- package/dist/services/code-review/ecc-bridge.js +23 -23
- package/dist/services/codegraph/codegraph-autorefresh.js +56 -39
- package/dist/services/codegraph/codegraph-config-repair-writer.js +4 -1
- package/dist/services/codegraph/codegraph-exclude-integrity.js +4 -1
- package/dist/services/codegraph/codegraph-exclude-repair.js +1 -2
- package/dist/services/codegraph/codegraph-index-integrity.js +1 -1
- package/dist/services/codegraph/codegraph-preflight-service.d.ts +3 -26
- package/dist/services/codegraph/codegraph-preflight-service.js +16 -37
- package/dist/services/codegraph/codegraph-preflight-structure.d.ts +32 -0
- package/dist/services/codegraph/codegraph-preflight-structure.js +33 -0
- package/dist/services/codegraph/codegraph-process-runner.js +12 -1
- package/dist/services/codegraph/codegraph-service.js +9 -1
- package/dist/services/compact/request-transition-hook.js +2 -1
- package/dist/services/compact/suggest-service.d.ts +3 -61
- package/dist/services/compact/suggest-service.js +9 -12
- package/dist/services/compact/suggest-types.d.ts +76 -0
- package/dist/services/compact/suggest-types.js +10 -0
- package/dist/services/compact-history/compact-history-service.js +2 -2
- package/dist/services/compact-statusline/compact-lifecycle-store.d.ts +2 -32
- package/dist/services/compact-statusline/compact-lifecycle-store.js +7 -12
- package/dist/services/compact-statusline/compact-lifecycle-types.d.ts +32 -0
- package/dist/services/compact-statusline/compact-lifecycle-types.js +8 -0
- package/dist/services/compact-statusline/compact-statusline-cell-table.d.ts +42 -0
- package/dist/services/compact-statusline/compact-statusline-cell-table.js +63 -0
- package/dist/services/compact-statusline/compact-statusline-service.d.ts +4 -21
- package/dist/services/compact-statusline/compact-statusline-service.js +9 -101
- package/dist/services/compact-statusline/compact-statusline-state.d.ts +4 -0
- package/dist/services/compact-statusline/compact-statusline-state.js +50 -0
- package/dist/services/config/config-migration.js +2 -2
- package/dist/services/config/config-restore.js +1 -1
- package/dist/services/config/config-rollback.js +1 -1
- package/dist/services/config/config-safety.js +28 -10
- package/dist/services/config/config-service.js +114 -31
- package/dist/services/config/model-routing.js +5 -2
- package/dist/services/config/proxy-service.js +6 -1
- package/dist/services/config/workspace-state-service.js +17 -5
- package/dist/services/context/auto-compact-dispatcher-input.d.ts +38 -0
- package/dist/services/context/auto-compact-dispatcher-input.js +9 -0
- package/dist/services/context/auto-compact-dispatcher.d.ts +2 -30
- package/dist/services/context/auto-compact-reader-input.d.ts +30 -0
- package/dist/services/context/auto-compact-reader-input.js +8 -0
- package/dist/services/context/auto-compact-reader.d.ts +2 -24
- package/dist/services/context/auto-compact-reader.js +4 -1
- package/dist/services/context/auto-compact-thresholds.d.ts +54 -0
- package/dist/services/context/auto-compact-thresholds.js +19 -0
- package/dist/services/context/auto-compact-types.d.ts +2 -46
- package/dist/services/context/auto-compact-types.js +1 -11
- package/dist/services/context/build-dispatch-system-prompt.js +2 -2
- package/dist/services/context/collector.js +8 -10
- package/dist/services/context/context-audit-hint.js +7 -5
- package/dist/services/context/context-audit-keys.d.ts +15 -0
- package/dist/services/context/context-audit-keys.js +77 -0
- package/dist/services/context/context-audit-limits.d.ts +20 -0
- package/dist/services/context/context-audit-limits.js +31 -0
- package/dist/services/context/context-audit-scan.d.ts +19 -0
- package/dist/services/context/context-audit-scan.js +246 -0
- package/dist/services/context/context-audit-types.d.ts +65 -0
- package/dist/services/context/context-audit-types.js +10 -0
- package/dist/services/context/context-audit.d.ts +13 -59
- package/dist/services/context/context-audit.js +38 -254
- package/dist/services/context/context-builder.js +4 -4
- package/dist/services/context/context-schema.d.ts +1 -1
- package/dist/services/context/context-schema.js +13 -13
- package/dist/services/context/doc-retriever.js +2 -2
- package/dist/services/context/harness-context-witness.js +43 -24
- package/dist/services/context/harness-window-config.js +27 -7
- package/dist/services/context/ide-detect.js +2 -1
- package/dist/services/context/index.d.ts +1 -1
- package/dist/services/context/memory-index-reader.js +3 -9
- package/dist/services/context/memory-preflight-config.js +3 -5
- package/dist/services/context/memory-preflight-service.d.ts +2 -20
- package/dist/services/context/memory-preflight-service.js +37 -68
- package/dist/services/context/memory-preflight-support.d.ts +48 -0
- package/dist/services/context/memory-preflight-support.js +56 -0
- package/dist/services/context/post-compact-reinjection.js +3 -3
- package/dist/services/context/renderer.js +7 -3
- package/dist/services/context/spillover-store.js +3 -1
- package/dist/services/context/summary-view.js +1 -1
- package/dist/services/context/threshold.js +2 -2
- package/dist/services/context/tokenizer.js +3 -3
- package/dist/services/crystallization/crystallization-brief-schema.d.ts +80 -0
- package/dist/services/crystallization/crystallization-brief-schema.js +159 -0
- package/dist/services/crystallization/crystallization-service.d.ts +7 -7
- package/dist/services/crystallization/crystallization-service.js +56 -55
- package/dist/services/crystallization/crystallization-store.d.ts +2 -2
- package/dist/services/crystallization/crystallization-store.js +37 -20
- package/dist/services/crystallization/crystallization-types.d.ts +4 -56
- package/dist/services/crystallization/crystallization-types.js +31 -184
- package/dist/services/crystallization/evidence-brief-builder.d.ts +3 -3
- package/dist/services/crystallization/evidence-brief-builder.js +32 -38
- package/dist/services/crystallization/index.d.ts +4 -4
- package/dist/services/crystallization/index.js +4 -4
- package/dist/services/dashboard/project-dashboard-service.js +7 -1
- package/dist/services/dispatch/await-batch-helpers.d.ts +55 -0
- package/dist/services/dispatch/await-batch-helpers.js +206 -0
- package/dist/services/dispatch/await-batch.d.ts +50 -7
- package/dist/services/dispatch/await-batch.js +97 -194
- package/dist/services/dispatch/batch-counter.js +2 -1
- package/dist/services/dispatch/conflict-replay.js +2 -2
- package/dist/services/dispatch/contract-store.js +5 -2
- package/dist/services/dispatch/dispatch-record-input-types.d.ts +81 -0
- package/dist/services/dispatch/dispatch-record-input-types.js +1 -0
- package/dist/services/dispatch/dispatch-record-types.d.ts +6 -73
- package/dist/services/dispatch/dispatch-record-upgrade-fields.d.ts +72 -0
- package/dist/services/dispatch/dispatch-record-upgrade-fields.js +242 -0
- package/dist/services/dispatch/dispatch-record-upgrade-guards.d.ts +15 -0
- package/dist/services/dispatch/dispatch-record-upgrade-guards.js +55 -0
- package/dist/services/dispatch/dispatch-record-upgrade.d.ts +2 -4
- package/dist/services/dispatch/dispatch-record-upgrade.js +21 -213
- package/dist/services/dispatch/dispatch-record-writer.js +54 -26
- package/dist/services/dispatch/dispatch-sub-agent.js +7 -2
- package/dist/services/dispatch/e2e-fixtures.js +1 -1
- package/dist/services/dispatch/isolation-lease.js +39 -17
- package/dist/services/dispatch/merge-back-runner.js +51 -11
- package/dist/services/dispatch/service-shutdown.js +4 -1
- package/dist/services/dispatch/slice-dag-types.d.ts +116 -0
- package/dist/services/dispatch/slice-dag-types.js +41 -0
- package/dist/services/dispatch/slice-dag.d.ts +3 -109
- package/dist/services/dispatch/slice-dag.js +13 -36
- package/dist/services/dispatch/sub-agent-batch-await.d.ts +47 -0
- package/dist/services/dispatch/sub-agent-batch-await.js +73 -0
- package/dist/services/dispatch/sub-agent-dispatcher-types.d.ts +122 -0
- package/dist/services/dispatch/sub-agent-dispatcher-types.js +12 -0
- package/dist/services/dispatch/sub-agent-dispatcher.d.ts +3 -145
- package/dist/services/dispatch/sub-agent-dispatcher.js +14 -70
- package/dist/services/dispatch/test-tool-detection.js +12 -1
- package/dist/services/doc/doc-generator.js +19 -4
- package/dist/services/doctor/doctor-service/checks/check-id-schema.js +16 -8
- package/dist/services/doctor/doctor-service/checks/codegraph-capability.d.ts +7 -2
- package/dist/services/doctor/doctor-service/checks/codegraph-capability.js +26 -8
- package/dist/services/doctor/doctor-service/checks/codegraph-exclude-integrity.js +16 -8
- package/dist/services/doctor/doctor-service/checks/codegraph-index-integrity.js +16 -8
- package/dist/services/doctor/doctor-service/checks/dist-source-version.js +18 -9
- package/dist/services/doctor/doctor-service/checks/ecc-hooks-schema-drift.js +20 -10
- package/dist/services/doctor/doctor-service/checks/gateguard-conflict.js +18 -9
- package/dist/services/doctor/doctor-service/checks/l3-memory-health.js +25 -11
- package/dist/services/doctor/doctor-service/checks/l3-orphan-sessions.js +12 -6
- package/dist/services/doctor/doctor-service/checks/multi-binary-drift.d.ts +7 -16
- package/dist/services/doctor/doctor-service/checks/multi-binary-drift.js +50 -70
- package/dist/services/doctor/doctor-service/checks/schema-validity.js +5 -1
- package/dist/services/doctor/doctor-service/checks/skill-presence.js +4 -2
- package/dist/services/doctor/doctor-service/checks/statusline-install.js +8 -4
- package/dist/services/doctor/doctor-service/checks/statusline-runtime.js +8 -4
- package/dist/services/doctor/doctor-service/checks/user-config.js +4 -2
- package/dist/services/doctor/doctor-service/checks/workspace-init.js +8 -4
- package/dist/services/doctor/doctor-service/checks/workspace-layout.js +19 -11
- package/dist/services/doctor/doctor-service/index.js +4 -2
- package/dist/services/doctor/doctor-service/multi-binary-drift-helpers.d.ts +40 -0
- package/dist/services/doctor/doctor-service/multi-binary-drift-helpers.js +49 -0
- package/dist/services/doctor/doctor-service/plugin-registry.js +1 -1
- package/dist/services/doctor/doctor-service/probe-types.d.ts +210 -0
- package/dist/services/doctor/doctor-service/probe-types.js +14 -0
- package/dist/services/doctor/doctor-service/types.d.ts +7 -197
- package/dist/services/doctor/index.d.ts +1 -1
- package/dist/services/doctor/index.js +1 -1
- package/dist/services/env/shell-probe.js +8 -2
- package/dist/services/evidence/evidence-body-builders.d.ts +23 -0
- package/dist/services/evidence/evidence-body-builders.js +197 -0
- package/dist/services/evidence/evidence-generator.js +6 -187
- package/dist/services/evolution/evolution-constraints.d.ts +43 -0
- package/dist/services/evolution/evolution-constraints.js +54 -0
- package/dist/services/evolution/evolution-evaluation-schema.d.ts +43 -0
- package/dist/services/evolution/evolution-evaluation-schema.js +43 -0
- package/dist/services/evolution/evolution-service.d.ts +6 -6
- package/dist/services/evolution/evolution-service.js +58 -58
- package/dist/services/evolution/evolution-store.d.ts +2 -2
- package/dist/services/evolution/evolution-store.js +37 -51
- package/dist/services/evolution/evolution-types.d.ts +2 -22
- package/dist/services/evolution/evolution-types.js +41 -101
- package/dist/services/evolution/independent-evaluator-runner.d.ts +3 -3
- package/dist/services/evolution/independent-evaluator-runner.js +20 -22
- package/dist/services/evolution/regression-skeptic-runner.d.ts +2 -2
- package/dist/services/evolution/regression-skeptic-runner.js +18 -18
- package/dist/services/feedback/feedback-promotion-service.js +48 -12
- package/dist/services/feedback/promotion-artifact-evidence.d.ts +2 -16
- package/dist/services/feedback/promotion-artifact-evidence.js +6 -83
- package/dist/services/feedback/promotion-artifact-json-evidence.d.ts +29 -0
- package/dist/services/feedback/promotion-artifact-json-evidence.js +39 -0
- package/dist/services/feedback/promotion-source-comments.d.ts +47 -0
- package/dist/services/feedback/promotion-source-comments.js +61 -0
- package/dist/services/final-review/final-review-contract.d.ts +26 -0
- package/dist/services/final-review/final-review-contract.js +14 -0
- package/dist/services/final-review/final-review-delivery.d.ts +63 -0
- package/dist/services/final-review/final-review-delivery.js +124 -0
- package/dist/services/final-review/final-review-evidence-budget.d.ts +125 -0
- package/dist/services/final-review/final-review-evidence-budget.js +218 -0
- package/dist/services/final-review/final-review-evidence-collect.d.ts +4 -0
- package/dist/services/final-review/final-review-evidence-collect.js +230 -0
- package/dist/services/final-review/final-review-evidence-sources.d.ts +138 -0
- package/dist/services/final-review/final-review-evidence-sources.js +139 -0
- package/dist/services/final-review/final-review-fifth-dim.d.ts +8 -0
- package/dist/services/final-review/final-review-fifth-dim.js +20 -0
- package/dist/services/final-review/final-review-gates.d.ts +119 -0
- package/dist/services/final-review/final-review-gates.js +214 -0
- package/dist/services/final-review/final-review-output-budget.d.ts +88 -0
- package/dist/services/final-review/final-review-output-budget.js +161 -0
- package/dist/services/final-review/final-review-prompt.d.ts +35 -0
- package/dist/services/final-review/final-review-prompt.js +112 -0
- package/dist/services/final-review/final-review-reviewer.d.ts +57 -0
- package/dist/services/final-review/final-review-reviewer.js +95 -0
- package/dist/services/final-review/final-review-runner.d.ts +3 -0
- package/dist/services/final-review/final-review-runner.js +158 -0
- package/dist/services/final-review/final-review-service.d.ts +119 -373
- package/dist/services/final-review/final-review-service.js +48 -1380
- package/dist/services/final-review/final-review-verdicts.d.ts +60 -0
- package/dist/services/final-review/final-review-verdicts.js +91 -0
- package/dist/services/final-review/index.d.ts +3 -3
- package/dist/services/final-review/index.js +2 -2
- package/dist/services/final-review/pre-post-diff.js +32 -29
- package/dist/services/fixture/fixture-capture-service.js +14 -6
- package/dist/services/fresh-context/trigger-scan.js +13 -2
- package/dist/services/fuzzy-matching/fuzzy-match-service.js +1 -1
- package/dist/services/fuzzy-matching/fzf-pick-service.js +6 -1
- package/dist/services/hooks/pre-tool-code-gate.js +2 -2
- package/dist/services/hooks/presence-marker-detector.js +12 -2
- package/dist/services/hooks/worktree-authorization-gate.js +21 -7
- package/dist/services/ide/adapters/claude-code-adapter.js +30 -17
- package/dist/services/ide/adapters/codex-adapter.js +3 -5
- package/dist/services/ide/adapters/cursor-adapter.js +3 -5
- package/dist/services/ide/adapters/hermes-adapter.js +2 -4
- package/dist/services/ide/adapters/openclaw-adapter.js +2 -4
- package/dist/services/ide/adapters/qoder-adapter.js +2 -4
- package/dist/services/ide/adapters/tongyi-lingma-adapter.js +1 -1
- package/dist/services/ide/adapters/trae-adapter.js +3 -5
- package/dist/services/ide/adapters/zcode-adapter.js +8 -7
- package/dist/services/ide/current-model-detector.js +2 -1
- package/dist/services/ide/ide-compact-types.d.ts +153 -0
- package/dist/services/ide/ide-compact-types.js +1 -0
- package/dist/services/ide/ide-registry.js +1 -1
- package/dist/services/ide/ide-types.d.ts +7 -145
- package/dist/services/ide/resource-profile.js +1 -1
- package/dist/services/ide/shared/atomic-json.js +2 -1
- package/dist/services/impact/impact-scan-service.js +12 -5
- package/dist/services/job/job-commit-verification.d.ts +73 -0
- package/dist/services/job/job-commit-verification.js +72 -0
- package/dist/services/job/job-orchestrator.d.ts +42 -0
- package/dist/services/job/job-orchestrator.js +110 -10
- package/dist/services/job/job-progress-store.d.ts +75 -5
- package/dist/services/job/job-progress-store.js +83 -6
- package/dist/services/job/job-rotation.d.ts +1 -1
- package/dist/services/job/job-rotation.js +5 -1
- package/dist/services/job/job-state-store.js +2 -2
- package/dist/services/job/job-types.js +49 -20
- package/dist/services/job/subagent-job-wrapper.d.ts +1 -1
- package/dist/services/job/subagent-job-wrapper.js +5 -1
- package/dist/services/job-snapshot/snapshot.js +2 -2
- package/dist/services/karpathy-cost/karpathy-cost-check-service.js +23 -13
- package/dist/services/legacy/legacy-detector.js +51 -5
- package/dist/services/lint/detect-eslint.js +10 -4
- package/dist/services/lint/detect-ocr-18.js +11 -3
- package/dist/services/lint/eslint-runner-support.d.ts +22 -0
- package/dist/services/lint/eslint-runner-support.js +185 -0
- package/dist/services/lint/eslint-runner-types.d.ts +69 -0
- package/dist/services/lint/eslint-runner-types.js +11 -0
- package/dist/services/lint/eslint-runner.d.ts +4 -56
- package/dist/services/lint/eslint-runner.js +24 -178
- package/dist/services/lint/npx-resolver.js +1 -3
- package/dist/services/lint/ocr-18-acquire.js +15 -4
- package/dist/services/lint/ocr-multilang-adapter.js +4 -5
- package/dist/services/llm/anthropic-runner.js +4 -1
- package/dist/services/log/log-commands-service.js +2 -6
- package/dist/services/log/logger.js +15 -12
- package/dist/services/log/retention.js +3 -2
- package/dist/services/loop/evaluator-dispatcher.js +78 -42
- package/dist/services/loop/loop-bee-relation-service.d.ts +4 -4
- package/dist/services/loop/loop-bee-relation-service.js +36 -40
- package/dist/services/loop/loop-bee-relation-store.d.ts +3 -3
- package/dist/services/loop/loop-bee-relation-store.js +9 -11
- package/dist/services/loop/loop-bee-relation-types.d.ts +1 -1
- package/dist/services/loop/loop-bee-relation-types.js +14 -25
- package/dist/services/loop/loop-release-service.d.ts +2 -2
- package/dist/services/loop/loop-release-service.js +3 -3
- package/dist/services/loop/loop-release-store.d.ts +2 -2
- package/dist/services/loop/loop-release-store.js +4 -6
- package/dist/services/loop/loop-release-types.d.ts +1 -1
- package/dist/services/loop/loop-release-types.js +21 -31
- package/dist/services/loop/monotonic-guard-types.d.ts +51 -0
- package/dist/services/loop/monotonic-guard-types.js +9 -0
- package/dist/services/loop/monotonic-guard.d.ts +2 -43
- package/dist/services/loop/monotonic-runner-types.d.ts +63 -0
- package/dist/services/loop/monotonic-runner-types.js +17 -0
- package/dist/services/loop/monotonic-runner.d.ts +3 -34
- package/dist/services/loop/monotonic-runner.js +10 -20
- package/dist/services/loop/run-driver.js +16 -8
- package/dist/services/loop/spec-service.d.ts +0 -5
- package/dist/services/loop/spec-service.js +49 -9
- package/dist/services/memory/llm-reranker-core.d.ts +108 -0
- package/dist/services/memory/llm-reranker-core.js +105 -0
- package/dist/services/memory/llm-reranker.d.ts +3 -94
- package/dist/services/memory/llm-reranker.js +13 -102
- package/dist/services/memory/memory-ingest-service.d.ts +1 -22
- package/dist/services/memory/memory-ingest-service.js +29 -76
- package/dist/services/memory/memory-ingest-support.d.ts +23 -0
- package/dist/services/memory/memory-ingest-support.js +81 -0
- package/dist/services/memory/memory-rotate-service.js +41 -8
- package/dist/services/memory/memory-search-service.js +8 -4
- package/dist/services/memory/project-context-service.js +26 -5
- package/dist/services/memory/project-memory-service/index/kind-dispatch.d.ts +2 -7
- package/dist/services/memory/project-memory-service/index/kind-dispatch.js +17 -136
- package/dist/services/memory/project-memory-service/index/project-memory-plan-exec.d.ts +7 -0
- package/dist/services/memory/project-memory-service/index/project-memory-plan-exec.js +150 -0
- package/dist/services/memory/project-memory-service/index/ranking.js +6 -3
- package/dist/services/memory/project-memory-service/index/reindex.js +14 -5
- package/dist/services/memory/project-memory-service/memory-kinds.d.ts +31 -0
- package/dist/services/memory/project-memory-service/memory-kinds.js +87 -0
- package/dist/services/memory/project-memory-service/parsers/frontmatter-parse.d.ts +24 -0
- package/dist/services/memory/project-memory-service/parsers/frontmatter-parse.js +104 -0
- package/dist/services/memory/project-memory-service/parsers/frontmatter.d.ts +2 -24
- package/dist/services/memory/project-memory-service/parsers/frontmatter.js +28 -79
- package/dist/services/memory/project-memory-service/parsers/markdown-pure.js +15 -3
- package/dist/services/memory/project-memory-service/store/atomic-write.js +34 -22
- package/dist/services/memory/project-memory-service/store/paths.js +2 -1
- package/dist/services/memory/project-memory-service/types.d.ts +3 -31
- package/dist/services/memory/project-memory-service/types.js +1 -76
- package/dist/services/migrate-skill-name/migrate.js +1 -1
- package/dist/services/migrate-skill-name/schema.js +1 -1
- package/dist/services/mode/mode-enforcement.js +4 -2
- package/dist/services/mode/mode-status-service.d.ts +27 -9
- package/dist/services/mode/mode-status-service.js +64 -12
- package/dist/services/observability/aggregation-helpers.d.ts +22 -0
- package/dist/services/observability/aggregation-helpers.js +65 -0
- package/dist/services/observability/aggregation-types.d.ts +66 -0
- package/dist/services/observability/aggregation-types.js +34 -0
- package/dist/services/observability/aggregation.d.ts +5 -53
- package/dist/services/observability/aggregation.js +17 -80
- package/dist/services/observability/jsonl-store.js +2 -1
- package/dist/services/observability/observability-schema.d.ts +71 -0
- package/dist/services/observability/observability-schema.js +64 -0
- package/dist/services/observability/observability-service.d.ts +3 -56
- package/dist/services/observability/observability-service.js +9 -49
- package/dist/services/openspec/coverage-evidence-reader.js +18 -11
- package/dist/services/openspec/openspec-archive-service.js +31 -24
- package/dist/services/openspec/openspec-init-service.js +8 -2
- package/dist/services/openspec/openspec-propose-from-doctor-service.js +1 -1
- package/dist/services/openspec/openspec-scan-service.js +4 -1
- package/dist/services/openspec/openspec-validate-service.js +26 -5
- package/dist/services/perf/perf-baseline-service.js +2 -6
- package/dist/services/polyrepo/polyrepo-dispatcher.js +2 -1
- package/dist/services/polyrepo/polyrepo-scanner.js +4 -1
- package/dist/services/prd/handoff-frontmatter-shape.d.ts +42 -0
- package/dist/services/prd/handoff-frontmatter-shape.js +67 -0
- package/dist/services/prd/handoff-frontmatter.js +1 -1
- package/dist/services/prd/handoff-path-resolution.d.ts +37 -0
- package/dist/services/prd/handoff-path-resolution.js +101 -0
- package/dist/services/prd/handoff-service.d.ts +1 -37
- package/dist/services/prd/handoff-service.js +16 -148
- package/dist/services/prd/prd-blocks-checker.js +28 -8
- package/dist/services/prd/project-scan-bootstrap-service.js +1 -5
- package/dist/services/prd/project-scan-reader.js +1 -4
- package/dist/services/prd/project-scan-sediment.js +1 -4
- package/dist/services/preferences/preferences-service.js +6 -8
- package/dist/services/preferences/preferences-types.js +4 -4
- package/dist/services/qa/bdd-test-style-contract.d.ts +41 -0
- package/dist/services/qa/bdd-test-style-contract.js +37 -0
- package/dist/services/qa/bdd-test-style-verifier.d.ts +2 -24
- package/dist/services/qa/bdd-test-style-verifier.js +5 -31
- package/dist/services/qa/browser-event-logger.js +2 -1
- package/dist/services/qa/qa-business-review-state.js +20 -5
- package/dist/services/qa/screenshot-archive-service.js +1 -3
- package/dist/services/rd/ast-gate.js +2 -2
- package/dist/services/rd/impl.js +1 -1
- package/dist/services/rd/rd-service.js +117 -43
- package/dist/services/rd/reviewer-dispatch-policy.js +4 -7
- package/dist/services/rd/standards-diagnostic.js +4 -1
- package/dist/services/rd/strategy.js +10 -3
- package/dist/services/rd/tactical-stage.js +2 -2
- package/dist/services/rd/types.js +9 -7
- package/dist/services/rd-swarm/rd-swarm-service.d.ts +2 -45
- package/dist/services/rd-swarm/rd-swarm-service.js +140 -21
- package/dist/services/rd-swarm/rd-swarm-types.d.ts +54 -0
- package/dist/services/rd-swarm/rd-swarm-types.js +10 -0
- package/dist/services/recommendations/capability-map-service.js +10 -2
- package/dist/services/recommendations/capability-seed-items.js +81 -14
- package/dist/services/recommendations/capability-seed-mappings.js +420 -48
- package/dist/services/recommendations/capability-seed-sources-mcp-server.d.ts +9 -0
- package/dist/services/recommendations/capability-seed-sources-mcp-server.js +233 -0
- package/dist/services/recommendations/capability-seed-sources.js +127 -32
- package/dist/services/release/release-state.d.ts +2 -2
- package/dist/services/release/release-state.js +44 -15
- package/dist/services/release/version-precheck-layer-changeset.d.ts +2 -0
- package/dist/services/release/version-precheck-layer-changeset.js +43 -0
- package/dist/services/release/version-precheck-service.d.ts +11 -36
- package/dist/services/release/version-precheck-service.js +20 -104
- package/dist/services/release/version-precheck-support.d.ts +53 -0
- package/dist/services/release/version-precheck-support.js +64 -0
- package/dist/services/retrospective/retrospective-index.js +17 -3
- package/dist/services/retrospective/retrospective-search-service.js +6 -2
- package/dist/services/retrospective/retrospective-show.js +4 -1
- package/dist/services/reviewer/model-family.js +7 -1
- package/dist/services/reviewer/providers/anthropic.d.ts +5 -0
- package/dist/services/reviewer/providers/anthropic.js +10 -1
- package/dist/services/reviewer/providers/openai.d.ts +5 -0
- package/dist/services/reviewer/providers/openai.js +10 -1
- package/dist/services/reviewer/reviewer-config.js +4 -2
- package/dist/services/reviewer/reviewer-service.d.ts +2 -36
- package/dist/services/reviewer/reviewer-service.js +2 -1
- package/dist/services/reviewer/reviewer-types.d.ts +43 -0
- package/dist/services/reviewer/reviewer-types.js +1 -0
- package/dist/services/role/role-registry.js +9 -1
- package/dist/services/runtime/runtime-detector.js +4 -1
- package/dist/services/runtime/vendors/claude-code.js +14 -6
- package/dist/services/runtime/vendors/codex.js +11 -3
- package/dist/services/runtime/vendors/copilot.js +11 -3
- package/dist/services/sc/sc-service.js +28 -15
- package/dist/services/scan/acceptance-coverage-service.js +4 -1
- package/dist/services/scan/api-diff-openapi-schema-read.d.ts +67 -0
- package/dist/services/scan/api-diff-openapi-schema-read.js +154 -0
- package/dist/services/scan/api-diff-openapi.d.ts +16 -2
- package/dist/services/scan/api-diff-openapi.js +19 -123
- package/dist/services/scan/api-diff-recorded.js +16 -3
- package/dist/services/scan/api-diff-service.js +49 -16
- package/dist/services/scan/api-diff-types.js +12 -3
- package/dist/services/scan/api-surface-helpers.d.ts +13 -0
- package/dist/services/scan/api-surface-helpers.js +31 -0
- package/dist/services/scan/api-surface-service.d.ts +2 -45
- package/dist/services/scan/api-surface-service.js +15 -26
- package/dist/services/scan/api-surface-types.d.ts +54 -0
- package/dist/services/scan/api-surface-types.js +10 -0
- package/dist/services/scan/archetype-detection.d.ts +37 -0
- package/dist/services/scan/archetype-detection.js +142 -0
- package/dist/services/scan/archetype-service.js +36 -125
- package/dist/services/scan/diff-scope-service.js +35 -15
- package/dist/services/scan/existing-system-service.js +16 -99
- package/dist/services/scan/existing-system-visual-tokens.d.ts +20 -0
- package/dist/services/scan/existing-system-visual-tokens.js +123 -0
- package/dist/services/scan/file-size-policy.d.ts +86 -0
- package/dist/services/scan/file-size-policy.js +184 -0
- package/dist/services/scan/file-size-scan.d.ts +30 -3
- package/dist/services/scan/file-size-scan.js +47 -9
- package/dist/services/scan/karpathy-service.js +6 -1
- package/dist/services/scan/orphan-service.js +86 -20
- package/dist/services/scan/type-sanity-service.js +104 -10
- package/dist/services/sediment/dispose-confirm.d.ts +2 -2
- package/dist/services/sediment/dispose-confirm.js +2 -2
- package/dist/services/sediment/json-schema.d.ts +1 -1
- package/dist/services/sediment/json-schema.js +25 -13
- package/dist/services/sediment/manifest-lint.d.ts +1 -12
- package/dist/services/sediment/manifest-lint.js +1 -7
- package/dist/services/sediment/pool-paths.js +13 -9
- package/dist/services/sediment/pool-read.d.ts +1 -1
- package/dist/services/sediment/pool-read.js +42 -18
- package/dist/services/sediment/pool-rebuild-index.d.ts +1 -1
- package/dist/services/sediment/pool-rebuild-index.js +5 -5
- package/dist/services/sediment/pool-write.d.ts +1 -1
- package/dist/services/sediment/pool-write.js +7 -7
- package/dist/services/sediment/promotion-gate.d.ts +1 -1
- package/dist/services/sediment/promotion-gate.js +9 -9
- package/dist/services/sediment/types.d.ts +8 -8
- package/dist/services/session/binding-status-service.js +14 -4
- package/dist/services/session/caller-binding-service.js +2 -1
- package/dist/services/session/getSessionDir.js +4 -1
- package/dist/services/session/index.d.ts +1 -1
- package/dist/services/session/index.js +1 -1
- package/dist/services/session/resolve-caller-id.js +7 -5
- package/dist/services/session/session-binding-bridge.js +26 -25
- package/dist/services/session/session-checkpoint-service.js +1 -1
- package/dist/services/session/session-file-schema.d.ts +70 -0
- package/dist/services/session/session-file-schema.js +68 -0
- package/dist/services/session/session-manager.d.ts +6 -6
- package/dist/services/session/session-manager.js +30 -29
- package/dist/services/session/session-resume-service.js +2 -1
- package/dist/services/share/bundle-format-constants.d.ts +85 -0
- package/dist/services/share/bundle-format-constants.js +100 -0
- package/dist/services/share/bundle-reader.d.ts +3 -3
- package/dist/services/share/bundle-reader.js +75 -68
- package/dist/services/share/bundle-types.d.ts +3 -70
- package/dist/services/share/bundle-types.js +38 -107
- package/dist/services/share/bundle-writer.d.ts +3 -3
- package/dist/services/share/bundle-writer.js +101 -91
- package/dist/services/share/run-state-contract.d.ts +1 -1
- package/dist/services/share/run-state-contract.js +7 -13
- package/dist/services/signal/cancel-handler.js +2 -1
- package/dist/services/skill/resume-detector.js +6 -4
- package/dist/services/skill/skill-scheduler.js +3 -1
- package/dist/services/skill/skill-search-service.js +4 -4
- package/dist/services/skillhub/release-diff.d.ts +2 -2
- package/dist/services/skillhub/release-diff.js +6 -10
- package/dist/services/skillhub/release-export.d.ts +2 -2
- package/dist/services/skillhub/release-export.js +15 -17
- package/dist/services/skillhub/release-gc-blobs.d.ts +2 -2
- package/dist/services/skillhub/release-gc-blobs.js +4 -4
- package/dist/services/skillhub/release-import.d.ts +2 -2
- package/dist/services/skillhub/release-import.js +13 -12
- package/dist/services/skillhub/release-retain.d.ts +3 -3
- package/dist/services/skillhub/release-retain.js +22 -22
- package/dist/services/skillhub/sqlite-store.d.ts +1 -1
- package/dist/services/skillhub/sqlite-store.js +12 -11
- package/dist/services/skillhub/tar-runtime.js +15 -15
- package/dist/services/skillhub/types.d.ts +4 -4
- package/dist/services/skills/hooks-codegate-superpowers.js +16 -7
- package/dist/services/skills/hooks-settings-service.js +61 -19
- package/dist/services/skills/presence-lease-service.js +26 -16
- package/dist/services/skills/presence-lease-types.d.ts +1 -1
- package/dist/services/skills/presence-lease-types.js +1 -1
- package/dist/services/skills/skill-conformance-service.js +40 -8
- package/dist/services/skills/skill-presence-service.js +16 -14
- package/dist/services/skills/skill-runbook-service.js +2 -1
- package/dist/services/skills/skill-statusline-model.d.ts +128 -0
- package/dist/services/skills/skill-statusline-model.js +163 -0
- package/dist/services/skills/skill-statusline-renderer.js +4 -3
- package/dist/services/skills/skill-statusline-service.d.ts +3 -100
- package/dist/services/skills/skill-statusline-service.js +56 -163
- package/dist/services/skills/statusline-palette.js +11 -6
- package/dist/services/skills/statusline-settings-service.js +2 -1
- package/dist/services/skills/sync-service-support.d.ts +65 -0
- package/dist/services/skills/sync-service-support.js +50 -0
- package/dist/services/skills/sync-service.d.ts +3 -66
- package/dist/services/skills/sync-service.js +15 -53
- package/dist/services/slice/calibration-store.js +8 -2
- package/dist/services/slice/cross-pass-edge-merger.d.ts +3 -28
- package/dist/services/slice/cross-pass-edge-merger.js +13 -63
- package/dist/services/slice/cross-pass-edge-static-scan-support.d.ts +69 -0
- package/dist/services/slice/cross-pass-edge-static-scan-support.js +74 -0
- package/dist/services/slice/granularity-decider.js +2 -2
- package/dist/services/slice/slice-archive-service.js +8 -3
- package/dist/services/slice/slice-check-service.js +19 -8
- package/dist/services/slice/slice-decompose-runners.js +61 -23
- package/dist/services/slice/slice-decompose-service.js +4 -4
- package/dist/services/slice/slice-review-state.js +8 -2
- package/dist/services/smoke/smoke-paths-state.js +9 -2
- package/dist/services/sop/gate-enforce-service.js +9 -2
- package/dist/services/sop/sop-advance-service.js +14 -2
- package/dist/services/sop/sop-check-service.js +16 -4
- package/dist/services/sop/sop-paths.js +3 -1
- package/dist/services/sop/sop-registry-service.js +5 -1
- package/dist/services/sop/sop-service.js +29 -7
- package/dist/services/sop/sop-types.js +5 -1
- package/dist/services/standards/ide-aware-standards-service.js +2 -2
- package/dist/services/standards/loop-engineering-lint.js +12 -5
- package/dist/services/standards/loop-engineering-readiness-lint.js +5 -5
- package/dist/services/standards/migrate-claude-rules-service.js +9 -7
- package/dist/services/standards/missing-standards-detector.js +1 -3
- package/dist/services/standards/project-context-support.d.ts +50 -0
- package/dist/services/standards/project-context-support.js +244 -0
- package/dist/services/standards/project-context-types.d.ts +30 -0
- package/dist/services/standards/project-context-types.js +11 -0
- package/dist/services/standards/project-context.d.ts +10 -22
- package/dist/services/standards/project-context.js +27 -214
- package/dist/services/standards/project-standards-service.js +43 -12
- package/dist/services/standards/standards-render-common.d.ts +15 -0
- package/dist/services/standards/standards-render-common.js +49 -0
- package/dist/services/standards/standards-render.d.ts +1 -2
- package/dist/services/standards/standards-render.js +31 -58
- package/dist/services/standards/ui-library-dispatch-block.js +1 -1
- package/dist/services/tech/tech-change-id-service.js +7 -7
- package/dist/services/tech/tech-service.js +73 -26
- package/dist/services/test-cache/test-cache-service.js +8 -3
- package/dist/services/upgrade/1x-detector-service.js +5 -3
- package/dist/services/upgrade/gitignore-migrate-service.js +5 -5
- package/dist/services/upgrade/upgrade-service.js +36 -13
- package/dist/services/verdict/envelope-parsers.d.ts +84 -0
- package/dist/services/verdict/envelope-parsers.js +146 -0
- package/dist/services/verdict/envelopes.d.ts +50 -60
- package/dist/services/verdict/envelopes.js +18 -156
- package/dist/services/verdict/verdict-aggregator.js +27 -3
- package/dist/services/vm/vm-lease.js +15 -3
- package/dist/services/web/aria-node.d.ts +27 -0
- package/dist/services/web/aria-node.js +1 -0
- package/dist/services/web/browser-acquire.js +3 -1
- package/dist/services/web/browser-cache-probe.d.ts +18 -0
- package/dist/services/web/browser-cache-probe.js +110 -0
- package/dist/services/web/browser-session-manager.js +6 -2
- package/dist/services/web/daemon-registry.js +4 -1
- package/dist/services/web/playwright-loader.d.ts +2 -70
- package/dist/services/web/playwright-types.d.ts +75 -0
- package/dist/services/web/playwright-types.js +1 -0
- package/dist/services/web/snapshot-pruner.d.ts +2 -23
- package/dist/services/web/snapshot-pruner.js +8 -3
- package/dist/services/web/web-daemon-service.js +1 -3
- package/dist/services/web/web-install-service.d.ts +3 -32
- package/dist/services/web/web-install-service.js +21 -100
- package/dist/services/web/web-install-types.d.ts +31 -0
- package/dist/services/web/web-install-types.js +13 -0
- package/dist/services/web/web-login-profile.js +7 -5
- package/dist/services/web/web-protocol.js +3 -1
- package/dist/services/workflow/pipeline-verify-gate-support.js +63 -17
- package/dist/services/workflow/pipeline-verify-service.js +17 -6
- package/dist/services/workflow/plan-reader.js +5 -1
- package/dist/services/workflow/plan-refresher.js +20 -6
- package/dist/services/workflow/plan-trigger-detector.js +31 -8
- package/dist/services/workflow/provision-dispatch-node.js +8 -3
- package/dist/services/workflow/workflow-autonomous-resume-helpers.d.ts +3 -1
- package/dist/services/workflow/workflow-autonomous-resume-helpers.js +35 -144
- package/dist/services/workflow/workflow-autonomous-resume-validation.d.ts +16 -0
- package/dist/services/workflow/workflow-autonomous-resume-validation.js +155 -0
- package/dist/services/workflow/workflow-autonomous-service.js +25 -13
- package/dist/services/workflow/workflow-graph-store-helpers.d.ts +51 -0
- package/dist/services/workflow/workflow-graph-store-helpers.js +111 -0
- package/dist/services/workflow/workflow-graph-store.d.ts +2 -35
- package/dist/services/workflow/workflow-graph-store.js +30 -107
- package/dist/services/workflow/workflow-graph-types.js +3 -3
- package/dist/services/workflow/workflow-inflight-probe.js +14 -7
- package/dist/services/workflow/workflow-loader.js +5 -1
- package/dist/services/workflow/workflow-node-lifecycle.js +22 -21
- package/dist/services/workflow/workflow-presence-lifecycle-types.d.ts +50 -0
- package/dist/services/workflow/workflow-presence-lifecycle-types.js +1 -0
- package/dist/services/workflow/workflow-presence-lifecycle.d.ts +2 -42
- package/dist/services/workflow/workflow-presence-lifecycle.js +19 -21
- package/dist/services/workflow/workflow-router-service.js +169 -27
- package/dist/services/workflow/workflow-skip-service.js +14 -4
- package/dist/services/workflow/workflow-spec-builders.d.ts +15 -0
- package/dist/services/workflow/workflow-spec-builders.js +121 -0
- package/dist/services/workflow/workflow-spec.d.ts +7 -6
- package/dist/services/workflow/workflow-spec.js +22 -116
- package/dist/services/workflow/workflow-state-store.js +2 -1
- package/dist/services/workspace/migrate-1-4-1-service.js +80 -14
- package/dist/services/workspace/migrate-service.js +66 -16
- package/dist/services/workspace/reconcile-migrate.js +6 -2
- package/dist/services/workspace/reconcile-service.js +37 -16
- package/dist/services/workspace/workspace-claude-settings-materializer.js +7 -5
- package/dist/services/workspace/workspace-clean-service.js +4 -1
- package/dist/services/workspace/workspace-service.js +11 -3
- package/dist/services/workspace/workspace-state-service.js +1 -1
- package/dist/services/worktree/git-worktree-parser.js +13 -3
- package/dist/services/worktree/host-worktree-reconciler.js +8 -4
- package/dist/services/worktree/long-path-cleanup.js +23 -6
- package/dist/services/worktree/worktree-gc-guard.js +3 -1
- package/dist/services/worktree/worktree-lease-types.d.ts +53 -0
- package/dist/services/worktree/worktree-lease-types.js +10 -0
- package/dist/services/worktree/worktree-lease.d.ts +2 -44
- package/dist/shared/array-guards.d.ts +30 -0
- package/dist/shared/array-guards.js +32 -0
- package/dist/shared/format-md-compact-internals.d.ts +26 -0
- package/dist/shared/format-md-compact-internals.js +125 -0
- package/dist/shared/format-md-compact.js +1 -117
- package/dist/shared/fs-utils.js +2 -1
- package/dist/shared/incrementing-number.js +3 -3
- package/dist/shared/json-parse.d.ts +71 -0
- package/dist/shared/json-parse.js +32 -0
- package/dist/shared/json-schema-mini.js +3 -1
- package/dist/shared/path-safety.js +3 -1
- package/dist/shared/path-utils.d.ts +20 -0
- package/dist/shared/path-utils.js +26 -0
- package/dist/shared/planner-response.js +3 -3
- package/dist/shared/stale-policy.js +1 -3
- package/dist/skills/peaks-maker/index.js +11 -11
- package/package.json +31 -8
- package/scripts/clean-dist.mjs +1 -2
- package/scripts/copy-templates.mjs +2 -4
- package/scripts/install-skills.mjs +305 -125
- package/scripts/sync-version.mjs +25 -8
- package/scripts/watch.mjs +8 -2
- package/skills/peaks-code/SKILL.md +1 -1
|
@@ -1,400 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* N4 — the reply carried no text block at all.
|
|
29
|
-
*
|
|
30
|
-
* Measured 2/3 on this repo's own machine, and it is NOT truncation: the
|
|
31
|
-
* provider answered with a response whose `content` has no `text` block (a
|
|
32
|
-
* reasoning-only turn, a refusal, or a content filter), so there is no JSON to
|
|
33
|
-
* parse and no budget to raise — an operator sent to "raise the budget" for
|
|
34
|
-
* this failure would be sent the wrong way. It gets its own class, its own
|
|
35
|
-
* `code`, and a message that says so, so it is diagnosable instead of being
|
|
36
|
-
* flattened into "not valid JSON".
|
|
37
|
-
*/
|
|
38
|
-
export class EmptyReviewReplyError extends Error {
|
|
39
|
-
code = 'EMPTY_FINAL_REVIEW_REPLY';
|
|
40
|
-
constructor(message) {
|
|
41
|
-
super(message);
|
|
42
|
-
this.name = 'EmptyReviewReplyError';
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* How many times an empty reply is retried before it is reported. The failure
|
|
47
|
-
* was 2/3 on the observed machine — intermittent, not systematic — so a small
|
|
48
|
-
* bounded retry converts most of it into a completed review, while 3 attempts
|
|
49
|
-
* keeps a genuinely broken provider from being hammered.
|
|
50
|
-
*/
|
|
51
|
-
export const MAX_EMPTY_REPLY_ATTEMPTS = 3;
|
|
52
|
-
/** The runner's own wording for "the response had no text block to return". */
|
|
53
|
-
function isEmptyReplyError(error) {
|
|
54
|
-
return error instanceof Error && /no text block/i.test(error.message);
|
|
55
|
-
}
|
|
56
|
-
function errorText(error) {
|
|
57
|
-
return error instanceof Error ? error.message : String(error);
|
|
58
|
-
}
|
|
59
|
-
/* ------------------------------------------------------------------ *
|
|
60
|
-
* D1 — on-disk evidence collection.
|
|
61
|
-
*
|
|
62
|
-
* The reviewer LLM has NO tools and NO filesystem access: whatever the
|
|
63
|
-
* service does not inline into the prompt does not exist for it. Feeding it
|
|
64
|
-
* only the success criteria forced a guess — the observed failure mode was a
|
|
65
|
-
* 4/4 `inconclusive` verdict, and the dangerous one is an invented `pass`
|
|
66
|
-
* that SKILL.md would read as a clean handoff. Everything below exists so the
|
|
67
|
-
* verdicts rest on what is actually on disk, and so a missing artifact is
|
|
68
|
-
* reported as MISSING instead of being silently skipped.
|
|
69
|
-
* ------------------------------------------------------------------ */
|
|
70
|
-
/**
|
|
71
|
-
* Total evidence budget across all sources. 40 KB ≈ 10k tokens of input, which
|
|
72
|
-
* keeps the prompt far inside any modern context window. Sources that do not
|
|
73
|
-
* fit are reported as OMITTED — never dropped silently.
|
|
74
|
-
*
|
|
75
|
-
* This is an INPUT cap and stays fixed per run. The output ceiling that has to
|
|
76
|
-
* sit opposite it is derived per call by `outputBudgetForEvidence()` below —
|
|
77
|
-
* the two used to drift apart, and that drift was the defect.
|
|
78
|
-
*
|
|
79
|
-
* RE-EVALUATED 2026-09-12 (F-BLOCK) — 32 KiB did NOT hold once the tenth
|
|
80
|
-
* source (`final-review-pre-post-diff`) was appended. Measured on this repo's
|
|
81
|
-
* own run (`2026-09-12-session-e37ef0`, rid
|
|
82
|
-
* `2026-09-12-codegraph-exclude-integrity`), the ten sources are 2,621 / 8,164
|
|
83
|
-
* / 9,492 / 10,839 / 11,422 / 13,050 / 13,462 / 13,852 / 17,745 / 20,543
|
|
84
|
-
* bytes — 121,190 bytes on disk, 76,321 bytes once the 8 KiB per-file cap is
|
|
85
|
-
* applied. Four sources at the cap spent the old 32,768 to the byte, so the
|
|
86
|
-
* appended tenth (added LAST, by design) was the first block the budget
|
|
87
|
-
* dropped — on every run, and on the compact set QA measured too (9 x 3.8 KB
|
|
88
|
-
* ≈ 34 KB, which the old cap already could not hold).
|
|
89
|
-
*
|
|
90
|
-
* 40 KiB is a budget that fits the measured ten-source set at the allocator's
|
|
91
|
-
* per-source unit (5 x 8 KiB units + the smaller ones), and it is the largest
|
|
92
|
-
* this budget may grow to today: the derived output ceiling is
|
|
93
|
-
* `3000 + 12288 + bytes/4`, so it reaches MAX_OUTPUT_TOKENS (32,000) at exactly
|
|
94
|
-
* 66,848 bytes of inlined evidence and is clamped from there on — a cap at or
|
|
95
|
-
* above that point buys the reviewer no more output room at all.
|
|
96
|
-
*
|
|
97
|
-
* F-CAP — an earlier version of this comment claimed 40 KiB (40,960 =
|
|
98
|
-
* 10 x 4,096) was "the smallest cap that affords one floor-sized slice to EVERY
|
|
99
|
-
* source in the ten-source set". That was arithmetic about a budget, not a
|
|
100
|
-
* statement about the allocator: sources are capped at
|
|
101
|
-
* MAX_EVIDENCE_BYTES_PER_FILE (10 KiB each, derived — see below) while a floor
|
|
102
|
-
* is per DIMENSION (four of them), so 10 x 4,096 was never what any source
|
|
103
|
-
* received. Re-measured on
|
|
104
|
-
* the same two saturated fixtures (QA round 4: the 9 x 40 KB fixture and this
|
|
105
|
-
* repo's own file sizes), raising the cap from 32 KiB to 40 KiB left **4 of the
|
|
106
|
-
* 10 sources inlined with zero bytes** — `rd/security-review`,
|
|
107
|
-
* `rd/bug-analysis`, `prd/handoff` and the appended
|
|
108
|
-
* `final-review-pre-post-diff` — and it did not deliver the baseline either;
|
|
109
|
-
* all it did was change WHICH source was starved (at 32 KiB that source was
|
|
110
|
-
* `rd/code-review`).
|
|
111
|
-
*
|
|
112
|
-
* So read this constant as "how much evidence fits", never as "which sources
|
|
113
|
-
* arrive". Arrival is decided by the allocator below (one whole unit per source,
|
|
114
|
-
* with a dimension's floor reserving its holder's unit) and, for the two sources
|
|
115
|
-
* a dimension's verdict may not outlive, by the delivery gates on top of it.
|
|
116
|
-
* And note the honest consequence, stated rather than papered over: on a
|
|
117
|
-
* saturated run the allocator still omits sources, so `prd/handoff.md` and the
|
|
118
|
-
* pre/post diff can be dropped — which is exactly why
|
|
119
|
-
* `enforceScopeContractDelivery()` and `enforcePrePostDiffAvailability()` exist
|
|
120
|
-
* and why a `pass` on those two dimensions is keyed on delivery, not on
|
|
121
|
-
* existence.
|
|
122
|
-
*/
|
|
123
|
-
export const MAX_EVIDENCE_BYTES_TOTAL = 40 * 1024;
|
|
124
|
-
/**
|
|
125
|
-
* Per-file evidence cap — and the allocator's UNIT. DERIVED, never typed.
|
|
126
|
-
*
|
|
127
|
-
* F5 — this used to be the literal `8 * 1024`, and the whole floor guarantee
|
|
128
|
-
* below was silently conditioned on `4 x perFile <= total`: the reservation
|
|
129
|
-
* promises every pending holder its own unit, and that promise holds only while
|
|
130
|
-
* the four units fit the budget together. At `8 * 1024` the inequality held by
|
|
131
|
-
* coincidence (4 x 8,192 = 32,768 <= 40,960) and nothing in the code said so —
|
|
132
|
-
* raising the cap past 10,240 would have starved all four dimensions in the
|
|
133
|
-
* same run, which reads as four independent red gates rather than one broken
|
|
134
|
-
* constant. The cap is therefore DIVIDED OUT OF the total: the relation is
|
|
135
|
-
* definitional instead of remembered, and `assertFloorReservationAffordable()`
|
|
136
|
-
* still checks it at the point of use (the division must also be exact).
|
|
137
|
-
*
|
|
138
|
-
* The value is 10,240 because that is `40,960 / 4` — the largest unit the
|
|
139
|
-
* four-dimension reservation can afford. It is also the smallest cap at which
|
|
140
|
-
* the decisive evidence source of this repo's own run can be delivered WHOLE:
|
|
141
|
-
* `qa/test-reports/<rid>.md` measured 9,492 bytes there, and `whole` is the
|
|
142
|
-
* delivery rule for every source whose producer publishes no conclusion literal
|
|
143
|
-
* (see `isDelivered`), so a cap under 9,492 makes `problem-resolution` and
|
|
144
|
-
* `no-new-bugs` undeliverable on every run — the always-red gate this module
|
|
145
|
-
* refuses to ship. Enforced in BYTES against the raw buffer, so multi-byte
|
|
146
|
-
* (CJK) content cannot slip past the cap.
|
|
147
|
-
*
|
|
148
|
-
* A source is inlined as `min(bytes, this cap)` — its whole unit — or not at
|
|
149
|
-
* all. So a `TRUNCATED` source block can only ever mean "the FILE is bigger
|
|
150
|
-
* than this cap"; it can no longer mean "the budget ran out while this file was
|
|
151
|
-
* being copied in". That distinction is the point of the all-or-nothing
|
|
152
|
-
* allocator below: the second reading is what let one byte of a 4,226-byte
|
|
153
|
-
* artifact be counted as delivered evidence (F-BLOCK-1BYTE).
|
|
154
|
-
*/
|
|
155
|
-
export const MAX_EVIDENCE_BYTES_PER_FILE = MAX_EVIDENCE_BYTES_TOTAL / REQUIRED_DIMENSIONS.length;
|
|
156
|
-
/* ------------------------------------------------------------------ *
|
|
157
|
-
* The anti-starvation reservation (the "floor").
|
|
158
|
-
*
|
|
159
|
-
* The allocator would otherwise be strictly first-come-first-served, and with
|
|
160
|
-
* this repo's own evidence set (`2026-09-12-session-e37ef0`, measured) the
|
|
161
|
-
* first four sources consumed the entire 32 KiB cap — 4 x 8,192 = 32,768, to
|
|
162
|
-
* the byte — before source 5 was even opened. `existing-functionality-intact`
|
|
163
|
-
* is supplied ONLY by `rd/tech-doc.md` (6th), `prd/handoff.md` (9th) and the
|
|
164
|
-
* appended pre/post diff (10th), so that dimension reached the reviewer with
|
|
165
|
-
* zero evidence on every run and its verdict was structurally locked to
|
|
166
|
-
* `inconclusive` no matter how good the work was. A gate that is always red is
|
|
167
|
-
* noise, and an operator trained to ignore noise has no gate at all — the same
|
|
168
|
-
* harm as a gate that never fires, only quieter.
|
|
169
|
-
*
|
|
170
|
-
* So each dimension with at least one readable source on disk gets one
|
|
171
|
-
* reservation, held for the FIRST such source; no source that is not that
|
|
172
|
-
* holder may spend it, and it is released the instant its holder is served.
|
|
173
|
-
* `floorHolders()` below decides who holds what and `collectEvidence()` is the
|
|
174
|
-
* only thing that spends it.
|
|
175
|
-
*
|
|
176
|
-
* WHAT A RESERVATION IS SIZED AT (all-or-nothing): the holder's own UNIT — its
|
|
177
|
-
* whole `min(bytes, perFileCap)` slice — not a fixed number of bytes. Under the
|
|
178
|
-
* all-or-nothing allocator a holder needs its entire unit to be served at all,
|
|
179
|
-
* so reserving less than that would promise something the allocator cannot
|
|
180
|
-
* keep. The promise stays affordable because a holder is at most one per
|
|
181
|
-
* dimension and there are four dimensions:
|
|
182
|
-
*
|
|
183
|
-
* REQUIRED_DIMENSIONS.length x MAX_EVIDENCE_BYTES_PER_FILE
|
|
184
|
-
* = 4 x 10,240 = 40,960 == MAX_EVIDENCE_BYTES_TOTAL = 40,960
|
|
185
|
-
*
|
|
186
|
-
* L4 — those are the DERIVED numbers and the relation is an EQUALITY, not an
|
|
187
|
-
* inequality with room to spare: the reservation spends the entire budget and
|
|
188
|
-
* leaves ZERO slack. This block used to read `= 4 x 8,192 = 32,768 <= 40,960`,
|
|
189
|
-
* which was true of the typed-literal cap and stopped being true the moment the
|
|
190
|
-
* cap was divided out of the total; left as written it read as 8 KiB of
|
|
191
|
-
* headroom that does not exist and would have invited "just raise the cap a
|
|
192
|
-
* little". One byte more per file breaks the invariant outright, which is why
|
|
193
|
-
* the cap is derived rather than typed and why
|
|
194
|
-
* `assertFloorReservationAffordable()` re-checks the derivation (exact integer
|
|
195
|
-
* quotient, product <= total) at the point of use instead of trusting this
|
|
196
|
-
* comment (pinned by a test).
|
|
197
|
-
*
|
|
198
|
-
* That equality is the whole invariant: at every step `budgetLeft` covers the
|
|
199
|
-
* units still owed to the pending holders, so a holder is always served once it
|
|
200
|
-
* is reached, and the invariant cannot silently rot while the three constants
|
|
201
|
-
* keep their published relationship.
|
|
202
|
-
*
|
|
203
|
-
* A reservation is held for a DIMENSION, not for any particular SOURCE:
|
|
204
|
-
* reserving one for `final-review-pre-post-diff` on top of its dimension's
|
|
205
|
-
* would make "a computed baseline the reviewer never saw" UNREACHABLE, and an
|
|
206
|
-
* unreachable branch inside a gate is one nobody can verify. The delivery gates
|
|
207
|
-
* below are the guarantee; see `enforcePrePostDiffAvailability` and
|
|
208
|
-
* `enforceScopeContractDelivery`.
|
|
209
|
-
* ------------------------------------------------------------------ */
|
|
210
|
-
/* ------------------------------------------------------------------ *
|
|
211
|
-
* D1 layer 3 — the OUTPUT budget.
|
|
212
|
-
*
|
|
213
|
-
* The input side above was raised (32 KiB then, 40 KiB today) but the
|
|
214
|
-
* output ceiling stayed hard-coded at 3000 tokens. Measured on this repo's own
|
|
215
|
-
* run (rid `2026-09-12-codegraph-exclude-integrity`, 9 sources, 32 KiB
|
|
216
|
-
* inlined, real anthropic provider): 3/3 attempts failed — 2x
|
|
217
|
-
* INCOMPLETE_FINAL_REVIEW with the JSON cut off mid-string, 1x a reply with no
|
|
218
|
-
* text block. A 4-dimension envelope (4 x `summary` + `evidence[]` +
|
|
219
|
-
* `confidence`, plus `overallSummary`) does not fit in 3000 tokens once the
|
|
220
|
-
* model has ~8k tokens of evidence it is required to cite.
|
|
221
|
-
*
|
|
222
|
-
* N4 — the first fix of this layer derived the ceiling as "3000 + bytes/8" and
|
|
223
|
-
* called 8192 "a backstop only: it does not bind today", citing a 4775-token
|
|
224
|
-
* measurement. Both claims were falsified by re-measurement on the SAME
|
|
225
|
-
* machine the gate ships on (`deepseek-flash[1M]` via
|
|
226
|
-
* `api.deepseek.com/anthropic`, 2026-09-12, QA run 3/3 red + orchestrator
|
|
227
|
-
* re-run 3/3 red, rid `2026-09-12-codegraph-exclude-integrity`, byte-identical
|
|
228
|
-
* prompt):
|
|
229
|
-
*
|
|
230
|
-
* max_tokens=7096 (what the old formula produced for the 32 KiB pack)
|
|
231
|
-
* -> TRUNCATED. `output_tokens=7096`, 14078 characters.
|
|
232
|
-
* max_tokens=8192 -> TRUNCATED, `output_tokens=8192`, 574 characters.
|
|
233
|
-
* max_tokens=16000 -> COMPLETE, `output_tokens=10108`.
|
|
234
|
-
* max_tokens=32000 -> COMPLETE, `output_tokens=8883`.
|
|
235
|
-
*
|
|
236
|
-
* So 8192 WAS the binding constraint and was BELOW the requirement: a gate
|
|
237
|
-
* whose budget is short is worse than a red gate, because it releases the
|
|
238
|
-
* envelope only when the model happens to be terse.
|
|
239
|
-
*
|
|
240
|
-
* The 8:1 bytes-per-token term prices the VISIBLE envelope against the evidence
|
|
241
|
-
* the model must cite, but it was never the whole cost. On a reasoning model
|
|
242
|
-
* `max_tokens` also caps the hidden reasoning that precedes the first character
|
|
243
|
-
* of output, and that cost is invisible to a bytes-per-token formula (the
|
|
244
|
-
* 574-character run above is exactly that: the whole budget consumed before the
|
|
245
|
-
* envelope began). `REASONING_HEADROOM_TOKENS` below is that missing term.
|
|
246
|
-
*
|
|
247
|
-
* A 16384 ceiling with 6144 of headroom (derived 13240) was then measured on
|
|
248
|
-
* the SAME machine and shown to be too thin too: 10 real-machine runs, 2
|
|
249
|
-
* failures, BOTH genuine truncation — and the second one reported BOTH signals
|
|
250
|
-
* at once ("reply ends mid-structure AND provider-reported output reached the
|
|
251
|
-
* ceiling") with `maxTokens=13240`. The requirement is therefore not "derived
|
|
252
|
-
* >= the one 10108 sample" but "derived comfortably above every observed
|
|
253
|
-
* truncation point", which is what the constants below are now sized for.
|
|
254
|
-
* ------------------------------------------------------------------ */
|
|
255
|
-
/**
|
|
256
|
-
* Floor — also the value that shipped before this fix, so no evidence set can
|
|
257
|
-
* end up with a smaller budget than it had. ~3000 tokens is enough for the
|
|
258
|
-
* envelope skeleton plus a short paragraph per dimension.
|
|
259
|
-
*/
|
|
260
|
-
export const MIN_OUTPUT_TOKENS = 3_000;
|
|
261
|
-
/**
|
|
262
|
-
* Headroom for the part of the reply that is not the envelope.
|
|
263
|
-
*
|
|
264
|
-
* A Messages-API-compatible endpoint applies `max_tokens` to the WHOLE
|
|
265
|
-
* response, and a reasoning model spends it on hidden reasoning before it
|
|
266
|
-
* emits a single character of the 4-dim envelope. Measured on this repo's own
|
|
267
|
-
* machine (2026-09-12, rid `2026-09-12-codegraph-exclude-integrity`,
|
|
268
|
-
* `deepseek-flash[1M]` via `api.deepseek.com/anthropic`): `max_tokens=8192`
|
|
269
|
-
* came back with `output_tokens=8192` and only **574** visible characters —
|
|
270
|
-
* the entire budget went to reasoning. A bytes-per-token estimate of the
|
|
271
|
-
* visible output cannot see that cost, which is why the previous formula
|
|
272
|
-
* budgeted 7096 for a reply that needs 10108 — and why a 13240 budget still
|
|
273
|
-
* truncated on 2 of 10 real runs.
|
|
274
|
-
*
|
|
275
|
-
* 12288 (12 KiB) is sized so the largest evidence pack the input caps allow
|
|
276
|
-
* lands at 25528 (see the formula below; the input cap is 40 KiB as of the
|
|
277
|
-
* F-BLOCK re-evaluation, and this floor was checked against the raised cap, not
|
|
278
|
-
* against the 32 KiB the measurements above were taken at — a 12 KiB headroom
|
|
279
|
-
* under a bigger cap is the conservative direction) — about 1.9x the largest
|
|
280
|
-
* value ever OBSERVED to truncate (13240), which is the margin the observed
|
|
281
|
-
* variance asks for. The numbers are in the block comment above.
|
|
282
|
-
*/
|
|
283
|
-
export const REASONING_HEADROOM_TOKENS = 12 * 1024;
|
|
284
|
-
/**
|
|
285
|
-
* Ceiling, 32_000: the value a real run on this machine was forced to in order
|
|
286
|
-
* to complete the envelope at all, and the largest this endpoint was observed
|
|
287
|
-
* to accept. 16384 was tried first and truncated 2/10 — a ceiling that is
|
|
288
|
-
* merely "above the last successful measurement" is not above the requirement,
|
|
289
|
-
* because the requirement moves with the model's reasoning spend.
|
|
290
|
-
*
|
|
291
|
-
* A model that caps output at 8192 will refuse this. That is still strictly
|
|
292
|
-
* better than shipping a budget measured to be too small, and the env lever
|
|
293
|
-
* below lets an operator pull it down without a code change.
|
|
294
|
-
*/
|
|
295
|
-
export const MAX_OUTPUT_TOKENS = 32_000;
|
|
296
|
-
/**
|
|
297
|
-
* Environment lever. The old failure message told the operator to "raise the
|
|
298
|
-
* budget" while the budget was a module constant with no CLI flag and no env
|
|
299
|
-
* var — an instruction that could not be carried out from any surface the
|
|
300
|
-
* operator has. This is that lever.
|
|
301
|
-
*
|
|
302
|
-
* The value is the output ceiling in tokens; it OVERRIDES the derivation below
|
|
303
|
-
* (it is not a bonus added to it). Unset/invalid/out-of-range handling is in
|
|
304
|
-
* `resolveOutputBudget`.
|
|
305
|
-
*/
|
|
306
|
-
export const MAX_OUTPUT_TOKENS_ENV = 'PEAKS_FINAL_REVIEW_MAX_OUTPUT_TOKENS';
|
|
307
|
-
/**
|
|
308
|
-
* Absolute upper bound the env lever may reach. An endpoint that accepts
|
|
309
|
-
* `max_tokens` at all accepts this; anything above it is a typo (a stray extra
|
|
310
|
-
* digit), not an intent, and clamping is safer than sending it.
|
|
311
|
-
*/
|
|
312
|
-
export const HARD_MAX_OUTPUT_TOKENS = 64_000;
|
|
313
|
-
/**
|
|
314
|
-
* Inlined bytes that buy one extra output token — 4:1.
|
|
315
|
-
*
|
|
316
|
-
* This term prices the visible envelope (4 x `summary` + `evidence[]` +
|
|
317
|
-
* `confidence` + `overallSummary`) against the evidence the model is required
|
|
318
|
-
* to cite. It was 8:1, which put the 32 KiB pack at 4096 tokens of visible
|
|
319
|
-
* output; the same pack has been observed to complete at 10108 and to truncate
|
|
320
|
-
* at 13240, so 8:1 was pricing the visible side BELOW its own measurement.
|
|
321
|
-
* 4:1 doubles it to 8192. It is still not treated as the whole budget — see
|
|
322
|
-
* `REASONING_HEADROOM_TOKENS`.
|
|
323
|
-
*/
|
|
324
|
-
export const EVIDENCE_BYTES_PER_OUTPUT_TOKEN = 4;
|
|
325
|
-
/**
|
|
326
|
-
* Output ceiling for a call whose prompt carries `includedEvidenceBytes` bytes
|
|
327
|
-
* of inlined evidence. Pure, total, and clamped on both ends — the same
|
|
328
|
-
* evidence pack always yields the same budget.
|
|
329
|
-
*/
|
|
330
|
-
export function outputBudgetForEvidence(includedEvidenceBytes) {
|
|
331
|
-
const scaled = MIN_OUTPUT_TOKENS +
|
|
332
|
-
REASONING_HEADROOM_TOKENS +
|
|
333
|
-
Math.ceil(Math.max(0, includedEvidenceBytes) / EVIDENCE_BYTES_PER_OUTPUT_TOKEN);
|
|
334
|
-
return Math.min(MAX_OUTPUT_TOKENS, Math.max(MIN_OUTPUT_TOKENS, scaled));
|
|
335
|
-
}
|
|
336
|
-
/**
|
|
337
|
-
* The budget the call actually uses: the derived one, unless
|
|
338
|
-
* `PEAKS_FINAL_REVIEW_MAX_OUTPUT_TOKENS` overrides it.
|
|
339
|
-
*
|
|
340
|
-
* An override that is not a positive integer THROWS rather than being ignored:
|
|
341
|
-
* a silent fallback would leave an operator who passed a bad value with the
|
|
342
|
-
* exact experience this lever exists to remove — a budget they cannot move.
|
|
343
|
-
* Out-of-range values are clamped, not rejected, so a model needing more than
|
|
344
|
-
* `HARD_MAX_OUTPUT_TOKENS` (or a model needing less than `MIN_OUTPUT_TOKENS`)
|
|
345
|
-
* still gets a call made.
|
|
346
|
-
*/
|
|
347
|
-
export function resolveOutputBudget(includedEvidenceBytes, env = process.env) {
|
|
348
|
-
const derived = outputBudgetForEvidence(includedEvidenceBytes);
|
|
349
|
-
const raw = env[MAX_OUTPUT_TOKENS_ENV];
|
|
350
|
-
if (raw === undefined || raw.trim() === '')
|
|
351
|
-
return derived;
|
|
352
|
-
const parsed = Number.parseInt(raw.trim(), 10);
|
|
353
|
-
if (!Number.isInteger(parsed) || parsed <= 0 || String(parsed) !== raw.trim()) {
|
|
354
|
-
throw new Error(`${MAX_OUTPUT_TOKENS_ENV} must be a positive integer number of output tokens (got "${raw}"); ` +
|
|
355
|
-
`unset it to use the derived budget of ${String(derived)} tokens.`);
|
|
356
|
-
}
|
|
357
|
-
return Math.min(HARD_MAX_OUTPUT_TOKENS, Math.max(MIN_OUTPUT_TOKENS, parsed));
|
|
358
|
-
}
|
|
359
|
-
/**
|
|
360
|
-
* The one source whose ABSENCE from the delivered prompt is not merely a
|
|
361
|
-
* missing file: it is the only source in the set that is a before/after
|
|
362
|
-
* comparison, and `existing-functionality-intact` is defined by it. Named once
|
|
363
|
-
* so the source builder and the delivery check cannot drift apart.
|
|
364
|
-
*/
|
|
365
|
-
const PRE_POST_DIFF_SOURCE_KEY = 'final-review-pre-post-diff';
|
|
366
|
-
/**
|
|
367
|
-
* The approved-scope contract. Also named once: it is the source
|
|
368
|
-
* `functional-completeness` is defined against and the one its delivery gate
|
|
369
|
-
* keys on.
|
|
370
|
-
*/
|
|
371
|
-
const SCOPE_CONTRACT_SOURCE_KEY = 'prd-handoff';
|
|
372
|
-
/**
|
|
373
|
-
* The source a DIMENSION'S DELIVERY GATE depends on — i.e. the source the
|
|
374
|
-
* dimension's `pass` may not outlive. `floorHolders` reserves that source's
|
|
375
|
-
* unit, and this table is why the reservation protects the RIGHT source.
|
|
376
|
-
*
|
|
377
|
-
* F1 — the floor used to go to the FIRST source that merely *mentioned* the
|
|
378
|
-
* dimension (`qa-test-report`, index 0, and `rd/tech-doc`, index 6), while both
|
|
379
|
-
* delivery gates keyed on sources at the very END of the order
|
|
380
|
-
* (`prd-handoff`, index 8, and the appended pre/post diff, index 9) that no
|
|
381
|
-
* reservation covered. On this repo's own run the budget was exhausted at
|
|
382
|
-
* source 4, so BOTH gate sources were omitted on every run — necessarily, not
|
|
383
|
-
* accidentally — while 8,192 bytes of floor were spent on `rd/tech-doc`, the
|
|
384
|
-
* one source prompt rule 6 declares insufficient for that same dimension.
|
|
385
|
-
*
|
|
386
|
-
* measured floor reservation after the fix:
|
|
387
|
-
* prd/handoff.md 8,164 + api-diff.txt 4,713 + qa/test-reports 9,492
|
|
388
|
-
* = 22,369 <= MAX_EVIDENCE_BYTES_TOTAL 40,960
|
|
389
|
-
*
|
|
390
|
-
* so all three holders are served WHOLE and the two gate-backed dimensions
|
|
391
|
-
* become deliverable again. A dimension absent from this table has no delivery
|
|
392
|
-
* gate, and its floor stays with the earliest source that supports it.
|
|
393
|
-
*/
|
|
394
|
-
const GATE_SOURCE_FOR_DIMENSION = {
|
|
395
|
-
'functional-completeness': SCOPE_CONTRACT_SOURCE_KEY,
|
|
396
|
-
'existing-functionality-intact': PRE_POST_DIFF_SOURCE_KEY
|
|
397
|
-
};
|
|
1
|
+
// src/services/final-review/final-review-service.ts
|
|
2
|
+
//
|
|
3
|
+
// The final-review service — public entry point. C wave 7 split its
|
|
4
|
+
// 1,857-line module into cohesive siblings in this directory
|
|
5
|
+
// (contract, evidence-budget, output-budget, evidence-sources,
|
|
6
|
+
// evidence-collect, prompt, delivery, gates, verdicts, reviewer,
|
|
7
|
+
// fifth-dim, runner). Every public name is re-exported below, so no
|
|
8
|
+
// importer's path or symbol changed.
|
|
9
|
+
//
|
|
10
|
+
// What stays HERE, deliberately: the delivery predicate and the
|
|
11
|
+
// dimension-routing gates that tests/unit/final-review/final-review-
|
|
12
|
+
// service.test.ts (guard C) pins by parsing this file — guard C reads
|
|
13
|
+
// THIS path only, and the test file is not this leaf to edit. The three
|
|
14
|
+
// gate implementations moved to final-review-gates.ts with the delivery
|
|
15
|
+
// judgement injected (their measured-history comments moved with them,
|
|
16
|
+
// attached); the thin same-named wrappers below are the routing the
|
|
17
|
+
// guard's by-name assertions read. evidenceSourcesFor keeps its
|
|
18
|
+
// function here because the guard regexes its table; the nine base
|
|
19
|
+
// sources it now composes live in final-review-evidence-sources.ts
|
|
20
|
+
import { API_DIFF_ARTIFACT_SEGMENTS, PRE_POST_DIFF_VERDICT_MARKER } from './pre-post-diff.js';
|
|
21
|
+
import { baseEvidenceSources, PRE_POST_DIFF_SOURCE_KEY } from './final-review-evidence-sources.js';
|
|
22
|
+
import { attachPrePostDiffEvidence as gateAttachPrePostDiffEvidence, enforcePrePostDiffAvailability as gateEnforcePrePostDiffAvailability, enforceScopeContractDelivery as gateEnforceScopeContractDelivery } from './final-review-gates.js';
|
|
23
|
+
import { IncompleteFinalReviewError } from './final-review-contract.js';
|
|
24
|
+
export { IncompleteFinalReviewError };
|
|
398
25
|
/**
|
|
399
26
|
* The on-disk sources, in allocation order.
|
|
400
27
|
*
|
|
@@ -413,75 +40,8 @@ const GATE_SOURCE_FOR_DIMENSION = {
|
|
|
413
40
|
* correct for the other nine sources; what changed is that a `pass` on this
|
|
414
41
|
* dimension now requires the block to have been DELIVERED, not merely computed.
|
|
415
42
|
*/
|
|
416
|
-
function evidenceSourcesFor(rid, prePostDiffAvailable) {
|
|
417
|
-
const sources =
|
|
418
|
-
{
|
|
419
|
-
key: 'qa-test-report',
|
|
420
|
-
label: 'QA execution report (per-command pass/fail counts)',
|
|
421
|
-
segments: ['qa', 'test-reports', `${rid}.md`],
|
|
422
|
-
supports: ['functional-completeness', 'problem-resolution', 'no-new-bugs'],
|
|
423
|
-
delivery: { kind: 'whole' }
|
|
424
|
-
},
|
|
425
|
-
{
|
|
426
|
-
key: 'qa-test-cases',
|
|
427
|
-
label: 'QA test cases (acceptance-criterion to test mapping)',
|
|
428
|
-
segments: ['qa', 'test-cases', `${rid}.md`],
|
|
429
|
-
supports: ['functional-completeness', 'problem-resolution'],
|
|
430
|
-
delivery: { kind: 'whole' }
|
|
431
|
-
},
|
|
432
|
-
{
|
|
433
|
-
key: 'qa-security-findings',
|
|
434
|
-
label: 'QA security findings',
|
|
435
|
-
segments: ['qa', `security-findings-${rid}.md`],
|
|
436
|
-
supports: ['no-new-bugs'],
|
|
437
|
-
delivery: { kind: 'whole' }
|
|
438
|
-
},
|
|
439
|
-
{
|
|
440
|
-
key: 'qa-performance-findings',
|
|
441
|
-
label: 'QA performance findings',
|
|
442
|
-
segments: ['qa', `performance-findings-${rid}.md`],
|
|
443
|
-
supports: ['no-new-bugs'],
|
|
444
|
-
delivery: { kind: 'whole' }
|
|
445
|
-
},
|
|
446
|
-
{
|
|
447
|
-
key: 'rd-code-review',
|
|
448
|
-
label: 'RD code review',
|
|
449
|
-
segments: ['rd', 'code-review.md'],
|
|
450
|
-
supports: ['no-new-bugs'],
|
|
451
|
-
delivery: { kind: 'whole' }
|
|
452
|
-
},
|
|
453
|
-
{
|
|
454
|
-
key: 'rd-security-review',
|
|
455
|
-
label: 'RD security review',
|
|
456
|
-
segments: ['rd', 'security-review.md'],
|
|
457
|
-
supports: ['no-new-bugs'],
|
|
458
|
-
delivery: { kind: 'whole' }
|
|
459
|
-
},
|
|
460
|
-
{
|
|
461
|
-
key: 'rd-tech-doc',
|
|
462
|
-
label: 'RD tech doc (public surface / design intent)',
|
|
463
|
-
segments: ['rd', 'tech-doc.md'],
|
|
464
|
-
supports: ['existing-functionality-intact'],
|
|
465
|
-
delivery: { kind: 'whole' }
|
|
466
|
-
},
|
|
467
|
-
{
|
|
468
|
-
key: 'rd-bug-analysis',
|
|
469
|
-
label: 'RD bug analysis (original problem statement)',
|
|
470
|
-
segments: ['rd', 'bug-analysis.md'],
|
|
471
|
-
supports: ['problem-resolution'],
|
|
472
|
-
delivery: { kind: 'whole' }
|
|
473
|
-
},
|
|
474
|
-
{
|
|
475
|
-
key: SCOPE_CONTRACT_SOURCE_KEY,
|
|
476
|
-
label: 'PRD handoff (approved scope + non-goals)',
|
|
477
|
-
// One capsule per slice since `2026-09-14-prd-capsule-rid-scoping`; the
|
|
478
|
-
// bare name is the pre-scoping tier and still lives on 3 sessions.
|
|
479
|
-
segments: ['prd', `handoff-${rid}.md`],
|
|
480
|
-
legacySegments: ['prd', 'handoff.md'],
|
|
481
|
-
supports: ['functional-completeness', 'existing-functionality-intact'],
|
|
482
|
-
delivery: { kind: 'whole' }
|
|
483
|
-
}
|
|
484
|
-
];
|
|
43
|
+
export function evidenceSourcesFor(rid, prePostDiffAvailable) {
|
|
44
|
+
const sources = baseEvidenceSources(rid);
|
|
485
45
|
if (prePostDiffAvailable) {
|
|
486
46
|
sources.push({
|
|
487
47
|
key: PRE_POST_DIFF_SOURCE_KEY,
|
|
@@ -499,323 +59,6 @@ function evidenceSourcesFor(rid, prePostDiffAvailable) {
|
|
|
499
59
|
}
|
|
500
60
|
return sources;
|
|
501
61
|
}
|
|
502
|
-
/**
|
|
503
|
-
* ENOENT is "no such file"; every other errno is "the file is there and this
|
|
504
|
-
* read failed". Only the first means the source does not exist for this run.
|
|
505
|
-
*/
|
|
506
|
-
function classifyReadFailure(error) {
|
|
507
|
-
const code = error?.code;
|
|
508
|
-
return code === 'ENOENT' ? 'missing' : 'unreadable';
|
|
509
|
-
}
|
|
510
|
-
/** Pure read: no commands, no git, no test execution — files only. */
|
|
511
|
-
function readEvidence(projectRoot, sessionId, rid, prePostDiffAvailable) {
|
|
512
|
-
const runtimeRoot = join(projectRoot, '.peaks', '_runtime', sessionId);
|
|
513
|
-
return evidenceSourcesFor(rid, prePostDiffAvailable).map(source => {
|
|
514
|
-
// The canonical location first; an older one is tried only when the
|
|
515
|
-
// canonical file is genuinely ABSENT (see `legacySegments`). A file that
|
|
516
|
-
// is present but unreadable stops the walk — falling through to an older
|
|
517
|
-
// copy would silently swap the evidence this run reports.
|
|
518
|
-
const attempts = [
|
|
519
|
-
source.segments,
|
|
520
|
-
...(source.legacySegments !== undefined ? [source.legacySegments] : [])
|
|
521
|
-
];
|
|
522
|
-
// The path reported when nothing resolved stays the CANONICAL one, so the
|
|
523
|
-
// operator is sent to where the artifact belongs, not to its old home.
|
|
524
|
-
let resolved = source.segments;
|
|
525
|
-
let raw = null;
|
|
526
|
-
let error = '';
|
|
527
|
-
let read = 'missing';
|
|
528
|
-
for (const segments of attempts) {
|
|
529
|
-
try {
|
|
530
|
-
raw = readFileSync(join(runtimeRoot, ...segments));
|
|
531
|
-
read = 'ok';
|
|
532
|
-
resolved = segments;
|
|
533
|
-
break;
|
|
534
|
-
}
|
|
535
|
-
catch (err) {
|
|
536
|
-
read = classifyReadFailure(err);
|
|
537
|
-
error = err instanceof Error ? err.message : String(err);
|
|
538
|
-
// A file that exists but cannot be read IS this source's file, so it
|
|
539
|
-
// is also the path the report must name — walking on to an older copy
|
|
540
|
-
// would silently swap the evidence this run reports.
|
|
541
|
-
if (read === 'unreadable') {
|
|
542
|
-
resolved = segments;
|
|
543
|
-
break;
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
return {
|
|
548
|
-
source,
|
|
549
|
-
relativePath: ['.peaks', '_runtime', sessionId, ...resolved].join('/'),
|
|
550
|
-
absolutePath: join(runtimeRoot, ...resolved),
|
|
551
|
-
raw,
|
|
552
|
-
read,
|
|
553
|
-
error,
|
|
554
|
-
blank: raw === null || raw.toString('utf8').trim().length === 0
|
|
555
|
-
};
|
|
556
|
-
});
|
|
557
|
-
}
|
|
558
|
-
/**
|
|
559
|
-
* The one source per dimension that the budget promises to reach — the source
|
|
560
|
-
* that dimension's DELIVERY GATE depends on where there is one, and otherwise
|
|
561
|
-
* the first non-blank candidate that can supply it. A dimension with several
|
|
562
|
-
* candidates needs exactly one of them to survive, so naming it costs the other
|
|
563
|
-
* sources nothing they were not already losing.
|
|
564
|
-
*
|
|
565
|
-
* F1 — the gate sources used to be eligible for no reservation at all, because
|
|
566
|
-
* they are last in the order and a later source can never be a "first mention".
|
|
567
|
-
* Reserving for them is what makes the two gates' promises reachable rather
|
|
568
|
-
* than merely correct: a gate that always fires because its source never fits
|
|
569
|
-
* is noise, not a gate. A dimension with no gate entry keeps the old rule.
|
|
570
|
-
*
|
|
571
|
-
* A blank source is deliberately not a holder: a floor reserved for a file that
|
|
572
|
-
* carries no evidence is a floor spent on nothing.
|
|
573
|
-
*/
|
|
574
|
-
function floorHolders(candidates) {
|
|
575
|
-
const holders = new Map();
|
|
576
|
-
const covered = new Set();
|
|
577
|
-
const indexByKey = new Map();
|
|
578
|
-
candidates.forEach((candidate, index) => {
|
|
579
|
-
if (candidate.blank)
|
|
580
|
-
return;
|
|
581
|
-
if (!indexByKey.has(candidate.source.key))
|
|
582
|
-
indexByKey.set(candidate.source.key, index);
|
|
583
|
-
});
|
|
584
|
-
// Pass 1 — the source each delivery gate depends on, whether or not it is the
|
|
585
|
-
// first to mention its dimension.
|
|
586
|
-
for (const dimension of REQUIRED_DIMENSIONS) {
|
|
587
|
-
const gateKey = GATE_SOURCE_FOR_DIMENSION[dimension];
|
|
588
|
-
if (gateKey === undefined)
|
|
589
|
-
continue;
|
|
590
|
-
const index = indexByKey.get(gateKey);
|
|
591
|
-
// No such source this run (e.g. no baseline was computed): the dimension
|
|
592
|
-
// falls back to the first-mention rule below.
|
|
593
|
-
if (index === undefined)
|
|
594
|
-
continue;
|
|
595
|
-
covered.add(dimension);
|
|
596
|
-
if (!holders.has(index))
|
|
597
|
-
holders.set(index, dimension);
|
|
598
|
-
}
|
|
599
|
-
// Pass 2 — every dimension without a gate: the first source that mentions it.
|
|
600
|
-
candidates.forEach((candidate, index) => {
|
|
601
|
-
if (candidate.blank)
|
|
602
|
-
return;
|
|
603
|
-
for (const dimension of candidate.source.supports) {
|
|
604
|
-
if (covered.has(dimension))
|
|
605
|
-
continue;
|
|
606
|
-
covered.add(dimension);
|
|
607
|
-
if (!holders.has(index))
|
|
608
|
-
holders.set(index, dimension);
|
|
609
|
-
}
|
|
610
|
-
});
|
|
611
|
-
return holders;
|
|
612
|
-
}
|
|
613
|
-
/**
|
|
614
|
-
* The unit the allocator hands out: one source's WHOLE slice — its bytes up to
|
|
615
|
-
* the per-file cap — or nothing at all.
|
|
616
|
-
*
|
|
617
|
-
* F-BLOCK-1BYTE — the allocator used to take `min(perFileCap, budgetLeft)`, so
|
|
618
|
-
* the last source the budget reached was cut wherever the budget ran out.
|
|
619
|
-
* `status: 'found'` then meant "at least one byte arrived", and every gate that
|
|
620
|
-
* asked "did the reviewer see this source?" was answering a question with a
|
|
621
|
-
* continuum of possible answers. The observed output (QA round 4, 9 x 4,551 B
|
|
622
|
-
* sources, `api-diff.txt` at 4,226 B): the pre/post diff was inlined as **1
|
|
623
|
-
* byte** — the character `#` — while the producer block still said
|
|
624
|
-
* `STATUS: COMPUTED … cite it`, the dimension kept its `pass`/`high`, the
|
|
625
|
-
* artifact was attached as evidence, and the envelope returned `allPass: true`.
|
|
626
|
-
* Each earlier fix had moved the threshold that separated "some bytes" from
|
|
627
|
-
* "the evidence"; this removes the continuum instead, so no predicate can be
|
|
628
|
-
* re-tuned into the same hole a fifth time.
|
|
629
|
-
*/
|
|
630
|
-
function sourceUnit(candidate) {
|
|
631
|
-
return candidate.raw === null
|
|
632
|
-
? 0
|
|
633
|
-
: Math.min(candidate.raw.byteLength, MAX_EVIDENCE_BYTES_PER_FILE);
|
|
634
|
-
}
|
|
635
|
-
/**
|
|
636
|
-
* F5 — the floor promise, checked where it is relied on instead of remembered.
|
|
637
|
-
*
|
|
638
|
-
* The allocator's loop invariant is `budgetLeft >= sum(units of pending
|
|
639
|
-
* holders)`, and its INITIAL condition is
|
|
640
|
-
* `REQUIRED_DIMENSIONS.length x MAX_EVIDENCE_BYTES_PER_FILE <=
|
|
641
|
-
* MAX_EVIDENCE_BYTES_TOTAL`. While that holds, every holder is served when it
|
|
642
|
-
* is reached and the reservation is a guarantee; the moment it fails, all four
|
|
643
|
-
* dimensions are starved in the same run — which surfaces as four independent
|
|
644
|
-
* red gates rather than as one broken constant, and is therefore the kind of
|
|
645
|
-
* breakage nobody diagnoses correctly.
|
|
646
|
-
*
|
|
647
|
-
* The cap is derived from the total so the inequality cannot be typed wrong,
|
|
648
|
-
* and this check covers the two ways a derivation can still go bad: a
|
|
649
|
-
* non-integer quotient (a fifth dimension, say) and a future re-typing of
|
|
650
|
-
* either constant. It is exported because a test asserts it directly.
|
|
651
|
-
*/
|
|
652
|
-
export function assertFloorReservationAffordable() {
|
|
653
|
-
if (!Number.isInteger(MAX_EVIDENCE_BYTES_PER_FILE)) {
|
|
654
|
-
throw new Error(`MAX_EVIDENCE_BYTES_PER_FILE is not an integer (${String(MAX_EVIDENCE_BYTES_PER_FILE)} = ${String(MAX_EVIDENCE_BYTES_TOTAL)} / ${String(REQUIRED_DIMENSIONS.length)}): the per-dimension floor reservation is not affordable at a fractional unit.`);
|
|
655
|
-
}
|
|
656
|
-
const owed = REQUIRED_DIMENSIONS.length * MAX_EVIDENCE_BYTES_PER_FILE;
|
|
657
|
-
if (owed > MAX_EVIDENCE_BYTES_TOTAL) {
|
|
658
|
-
throw new Error(`the floor reservation is not affordable: ${String(REQUIRED_DIMENSIONS.length)} dimensions x ${String(MAX_EVIDENCE_BYTES_PER_FILE)} bytes per file = ${String(owed)} exceeds MAX_EVIDENCE_BYTES_TOTAL (${String(MAX_EVIDENCE_BYTES_TOTAL)}). Every dimension would be starved in the same run.`);
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
function collectEvidence(projectRoot, sessionId, rid, prePostDiffAvailable) {
|
|
662
|
-
assertFloorReservationAffordable();
|
|
663
|
-
const candidates = readEvidence(projectRoot, sessionId, rid, prePostDiffAvailable);
|
|
664
|
-
const holders = floorHolders(candidates);
|
|
665
|
-
/** Every source's all-or-nothing unit, computed before any byte is spent. */
|
|
666
|
-
const units = candidates.map(candidate => sourceUnit(candidate));
|
|
667
|
-
/** Holders that have not been served yet — the floors still owed. */
|
|
668
|
-
const pending = new Set(holders.keys());
|
|
669
|
-
const collected = [];
|
|
670
|
-
let budgetLeft = MAX_EVIDENCE_BYTES_TOTAL;
|
|
671
|
-
for (const [index, candidate] of candidates.entries()) {
|
|
672
|
-
const { source, relativePath, absolutePath, raw } = candidate;
|
|
673
|
-
const base = { source, relativePath, absolutePath };
|
|
674
|
-
if (raw === null) {
|
|
675
|
-
// `read` is never `'ok'` here (it is set exactly when the read failed),
|
|
676
|
-
// and `'ok'` is not a status — the branch is what says which of the two
|
|
677
|
-
// failure facts this is.
|
|
678
|
-
const status = candidate.read === 'unreadable' ? 'unreadable' : 'missing';
|
|
679
|
-
collected.push({
|
|
680
|
-
...base,
|
|
681
|
-
status,
|
|
682
|
-
totalBytes: 0,
|
|
683
|
-
includedBytes: 0,
|
|
684
|
-
content: '',
|
|
685
|
-
reason: candidate.read === 'missing'
|
|
686
|
-
? candidate.error
|
|
687
|
-
: `the file exists but could not be read (${candidate.error})`
|
|
688
|
-
});
|
|
689
|
-
continue;
|
|
690
|
-
}
|
|
691
|
-
if (candidate.blank) {
|
|
692
|
-
collected.push({
|
|
693
|
-
...base,
|
|
694
|
-
status: 'empty',
|
|
695
|
-
totalBytes: raw.byteLength,
|
|
696
|
-
includedBytes: 0,
|
|
697
|
-
content: '',
|
|
698
|
-
reason: `file exists but contains no reviewable content (${raw.byteLength} bytes)`
|
|
699
|
-
});
|
|
700
|
-
continue;
|
|
701
|
-
}
|
|
702
|
-
const unit = units[index] ?? 0;
|
|
703
|
-
// The floors still owed to dimensions whose holder has not been served are
|
|
704
|
-
// spent only on those holders: a source that needs no help cannot eat the
|
|
705
|
-
// last dimension's only chance at being reviewed. Reserved at the holder's
|
|
706
|
-
// own unit, because under all-or-nothing a partial slice serves nothing.
|
|
707
|
-
const reservedElsewhere = [...pending]
|
|
708
|
-
.filter(holder => holder !== index)
|
|
709
|
-
.reduce((sum, holder) => sum + (units[holder] ?? 0), 0);
|
|
710
|
-
const allowance = budgetLeft - reservedElsewhere;
|
|
711
|
-
// ALL-OR-NOTHING: this source is inlined as its whole unit or it is
|
|
712
|
-
// reported omitted. There is deliberately no third outcome — a partial
|
|
713
|
-
// slice would re-create the continuum F-BLOCK-1BYTE was found in.
|
|
714
|
-
if (allowance < unit) {
|
|
715
|
-
const waiting = [...pending]
|
|
716
|
-
.filter(holder => holder !== index)
|
|
717
|
-
.map(holder => `${holders.get(holder)} (source ${candidates[holder]?.source.key ?? '?'})`);
|
|
718
|
-
collected.push({
|
|
719
|
-
...base,
|
|
720
|
-
status: 'omitted',
|
|
721
|
-
totalBytes: raw.byteLength,
|
|
722
|
-
includedBytes: 0,
|
|
723
|
-
content: '',
|
|
724
|
-
reason: reservedElsewhere > 0
|
|
725
|
-
? `${reservedElsewhere} bytes of the budget are reserved for dimension(s) ${waiting.join(', ')} — their only remaining evidence comes later in the source order; this source is inlined WHOLE or not at all and needs ${unit} bytes, while ${budgetLeft} were left (${allowance} after the reservation)`
|
|
726
|
-
: `total evidence budget (${MAX_EVIDENCE_BYTES_TOTAL} bytes) is exhausted — this source is inlined WHOLE or not at all and needs ${unit} bytes, ${budgetLeft} were left`
|
|
727
|
-
});
|
|
728
|
-
continue;
|
|
729
|
-
}
|
|
730
|
-
const content = raw.subarray(0, unit).toString('utf8');
|
|
731
|
-
budgetLeft -= unit;
|
|
732
|
-
pending.delete(index);
|
|
733
|
-
collected.push({
|
|
734
|
-
...base,
|
|
735
|
-
status: 'found',
|
|
736
|
-
totalBytes: raw.byteLength,
|
|
737
|
-
includedBytes: unit,
|
|
738
|
-
content,
|
|
739
|
-
reason: ''
|
|
740
|
-
});
|
|
741
|
-
}
|
|
742
|
-
return collected;
|
|
743
|
-
}
|
|
744
|
-
function renderEvidenceSection(collected) {
|
|
745
|
-
return collected
|
|
746
|
-
.map((item, index) => {
|
|
747
|
-
const heading = `### [${index + 1}] ${item.source.key} — ${item.source.label}`;
|
|
748
|
-
const supports = `SUPPORTS: ${item.source.supports.join(', ')}`;
|
|
749
|
-
if (item.status === 'found') {
|
|
750
|
-
const status = item.includedBytes < item.totalBytes
|
|
751
|
-
? `FOUND at ${item.relativePath} — TRUNCATED, showing the first ${item.includedBytes} of ${item.totalBytes} bytes`
|
|
752
|
-
: `FOUND at ${item.relativePath} — ${item.totalBytes} bytes`;
|
|
753
|
-
return `${heading}\n${supports}\nSTATUS: ${status}\n<<<EVIDENCE\n${item.content}\n>>>EVIDENCE`;
|
|
754
|
-
}
|
|
755
|
-
if (item.status === 'unreadable') {
|
|
756
|
-
// F4 — NOT "missing". The reviewer is told the file is there and the
|
|
757
|
-
// read failed, which is a different instruction to a human than "this
|
|
758
|
-
// run had no PRD phase": one is a fact about the run, the other is a
|
|
759
|
-
// fact about the machine, and only the second is fixable.
|
|
760
|
-
return `${heading}\n${supports}\nSTATUS: UNREADABLE — no evidence available from ${item.relativePath}: ${item.reason}`;
|
|
761
|
-
}
|
|
762
|
-
return `${heading}\n${supports}\nSTATUS: MISSING (${item.status}) — no evidence available from ${item.relativePath}: ${item.reason}`;
|
|
763
|
-
})
|
|
764
|
-
.join('\n\n');
|
|
765
|
-
}
|
|
766
|
-
/**
|
|
767
|
-
* The producer's own status, told to the reviewer in as many words.
|
|
768
|
-
*
|
|
769
|
-
* It exists because the artifact's ABSENCE is not self-explanatory: a reviewer
|
|
770
|
-
* shown nothing about `existing-functionality-intact` beyond two design-intent
|
|
771
|
-
* documents cannot tell "the diff was clean" from "no diff was ever computed",
|
|
772
|
-
* and the pre-fix run resolved that ambiguity by quietly returning
|
|
773
|
-
* `inconclusive` forever. Naming the reason is what makes the unavailable case
|
|
774
|
-
* a stated fact instead of an invisible one.
|
|
775
|
-
*
|
|
776
|
-
* Kept short on purpose: it rides inside the same prompt that is byte-capped at
|
|
777
|
-
* `MAX_EVIDENCE_BYTES_TOTAL` + scaffolding.
|
|
778
|
-
*
|
|
779
|
-
* F-BLOCK: this block may only say COMPUTED when the artifact it names was
|
|
780
|
-
* actually inlined. Saying "computed — cite it" one line above a source block
|
|
781
|
-
* reading `STATUS: MISSING (omitted)` told the reviewer to cite evidence it had
|
|
782
|
-
* not been given, and that contradiction is what let a `pass` survive a
|
|
783
|
-
* baseline nobody saw.
|
|
784
|
-
*/
|
|
785
|
-
function renderPrePostDiffStatus(prePostDiff, delivered) {
|
|
786
|
-
const heading = '## Pre/post baseline diff producer (existing-functionality-intact)';
|
|
787
|
-
if (prePostDiff.status === 'computed' && delivered) {
|
|
788
|
-
return [
|
|
789
|
-
heading,
|
|
790
|
-
`STATUS: COMPUTED — ${prePostDiff.summary}`,
|
|
791
|
-
`Artifact: ${prePostDiff.relativePath} (the next source block). Cite it with evidence kind "pre-post-diff"; it is the structural before/after comparison this dimension's definition asks for.`
|
|
792
|
-
].join('\n');
|
|
793
|
-
}
|
|
794
|
-
if (prePostDiff.status === 'computed') {
|
|
795
|
-
return [
|
|
796
|
-
heading,
|
|
797
|
-
`STATUS: COMPUTED ON DISK, NOT DELIVERED — the baseline was produced at ${prePostDiff.relativePath}, but its source block could not be inlined into this prompt (the evidence budget omitted it), so it is NOT among the evidence above and you have NOT seen it.`,
|
|
798
|
-
'Report "existing-functionality-intact" as "inconclusive" and name this in its summary. Do NOT report "pass": the service downgrades a "pass" on this dimension whenever the comparison was not delivered, and a comparison you were not shown is not a comparison that came back clean.'
|
|
799
|
-
].join('\n');
|
|
800
|
-
}
|
|
801
|
-
const consequence = prePostDiff.inGitWorkTree
|
|
802
|
-
? 'This project IS a git work tree, so a baseline was expected to be computable: the service treats its absence as a tooling failure and downgrades a "pass" on this dimension to "inconclusive" before any human sees it.'
|
|
803
|
-
: 'This project is not a git work tree, so no baseline can be computed at all. The service downgrades a "pass" on this dimension to "inconclusive" here TOO: "this project keeps no baseline" explains why the evidence is absent, and the absence of a comparison is not a comparison that came back clean.';
|
|
804
|
-
return [
|
|
805
|
-
heading,
|
|
806
|
-
`STATUS: UNAVAILABLE — ${prePostDiff.reason}.`,
|
|
807
|
-
`No pre/post baseline diff exists for this run. Report "existing-functionality-intact" as "inconclusive" with confidence "low" and name the reason above in its summary. Do NOT report "pass": a missing baseline is not evidence of no drift. ${consequence}`
|
|
808
|
-
].join('\n');
|
|
809
|
-
}
|
|
810
|
-
const EVIDENCE_RULES = `## Binding rules for the four verdicts
|
|
811
|
-
1. A dimension may be "pass" ONLY if at least one source in its SUPPORTS list has STATUS: FOUND above, and that source's content actually supports the verdict. The service re-checks this: a "pass" whose supporting sources are all missing/empty/omitted is downgraded to "inconclusive" before any human sees it.
|
|
812
|
-
2. If the evidence a dimension needs is MISSING, EMPTY, or OMITTED, return "inconclusive" with confidence "low". Do not guess "pass".
|
|
813
|
-
3. Absence of evidence is not evidence of absence: "no problem found in what I was given" is "inconclusive", never "pass".
|
|
814
|
-
4. Cite the bracketed source numbers (e.g. "[1]", "[5]") you relied on in each dimension's "evidence[].description"; use an empty list when the verdict is "inconclusive".
|
|
815
|
-
5. "allPass" may be true only when all four verdicts are "pass", and every non-"pass" dimension must be listed in "needsAttention".
|
|
816
|
-
6. "existing-functionality-intact" may be "pass" ONLY when the pre/post baseline diff block above shows STATUS: FOUND. A design-intent document (RD tech doc, PRD handoff) states what was INTENDED; it is not a before/after comparison of the test surface or the public API surface, and a "pass" resting on one is downgraded to "inconclusive" by the service before any human sees it.
|
|
817
|
-
7. An "inconclusive" verdict cannot be confident: report it with confidence "low" (or "medium" when the reviewer is sure the evidence is merely incomplete). "high" on "inconclusive" is a contradiction and the service clamps it.
|
|
818
|
-
8. "functional-completeness" may be "pass" ONLY when the approved-scope contract block (prd-handoff, the source carrying the approved scope and non-goals) is present above with its WHOLE byte count — a STATUS line reading "FOUND at ... — N bytes", not a truncated or an omitted one. A passing test report shows that something was built; only the contract shows that what was built IS the approved scope. The service re-checks this: it downgrades a "pass" on this dimension whenever that contract exists for the run but was not delivered in full.`;
|
|
819
62
|
/**
|
|
820
63
|
* Which dimensions the reviewer was actually given evidence FOR. A dimension
|
|
821
64
|
* absent from this set has no delivered evidence behind it.
|
|
@@ -836,7 +79,7 @@ const EVIDENCE_RULES = `## Binding rules for the four verdicts
|
|
|
836
79
|
* judgement — so a source that cannot be delivered for a dimension cannot back
|
|
837
80
|
* a pass on it either, here or anywhere else.
|
|
838
81
|
*/
|
|
839
|
-
function dimensionsWithEvidence(collected) {
|
|
82
|
+
export function dimensionsWithEvidence(collected) {
|
|
840
83
|
const available = new Set();
|
|
841
84
|
for (const item of collected) {
|
|
842
85
|
for (const dimension of item.source.supports) {
|
|
@@ -846,30 +89,6 @@ function dimensionsWithEvidence(collected) {
|
|
|
846
89
|
}
|
|
847
90
|
return available;
|
|
848
91
|
}
|
|
849
|
-
/**
|
|
850
|
-
* The honesty guarantee (D1), enforced after parsing rather than merely asked
|
|
851
|
-
* for in the prompt. Prompt instructions are advisory — a model can still
|
|
852
|
-
* answer `pass` — so this pass makes the property structural: a dimension with
|
|
853
|
-
* no supporting evidence on disk is rewritten to `inconclusive` / `low`
|
|
854
|
-
* regardless of what the model returned.
|
|
855
|
-
*
|
|
856
|
-
* A `fail` is never softened: it is already stricter than `inconclusive`.
|
|
857
|
-
*/
|
|
858
|
-
function enforceEvidenceBackedVerdicts(dimensions, evidenceAvailableFor) {
|
|
859
|
-
return dimensions.map(dimension => {
|
|
860
|
-
if (dimension.verdict !== 'pass')
|
|
861
|
-
return dimension;
|
|
862
|
-
if (evidenceAvailableFor.has(dimension.dimension))
|
|
863
|
-
return dimension;
|
|
864
|
-
const downgraded = {
|
|
865
|
-
...dimension,
|
|
866
|
-
verdict: 'inconclusive',
|
|
867
|
-
confidence: 'low',
|
|
868
|
-
summary: `${dimension.summary} [evidence-gate: verdict downgraded from "pass" to "inconclusive" — no on-disk evidence source supporting "${dimension.dimension}" was available to the reviewer.]`
|
|
869
|
-
};
|
|
870
|
-
return downgraded;
|
|
871
|
-
});
|
|
872
|
-
}
|
|
873
92
|
/**
|
|
874
93
|
* DELIVERED — the module's ONE delivery judgement.
|
|
875
94
|
*
|
|
@@ -907,7 +126,7 @@ function enforceEvidenceBackedVerdicts(dimensions, evidenceAvailableFor) {
|
|
|
907
126
|
* this dimension's evidence" is exactly the presence-as-substance error. The
|
|
908
127
|
* check lives here so no caller can skip it.
|
|
909
128
|
*/
|
|
910
|
-
function isDelivered(item, dimension) {
|
|
129
|
+
export function isDelivered(item, dimension) {
|
|
911
130
|
if (!item.source.supports.includes(dimension))
|
|
912
131
|
return false;
|
|
913
132
|
// No bytes arrived at all: missing, empty, unreadable, omitted.
|
|
@@ -929,384 +148,25 @@ function isDelivered(item, dimension) {
|
|
|
929
148
|
* mean the same thing by "the reviewer saw the comparison". It reads the ONE
|
|
930
149
|
* predicate; it does not re-decide anything.
|
|
931
150
|
*/
|
|
932
|
-
function prePostDiffDelivered(collected) {
|
|
933
|
-
const block = collected.find(item => item.source.key === PRE_POST_DIFF_SOURCE_KEY);
|
|
151
|
+
export function prePostDiffDelivered(collected) {
|
|
152
|
+
const block = collected.find((item) => item.source.key === PRE_POST_DIFF_SOURCE_KEY);
|
|
934
153
|
return block !== undefined && isDelivered(block, 'existing-functionality-intact');
|
|
935
154
|
}
|
|
936
155
|
/**
|
|
937
|
-
*
|
|
938
|
-
*
|
|
939
|
-
*
|
|
940
|
-
*
|
|
941
|
-
*
|
|
942
|
-
* than the per-file cap can never be delivered: not on this run, not on any
|
|
943
|
-
* run, not with any budget, because the budget is not what cuts it. Raising
|
|
944
|
-
* `MAX_EVIDENCE_BYTES_TOTAL` changes nothing either, since the per-file cap is
|
|
945
|
-
* derived from it and the reservation spends all of it.
|
|
946
|
-
*
|
|
947
|
-
* (`conclusion` sources are deliberately NOT covered. A marker missing from the
|
|
948
|
-
* first cap-sized bytes of an oversize artifact might simply live past the cut,
|
|
949
|
-
* so "undeliverable forever" is not a claim this module can make about them —
|
|
950
|
-
* and an over-claiming check is the failure mode this file keeps closing.)
|
|
951
|
-
*/
|
|
952
|
-
function isStructurallyUndeliverable(item) {
|
|
953
|
-
if (item.source.delivery.kind !== 'whole')
|
|
954
|
-
return false;
|
|
955
|
-
return item.totalBytes > MAX_EVIDENCE_BYTES_PER_FILE;
|
|
956
|
-
}
|
|
957
|
-
/**
|
|
958
|
-
* H2 — every required dimension whose verdict is locked to `inconclusive` by
|
|
959
|
-
* BYTE ARITHMETIC rather than by the reviewer's judgement.
|
|
960
|
-
*
|
|
961
|
-
* The defect this exists for: `qa/test-reports/<rid>.md` measured 9,492 bytes
|
|
962
|
-
* on this repo's own run and is the ONLY source on disk for `problem-resolution`
|
|
963
|
-
* and `no-new-bugs` (the other sources that support them are absent), against a
|
|
964
|
-
* per-file cap of 10,240 — a 748-byte margin on a file that is REWRITTEN every
|
|
965
|
-
* round and only grows. The moment it crosses 10,240 both dimensions go
|
|
966
|
-
* permanently red, and NOTHING said so: `assertFloorReservationAffordable()`
|
|
967
|
-
* only checks the constant-level relation (`4 x cap <= total`), never whether
|
|
968
|
-
* any actual source fits the cap it must live under, so the red handoff read as
|
|
969
|
-
* "the reviewer was unsure" instead of "no evidence can ever reach the
|
|
970
|
-
* reviewer". A gate that is always red and never explains itself is noise, and
|
|
971
|
-
* this is the same "always red" harm the floor reservation was built to remove
|
|
972
|
-
* — one layer down.
|
|
973
|
-
*
|
|
974
|
-
* The conditions, all three of which must hold, are chosen so the report cannot
|
|
975
|
-
* be noise:
|
|
976
|
-
* 1. NOTHING on disk can back the dimension (no `isDelivered`), and
|
|
977
|
-
* 2. there IS something on disk to deliver (a source that was never written
|
|
978
|
-
* is not a delivery failure — same reasoning as the scope-contract gate's
|
|
979
|
-
* `missing` exemption: a run with no QA phase has no report to lose), and
|
|
980
|
-
* 3. EVERY one of those on-disk sources is structurally undeliverable — a
|
|
981
|
-
* single source that merely did not fit TODAY (budget-exhausted `omitted`)
|
|
982
|
-
* is a different, self-correcting state and is left to the allocator.
|
|
983
|
-
*
|
|
984
|
-
* The report is consumed by the prompt (stated to the reviewer), by the
|
|
985
|
-
* envelope (a marker on each dimension's summary) and by `needsAttention`
|
|
986
|
-
* (which also clears `allPass`) — see `enforceDeliveryReachability` and
|
|
987
|
-
* `renderDeliveryReachabilityStatus`. It is a LOUD STATEMENT, not a silent
|
|
988
|
-
* downgrade, and it is deliberately not a throw: a crash would destroy the
|
|
989
|
-
* evidence for the three dimensions that ARE deliverable, and the honest fact
|
|
990
|
-
* here is per-dimension, so it is reported per-dimension.
|
|
991
|
-
*/
|
|
992
|
-
export function undeliverableDimensions(collected) {
|
|
993
|
-
const report = [];
|
|
994
|
-
for (const dimension of REQUIRED_DIMENSIONS) {
|
|
995
|
-
const supporting = collected.filter(item => item.source.supports.includes(dimension));
|
|
996
|
-
if (supporting.some(item => isDelivered(item, dimension)))
|
|
997
|
-
continue;
|
|
998
|
-
const onDisk = supporting.filter(item => item.totalBytes > 0);
|
|
999
|
-
if (onDisk.length === 0)
|
|
1000
|
-
continue;
|
|
1001
|
-
if (!onDisk.every(isStructurallyUndeliverable))
|
|
1002
|
-
continue;
|
|
1003
|
-
report.push({
|
|
1004
|
-
dimension,
|
|
1005
|
-
sources: onDisk.map(item => ({
|
|
1006
|
-
key: item.source.key,
|
|
1007
|
-
relativePath: item.relativePath,
|
|
1008
|
-
totalBytes: item.totalBytes
|
|
1009
|
-
}))
|
|
1010
|
-
});
|
|
1011
|
-
}
|
|
1012
|
-
return report;
|
|
1013
|
-
}
|
|
1014
|
-
/**
|
|
1015
|
-
* H2 — tell the reviewer, in the prompt, that a dimension has no deliverable
|
|
1016
|
-
* source at all. Emitted only when there is something to say, so the byte
|
|
1017
|
-
* budget is not spent on an empty section every run.
|
|
1018
|
-
*
|
|
1019
|
-
* It reads like `renderPrePostDiffStatus` on purpose: the reviewer is told the
|
|
1020
|
-
* FACT and what to answer with, so a permanently red dimension arrives at the
|
|
1021
|
-
* human with its cause attached instead of as an unexplained `inconclusive`.
|
|
1022
|
-
*/
|
|
1023
|
-
function renderDeliveryReachabilityStatus(report) {
|
|
1024
|
-
if (report.length === 0)
|
|
1025
|
-
return '';
|
|
1026
|
-
// Kept to one line per source and one line per dimension: this block rides
|
|
1027
|
-
// inside the same byte-capped prompt as the evidence it describes, and the
|
|
1028
|
-
// evidence blocks above already carry each source's path and status.
|
|
1029
|
-
const lines = report.map(entry => {
|
|
1030
|
-
const sources = entry.sources
|
|
1031
|
-
.map(item => `${item.key} (${String(item.totalBytes)} bytes)`)
|
|
1032
|
-
.join(', ');
|
|
1033
|
-
return ` - ${entry.dimension}: NO deliverable source. ${sources} exceeds the per-file cap of ${String(MAX_EVIDENCE_BYTES_PER_FILE)} bytes, and a source is inlined WHOLE or not at all.`;
|
|
1034
|
-
});
|
|
1035
|
-
return [
|
|
1036
|
-
'## Evidence delivery reachability (structural)',
|
|
1037
|
-
`The allocator inlines at most ${String(MAX_EVIDENCE_BYTES_PER_FILE)} bytes of any one source, WHOLE or not at all, and a source delivered under the "whole" rule is delivered only when the reviewer received ALL of it. For the dimension(s) below, every source on disk that supports it is larger than that cap, so no source CAN be delivered — not on this run and not on any run, whatever the budget.`,
|
|
1038
|
-
...lines,
|
|
1039
|
-
'Report each of them as "inconclusive" with confidence "low" and name this reason in its summary. Do NOT report "pass": the service re-checks it, and a "pass" here is downgraded. This is a STRUCTURAL impossibility, not a judgement you are being asked to make — say so rather than reporting an unexplained "inconclusive".'
|
|
1040
|
-
].join('\n');
|
|
1041
|
-
}
|
|
1042
|
-
/**
|
|
1043
|
-
* H2 — the envelope half of the same fact. A dimension named by
|
|
1044
|
-
* `undeliverableDimensions()` cannot be `pass` (no source on disk can carry its
|
|
1045
|
-
* conclusion), so this is not where the verdict is decided — that is
|
|
1046
|
-
* `enforceEvidenceBackedVerdicts`, and this gate re-applies it so the property
|
|
1047
|
-
* does not depend on that gate's order. What this adds is the REASON: the
|
|
1048
|
-
* dimension's own summary says it is red by construction. Because the verdict
|
|
1049
|
-
* it leaves behind is non-`pass`, the dimension also reaches `needsAttention`
|
|
1050
|
-
* (and clears `allPass`) through the ordinary verdict route, which is the field
|
|
1051
|
-
* the CLI envelope prints — so the human is told WHY instead of being left to
|
|
1052
|
-
* read a permanent red as the reviewer's uncertainty.
|
|
1053
|
-
*/
|
|
1054
|
-
function enforceDeliveryReachability(dimensions, report) {
|
|
1055
|
-
if (report.length === 0)
|
|
1056
|
-
return dimensions;
|
|
1057
|
-
const byDimension = new Map(report.map(entry => [entry.dimension, entry]));
|
|
1058
|
-
return dimensions.map(dimension => {
|
|
1059
|
-
const entry = byDimension.get(dimension.dimension);
|
|
1060
|
-
if (entry === undefined)
|
|
1061
|
-
return dimension;
|
|
1062
|
-
// The ENVELOPE is not byte-capped, so it names the path as well as the
|
|
1063
|
-
// size: the path is what a human has to act on to fix it.
|
|
1064
|
-
const detail = entry.sources
|
|
1065
|
-
.map(item => `${item.key} at ${item.relativePath} (${String(item.totalBytes)} bytes)`)
|
|
1066
|
-
.join(', ');
|
|
1067
|
-
const marker = `[delivery-reachability: ${dimension.verdict === 'pass'
|
|
1068
|
-
? 'verdict downgraded from "pass" to "inconclusive"'
|
|
1069
|
-
: `gate ran; verdict "${dimension.verdict}" is already non-"pass" and is left unchanged`} — EVERY source on disk that supports "${dimension.dimension}" (${detail}) is larger than the per-file delivery cap of ${String(MAX_EVIDENCE_BYTES_PER_FILE)} bytes, and a source is inlined WHOLE or not at all, so no evidence for this dimension can ever reach the reviewer. The dimension is red by byte arithmetic, not by the reviewer's judgement, and it is listed in needsAttention for that reason.]`;
|
|
1070
|
-
const annotated = {
|
|
1071
|
-
...dimension,
|
|
1072
|
-
summary: `${dimension.summary} ${marker}`
|
|
1073
|
-
};
|
|
1074
|
-
if (dimension.verdict !== 'pass')
|
|
1075
|
-
return annotated;
|
|
1076
|
-
return { ...annotated, verdict: 'inconclusive', confidence: 'low' };
|
|
1077
|
-
});
|
|
1078
|
-
}
|
|
1079
|
-
/**
|
|
1080
|
-
* The pre/post-diff half of the honesty rule: a `pass` on
|
|
1081
|
-
* `existing-functionality-intact` may not outlive the baseline it claims.
|
|
1082
|
-
*
|
|
1083
|
-
* It fires whenever no baseline was DELIVERED to the reviewer, for EVERY reason:
|
|
1084
|
-
* no base ref resolvable, a base that resolves to HEAD itself, a project that is
|
|
1085
|
-
* not a git work tree at all, or a baseline that WAS computed but whose source
|
|
1086
|
-
* block never reached the prompt (the budget omitted it). The first three are
|
|
1087
|
-
* causes of a missing artifact; the last is a missing DELIVERY of an artifact
|
|
1088
|
-
* that exists. None of the four is a licence to trust the claim: they are the
|
|
1089
|
-
* CAUSE of the missing evidence, and a comparison the reviewer never saw is the
|
|
1090
|
-
* absence of an answer, never an answer that came back clean.
|
|
1091
|
-
*
|
|
1092
|
-
* The earlier version exempted the non-git case, reasoning that "a non-git
|
|
1093
|
-
* project would otherwise be permanently red". That reasoning was wrong in the
|
|
1094
|
-
* one way this whole primitive exists to prevent: the permanently red verdict is
|
|
1095
|
-
* the HONEST one (nothing was ever compared), while green-with-no-evidence is a
|
|
1096
|
-
* forged clean handoff. Worse, it pierced the structural gate that
|
|
1097
|
-
* `enforceEvidenceBackedVerdicts` exists to be — a `pass` whose supporting
|
|
1098
|
-
* evidence is entirely absent is downgraded there, and it must not be let
|
|
1099
|
-
* through by supplying a REASON for the absence.
|
|
1100
|
-
*
|
|
1101
|
-
* The version that followed it keyed on `prePostDiff.status === 'computed'` —
|
|
1102
|
-
* "a baseline exists on disk" read as "the reviewer saw one". Those two facts
|
|
1103
|
-
* separate the moment the budget omits the block, and the observed output was
|
|
1104
|
-
* self-contradicting: the producer's own block said `STATUS: COMPUTED` one line
|
|
1105
|
-
* above the source block's `STATUS: MISSING (omitted)`, the model was handed a
|
|
1106
|
-
* `pass` it could not have justified, and the service attached the artifact to
|
|
1107
|
-
* the dimension as evidence on top. `delivered` is that missing term.
|
|
1108
|
-
*
|
|
1109
|
-
* The gate also leaves its marker on the dimension whenever it fires, even if
|
|
1110
|
-
* that dimension is already non-`pass`: with both gates firing, the earlier
|
|
1111
|
-
* version's early return left only the OTHER gate's marker behind, so the
|
|
1112
|
-
* envelope could not tell an operator that this gate had run at all.
|
|
1113
|
-
*/
|
|
1114
|
-
function enforcePrePostDiffAvailability(dimensions, prePostDiff, collected) {
|
|
1115
|
-
if (prePostDiff.status === 'computed' && prePostDiffDelivered(collected))
|
|
1116
|
-
return dimensions;
|
|
1117
|
-
const why = prePostDiff.status !== 'computed'
|
|
1118
|
-
? prePostDiff.inGitWorkTree
|
|
1119
|
-
? 'this is a git work tree, but no pre/post baseline diff could be produced for this run'
|
|
1120
|
-
: 'this project is not a git work tree, so no pre/post baseline diff can exist for it'
|
|
1121
|
-
: 'a baseline WAS computed for this run, but its source block was not delivered into the reviewer prompt (the evidence budget omitted it), so the reviewer never saw the comparison it would have to rest on';
|
|
1122
|
-
const reason = prePostDiff.status === 'computed'
|
|
1123
|
-
? 'the artifact exists on disk but did not reach the reviewer'
|
|
1124
|
-
: prePostDiff.reason;
|
|
1125
|
-
return dimensions.map(dimension => {
|
|
1126
|
-
if (dimension.dimension !== 'existing-functionality-intact')
|
|
1127
|
-
return dimension;
|
|
1128
|
-
const marker = `[pre-post-diff-gate: ${dimension.verdict === 'pass'
|
|
1129
|
-
? 'verdict downgraded from "pass" to "inconclusive"'
|
|
1130
|
-
: `gate ran; verdict "${dimension.verdict}" is already non-"pass" and is left unchanged`} — ${why}. Reason: ${reason}]`;
|
|
1131
|
-
const annotated = {
|
|
1132
|
-
...dimension,
|
|
1133
|
-
summary: `${dimension.summary} ${marker}`
|
|
1134
|
-
};
|
|
1135
|
-
if (dimension.verdict !== 'pass')
|
|
1136
|
-
return annotated;
|
|
1137
|
-
return { ...annotated, verdict: 'inconclusive', confidence: 'low' };
|
|
1138
|
-
});
|
|
1139
|
-
}
|
|
1140
|
-
/**
|
|
1141
|
-
* F-NIT — `inconclusive` is the one verdict that cannot carry `high`
|
|
1142
|
-
* confidence. "I am highly confident that I could not tell" is the schema's
|
|
1143
|
-
* one self-contradicting combination, and it reads as a strong statement to the
|
|
1144
|
-
* human this envelope is handed to. Both service gates that produce an
|
|
1145
|
-
* `inconclusive` a reviewer did not write already write `low`; this closes the
|
|
1146
|
-
* same hole for the ones the reviewer DID write, clamping to `medium` (the
|
|
1147
|
-
* upper bound `references/4-dimensions.md` documents for this verdict) and
|
|
1148
|
-
* leaving a marker so the clamp is auditable rather than silent.
|
|
1149
|
-
*
|
|
1150
|
-
* `fail` and `pass` are untouched: their confidence says something real.
|
|
156
|
+
* Routing for the verdict gates (their implementations, with their
|
|
157
|
+
* measured-history comments, live in final-review-gates.ts). This module
|
|
158
|
+
* owns the delivery judgement — guard C pins isDelivered's single home
|
|
159
|
+
* here — so the gates are called through it: the pre/post gate receives
|
|
160
|
+
* the delivered fact, the scope gate receives the predicate itself.
|
|
1151
161
|
*/
|
|
1152
|
-
function
|
|
1153
|
-
return dimensions
|
|
1154
|
-
if (dimension.verdict !== 'inconclusive' || dimension.confidence !== 'high')
|
|
1155
|
-
return dimension;
|
|
1156
|
-
return {
|
|
1157
|
-
...dimension,
|
|
1158
|
-
confidence: 'medium',
|
|
1159
|
-
summary: `${dimension.summary} [confidence-gate: confidence clamped from "high" to "medium" — an "inconclusive" verdict cannot be highly confident that it could not tell.]`
|
|
1160
|
-
};
|
|
1161
|
-
});
|
|
162
|
+
export function enforcePrePostDiffAvailability(dimensions, prePostDiff, collected) {
|
|
163
|
+
return gateEnforcePrePostDiffAvailability(dimensions, prePostDiff, prePostDiffDelivered(collected));
|
|
1162
164
|
}
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
* `functional-completeness`: the approved-scope contract.
|
|
1166
|
-
*
|
|
1167
|
-
* F-SAME-SHAPE — the hole the pre/post-diff gate closes for
|
|
1168
|
-
* `existing-functionality-intact` was still open one dimension over. That
|
|
1169
|
-
* dimension's rule is "a `pass` may not outlive the source it rests on", and
|
|
1170
|
-
* `functional-completeness` is DEFINED against the approved scope and its
|
|
1171
|
-
* non-goals — `prd/handoff.md` — but its `pass` only requires SOME source in its
|
|
1172
|
-
* SUPPORTS list to be FOUND, and `qa-test-report` (the first source in the
|
|
1173
|
-
* order, so the one the budget can never starve) also supports it. Measured on
|
|
1174
|
-
* both saturated fixtures: `prd/handoff.md` was inlined with zero bytes on every
|
|
1175
|
-
* run while the dimension still came back `pass`/`high` — the contract that
|
|
1176
|
-
* says what "complete" meant was never shown to the reviewer judging
|
|
1177
|
-
* completeness. Exactly the forged-clean-handoff shape, one dimension over.
|
|
1178
|
-
*
|
|
1179
|
-
* The delivery test for this source is the module's `whole` rule (`isDelivered`),
|
|
1180
|
-
* not "some bytes arrived": the contract is prose whose point is the part a
|
|
1181
|
-
* truncation would cut (scope then non-goals), so a partial slice is not a
|
|
1182
|
-
* weaker version of the contract — it is a different document, and the reviewer
|
|
1183
|
-
* would be judging "complete" against a scope list that stops mid-sentence.
|
|
1184
|
-
*
|
|
1185
|
-
* The gate deliberately does NOT fire when the contract is `missing` — ENOENT,
|
|
1186
|
-
* i.e. the file is absent from the project, whether because the workflow has no
|
|
1187
|
-
* PRD phase or because it never wrote one. A workflow with no PRD phase has no
|
|
1188
|
-
* contract to lose, and reddening that dimension forever would be the "gate
|
|
1189
|
-
* that is always red is noise" failure this primitive already names.
|
|
1190
|
-
*
|
|
1191
|
-
* F4 — what the gate fires on is the opposite fact: the contract IS there for
|
|
1192
|
-
* this run and did not reach the reviewer. `totalBytes === 0` used to stand in
|
|
1193
|
-
* for "not there", and it was wider than the comment above it: a 0-byte
|
|
1194
|
-
* `prd/handoff.md` is `empty`, not `missing` — the file exists, the PRD phase
|
|
1195
|
-
* ran, and the reviewer received no contract at all — yet the early return
|
|
1196
|
-
* skipped the gate and let `functional-completeness` come back `pass`/`high`.
|
|
1197
|
-
* The same line collapsed `unreadable` (EACCES / EBUSY: the contract exists and
|
|
1198
|
-
* this process could not open it) into "no PRD phase". The test is now the
|
|
1199
|
-
* STATUS the read phase produced, so "there is nothing to deliver" and "there
|
|
1200
|
-
* is something to deliver and it did not arrive" cannot be the same branch.
|
|
1201
|
-
*/
|
|
1202
|
-
function enforceScopeContractDelivery(dimensions, collected) {
|
|
1203
|
-
const block = collected.find(item => item.source.key === SCOPE_CONTRACT_SOURCE_KEY);
|
|
1204
|
-
if (block === undefined || block.status === 'missing')
|
|
1205
|
-
return dimensions;
|
|
1206
|
-
if (isDelivered(block, 'functional-completeness'))
|
|
1207
|
-
return dimensions;
|
|
1208
|
-
return dimensions.map(dimension => {
|
|
1209
|
-
if (dimension.dimension !== 'functional-completeness')
|
|
1210
|
-
return dimension;
|
|
1211
|
-
const marker = `[scope-contract-gate: ${dimension.verdict === 'pass'
|
|
1212
|
-
? 'verdict downgraded from "pass" to "inconclusive"'
|
|
1213
|
-
: `gate ran; verdict "${dimension.verdict}" is already non-"pass" and is left unchanged`} — the approved-scope contract (${block.relativePath}) exists for this run, but it did not reach the reviewer in full (${block.includedBytes} of ${block.totalBytes} bytes, status "${block.status}"), so "functional-completeness" was judged without the scope and non-goals it is defined against. Reason: ${block.reason || 'inlined only in part'}]`;
|
|
1214
|
-
const annotated = {
|
|
1215
|
-
...dimension,
|
|
1216
|
-
summary: `${dimension.summary} ${marker}`
|
|
1217
|
-
};
|
|
1218
|
-
if (dimension.verdict !== 'pass')
|
|
1219
|
-
return annotated;
|
|
1220
|
-
return { ...annotated, verdict: 'inconclusive', confidence: 'low' };
|
|
1221
|
-
});
|
|
165
|
+
export function enforceScopeContractDelivery(dimensions, collected) {
|
|
166
|
+
return gateEnforceScopeContractDelivery(dimensions, collected, (item, dimension) => isDelivered(item, dimension));
|
|
1222
167
|
}
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
* `EvidenceItem`.
|
|
1226
|
-
*
|
|
1227
|
-
* The service does this rather than trusting the reviewer to cite the source:
|
|
1228
|
-
* the dimension's contract NAMES this evidence kind and this artifact path, and
|
|
1229
|
-
* a machine-produced fact that only appears when an LLM remembers to type it is
|
|
1230
|
-
* not a guarantee. Appending cannot upgrade a verdict — the gate above has
|
|
1231
|
-
* already run, and this only makes the artifact the verdict rests on auditable.
|
|
1232
|
-
*
|
|
1233
|
-
* It follows the same delivery rule as the gate — it asks the SAME function, on
|
|
1234
|
-
* purpose: attaching the artifact to a dimension whose reviewer never received
|
|
1235
|
-
* that block would re-create the very contradiction this layer exists to remove
|
|
1236
|
-
* — an envelope claiming a `pre-post-diff` evidence item next to an
|
|
1237
|
-
* `inconclusive` verdict that says the comparison was never seen.
|
|
1238
|
-
*/
|
|
1239
|
-
function attachPrePostDiffEvidence(dimensions, prePostDiff, collected) {
|
|
1240
|
-
if (prePostDiff.status !== 'computed' || !prePostDiffDelivered(collected))
|
|
1241
|
-
return dimensions;
|
|
1242
|
-
const item = {
|
|
1243
|
-
kind: 'pre-post-diff',
|
|
1244
|
-
description: prePostDiff.summary,
|
|
1245
|
-
artifact: prePostDiff.relativePath
|
|
1246
|
-
};
|
|
1247
|
-
return dimensions.map(dimension => {
|
|
1248
|
-
if (dimension.dimension !== 'existing-functionality-intact')
|
|
1249
|
-
return dimension;
|
|
1250
|
-
const alreadyPresent = dimension.evidence.some(existing => existing.kind === 'pre-post-diff' && existing.artifact === item.artifact);
|
|
1251
|
-
if (alreadyPresent)
|
|
1252
|
-
return dimension;
|
|
1253
|
-
return { ...dimension, evidence: [...dimension.evidence, item] };
|
|
1254
|
-
});
|
|
1255
|
-
}
|
|
1256
|
-
/**
|
|
1257
|
-
* F2 — read what the delivered conclusion SAYS, and make a detected drift
|
|
1258
|
-
* impossible to hand over as "nothing needs attention".
|
|
1259
|
-
*
|
|
1260
|
-
* The layer that made delivery a machine fact stopped one step short: it
|
|
1261
|
-
* verified that the reviewer received the `VERDICT:` line and then threw the
|
|
1262
|
-
* line away, keeping only a boolean. Measured (QA round 5, one export removed,
|
|
1263
|
-
* the model replying 4/4 `pass`):
|
|
1264
|
-
*
|
|
1265
|
-
* VERDICT: STRUCTURAL DRIFT DETECTED — ... 1 export name(s).
|
|
1266
|
-
* existing-functionality-intact: pass/high
|
|
1267
|
-
* allPass: true | needsAttention: []
|
|
1268
|
-
*
|
|
1269
|
-
* — an envelope that says "clean handoff" directly above the evidence it
|
|
1270
|
-
* attached itself, whose first line says a removal was detected. That is the
|
|
1271
|
-
* same shape as every other hole here: a check that ran, whose RESULT was never
|
|
1272
|
-
* consumed, so the check's presence was mistaken for its verdict.
|
|
1273
|
-
*
|
|
1274
|
-
* The verdict is deliberately NOT forced to `fail`: a removal can be authorized
|
|
1275
|
-
* by the approved scope, and that judgement belongs to the reviewer and to the
|
|
1276
|
-
* human. What is refused is SILENCE — a drift the service detected is a
|
|
1277
|
-
* dimension a human has to look at, so it is named in `needsAttention` (which
|
|
1278
|
-
* also clears `allPass`: a handoff with an open question is not a clean one)
|
|
1279
|
-
* and its dimension says so in its own summary.
|
|
1280
|
-
*
|
|
1281
|
-
* `indeterminate` counts too. A delivered conclusion this service cannot
|
|
1282
|
-
* classify is not "no drift" — it is a conclusion nobody read, which is the
|
|
1283
|
-
* defect this function exists to close, so it is surfaced rather than dropped.
|
|
1284
|
-
*/
|
|
1285
|
-
function enforceStructuralDriftAttention(dimensions, deliveredConclusion) {
|
|
1286
|
-
// `null` is "no conclusion was DELIVERED" — the gate above owns that case. It
|
|
1287
|
-
// must not be folded into `indeterminate`: a project with no baseline has not
|
|
1288
|
-
// delivered an unreadable conclusion, it has delivered none, and saying
|
|
1289
|
-
// otherwise would put a false "the comparison was never read" marker on every
|
|
1290
|
-
// non-git run.
|
|
1291
|
-
if (deliveredConclusion === null)
|
|
1292
|
-
return { dimensions, mustAttend: false };
|
|
1293
|
-
if (deliveredConclusion === 'no-drift' || deliveredConclusion === 'additions-only') {
|
|
1294
|
-
return { dimensions, mustAttend: false };
|
|
1295
|
-
}
|
|
1296
|
-
const what = deliveredConclusion === 'drift-detected'
|
|
1297
|
-
? 'the delivered pre/post baseline diff reports STRUCTURAL DRIFT DETECTED'
|
|
1298
|
-
: 'the delivered pre/post baseline diff carries a VERDICT line this service cannot classify, so the comparison was never actually read';
|
|
1299
|
-
return {
|
|
1300
|
-
dimensions: dimensions.map(dimension => {
|
|
1301
|
-
if (dimension.dimension !== 'existing-functionality-intact')
|
|
1302
|
-
return dimension;
|
|
1303
|
-
return {
|
|
1304
|
-
...dimension,
|
|
1305
|
-
summary: `${dimension.summary} [pre-post-diff-drift-gate: ${what}. The removal may well be authorized by the approved scope — that is the reviewer's and the human's call — but a detected drift is never "nothing needs attention": this dimension is listed in needsAttention and allPass is false.]`
|
|
1306
|
-
};
|
|
1307
|
-
}),
|
|
1308
|
-
mustAttend: true
|
|
1309
|
-
};
|
|
168
|
+
export function attachPrePostDiffEvidence(dimensions, prePostDiff, collected) {
|
|
169
|
+
return gateAttachPrePostDiffEvidence(dimensions, prePostDiff, prePostDiffDelivered(collected));
|
|
1310
170
|
}
|
|
1311
171
|
/**
|
|
1312
172
|
* True when the reply ends INSIDE a JSON string or with brackets still open —
|
|
@@ -1318,7 +178,7 @@ function enforceStructuralDriftAttention(dimensions, deliveredConclusion) {
|
|
|
1318
178
|
* A minimal scanner is enough — braces and quotes inside string literals are
|
|
1319
179
|
* skipped, escapes are honoured, and the text is never parsed.
|
|
1320
180
|
*/
|
|
1321
|
-
function looksTruncated(text) {
|
|
181
|
+
export function looksTruncated(text) {
|
|
1322
182
|
let inString = false;
|
|
1323
183
|
let escaped = false;
|
|
1324
184
|
let depth = 0;
|
|
@@ -1341,201 +201,9 @@ function looksTruncated(text) {
|
|
|
1341
201
|
}
|
|
1342
202
|
return inString || depth > 0;
|
|
1343
203
|
}
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
}
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
* numbers when they are the only thing pointing at the ceiling. Measured on
|
|
1351
|
-
* this repo's machine: `input_tokens: 150` for a ~32 KiB prompt, so a reporter
|
|
1352
|
-
* that cannot count the input should not be trusted to count the output.
|
|
1353
|
-
*/
|
|
1354
|
-
function describeTruncationSignal(structurallyCut, ceilingReached) {
|
|
1355
|
-
if (structurallyCut && ceilingReached) {
|
|
1356
|
-
return 'reply ends mid-structure AND provider-reported output reached the ceiling';
|
|
1357
|
-
}
|
|
1358
|
-
if (structurallyCut) {
|
|
1359
|
-
return 'reply ends mid-structure (structural)';
|
|
1360
|
-
}
|
|
1361
|
-
return 'provider-reported output reached the ceiling only — the provider’s usage reporting is not trustworthy on its own, so verify before raising anything';
|
|
1362
|
-
}
|
|
1363
|
-
/**
|
|
1364
|
-
* N4 — call the reviewer, retrying an EMPTY reply a bounded number of times.
|
|
1365
|
-
*
|
|
1366
|
-
* The empty reply is a separate failure mode from truncation and was measured
|
|
1367
|
-
* at 2/3 on this repo's machine. It is intermittent, so a bounded retry turns
|
|
1368
|
-
* most occurrences back into a completed review; when it does not, the caller
|
|
1369
|
-
* gets `EmptyReviewReplyError` — classified and diagnosable — instead of a
|
|
1370
|
-
* truncation message that sends the operator to raise a budget that was never
|
|
1371
|
-
* the problem.
|
|
1372
|
-
*
|
|
1373
|
-
* The classification reads the runner's message because `LlmRunner` is a
|
|
1374
|
-
* structural interface here (this module deliberately does not depend on the
|
|
1375
|
-
* concrete provider module); "no text block" is the runner's own fixed wording
|
|
1376
|
-
* for a response with no text content.
|
|
1377
|
-
*/
|
|
1378
|
-
async function callReviewer(runner, userPrompt, budget) {
|
|
1379
|
-
let lastError;
|
|
1380
|
-
for (let attempt = 1; attempt <= MAX_EMPTY_REPLY_ATTEMPTS; attempt += 1) {
|
|
1381
|
-
try {
|
|
1382
|
-
return await runner.call(SYSTEM_PROMPT, userPrompt, { maxTokens: budget.maxTokens });
|
|
1383
|
-
}
|
|
1384
|
-
catch (error) {
|
|
1385
|
-
if (!isEmptyReplyError(error))
|
|
1386
|
-
throw error;
|
|
1387
|
-
lastError = error;
|
|
1388
|
-
}
|
|
1389
|
-
}
|
|
1390
|
-
throw new EmptyReviewReplyError(`The provider returned NO TEXT BLOCK on ${String(MAX_EMPTY_REPLY_ATTEMPTS)}/${String(MAX_EMPTY_REPLY_ATTEMPTS)} attempts — this is an EMPTY-REPLY failure, NOT an output-budget truncation: the response carried no text content (a reasoning-only turn, a refusal, or a content filter), so there was no JSON to parse and raising the budget would not have helped. ${describeOutputBudget(budget.maxTokens, 0, 0)}. Last provider error: ${errorText(lastError)}`);
|
|
1391
|
-
}
|
|
1392
|
-
/**
|
|
1393
|
-
* `allPass` / `needsAttention` are DERIVED from the verdicts, never copied
|
|
1394
|
-
* verbatim from the model's own summary fields: a model that writes a fabricated
|
|
1395
|
-
* `pass` line plus a matching `allPass: true` would otherwise produce exactly
|
|
1396
|
-
* the forged clean handoff this primitive exists to prevent, and once the gate
|
|
1397
|
-
* above rewrites a verdict the two would silently disagree.
|
|
1398
|
-
*
|
|
1399
|
-
* Both fields are only ever NARROWED, never widened — `allPass` cannot become
|
|
1400
|
-
* true unless the model also said true and no dimension is non-`pass`, and a
|
|
1401
|
-
* dimension the model itself flagged is never dropped from `needsAttention`.
|
|
1402
|
-
*/
|
|
1403
|
-
function summarizeVerdicts(dimensions, modelFlags,
|
|
1404
|
-
/**
|
|
1405
|
-
* Dimensions the SERVICE must flag whatever the model said — today, the one
|
|
1406
|
-
* F2 names when the delivered baseline reports structural drift. A handoff
|
|
1407
|
-
* with a machine-detected drift in it is not clean, so this also clears
|
|
1408
|
-
* `allPass`, exactly as a non-`pass` verdict does.
|
|
1409
|
-
*/
|
|
1410
|
-
mustAttend = []) {
|
|
1411
|
-
const nonPass = dimensions.filter(d => d.verdict !== 'pass').map(d => d.dimension);
|
|
1412
|
-
const flaggedByModel = Array.isArray(modelFlags.needsAttention)
|
|
1413
|
-
? modelFlags.needsAttention
|
|
1414
|
-
: [];
|
|
1415
|
-
return {
|
|
1416
|
-
allPass: modelFlags.allPass !== false &&
|
|
1417
|
-
dimensions.length > 0 &&
|
|
1418
|
-
nonPass.length === 0 &&
|
|
1419
|
-
mustAttend.length === 0,
|
|
1420
|
-
needsAttention: [...new Set([...flaggedByModel, ...nonPass, ...mustAttend])]
|
|
1421
|
-
};
|
|
1422
|
-
}
|
|
1423
|
-
export async function prepareFinalReview(rid, opts) {
|
|
1424
|
-
const auditGoalPath = join(opts.projectRoot, '.peaks', '_runtime', opts.sessionId, 'audit-goal', `${rid}.json`);
|
|
1425
|
-
let approvedGoal;
|
|
1426
|
-
try {
|
|
1427
|
-
approvedGoal = JSON.parse(readFileSync(auditGoalPath, 'utf8'));
|
|
1428
|
-
}
|
|
1429
|
-
catch (err) {
|
|
1430
|
-
throw new Error(`Cannot read approved goal from ${auditGoalPath}: ${err.message}`);
|
|
1431
|
-
}
|
|
1432
|
-
// The pre/post baseline diff is produced BEFORE the read phase: it is the one
|
|
1433
|
-
// step in this service that runs a command (`git`, read-only) and writes a
|
|
1434
|
-
// file, and it has to finish first because its artifact is also an evidence
|
|
1435
|
-
// source below. Everything after this line is still pure file reading.
|
|
1436
|
-
const prePostDiff = producePrePostDiff({
|
|
1437
|
-
projectRoot: opts.projectRoot,
|
|
1438
|
-
sessionId: opts.sessionId,
|
|
1439
|
-
...(opts.baseRef === undefined ? {} : { baseRef: opts.baseRef })
|
|
1440
|
-
});
|
|
1441
|
-
const evidence = collectEvidence(opts.projectRoot, opts.sessionId, rid, prePostDiff.status === 'computed');
|
|
1442
|
-
// F-BLOCK: the fourth dimension needs the baseline to have been DELIVERED,
|
|
1443
|
-
// not merely computed, so the delivery fact is measured on the collected
|
|
1444
|
-
// evidence (what the prompt carries) rather than read off the producer's
|
|
1445
|
-
// status (what exists on disk).
|
|
1446
|
-
const ppdDelivered = prePostDiffDelivered(evidence);
|
|
1447
|
-
// H2: a dimension whose every on-disk source is structurally undeliverable is
|
|
1448
|
-
// a fact about the EVIDENCE, so it is measured where the evidence is and
|
|
1449
|
-
// stated in both the prompt and the envelope — an always-red dimension that
|
|
1450
|
-
// explains itself is a finding; one that does not is noise.
|
|
1451
|
-
const undeliverable = undeliverableDimensions(evidence);
|
|
1452
|
-
const reachabilityStatus = renderDeliveryReachabilityStatus(undeliverable);
|
|
1453
|
-
const userPrompt = [
|
|
1454
|
-
`Approved goal's success criteria: ${JSON.stringify(approvedGoal.successCriteria)}`,
|
|
1455
|
-
'',
|
|
1456
|
-
'## On-disk evidence',
|
|
1457
|
-
'You have NO tools and NO filesystem access — the blocks below are ALL the evidence that exists for this review. They were collected read-only by the service; nothing was executed.',
|
|
1458
|
-
'',
|
|
1459
|
-
renderEvidenceSection(evidence),
|
|
1460
|
-
'',
|
|
1461
|
-
renderPrePostDiffStatus(prePostDiff, ppdDelivered),
|
|
1462
|
-
'',
|
|
1463
|
-
...(reachabilityStatus === '' ? [] : [reachabilityStatus, '']),
|
|
1464
|
-
EVIDENCE_RULES,
|
|
1465
|
-
'',
|
|
1466
|
-
'Prepare the 4-dim review evidence.'
|
|
1467
|
-
].join('\n');
|
|
1468
|
-
// D1 layer 3: the ceiling follows the evidence actually inlined, so the two
|
|
1469
|
-
// sides of the call cannot drift apart again. N4 adds the env lever on top.
|
|
1470
|
-
const includedEvidenceBytes = evidence.reduce((sum, item) => sum + item.includedBytes, 0);
|
|
1471
|
-
const derivedMaxTokens = outputBudgetForEvidence(includedEvidenceBytes);
|
|
1472
|
-
const maxTokens = resolveOutputBudget(includedEvidenceBytes);
|
|
1473
|
-
const response = await callReviewer(opts.llmRunner, userPrompt, { maxTokens, derivedMaxTokens });
|
|
1474
|
-
let parsed;
|
|
1475
|
-
try {
|
|
1476
|
-
parsed = JSON.parse(response.output);
|
|
1477
|
-
}
|
|
1478
|
-
catch (err) {
|
|
1479
|
-
const budget = describeOutputBudget(maxTokens, response.tokens.output, response.output.length);
|
|
1480
|
-
// N4 — the STRUCTURAL judgement is primary: `looksTruncated()` reads the
|
|
1481
|
-
// reply itself and needs no cooperation from the provider. The
|
|
1482
|
-
// `output_tokens >= maxTokens` comparison is kept as a corroborating
|
|
1483
|
-
// signal, but it cannot be the only one: this endpoint reported
|
|
1484
|
-
// `input_tokens: 150` for a ~32 KiB prompt, so its usage numbers are not
|
|
1485
|
-
// trustworthy on their own, and a provider that under-reports a truncated
|
|
1486
|
-
// reply would otherwise have it filed below as "not valid JSON" — sending
|
|
1487
|
-
// an operator to look for a schema bug that does not exist. Which signal
|
|
1488
|
-
// fired is reported, so the diagnosis is auditable rather than inferred.
|
|
1489
|
-
const structurallyCut = looksTruncated(response.output);
|
|
1490
|
-
const ceilingReached = response.tokens.output >= maxTokens;
|
|
1491
|
-
if (structurallyCut || ceilingReached) {
|
|
1492
|
-
throw new IncompleteFinalReviewError(`LLM output was TRUNCATED by the output budget before the 4-dim envelope was complete — this is an OUTPUT-BUDGET failure, not a malformed reply. Raise the budget: it scales with inlined evidence bytes, and ${MAX_OUTPUT_TOKENS_ENV} overrides it outright for this run. Signal: ${describeTruncationSignal(structurallyCut, ceilingReached)}. ${budget}. Parser said: ${err.message}`);
|
|
1493
|
-
}
|
|
1494
|
-
throw new IncompleteFinalReviewError(`LLM output is not valid JSON: ${err.message} (${budget})`);
|
|
1495
|
-
}
|
|
1496
|
-
const output = parsed;
|
|
1497
|
-
const presentDimensions = new Set(output.dimensions.map((d) => d.dimension));
|
|
1498
|
-
const missing = REQUIRED_DIMENSIONS.filter(d => !presentDimensions.has(d));
|
|
1499
|
-
if (missing.length > 0) {
|
|
1500
|
-
// A reply that stopped at the ceiling and still parsed is still a budget
|
|
1501
|
-
// problem, so the same diagnosis is attached here. N4: the structural
|
|
1502
|
-
// reading counts too — a reply cut off mid-array parses only because the
|
|
1503
|
-
// envelope it produced happened to be closed early.
|
|
1504
|
-
const budgetExhausted = response.tokens.output >= maxTokens || looksTruncated(response.output);
|
|
1505
|
-
throw new IncompleteFinalReviewError(`Missing required dimensions: ${missing.join(', ')}${budgetExhausted
|
|
1506
|
-
? ` — the provider hit the output budget and the reply was cut short (${describeOutputBudget(maxTokens, response.tokens.output, response.output.length)}); raise the budget instead of retrying blindly.`
|
|
1507
|
-
: ''}`);
|
|
1508
|
-
}
|
|
1509
|
-
// F2: what the DELIVERED conclusion says. Read before the verdicts are
|
|
1510
|
-
// assembled, because a detected drift has to reach `needsAttention` whatever
|
|
1511
|
-
// the reviewer answered. `null` — nothing delivered — is NOT
|
|
1512
|
-
// `indeterminate`: see `enforceStructuralDriftAttention`.
|
|
1513
|
-
const ppdBlock = evidence.find(item => item.source.key === PRE_POST_DIFF_SOURCE_KEY);
|
|
1514
|
-
const ppdConclusion = ppdBlock !== undefined && ppdDelivered ? classifyPrePostDiffVerdict(ppdBlock.content) : null;
|
|
1515
|
-
// D1: the verdicts must rest on evidence that actually existed, and the
|
|
1516
|
-
// derived summary flags must match the verdicts — see the helpers above.
|
|
1517
|
-
const gated = enforceStructuralDriftAttention(output.dimensions, ppdConclusion);
|
|
1518
|
-
const dimensions = attachPrePostDiffEvidence(enforceDeliveryReachability(enforceScopeContractDelivery(enforcePrePostDiffAvailability(enforceEvidenceBackedVerdicts(clampInconclusiveConfidence(gated.dimensions), dimensionsWithEvidence(evidence)), prePostDiff, evidence), evidence), undeliverable), prePostDiff, evidence);
|
|
1519
|
-
// H2 adds nothing to `mustAttend` on purpose: `enforceDeliveryReachability`
|
|
1520
|
-
// has already made every undeliverable dimension non-`pass`, so it is in
|
|
1521
|
-
// `needsAttention` (and `allPass` is false) through the verdict route. A
|
|
1522
|
-
// second flag for the same dimension would be a check whose result nothing
|
|
1523
|
-
// reads — the shape this primitive exists to catch.
|
|
1524
|
-
const { allPass, needsAttention } = summarizeVerdicts(dimensions, output, [
|
|
1525
|
-
...(gated.mustAttend ? ['existing-functionality-intact'] : [])
|
|
1526
|
-
]);
|
|
1527
|
-
return { ...output, dimensions, allPass, needsAttention };
|
|
1528
|
-
}
|
|
1529
|
-
export function decideFifthDimension(input) {
|
|
1530
|
-
if (input.audit === null)
|
|
1531
|
-
return { verdict: 'inconclusive', reason: 'AUDIT_GUARD_NOT_RUN' };
|
|
1532
|
-
if (isStale(input.audit.auditedAt, input.nowMs))
|
|
1533
|
-
return { verdict: 'inconclusive', reason: 'AUDIT_STALE' };
|
|
1534
|
-
if (input.audit.crossCheck.guardVsAudit === 'diverge')
|
|
1535
|
-
return { verdict: 'inconclusive', reason: 'AUDIT_CROSS_CHECK_DIVERGE' };
|
|
1536
|
-
if (input.audit.verdict === 'consistent')
|
|
1537
|
-
return { verdict: 'pass', reason: 'audit consistent' };
|
|
1538
|
-
if (input.audit.verdict === 'drifted')
|
|
1539
|
-
return { verdict: 'fail', reason: 'audit drifted' };
|
|
1540
|
-
return { verdict: 'inconclusive', reason: 'audit inconclusive' };
|
|
1541
|
-
}
|
|
204
|
+
export { assertFloorReservationAffordable, MAX_EVIDENCE_BYTES_PER_FILE, MAX_EVIDENCE_BYTES_TOTAL } from './final-review-evidence-budget.js';
|
|
205
|
+
export { EVIDENCE_BYTES_PER_OUTPUT_TOKEN, HARD_MAX_OUTPUT_TOKENS, MAX_OUTPUT_TOKENS, MAX_OUTPUT_TOKENS_ENV, MIN_OUTPUT_TOKENS, outputBudgetForEvidence, REASONING_HEADROOM_TOKENS, resolveOutputBudget } from './final-review-output-budget.js';
|
|
206
|
+
export { EmptyReviewReplyError, MAX_EMPTY_REPLY_ATTEMPTS } from './final-review-reviewer.js';
|
|
207
|
+
export { undeliverableDimensions } from './final-review-delivery.js';
|
|
208
|
+
export { prepareFinalReview } from './final-review-runner.js';
|
|
209
|
+
export { decideFifthDimension } from './final-review-fifth-dim.js';
|