@khoavu882/doflow 2.0.0-beta.4 → 2.0.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/README.md +12 -11
- package/bin/doflow.js +21 -11
- package/core/.antigravity-plugin/plugin.json +1 -3
- package/core/.claude-plugin/plugin.json +1 -1
- package/core/.codex-plugin/plugin.json +1 -1
- package/core/.plugin/plugin.json +1 -1
- package/core/harnesses/claude/hooks/lib.sh +43 -24
- package/core/harnesses/claude/hooks/mcp-tool-guard.sh +7 -17
- package/core/harnesses/claude/hooks/permission-audit.sh +3 -0
- package/core/harnesses/claude/hooks/post-compact.sh +35 -32
- package/core/harnesses/claude/hooks/post-edit-lint.sh +2 -0
- package/core/harnesses/claude/hooks/post-shell-observe.sh +48 -0
- package/core/harnesses/claude/hooks/post-tool-failure.sh +3 -0
- package/core/harnesses/claude/hooks/pre-bash-guard.sh +7 -17
- package/core/harnesses/claude/hooks/pre-compact.sh +23 -27
- package/core/harnesses/claude/hooks/pre-implement-gate.sh +7 -18
- package/core/harnesses/claude/hooks/session-end.sh +23 -10
- package/core/harnesses/claude/hooks/session-start.sh +5 -3
- package/core/harnesses/claude/hooks/skill-config-audit.sh +3 -0
- package/core/harnesses/claude/hooks/stop-check.sh +5 -3
- package/core/harnesses/claude/hooks/subagent-audit.sh +5 -5
- package/core/harnesses/claude/hooks/user-prompt-submit.sh +5 -3
- package/core/harnesses/claude/settings/settings.json +18 -0
- package/core/harnesses/claude/statusline/doflow-statusline.sh +1 -0
- package/core/harnesses/codex/agents/core-implementer.toml +1 -0
- package/core/harnesses/codex/agents/delivery-lead.toml +10 -0
- package/core/harnesses/codex/agents/quality-guardian.toml +1 -1
- package/core/harnesses/codex/agents/research-writer.toml +1 -1
- package/core/harnesses/codex/agents/reviewer.toml +10 -0
- package/core/harnesses/codex/agents/spec-analyst.toml +1 -1
- package/core/harnesses/codex/agents/system-architect.toml +1 -1
- package/core/harnesses/codex/hooks/lib.sh +44 -25
- package/core/harnesses/codex/hooks/mcp-tool-guard.sh +7 -15
- package/core/harnesses/codex/hooks/post-edit-lint.impl.sh +3 -1
- package/core/harnesses/codex/hooks/pre-bash-guard.sh +7 -15
- package/core/harnesses/codex/hooks/pre-compact.impl.sh +29 -26
- package/core/harnesses/codex/hooks/pre-implement-gate.sh +7 -16
- package/core/harnesses/codex/hooks/session-end.impl.sh +23 -10
- package/core/harnesses/codex/hooks/session-start.sh +5 -3
- package/core/harnesses/codex/hooks/stop-check.sh +6 -5
- package/core/harnesses/codex/hooks/subagent-audit.sh +18 -6
- package/core/harnesses/codex/hooks/user-prompt-submit.sh +5 -4
- package/core/harnesses/copilot/hooks/doflow-lib.sh +93 -0
- package/core/harnesses/copilot/hooks/doflow-mcp-tool-guard.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-pre-bash-guard.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-pre-implement-gate.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-session-start.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-stop-check.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-subagent-start.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-subagent-stop.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow.json +57 -0
- package/core/harnesses/kiro/agents/doflow.json +1 -1
- package/core/harnesses/kiro/hooks/lib.sh +26 -6
- package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +21 -5
- package/core/harnesses/kiro/hooks/pre-bash-guard.sh +21 -5
- package/core/harnesses/kiro/hooks/pre-implement-gate.sh +21 -5
- package/core/harnesses/kiro/hooks/session-start.sh +12 -5
- package/core/harnesses/kiro/hooks/stop-check.sh +22 -30
- package/core/harnesses/pi/extensions/doflow-guard.js +167 -0
- package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +27 -12
- package/core/harnesses/shared/hooks/adapters/antigravity.js +14 -57
- package/core/harnesses/shared/hooks/bridge/bridge.sh +277 -0
- package/core/harnesses/shared/hooks/bridge/envelope.jq +129 -0
- package/core/harnesses/shared/hooks/bridge/observe-model.sh +75 -0
- package/core/harnesses/shared/hooks/bridge/respond.sh +70 -0
- package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +28 -14
- package/core/harnesses/shared/hooks/policies/capture-failure.sh +51 -16
- package/core/harnesses/shared/hooks/policies/compact-text.sh +46 -0
- package/core/harnesses/shared/hooks/policies/contract.sh +78 -0
- package/core/harnesses/shared/hooks/policies/deny-json.sh +35 -9
- package/core/harnesses/shared/hooks/policies/emission-rules.sh +37 -0
- package/core/harnesses/shared/hooks/policies/emission.jq +209 -0
- package/core/harnesses/shared/hooks/policies/emission.sh +383 -0
- package/core/harnesses/shared/hooks/policies/identity.sh +177 -0
- package/core/harnesses/shared/hooks/policies/lib.sh +65 -40
- package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +48 -35
- package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +284 -33
- package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +180 -110
- package/core/harnesses/shared/hooks/policies/session-context.sh +66 -7
- package/core/harnesses/shared/hooks/policies/stop-check.sh +61 -23
- package/core/harnesses/shared/hooks/policies/subagent-audit.sh +39 -6
- package/core/harnesses/shared/hooks/policies/summary-store.sh +347 -0
- package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +97 -159
- package/core/harnesses/shared/hooks/stream-hook-runner.js +114 -187
- package/core/harnesses/shared/locator/doflow-run +2 -3
- package/core/registry/advice.json +3 -3
- package/core/registry/assets.json +72 -80
- package/core/registry/capabilities.json +12 -0
- package/core/registry/contracts.json +60 -33
- package/core/registry/contribution-points.json +32 -0
- package/core/registry/gates.json +32 -0
- package/core/registry/guidance-caps.json +40 -0
- package/core/registry/harnesses.json +1542 -277
- package/core/registry/lifecycle.json +2837 -37
- package/core/registry/mcp.json +5 -1
- package/core/registry/models.json +7 -1
- package/core/registry/package-templates/README.md +19 -0
- package/core/registry/package-templates/agent/files/agent.md +9 -0
- package/core/registry/package-templates/agent/template.json +4 -0
- package/core/registry/package-templates/capability-provider/files/provider.json +9 -0
- package/core/registry/package-templates/capability-provider/template.json +11 -0
- package/core/registry/package-templates/debate-preset/files/preset.json +26 -0
- package/core/registry/package-templates/debate-preset/template.json +4 -0
- package/core/registry/package-templates/review-preset/files/preset.json +30 -0
- package/core/registry/package-templates/review-preset/template.json +4 -0
- package/core/registry/package-templates/skill/files/SKILL.md +8 -0
- package/core/registry/package-templates/skill/template.json +4 -0
- package/core/registry/package-templates/worker/files/worker.json +92 -0
- package/core/registry/package-templates/worker/template.json +11 -0
- package/core/registry/packages/doflow/doflow-package.json +15 -0
- package/core/registry/packages/doflow/presets/debate-3.json +26 -0
- package/core/registry/protocols.json +61 -0
- package/core/registry/routes.json +17 -0
- package/core/registry/rules.json +431 -0
- package/core/registry/settings.json +276 -0
- package/core/registry/storage.json +701 -30
- package/core/registry/telemetry.json +236 -0
- package/core/registry/transcripts.json +142 -0
- package/core/registry/verification.json +76 -1
- package/core/registry/workflows.json +55 -0
- package/core/shared/agent-specs/core-implementer.md +3 -0
- package/core/shared/agent-specs/delivery-lead.md +68 -0
- package/core/shared/agent-specs/quality-guardian.md +5 -1
- package/core/shared/agent-specs/research-writer.md +10 -3
- package/core/shared/agent-specs/reviewer.md +53 -0
- package/core/shared/agent-specs/spec-analyst.md +5 -6
- package/core/shared/agent-specs/system-architect.md +5 -1
- package/core/shared/guidance/DOFLOW_CORE.md +1 -5
- package/core/shared/guidance/modes/MODE_DeepResearch.md +1 -1
- package/core/shared/guidance/modes/MODE_Orchestration.md +4 -3
- package/core/shared/guidance/modes/MODE_Task_Management.md +14 -14
- package/core/shared/guidance/pointers/codex.md +1 -4
- package/core/shared/guidance/references/ARTIFACT_FORMAT.md +30 -16
- package/core/shared/guidance/references/ARTIFACT_VOICE.md +3 -2
- package/core/shared/guidance/references/CONSTITUTION_BASE.md +17 -15
- package/core/shared/guidance/references/DIAGRAM_GUIDE.md +1 -1
- package/core/shared/guidance/references/DOFLOW_CHAIN.md +17 -19
- package/core/shared/guidance/references/EVIDENCE_LEDGER.md +7 -8
- package/core/shared/guidance/references/MODEL_SELECTION.md +5 -3
- package/core/shared/guidance/references/ORCHESTRATION.md +1 -2
- package/core/shared/guidance/rules/RULES.md +45 -0
- package/core/shared/scripts/doflow/bash/do-args.sh +37 -0
- package/core/shared/scripts/doflow/bash/do-exec-paths.sh +4 -0
- package/core/shared/scripts/doflow/bash/do-git-state.sh +15 -4
- package/core/shared/scripts/doflow/bash/do-parallel-check.sh +4 -0
- package/core/shared/scripts/doflow/bash/do-paths.sh +40 -2
- package/core/shared/scripts/doflow/bash/do-prereqs.sh +2 -1
- package/core/shared/scripts/doflow/bash/do-review-package.sh +4 -0
- package/core/shared/scripts/doflow/bash/do-task-brief.sh +58 -16
- package/core/shared/scripts/doflow/bash/render-audit.sh +26 -2
- package/core/shared/scripts/doflow/bash/render-diagrams.sh +1 -0
- package/core/shared/scripts/doflow/bash/render-puml.sh +2 -1
- package/core/shared/scripts/doflow/bash/validate-artifacts.sh +48 -4
- package/core/shared/scripts/doflow/bin/doflow-run +154 -35
- package/core/shared/skills/do/SKILL.md +12 -10
- package/core/shared/skills/do/references/advice_calls.md +26 -0
- package/core/shared/skills/do/references/pm_routing.md +1 -2
- package/core/shared/skills/do/references/program_mode.md +31 -0
- package/core/shared/skills/do-brainstorm/SKILL.md +20 -74
- package/core/shared/skills/do-brainstorm/references/handoff_branches.md +35 -0
- package/core/shared/skills/do-brainstorm/references/intent_seed.md +21 -0
- package/core/shared/skills/do-brainstorm/references/round_log.md +17 -0
- package/core/shared/skills/do-code-review/SKILL.md +41 -144
- package/core/shared/skills/do-code-review/content-types/markdown.md +24 -2
- package/core/shared/skills/do-code-review/references/tools.md +90 -0
- package/core/shared/skills/do-code-review/scripts/doc_quality_checker.py +549 -7
- package/core/shared/skills/do-design/SKILL.md +24 -23
- package/core/shared/skills/do-diagnose/SKILL.md +14 -4
- package/core/shared/skills/do-document/SKILL.md +11 -1
- package/core/shared/skills/do-document/references/deep_research.md +1 -2
- package/core/shared/skills/do-document/references/embedded-research.md +18 -6
- package/core/shared/skills/do-document/references/implementation-flow.md +5 -6
- package/core/shared/skills/do-execute-plan/SKILL.md +17 -59
- package/core/shared/skills/do-execute-plan/references/handoff_record.md +7 -2
- package/core/shared/skills/do-execute-plan/references/parallel_dispatch.md +13 -0
- package/core/shared/skills/do-execute-plan/references/readiness_gate.md +28 -1
- package/core/shared/skills/do-execute-plan/references/run_position.md +13 -1
- package/core/shared/skills/do-execute-plan/references/scaffold.md +17 -9
- package/core/shared/skills/do-execute-plan/references/state_checkpoint.md +11 -0
- package/core/shared/skills/do-execute-plan/references/task_attempts.md +78 -24
- package/core/shared/skills/do-flow/SKILL.md +8 -9
- package/core/shared/skills/do-flow/references/task_classes.md +2 -2
- package/core/shared/skills/do-git/SKILL.md +4 -0
- package/core/shared/skills/do-implement/SKILL.md +7 -9
- package/core/shared/skills/do-plan/SKILL.md +29 -19
- package/core/shared/skills/do-test/SKILL.md +7 -4
- package/core/shared/templates/doflow/constitution-template.md +9 -3
- package/core/shared/templates/doflow/data-model-template.md +1 -1
- package/core/shared/templates/doflow/design-template.md +113 -16
- package/core/shared/templates/doflow/plan-template.md +11 -10
- package/core/shared/templates/doflow/question-log-template.md +14 -2
- package/core/shared/templates/doflow/requirement-template.md +1 -1
- package/core/worker-evidence/antigravity/checks.json +14 -0
- package/core/worker-evidence/codex/2026-10-18-cli-check.json +107 -0
- package/core/worker-evidence/codex/2026-10-18-live-run-edit.json +73 -0
- package/core/worker-evidence/codex/2026-10-18-live-run-read-only.json +62 -0
- package/core/worker-evidence/codex/2026-10-24-cli-check.json +126 -0
- package/core/worker-evidence/codex/checks.json +38 -0
- package/package.json +10 -6
- package/src/adapters/antigravity/index.js +226 -113
- package/src/adapters/claude/index.js +39 -5
- package/src/adapters/codex/agents.js +25 -15
- package/src/adapters/codex/hooks.js +40 -5
- package/src/adapters/codex/index.js +100 -22
- package/src/adapters/codex/native-projection.js +6 -2
- package/src/adapters/copilot/index.js +58 -23
- package/src/adapters/copy-tree.js +294 -48
- package/src/adapters/guidance-manifest.js +71 -0
- package/src/adapters/guidance-pointer.js +19 -0
- package/src/adapters/hook-commands.js +6 -10
- package/src/adapters/index.js +1 -0
- package/src/adapters/kiro/index.js +38 -13
- package/src/adapters/opencode/index.js +8 -8
- package/src/adapters/pi/hooks.js +120 -0
- package/src/adapters/pi/index.js +48 -16
- package/src/adapters/resolve-source.js +26 -2
- package/src/cli/commands/install.js +3 -2
- package/src/cli/commands/reconcile.js +53 -9
- package/src/cli/commands/remove.js +2 -2
- package/src/cli/commands/rollback.js +6 -3
- package/src/cli/commands/self-update.js +18 -9
- package/src/cli/commands/status.js +21 -2
- package/src/cli/commands/ui.js +99 -0
- package/src/cli/commands/update.js +3 -2
- package/src/cli/index.js +338 -33
- package/src/cli/runtime-commands.js +243 -52
- package/src/cli/shared.js +0 -2
- package/src/helper/git.js +1 -1
- package/src/helper/guardrails.js +1 -1
- package/src/helper/marker-merge.js +31 -13
- package/src/helper/package-ids.js +37 -0
- package/src/helper/semver.js +94 -0
- package/src/install/backup.js +105 -15
- package/src/install/context.js +1 -1
- package/src/install/targets.js +31 -16
- package/src/lifecycle/coverage.js +74 -0
- package/src/lifecycle/hook-step.js +61 -0
- package/src/lifecycle/hook-tier.js +160 -0
- package/src/lifecycle/index.js +434 -86
- package/src/lifecycle/retired-harnesses.js +318 -0
- package/src/lifecycle/view.js +35 -15
- package/src/registry/index.js +686 -32
- package/src/registry/worker-ids.js +45 -0
- package/src/release/build-package.js +0 -3
- package/src/release/docs-blocks.js +492 -0
- package/src/release/preflight.js +425 -0
- package/src/release/rehearse.js +401 -0
- package/src/release/release-workflow.js +733 -0
- package/src/release/sync-plugin-versions.js +43 -7
- package/src/runtime/advice/bench-gate.js +25 -0
- package/src/runtime/advice/cli.js +59 -16
- package/src/runtime/advice/client.js +32 -9
- package/src/runtime/advice/config.js +98 -133
- package/src/runtime/advice/doctor.js +1 -1
- package/src/runtime/advice/elevation.js +97 -0
- package/src/runtime/advice/endpoint-guard.js +9 -3
- package/src/runtime/advice/mode.js +37 -0
- package/src/runtime/advice/record.js +18 -5
- package/src/runtime/advice/registry.js +4 -1
- package/src/runtime/advice/shadow-config.js +229 -0
- package/src/runtime/advice/shadow-dispatcher.js +276 -0
- package/src/runtime/brief-rules.js +81 -0
- package/src/runtime/brief-tools.js +111 -34
- package/src/runtime/candidate.js +514 -0
- package/src/runtime/capabilities/executor.js +450 -0
- package/src/runtime/capabilities/loopback.js +41 -0
- package/src/runtime/capabilities/sanitizer.js +88 -0
- package/src/runtime/capability-router.js +135 -3
- package/src/runtime/checkouts.js +73 -11
- package/src/runtime/claims.js +27 -12
- package/src/runtime/cli-result.js +11 -4
- package/src/runtime/cli.js +221 -277
- package/src/runtime/config-home.js +42 -0
- package/src/runtime/context/arbitrate.js +767 -0
- package/src/runtime/context/budget.js +203 -0
- package/src/runtime/context/compose.js +1367 -0
- package/src/runtime/context/delivery-store.js +100 -0
- package/src/runtime/context/emission-rules.js +118 -0
- package/src/runtime/context/identity.js +227 -0
- package/src/runtime/context/mandatory.js +594 -0
- package/src/runtime/context/pointer.js +142 -0
- package/src/runtime/context/primary-command.js +117 -0
- package/src/runtime/context/primary.js +344 -0
- package/src/runtime/context/summary-store.js +655 -0
- package/src/runtime/context-pack.js +1106 -245
- package/src/runtime/decision-register.js +13 -4
- package/src/runtime/dispatch/admission.js +392 -0
- package/src/runtime/dispatch/attempt-end.js +274 -0
- package/src/runtime/dispatch/command.js +628 -0
- package/src/runtime/dispatch/controls.js +904 -0
- package/src/runtime/dispatch/envelope.js +276 -0
- package/src/runtime/dispatch/grants.js +310 -0
- package/src/runtime/dispatch/integrate.js +617 -0
- package/src/runtime/dispatch/loop.js +1835 -0
- package/src/runtime/dispatch/store.js +352 -0
- package/src/runtime/dispatch/verify-run.js +201 -0
- package/src/runtime/evidence-fields.js +39 -0
- package/src/runtime/evidence-ledger.js +41 -7
- package/src/runtime/failure/capture.js +3 -1
- package/src/runtime/failure/cli.js +25 -9
- package/src/runtime/failure/home.js +57 -30
- package/src/runtime/failure/store.js +7 -2
- package/src/runtime/feature-pointer.js +174 -0
- package/src/runtime/feature-resolve.js +13 -2
- package/src/runtime/fleet/command.js +77 -0
- package/src/runtime/fleet/projects.js +178 -0
- package/src/runtime/fleet/tree.js +383 -0
- package/src/runtime/freshness.js +77 -10
- package/src/runtime/git-memo.js +46 -0
- package/src/runtime/guidance/ablation.js +427 -0
- package/src/runtime/health.js +565 -17
- package/src/runtime/history-compactor.js +13 -3
- package/src/runtime/implementation-gate.js +277 -22
- package/src/runtime/inventory/coverage.js +1 -1
- package/src/runtime/inventory/index.js +1 -1
- package/src/runtime/inventory/precedence.js +2 -2
- package/src/runtime/inventory/siblings.js +6 -4
- package/src/runtime/knowledge/code-corpus.js +277 -0
- package/src/runtime/knowledge/code-lexical.js +130 -0
- package/src/runtime/knowledge/code-retrieval.js +307 -0
- package/src/runtime/knowledge/code-validation.js +106 -0
- package/src/runtime/knowledge/gp3-gate.js +74 -0
- package/src/runtime/knowledge/graph-adapter.js +288 -0
- package/src/runtime/knowledge/graph-fallback.js +394 -0
- package/src/runtime/knowledge/hybrid.js +136 -8
- package/src/runtime/knowledge/retrieval.js +34 -1
- package/src/runtime/knowledge/rtk-compress.js +77 -0
- package/src/runtime/knowledge/semble-bridge.py +106 -0
- package/src/runtime/knowledge/semble-lifecycle.js +114 -0
- package/src/runtime/knowledge/semble-provider.js +365 -0
- package/src/runtime/lifecycle/cli.js +16 -6
- package/src/runtime/lifecycle/event-store.js +8 -6
- package/src/runtime/lifecycle/followup.js +6 -1
- package/src/runtime/lifecycle/overview.js +24 -1
- package/src/runtime/lifecycle/report-store.js +8 -10
- package/src/runtime/lifecycle/retention.js +95 -31
- package/src/runtime/lifecycle/root.js +9 -1
- package/src/runtime/lifecycle/status.js +34 -17
- package/src/runtime/lifecycle/store-upkeep.js +56 -7
- package/src/runtime/mask.js +134 -7
- package/src/runtime/mcp-consultations.js +40 -6
- package/src/runtime/model-router.js +79 -7
- package/src/runtime/models/catalogue.js +191 -0
- package/src/runtime/models/command.js +153 -0
- package/src/runtime/off-words.js +12 -0
- package/src/runtime/outcome.js +3 -3
- package/src/runtime/packages/command.js +1009 -0
- package/src/runtime/packages/contributions.js +116 -0
- package/src/runtime/packages/digest.js +132 -0
- package/src/runtime/packages/fetch.js +182 -0
- package/src/runtime/packages/grants.js +192 -0
- package/src/runtime/packages/index-file.js +134 -0
- package/src/runtime/packages/instruction.js +90 -0
- package/src/runtime/packages/manifest.js +331 -0
- package/src/runtime/packages/points.js +477 -0
- package/src/runtime/packages/programs.js +216 -0
- package/src/runtime/packages/scaffold.js +277 -0
- package/src/runtime/packages/source.js +90 -0
- package/src/runtime/packages/state.js +164 -0
- package/src/runtime/packages/store.js +449 -0
- package/src/runtime/packages/tar.js +106 -0
- package/src/runtime/packages/view.js +406 -0
- package/src/runtime/plan-check.js +59 -0
- package/src/runtime/plan-content.js +324 -0
- package/src/runtime/policy-init.js +118 -0
- package/src/runtime/profiles/doctor.js +96 -0
- package/src/runtime/profiles/resolve.js +244 -0
- package/src/runtime/profiles/rollback.js +254 -0
- package/src/runtime/profiles/sampler.js +41 -0
- package/src/runtime/readiness-record.js +16 -11
- package/src/runtime/readiness.js +127 -16
- package/src/runtime/research-request.js +50 -25
- package/src/runtime/retrieval-plan.js +402 -73
- package/src/runtime/retrieval-record.js +293 -0
- package/src/runtime/review/command.js +246 -0
- package/src/runtime/review/dominance.js +68 -0
- package/src/runtime/review/findings.js +434 -0
- package/src/runtime/review/identity.js +255 -0
- package/src/runtime/review/protocol-command.js +109 -0
- package/src/runtime/review/protocols.js +405 -0
- package/src/runtime/review/resolve-protocol.js +302 -0
- package/src/runtime/review/start.js +530 -0
- package/src/runtime/roadmap/facts.js +479 -0
- package/src/runtime/roadmap/gates.js +270 -0
- package/src/runtime/roadmap/loop.js +1636 -0
- package/src/runtime/roadmap/merge.js +381 -0
- package/src/runtime/roadmap/rows.js +1113 -0
- package/src/runtime/roadmap/seed.js +951 -0
- package/src/runtime/rules-report.js +336 -0
- package/src/runtime/scaffold/artifacts.js +63 -9
- package/src/runtime/scaffold/generate.js +21 -9
- package/src/runtime/settings/autopilot.js +106 -0
- package/src/runtime/settings/cli.js +280 -0
- package/src/runtime/settings/config-write.js +314 -0
- package/src/runtime/settings/files.js +271 -0
- package/src/runtime/settings/legacy.js +154 -0
- package/src/runtime/settings/lexical.js +56 -0
- package/src/runtime/settings/prepared-run.js +269 -0
- package/src/runtime/settings/project-layers.js +118 -0
- package/src/runtime/settings/refs.js +288 -0
- package/src/runtime/settings/registry.js +553 -0
- package/src/runtime/settings/resolve.js +713 -0
- package/src/runtime/settings/shared.js +37 -0
- package/src/runtime/settings/worker-billing.js +107 -0
- package/src/runtime/settings/write.js +269 -0
- package/src/runtime/slot-command.js +78 -0
- package/src/runtime/slots.js +671 -0
- package/src/runtime/source-version.js +290 -0
- package/src/runtime/state-root.js +217 -0
- package/src/runtime/storage/cli.js +12 -5
- package/src/runtime/storage/engine.js +9 -2
- package/src/runtime/storage/registry.js +14 -1
- package/src/runtime/storage/roots.js +66 -16
- package/src/runtime/storage/rules.js +82 -10
- package/src/runtime/storage/upkeep.js +10 -2
- package/src/runtime/storage/work-status.js +245 -26
- package/src/runtime/task-attempts.js +589 -69
- package/src/runtime/task-classifier.js +47 -11
- package/src/runtime/task-graph.js +373 -0
- package/src/runtime/task-run.js +778 -0
- package/src/runtime/task-scope.js +202 -56
- package/src/runtime/task-sources.js +147 -0
- package/src/runtime/task-state.js +440 -65
- package/src/runtime/team/admission.js +301 -0
- package/src/runtime/team/envelope.js +324 -0
- package/src/runtime/team/review-guard.js +156 -0
- package/src/runtime/trace/analyze.js +427 -0
- package/src/runtime/trace/baseline.js +770 -0
- package/src/runtime/trace/ledger.js +63 -140
- package/src/runtime/trace/otlp-export.js +568 -0
- package/src/runtime/trace/otlp-map.js +481 -0
- package/src/runtime/trace/spans.js +745 -0
- package/src/runtime/trace/telemetry.js +225 -0
- package/src/runtime/trace/views.js +1 -0
- package/src/runtime/transcripts/command.js +98 -0
- package/src/runtime/transcripts/cursor.js +326 -0
- package/src/runtime/transcripts/read.js +126 -0
- package/src/runtime/transcripts/readers/claude-jsonl.js +74 -0
- package/src/runtime/transcripts/readers/codex-rollout.js +70 -0
- package/src/runtime/transcripts/readers/copilot-events.js +90 -0
- package/src/runtime/transcripts/readers/kiro-jsonl.js +77 -0
- package/src/runtime/transcripts/readers/pi-jsonl.js +59 -0
- package/src/runtime/transcripts/registry.js +161 -0
- package/src/runtime/transcripts/stats.js +161 -0
- package/src/runtime/verification/check-key.js +628 -0
- package/src/runtime/verification/contract-runner.js +546 -11
- package/src/runtime/verification/engine.js +499 -9
- package/src/runtime/verification/impact.js +617 -0
- package/src/runtime/verification/inflight.js +205 -0
- package/src/runtime/verification/registry.js +176 -0
- package/src/runtime/verification/reuse.js +184 -0
- package/src/runtime/verification/telemetry-emit.js +254 -0
- package/src/runtime/verification/test-selection.js +67 -7
- package/src/runtime/worker/active.js +90 -0
- package/src/runtime/worker/cancel.js +222 -0
- package/src/runtime/worker/catalogue.js +122 -0
- package/src/runtime/worker/command.js +677 -0
- package/src/runtime/worker/evidence.js +288 -0
- package/src/runtime/worker/launch.js +288 -0
- package/src/runtime/worker/models.js +137 -0
- package/src/runtime/worker/parsers.js +218 -0
- package/src/runtime/worker/probes/index.js +706 -0
- package/src/runtime/worker/process-tree.js +415 -0
- package/src/runtime/worker/quota.js +43 -0
- package/src/runtime/worker/readiness.js +713 -0
- package/src/runtime/worker/run-store.js +166 -0
- package/src/runtime/worker/supervisor.js +541 -0
- package/src/runtime/worker/worktree-check.js +252 -0
- package/src/runtime/workflow-engine.js +5 -1
- package/src/runtime/workflow-orchestrator.js +578 -63
- package/src/runtime/worktree.js +149 -22
- package/src/state/index.js +1 -1
- package/src/state/lockfile.js +64 -10
- package/src/state/run-lock.js +1 -1
- package/types/node-commonjs.d.ts +106 -7
- package/core/.antigravity-plugin/hooks.json +0 -39
- package/core/harnesses/gemini/hooks/hooks.json +0 -77
- package/core/harnesses/gemini/hooks/lib.sh +0 -302
- package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +0 -21
- package/core/harnesses/gemini/hooks/post-edit-lint.sh +0 -39
- package/core/harnesses/gemini/hooks/pre-bash-guard.sh +0 -21
- package/core/harnesses/gemini/hooks/pre-compact.sh +0 -38
- package/core/harnesses/gemini/hooks/pre-implement-gate.sh +0 -22
- package/core/harnesses/gemini/hooks/session-end.impl.sh +0 -89
- package/core/harnesses/gemini/hooks/session-end.sh +0 -5
- package/core/harnesses/gemini/hooks/session-start.sh +0 -5
- package/core/harnesses/gemini/settings/adapter-defaults.json +0 -4
- package/core/harnesses/shared/hooks/adapters/arg-fields.js +0 -19
- package/core/harnesses/shared/hooks/adapters/gemini.js +0 -114
- package/core/harnesses/shared/hooks/hooks.json +0 -47
- package/core/shared/guidance/FLAGS.md +0 -25
- package/core/shared/guidance/rules/RULE_01_SAFETY.md +0 -25
- package/core/shared/guidance/rules/RULE_02_WORKFLOW.md +0 -23
- package/core/shared/guidance/rules/RULE_03_QUALITY.md +0 -14
- package/core/shared/guidance/rules/RULE_04_QUESTIONS.md +0 -46
- package/src/adapters/gemini/hooks.js +0 -210
- package/src/adapters/gemini/index.js +0 -390
|
@@ -22,10 +22,12 @@
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
const path = require('node:path');
|
|
25
|
+
const os = require('node:os');
|
|
25
26
|
const nodeFs = require('node:fs');
|
|
26
27
|
const { spawnSync } = require('node:child_process');
|
|
27
28
|
const { detectCommands, applyTargetPattern, TARGET_PATTERN_TOKEN } = require('../command-detect.js');
|
|
28
|
-
const { selectChangedTests } = require('./test-selection.js');
|
|
29
|
+
const { selectChangedTests, formatSelectedTestPattern, testFilesFor, conventionFor, quotePath } = require('./test-selection.js');
|
|
30
|
+
const { selectImpactedTests } = require('./impact.js');
|
|
29
31
|
const { RecoveryManager } = require('../recovery.js');
|
|
30
32
|
const { finishRuntime, usageError } = require('../cli-result.js');
|
|
31
33
|
const { REPO_ROOT } = require('../../helper/repo-root.js');
|
|
@@ -37,6 +39,9 @@ const { readReadinessRecord, RECORD_VERSION } = require('../readiness-record.js'
|
|
|
37
39
|
const { verifyReadinessCheck, featureSlugFor } = require('../implementation-gate.js');
|
|
38
40
|
const { assertSafeTaskId } = require('../evidence-ledger.js');
|
|
39
41
|
const { upkeepAfterVerify } = require('../storage/upkeep.js');
|
|
42
|
+
const { emitVerifyTelemetry } = require('./telemetry-emit.js');
|
|
43
|
+
const { resolveRunsLocation, outerProjectRoot } = require('../trace/ledger.js');
|
|
44
|
+
const { projectRoot: lifecycleProjectRoot } = require('../lifecycle/root.js');
|
|
40
45
|
const {
|
|
41
46
|
VerificationContractRunner,
|
|
42
47
|
FATAL_CHECK_MARKERS,
|
|
@@ -49,8 +54,30 @@ const {
|
|
|
49
54
|
TIER_STATUSES,
|
|
50
55
|
RECOVERY_OUTCOMES,
|
|
51
56
|
REGISTRY_FILENAME,
|
|
57
|
+
v0Enabled,
|
|
58
|
+
v0Applies,
|
|
59
|
+
levelOf,
|
|
52
60
|
} = require('./registry.js');
|
|
53
61
|
|
|
62
|
+
const sha16 = (text ) => nodeCrypto.createHash('sha256').update(String(text), 'utf8').digest('hex').slice(0, 16);
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Saves a failed-first state file atomically via temp file + rename with mode 0600.
|
|
66
|
+
* If checks is empty, any existing state file is removed (IC-016).
|
|
67
|
+
*/
|
|
68
|
+
function saveFailedFirstFile(stateDir , stateFile , state , fsImpl = nodeFs) {
|
|
69
|
+
try {
|
|
70
|
+
if (!state || !state.checks || Object.keys(state.checks).length === 0) {
|
|
71
|
+
try { fsImpl.unlinkSync(stateFile); } catch {}
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
fsImpl.mkdirSync(stateDir, { recursive: true });
|
|
75
|
+
const tmp = path.join(stateDir, `.failed-first.${nodeCrypto.randomBytes(6).toString('hex')}.tmp`);
|
|
76
|
+
fsImpl.writeFileSync(tmp, JSON.stringify(state, null, 2) + '\n', { mode: 0o600 });
|
|
77
|
+
fsImpl.renameSync(tmp, stateFile);
|
|
78
|
+
} catch {}
|
|
79
|
+
}
|
|
80
|
+
|
|
54
81
|
// `handleVerifyCommand` (moved from bin/doflow.js) used bin/doflow.js's own REPO_ROOT — that file's
|
|
55
82
|
// SCRIPT_DIR is bin/, so its repo root is path.dirname(SCRIPT_DIR). This module takes the same root
|
|
56
83
|
// from src/helper/repo-root.js rather than counting its own depth.
|
|
@@ -104,6 +131,11 @@ class VerificationEngine {
|
|
|
104
131
|
policy ;
|
|
105
132
|
runner ;
|
|
106
133
|
recoveryOverride ;
|
|
134
|
+
v0 ;
|
|
135
|
+
/** 089 IC-005: the `verify.reuse` setting, resolved once by the verb; false unless injected. */
|
|
136
|
+
reuse ;
|
|
137
|
+
platform ;
|
|
138
|
+
configDir ;
|
|
107
139
|
|
|
108
140
|
/**
|
|
109
141
|
* @param {Object} [options]
|
|
@@ -114,12 +146,19 @@ class VerificationEngine {
|
|
|
114
146
|
* @param {Function} [options.exec] injection seam for shell execution; `spawnSync` contract
|
|
115
147
|
* @param {Function} [options.clock] injection seam returning epoch milliseconds
|
|
116
148
|
* @param {RecoveryManager} [options.recovery] overrides the per-risk-level manager
|
|
149
|
+
* @param {string} [options.logDir] where failing checks' full logs go (`verifyStateRoot`)
|
|
150
|
+
* @param {string} [options.platform] platform used for command execution
|
|
151
|
+
* @param {string} [options.configDir] resolved verification state store
|
|
152
|
+
* @param {boolean} [options.v0] whether V0 behavior is enabled (IC-015)
|
|
153
|
+
* @param {boolean} [options.reuse] whether recorded PASSes are stored and reused (089 IC-005)
|
|
117
154
|
*/
|
|
118
155
|
constructor(options = {}) {
|
|
119
156
|
this.cwd = options.cwd || process.cwd();
|
|
120
157
|
this.fsImpl = options.fsImpl || nodeFs;
|
|
121
158
|
this.exec = options.exec || spawnSync;
|
|
122
159
|
this.clock = options.clock || Date.now;
|
|
160
|
+
this.platform = options.platform || process.platform;
|
|
161
|
+
this.configDir = options.configDir || (options.logDir ? path.resolve(options.logDir, '..', '..', '..') : path.join(this.cwd, '.doflow'));
|
|
123
162
|
this.registry = loadVerificationRegistry({
|
|
124
163
|
repoRoot: options.repoRoot,
|
|
125
164
|
registryDir: options.registryDir,
|
|
@@ -128,8 +167,16 @@ class VerificationEngine {
|
|
|
128
167
|
fsImpl: this.fsImpl,
|
|
129
168
|
});
|
|
130
169
|
this.policy = this.registry.policy || {};
|
|
131
|
-
this.runner = new VerificationContractRunner({
|
|
170
|
+
this.runner = new VerificationContractRunner({
|
|
171
|
+
cwd: this.cwd,
|
|
172
|
+
exec: this.exec,
|
|
173
|
+
logDir: options.logDir,
|
|
174
|
+
platform: this.platform,
|
|
175
|
+
fsImpl: this.fsImpl,
|
|
176
|
+
});
|
|
132
177
|
this.recoveryOverride = options.recovery || null;
|
|
178
|
+
this.v0 = options.v0 !== undefined ? Boolean(options.v0) : true;
|
|
179
|
+
this.reuse = options.reuse === true;
|
|
133
180
|
}
|
|
134
181
|
|
|
135
182
|
/** @returns {string} ISO timestamp from the injected clock */
|
|
@@ -236,7 +283,8 @@ class VerificationEngine {
|
|
|
236
283
|
if (!required && !advisory) return base;
|
|
237
284
|
|
|
238
285
|
if (tier.source === 'detected-command') {
|
|
239
|
-
const
|
|
286
|
+
const v0Active = Boolean(this.v0 && v0Applies(this.registry, riskLevel));
|
|
287
|
+
const selectorContext = { changedFiles: input.changedFiles, scope, root: detection.projectRoot || input.projectRoot || this.cwd, v0Active, riskLevel };
|
|
240
288
|
return Object.assign(base, this.resolveCommandTier(tier, detection, declaredNone, absent, input.targetPattern, selectorContext));
|
|
241
289
|
}
|
|
242
290
|
if (tier.source === 'caller-supplied' && tier.id === 'structural-invariants') {
|
|
@@ -293,6 +341,7 @@ class VerificationEngine {
|
|
|
293
341
|
.filter((t ) => t.required && t.resolution === 'UNRESOLVED')
|
|
294
342
|
.map((t ) => ({ tier: t.id, reason: t.reason }));
|
|
295
343
|
|
|
344
|
+
const v0Active = Boolean(this.v0 && v0Applies(this.registry, riskLevel));
|
|
296
345
|
return {
|
|
297
346
|
version: this.registry.version,
|
|
298
347
|
taskId: input.taskId,
|
|
@@ -313,6 +362,7 @@ class VerificationEngine {
|
|
|
313
362
|
declaredNone: detection.declaredNone,
|
|
314
363
|
errors: detection.errors,
|
|
315
364
|
},
|
|
365
|
+
...(v0Active ? { testTargeted: detection?.commands?.testTargeted || null } : {}),
|
|
316
366
|
scope: {
|
|
317
367
|
maxFiles: Number.isFinite(scope.maxFiles) ? scope.maxFiles : null,
|
|
318
368
|
allowedPaths: toStringArray(scope.allowedPaths),
|
|
@@ -369,6 +419,47 @@ class VerificationEngine {
|
|
|
369
419
|
} else {
|
|
370
420
|
selection = { basis: 'none', selectedCount: null, totalCount: null, fallback: 'not-scopable' };
|
|
371
421
|
}
|
|
422
|
+
|
|
423
|
+
// Feature 090 (IC-003): when v0 is active and files changed, augment with static impact analysis
|
|
424
|
+
const v0Active = ctx.v0Active !== undefined ? ctx.v0Active : Boolean(this.v0);
|
|
425
|
+
if (v0Active && observation && Array.isArray(observation.files) && observation.files.length > 0) {
|
|
426
|
+
const conv = conventionFor(found);
|
|
427
|
+
if (conv) {
|
|
428
|
+
const hasNonTest = observation.files.some((f ) => !conv.isTest(f));
|
|
429
|
+
const noChangedTests = selected && selected.pattern === null && selected.selection?.fallback === 'no-changed-tests';
|
|
430
|
+
if (hasNonTest || noChangedTests) {
|
|
431
|
+
const impact = selectImpactedTests({
|
|
432
|
+
root: ctx.root || this.cwd,
|
|
433
|
+
found,
|
|
434
|
+
changedFiles: observation.files,
|
|
435
|
+
fsImpl: this.fsImpl,
|
|
436
|
+
});
|
|
437
|
+
if (impact && impact.mode === 'selected' && impact.tests.length > 0 && impact.tests.length < impact.totalCount) {
|
|
438
|
+
const directlyChanged = observation.files.filter((f ) => conv.isTest(f));
|
|
439
|
+
const combined = [...new Set([...directlyChanged, ...impact.tests])].sort();
|
|
440
|
+
pattern = formatSelectedTestPattern({
|
|
441
|
+
selectedTests: combined,
|
|
442
|
+
guardDir: conv.guardDir,
|
|
443
|
+
platform: process.platform,
|
|
444
|
+
});
|
|
445
|
+
selection = {
|
|
446
|
+
basis: 'impacted-tests',
|
|
447
|
+
selectedCount: combined.length,
|
|
448
|
+
totalCount: impact.totalCount,
|
|
449
|
+
fallback: null,
|
|
450
|
+
};
|
|
451
|
+
} else if (impact && impact.mode === 'full') {
|
|
452
|
+
pattern = null;
|
|
453
|
+
selection = {
|
|
454
|
+
basis: 'none',
|
|
455
|
+
selectedCount: null,
|
|
456
|
+
totalCount: impact.totalCount,
|
|
457
|
+
fallback: 'unknown-files',
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
372
463
|
}
|
|
373
464
|
}
|
|
374
465
|
const { command, substituted } = applyTargetPattern(found.command, pattern);
|
|
@@ -638,11 +729,83 @@ class VerificationEngine {
|
|
|
638
729
|
let fatalTier = null;
|
|
639
730
|
let checksRun = 0;
|
|
640
731
|
let checksPassed = 0;
|
|
732
|
+
let checksReused = 0;
|
|
641
733
|
// One evaluation pass, one cache: two tiers declaring the same command (targeted and broad
|
|
642
734
|
// tests are often both `npm test`) run it once and share the result. A recovery retry calls
|
|
643
735
|
// runContract again and gets a fresh cache, so retries always observe live behaviour.
|
|
644
736
|
const dedupeCache = new Map ();
|
|
645
737
|
|
|
738
|
+
const v0Active = Boolean(this.v0 && v0Applies(this.registry, contract.riskLevel));
|
|
739
|
+
if (v0Active || this.reuse) {
|
|
740
|
+
// 089 IC-005: the runner decides reuse eligibility per level; a recovery retry comes back here.
|
|
741
|
+
this.runner.beginContract({
|
|
742
|
+
v0: this.v0,
|
|
743
|
+
reuse: this.reuse,
|
|
744
|
+
registry: this.registry,
|
|
745
|
+
riskLevel: contract.riskLevel,
|
|
746
|
+
configDir: this.configDir,
|
|
747
|
+
});
|
|
748
|
+
} else {
|
|
749
|
+
// A reused engine must not carry L1/L2 sharing or reuse into an L3 or flag-off contract.
|
|
750
|
+
this.runner.inflightContext = undefined;
|
|
751
|
+
this.runner.reuseContext = undefined;
|
|
752
|
+
this.runner.reuseStore = undefined;
|
|
753
|
+
}
|
|
754
|
+
let taskIdValid = false;
|
|
755
|
+
if (typeof contract.taskId === 'string') {
|
|
756
|
+
try {
|
|
757
|
+
assertSafeTaskId(contract.taskId);
|
|
758
|
+
taskIdValid = true;
|
|
759
|
+
} catch {
|
|
760
|
+
taskIdValid = false;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
const stateDir = path.join(this.configDir, 'state', 'verification', 'v0');
|
|
765
|
+
const stateFile = taskIdValid ? path.join(stateDir, `failed-first-${contract.taskId}.json`) : '';
|
|
766
|
+
let realRootSha = '';
|
|
767
|
+
try {
|
|
768
|
+
const real = typeof this.fsImpl.realpathSync === 'function'
|
|
769
|
+
? this.fsImpl.realpathSync(this.cwd)
|
|
770
|
+
: path.resolve(this.cwd);
|
|
771
|
+
realRootSha = sha16(real);
|
|
772
|
+
} catch {
|
|
773
|
+
realRootSha = sha16(path.resolve(this.cwd));
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
let failedFirstState = null;
|
|
777
|
+
if (v0Active && taskIdValid) {
|
|
778
|
+
try {
|
|
779
|
+
const content = this.fsImpl.readFileSync(stateFile, 'utf8');
|
|
780
|
+
const parsed = JSON.parse(content);
|
|
781
|
+
if (parsed && parsed.v === 1 && parsed.cwd === realRootSha && parsed.checks && typeof parsed.checks === 'object') {
|
|
782
|
+
failedFirstState = parsed;
|
|
783
|
+
}
|
|
784
|
+
} catch {
|
|
785
|
+
failedFirstState = null;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
let stateDirty = false;
|
|
789
|
+
|
|
790
|
+
const hasTargeted = Object.hasOwn(contract, 'testTargeted');
|
|
791
|
+
let foundTargeted = hasTargeted ? contract.testTargeted : contract.detection?.commands?.testTargeted;
|
|
792
|
+
if (!hasTargeted && !foundTargeted && v0Active && taskIdValid) {
|
|
793
|
+
try {
|
|
794
|
+
const det = detectCommands({ projectRoot: this.cwd, fsImpl: this.fsImpl });
|
|
795
|
+
foundTargeted = det?.commands?.testTargeted;
|
|
796
|
+
} catch {}
|
|
797
|
+
}
|
|
798
|
+
const convention = (foundTargeted && typeof foundTargeted.command === 'string' && foundTargeted.command.includes(TARGET_PATTERN_TOKEN))
|
|
799
|
+
? conventionFor(foundTargeted)
|
|
800
|
+
: null;
|
|
801
|
+
|
|
802
|
+
if (convention && this.runner) {
|
|
803
|
+
this.runner.convention = convention;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
let failedFirstAttempted = false;
|
|
807
|
+
let failedFirstHalted = false;
|
|
808
|
+
|
|
646
809
|
for (const tier of contract.tiers) {
|
|
647
810
|
const entry = {
|
|
648
811
|
id: tier.id,
|
|
@@ -671,6 +834,23 @@ class VerificationEngine {
|
|
|
671
834
|
continue;
|
|
672
835
|
}
|
|
673
836
|
|
|
837
|
+
if (failedFirstHalted && tier.kind === 'behavioral') {
|
|
838
|
+
entry.status = 'NOT_RUN';
|
|
839
|
+
entry.reason = 'failed-first-still-failing';
|
|
840
|
+
for (const check of tier.checks) {
|
|
841
|
+
entry.checks.push({
|
|
842
|
+
name: check.name,
|
|
843
|
+
tier: tier.id,
|
|
844
|
+
role: check.role,
|
|
845
|
+
command: check.command,
|
|
846
|
+
status: 'NOT_RUN',
|
|
847
|
+
reason: 'failed-first-still-failing',
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
reportTiers.push(entry);
|
|
851
|
+
continue;
|
|
852
|
+
}
|
|
853
|
+
|
|
674
854
|
if (tier.modelBased) {
|
|
675
855
|
Object.assign(entry, this.evaluateModelTier(tier, runtimeInput.modelReview));
|
|
676
856
|
reportTiers.push(entry);
|
|
@@ -684,14 +864,147 @@ class VerificationEngine {
|
|
|
684
864
|
}
|
|
685
865
|
|
|
686
866
|
for (const check of tier.checks) {
|
|
867
|
+
if (failedFirstHalted && tier.kind === 'behavioral') {
|
|
868
|
+
entry.checks.push({
|
|
869
|
+
name: check.name,
|
|
870
|
+
tier: tier.id,
|
|
871
|
+
role: check.role,
|
|
872
|
+
command: check.command,
|
|
873
|
+
status: 'NOT_RUN',
|
|
874
|
+
reason: 'failed-first-still-failing',
|
|
875
|
+
});
|
|
876
|
+
continue;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
let runNarrowed = false;
|
|
880
|
+
let matchedFiles = [];
|
|
881
|
+
let narrowedCommand = null;
|
|
882
|
+
let narrowedStrikes = 0;
|
|
883
|
+
|
|
884
|
+
if (v0Active && taskIdValid && convention && !failedFirstAttempted && tier.kind === 'behavioral') {
|
|
885
|
+
try {
|
|
886
|
+
const savedCheck = failedFirstState?.checks?.[check.name]
|
|
887
|
+
|| (failedFirstState?.checks ? Object.values(failedFirstState.checks).find((c ) => c && c.cmd === sha16(check.command)) : null);
|
|
888
|
+
const cmdChanged = savedCheck ? savedCheck.cmd !== sha16(check.command) : false;
|
|
889
|
+
const effectiveStrikes = cmdChanged ? 0 : (Number.isSafeInteger(savedCheck?.strikes) && savedCheck.strikes >= 0 ? savedCheck.strikes : 0);
|
|
890
|
+
// A file can fail in isolation while the full suite passes (for example, suite setup).
|
|
891
|
+
// After two narrowed failures, recheck the full scope so saved state cannot pin FAIL.
|
|
892
|
+
if (savedCheck && effectiveStrikes < 2 && Array.isArray(savedCheck.files) && savedCheck.files.length > 0) {
|
|
893
|
+
const universeFiles = testFilesFor(foundTargeted, this.cwd, this.fsImpl);
|
|
894
|
+
matchedFiles = universeFiles.filter((f ) => savedCheck.files.includes(sha16(f)));
|
|
895
|
+
if (matchedFiles.length > 0) {
|
|
896
|
+
const pattern = matchedFiles.map((f ) => quotePath(f, this.platform || process.platform)).join(' ');
|
|
897
|
+
const sub = applyTargetPattern(foundTargeted.command, pattern);
|
|
898
|
+
if (sub.substituted && sub.command) {
|
|
899
|
+
narrowedCommand = sub.command;
|
|
900
|
+
runNarrowed = true;
|
|
901
|
+
narrowedStrikes = effectiveStrikes;
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
} catch {
|
|
906
|
+
runNarrowed = false;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
if (runNarrowed && narrowedCommand) {
|
|
911
|
+
failedFirstAttempted = true;
|
|
912
|
+
const narrowedResult = this.runner.runCheck(check.name, narrowedCommand, check.timeoutMs, null);
|
|
913
|
+
if (narrowedResult.status === 'FAIL') {
|
|
914
|
+
narrowedResult.tier = tier.id;
|
|
915
|
+
narrowedResult.role = check.role;
|
|
916
|
+
if (check.requirementId) narrowedResult.requirementId = check.requirementId;
|
|
917
|
+
narrowedResult.failedFirst = { ran: true, status: 'FAIL', files: matchedFiles.length };
|
|
918
|
+
entry.checks.push(narrowedResult);
|
|
919
|
+
checksRun += 1;
|
|
920
|
+
failedChecks.push(narrowedResult.name);
|
|
921
|
+
failedFirstHalted = true;
|
|
922
|
+
|
|
923
|
+
const metric = this.runner.metrics.get(narrowedResult);
|
|
924
|
+
if (metric) metric.failedFirst = 'fail';
|
|
925
|
+
|
|
926
|
+
if (v0Active && taskIdValid) {
|
|
927
|
+
const filesToSave = narrowedResult.failedFiles && narrowedResult.failedFiles.length > 0
|
|
928
|
+
? narrowedResult.failedFiles
|
|
929
|
+
: matchedFiles;
|
|
930
|
+
if (filesToSave && filesToSave.length > 0) {
|
|
931
|
+
if (!failedFirstState) failedFirstState = { v: 1, cwd: realRootSha, checks: {} };
|
|
932
|
+
failedFirstState.checks[check.name] = {
|
|
933
|
+
cmd: sha16(check.command),
|
|
934
|
+
files: filesToSave.map(sha16),
|
|
935
|
+
strikes: narrowedStrikes + 1,
|
|
936
|
+
};
|
|
937
|
+
stateDirty = true;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
continue;
|
|
941
|
+
} else {
|
|
942
|
+
// Narrowed PASS: the full check runs and alone decides
|
|
943
|
+
const fullResult = this.runner.runCheck(check.name, check.command, check.timeoutMs, dedupeCache);
|
|
944
|
+
fullResult.tier = tier.id;
|
|
945
|
+
fullResult.role = check.role;
|
|
946
|
+
if (check.requirementId) fullResult.requirementId = check.requirementId;
|
|
947
|
+
fullResult.failedFirst = { ran: true, status: 'PASS', files: matchedFiles.length };
|
|
948
|
+
entry.checks.push(fullResult);
|
|
949
|
+
checksRun += 1;
|
|
950
|
+
if (fullResult.reused) checksReused += 1;
|
|
951
|
+
if (fullResult.status === 'PASS') checksPassed += 1;
|
|
952
|
+
else failedChecks.push(fullResult.name);
|
|
953
|
+
|
|
954
|
+
const metric = this.runner.metrics.get(fullResult);
|
|
955
|
+
if (metric) metric.failedFirst = 'pass';
|
|
956
|
+
|
|
957
|
+
if (v0Active && taskIdValid) {
|
|
958
|
+
if (fullResult.status === 'PASS') {
|
|
959
|
+
if (failedFirstState?.checks?.[check.name]) {
|
|
960
|
+
delete failedFirstState.checks[check.name];
|
|
961
|
+
stateDirty = true;
|
|
962
|
+
}
|
|
963
|
+
} else if (fullResult.status === 'FAIL') {
|
|
964
|
+
if (fullResult.failedFiles && fullResult.failedFiles.length > 0) {
|
|
965
|
+
if (!failedFirstState) failedFirstState = { v: 1, cwd: realRootSha, checks: {} };
|
|
966
|
+
failedFirstState.checks[check.name] = {
|
|
967
|
+
cmd: sha16(check.command),
|
|
968
|
+
files: fullResult.failedFiles.map(sha16),
|
|
969
|
+
};
|
|
970
|
+
stateDirty = true;
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
continue;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
|
|
687
978
|
const result = this.runner.runCheck(check.name, check.command, check.timeoutMs, dedupeCache);
|
|
688
979
|
result.tier = tier.id;
|
|
689
980
|
result.role = check.role;
|
|
690
981
|
if (check.requirementId) result.requirementId = check.requirementId;
|
|
691
982
|
entry.checks.push(result);
|
|
692
983
|
checksRun += 1;
|
|
984
|
+
if (result.reused) checksReused += 1;
|
|
693
985
|
if (result.status === 'PASS') checksPassed += 1;
|
|
694
986
|
else failedChecks.push(result.name);
|
|
987
|
+
|
|
988
|
+
const metric = this.runner.metrics.get(result);
|
|
989
|
+
if (metric) metric.failedFirst = 'none';
|
|
990
|
+
|
|
991
|
+
if (v0Active && taskIdValid && tier.kind === 'behavioral') {
|
|
992
|
+
if (result.status === 'FAIL') {
|
|
993
|
+
if (result.failedFiles && result.failedFiles.length > 0) {
|
|
994
|
+
if (!failedFirstState) failedFirstState = { v: 1, cwd: realRootSha, checks: {} };
|
|
995
|
+
failedFirstState.checks[check.name] = {
|
|
996
|
+
cmd: sha16(check.command),
|
|
997
|
+
files: result.failedFiles.map(sha16),
|
|
998
|
+
};
|
|
999
|
+
stateDirty = true;
|
|
1000
|
+
}
|
|
1001
|
+
} else if (result.status === 'PASS') {
|
|
1002
|
+
if (failedFirstState?.checks?.[check.name]) {
|
|
1003
|
+
delete failedFirstState.checks[check.name];
|
|
1004
|
+
stateDirty = true;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
695
1008
|
}
|
|
696
1009
|
|
|
697
1010
|
const anyFailed = entry.checks.some((c ) => c.status === 'FAIL');
|
|
@@ -712,6 +1025,10 @@ class VerificationEngine {
|
|
|
712
1025
|
reportTiers.push(entry);
|
|
713
1026
|
}
|
|
714
1027
|
|
|
1028
|
+
if (stateDirty && v0Active && taskIdValid) {
|
|
1029
|
+
saveFailedFirstFile(stateDir, stateFile, failedFirstState, this.fsImpl);
|
|
1030
|
+
}
|
|
1031
|
+
|
|
715
1032
|
const requiredTiers = reportTiers.filter((t) => t.required);
|
|
716
1033
|
const requiredFailed = requiredTiers.filter((t) => t.status === 'FAIL');
|
|
717
1034
|
const requiredUnverified = requiredTiers.filter((t) => t.status === 'UNRESOLVED' || t.status === 'NOT_RUN');
|
|
@@ -760,6 +1077,7 @@ class VerificationEngine {
|
|
|
760
1077
|
checksRun,
|
|
761
1078
|
checksPassed,
|
|
762
1079
|
checksFailed: checksRun - checksPassed,
|
|
1080
|
+
...(this.reuse ? { checksReused } : {}),
|
|
763
1081
|
tiersRequired: requiredTiers.length,
|
|
764
1082
|
tiersPassed: requiredTiers.filter((t) => t.status === 'PASS').length,
|
|
765
1083
|
tiersFailed: requiredFailed.length,
|
|
@@ -912,7 +1230,23 @@ class VerificationEngine {
|
|
|
912
1230
|
failureSignature(report ) {
|
|
913
1231
|
const failed = [...(report.failedChecks || [])].sort().join('|');
|
|
914
1232
|
const unresolved = (report.unresolved || []).map((u ) => u.tier).sort().join('|');
|
|
915
|
-
|
|
1233
|
+
const failedFiles = new Set ();
|
|
1234
|
+
const failedTests = new Set ();
|
|
1235
|
+
for (const tier of (report.tiers || [])) {
|
|
1236
|
+
for (const check of (tier.checks || [])) {
|
|
1237
|
+
if (check.status === 'FAIL') {
|
|
1238
|
+
if (Array.isArray(check.failedFiles)) {
|
|
1239
|
+
for (const f of check.failedFiles) failedFiles.add(f);
|
|
1240
|
+
}
|
|
1241
|
+
if (Array.isArray(check.failedTests)) {
|
|
1242
|
+
for (const t of check.failedTests) failedTests.add(t);
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
const filesPart = [...failedFiles].sort().join('|');
|
|
1248
|
+
const testsPart = [...failedTests].sort().join('|');
|
|
1249
|
+
return `${report.status}::${failed}::${unresolved}::${filesPart}::${testsPart}`;
|
|
916
1250
|
}
|
|
917
1251
|
|
|
918
1252
|
/**
|
|
@@ -1260,7 +1594,136 @@ function validateVerificationInput({ cwd, verificationInput, fs = nodeFs, crypto
|
|
|
1260
1594
|
* @param {string} [options.baselineRef] commit or ref to measure change against (IC-022)
|
|
1261
1595
|
* @returns {number} exit code
|
|
1262
1596
|
*/
|
|
1263
|
-
|
|
1597
|
+
/**
|
|
1598
|
+
* Where verification keeps its own state (check logs, the verify-moment upkeep): the store the
|
|
1599
|
+
* lifecycle owns — `DOFLOW_CONFIG_DIR`, else the nearest existing `.doflow` above the main checkout,
|
|
1600
|
+
* else the install this runtime was copied into, else an existing `~/.doflow`. Only when none
|
|
1601
|
+
* exists (a project never set up) is it the owner's `.doflow`.
|
|
1602
|
+
* A `.doflow` created under the working directory of a dispatch attempt worktree captured every
|
|
1603
|
+
* later record from there and was deleted with the worktree (088 G-O1 D4).
|
|
1604
|
+
* @param {Object} [options]
|
|
1605
|
+
* @param {string} [options.start] the verify's working directory (default: cwd)
|
|
1606
|
+
* @param {Object} [options.env=process.env]
|
|
1607
|
+
* @param {string} [options.homeDir]
|
|
1608
|
+
* @param {string|null} [options.installRoot] this runtime's package root
|
|
1609
|
+
* @param {Object} [options.fsImpl]
|
|
1610
|
+
* @returns {{configDir: string, projectRoot: string|null}} `projectRoot` is the directory holding
|
|
1611
|
+
* `configDir` when it is named `.doflow` (the root the storage upkeep takes), else null
|
|
1612
|
+
*/
|
|
1613
|
+
function verifyStateRoot({ start, env = process.env, homeDir = os.homedir(), installRoot = REPO_ROOT, fsImpl = nodeFs } = {}) {
|
|
1614
|
+
// Match worker/lifecycle state ownership before looking for a store. A sibling linked checkout
|
|
1615
|
+
// cannot walk up to the main checkout, and a nested .doflow is never a store root (110/111).
|
|
1616
|
+
const from = lifecycleProjectRoot(outerProjectRoot(path.resolve(start || process.cwd())));
|
|
1617
|
+
const isDir = (dir ) => { try { return fsImpl.statSync(dir).isDirectory(); } catch { return false; } };
|
|
1618
|
+
const at = resolveRunsLocation({ start: from, env, homeDir, fsImpl });
|
|
1619
|
+
let configDir = path.join(from, '.doflow');
|
|
1620
|
+
if (at.origin === 'DOFLOW_CONFIG_DIR' || at.origin === 'project') configDir = at.configDir;
|
|
1621
|
+
else {
|
|
1622
|
+
const marker = `${path.sep}.doflow${path.sep}`;
|
|
1623
|
+
const cut = installRoot ? `${path.resolve(installRoot)}${path.sep}`.indexOf(marker) : -1;
|
|
1624
|
+
const install = cut >= 0 ? path.resolve(installRoot).slice(0, cut) + path.sep + '.doflow' : null;
|
|
1625
|
+
if (install && isDir(install)) configDir = install;
|
|
1626
|
+
else if (isDir(at.configDir)) configDir = at.configDir; // ~/.doflow
|
|
1627
|
+
}
|
|
1628
|
+
return { configDir, projectRoot: path.basename(configDir) === '.doflow' ? path.dirname(configDir) : null };
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
const VERIFY_GIT_TIMEOUT_MS = 2000;
|
|
1632
|
+
const VERIFY_GIT_ENV = ['GIT_DIR', 'GIT_WORK_TREE', 'GIT_INDEX_FILE', 'GIT_COMMON_DIR', 'GIT_OBJECT_DIRECTORY'];
|
|
1633
|
+
|
|
1634
|
+
/** Capture the checkout head and status before verification commands run. A moving head is unbound. */
|
|
1635
|
+
function verificationGitState(cwd , env = process.env) {
|
|
1636
|
+
const gitEnv = { ...env };
|
|
1637
|
+
for (const key of VERIFY_GIT_ENV) delete gitEnv[key];
|
|
1638
|
+
const git = (args ) => spawnSync('git', args, {
|
|
1639
|
+
cwd,
|
|
1640
|
+
env: gitEnv,
|
|
1641
|
+
encoding: 'utf8',
|
|
1642
|
+
timeout: VERIFY_GIT_TIMEOUT_MS,
|
|
1643
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
1644
|
+
});
|
|
1645
|
+
try {
|
|
1646
|
+
const before = git(['rev-parse', '--verify', 'HEAD']);
|
|
1647
|
+
if (before.status !== 0) return null;
|
|
1648
|
+
const head = before.stdout.trim();
|
|
1649
|
+
if (!/^[0-9a-f]{40}$/.test(head)) return null;
|
|
1650
|
+
const status = git(['--no-optional-locks', 'status', '--porcelain=v1', '-z', '--untracked-files=all']);
|
|
1651
|
+
const after = git(['rev-parse', '--verify', 'HEAD']);
|
|
1652
|
+
if (after.status !== 0 || after.stdout.trim() !== head) return null;
|
|
1653
|
+
return {
|
|
1654
|
+
head,
|
|
1655
|
+
clean: status.status === 0 && status.stdout.length === 0,
|
|
1656
|
+
projectRoot: verificationRunsRoot(cwd),
|
|
1657
|
+
};
|
|
1658
|
+
} catch {
|
|
1659
|
+
return null;
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
/** IC-007: the checkout whose state holds the run records of a verify run in `cwd` (the lifecycle root). */
|
|
1664
|
+
function verificationRunsRoot(cwd ) {
|
|
1665
|
+
return lifecycleProjectRoot(outerProjectRoot(path.resolve(cwd)));
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
/** IC-007: the one file the run records of `taskId` are appended to, under `verificationRunsRoot`. */
|
|
1669
|
+
function verificationRunRecordFile(root , taskId ) {
|
|
1670
|
+
return path.join(root, '.doflow', 'state', 'verification', 'runs', `${taskId}.jsonl`);
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
/** 089 IC-006: how many checks of a report carry the `reused` label. */
|
|
1674
|
+
function reusedCount(report ) {
|
|
1675
|
+
if (typeof report?.counts?.checksReused === 'number') return report.counts.checksReused;
|
|
1676
|
+
let count = 0;
|
|
1677
|
+
for (const tier of Array.isArray(report?.tiers) ? report.tiers : []) {
|
|
1678
|
+
for (const check of Array.isArray(tier?.checks) ? tier.checks : []) {
|
|
1679
|
+
if (check && typeof check === 'object' && check.reused) count += 1;
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
return count;
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
/** Persist the already printed report as one JSONL line; state I/O never changes its answer.
|
|
1686
|
+
* 089 IC-006: with reuse on for the run, `reused` (the count) precedes `reportSha256`; with reuse
|
|
1687
|
+
* off the record has no `reused` key. 095b IC-004: `risk` (the report's risk level) follows `verdict`;
|
|
1688
|
+
* readers default a missing one, and `verifyRecordReasons` ignores it. */
|
|
1689
|
+
function appendVerificationRunRecord({ taskId, report, gitState, reuse = false, fsImpl = nodeFs } ) {
|
|
1690
|
+
if (!gitState) return;
|
|
1691
|
+
try {
|
|
1692
|
+
const reportJson = JSON.stringify(report, null, 2);
|
|
1693
|
+
const record = {
|
|
1694
|
+
schema: 1,
|
|
1695
|
+
taskId,
|
|
1696
|
+
head: gitState.head,
|
|
1697
|
+
clean: gitState.clean,
|
|
1698
|
+
verdict: report.status,
|
|
1699
|
+
risk: report.riskLevel,
|
|
1700
|
+
at: new Date().toISOString(),
|
|
1701
|
+
...(reuse === true ? { reused: reusedCount(report) } : {}),
|
|
1702
|
+
reportSha256: `sha256:${nodeCrypto.createHash('sha256').update(reportJson, 'utf8').digest('hex')}`,
|
|
1703
|
+
};
|
|
1704
|
+
const file = verificationRunRecordFile(gitState.projectRoot, taskId);
|
|
1705
|
+
fsImpl.mkdirSync(path.dirname(file), { recursive: true });
|
|
1706
|
+
fsImpl.appendFileSync(file, `${JSON.stringify(record)}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
1707
|
+
} catch {
|
|
1708
|
+
// Reporting is authoritative. A read-only or unavailable state directory is only a lost record.
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
/**
|
|
1713
|
+
* 089 IC-005: the `verify.reuse` setting, read once per verify command. Only a valid resolved `on`
|
|
1714
|
+
* enables reuse; an invalid value, a load error or any fault is the key's fail-safe `off`.
|
|
1715
|
+
*/
|
|
1716
|
+
function readVerifyReuse(env = process.env) {
|
|
1717
|
+
try {
|
|
1718
|
+
const { loadSettings } = require('../settings/resolve.js');
|
|
1719
|
+
const resolution = loadSettings({ global: true, env: { ...env } }).get('verify.reuse', 'global');
|
|
1720
|
+
return resolution?.state === 'ok' && resolution.value === 'on';
|
|
1721
|
+
} catch {
|
|
1722
|
+
return false;
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
function handleVerifyCommand({ taskId, action = 'report', risk, planPath, json = false, projectRoot, slug = null, scope = null, verificationInput = null, verificationPhase = null, baselineRef = null, env = process.env, fsImpl = nodeFs }
|
|
1264
1727
|
|
|
1265
1728
|
|
|
1266
1729
|
|
|
@@ -1272,6 +1735,8 @@ function handleVerifyCommand({ taskId, action = 'report', risk, planPath, json =
|
|
|
1272
1735
|
|
|
1273
1736
|
|
|
1274
1737
|
|
|
1738
|
+
|
|
1739
|
+
|
|
1275
1740
|
= {}) {
|
|
1276
1741
|
// The task id names state files here, so it obeys the rule every other task verb applies.
|
|
1277
1742
|
try {
|
|
@@ -1334,10 +1799,13 @@ function handleVerifyCommand({ taskId, action = 'report', risk, planPath, json =
|
|
|
1334
1799
|
if (!declared) declared = { paths: [...recordedScope], origin: 'readiness-record', record: recordRel };
|
|
1335
1800
|
}
|
|
1336
1801
|
|
|
1802
|
+
const state = verifyStateRoot({ start: cwd, env, fsImpl });
|
|
1803
|
+
const v0 = v0Enabled(env, state.configDir, fsImpl);
|
|
1804
|
+
const reuse = readVerifyReuse(env);
|
|
1337
1805
|
let engine ;
|
|
1338
1806
|
let contract ;
|
|
1339
1807
|
try {
|
|
1340
|
-
engine = new VerificationEngine({ cwd, repoRoot: REPO_ROOT });
|
|
1808
|
+
engine = new VerificationEngine({ cwd, repoRoot: REPO_ROOT, logDir: path.join(state.configDir, 'state', 'verification', 'logs'), configDir: state.configDir, v0, reuse, fsImpl });
|
|
1341
1809
|
// The bound comes from the feature's plan, this checkout's or the main checkout's, and from a
|
|
1342
1810
|
// declared scope; with neither the change-scope tier is UNRESOLVED and says where it looked.
|
|
1343
1811
|
const resolved = resolveScopeBound({ projectRoot: cwd, slug, planPath, declared, baselineRef });
|
|
@@ -1389,6 +1857,7 @@ function handleVerifyCommand({ taskId, action = 'report', risk, planPath, json =
|
|
|
1389
1857
|
return usageError('verify', `unknown --action '${action}'. Valid: report (default), contract`, json);
|
|
1390
1858
|
}
|
|
1391
1859
|
|
|
1860
|
+
const runGitState = action === 'report' ? verificationGitState(cwd, env) : null;
|
|
1392
1861
|
const report = engine.runContract(contract);
|
|
1393
1862
|
// A task whose run still has its gated stage pending is not verified while no READY readiness
|
|
1394
1863
|
// record exists: the checks passing says nothing about whether the work should have started.
|
|
@@ -1414,9 +1883,21 @@ function handleVerifyCommand({ taskId, action = 'report', risk, planPath, json =
|
|
|
1414
1883
|
if (boundLines.has(tier.id)) console.log(boundLines.get(tier.id));
|
|
1415
1884
|
if (tier.reason) console.log(` ${tier.reason}`);
|
|
1416
1885
|
if (tier.selection) console.log(` ${selectionText(tier.selection)}`);
|
|
1886
|
+
if (Array.isArray(tier.checks)) {
|
|
1887
|
+
for (const check of tier.checks) {
|
|
1888
|
+
if (check?.reused) {
|
|
1889
|
+
const atIso = typeof check.reused.recordedAt === 'number'
|
|
1890
|
+
? new Date(check.reused.recordedAt).toISOString()
|
|
1891
|
+
: String(check.reused.recordedAt);
|
|
1892
|
+
console.log(` reused ${atIso} ref ${check.reused.ref}`);
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1417
1896
|
}
|
|
1418
1897
|
console.log('─'.repeat(78));
|
|
1419
|
-
|
|
1898
|
+
const reused = report.counts?.checksReused ?? reusedCount(report);
|
|
1899
|
+
const reusedSuffix = reused > 0 ? ` (${reused} reused, not executed)` : '';
|
|
1900
|
+
console.log(`Checks: ${report.counts.checksPassed}/${report.counts.checksRun} passed${reusedSuffix}`);
|
|
1420
1901
|
if (report.unresolved.length) {
|
|
1421
1902
|
console.log('Never evaluated (not a pass):');
|
|
1422
1903
|
for (const u of report.unresolved) console.log(` ${u.tier}${u.required ? ' [required]' : ''} — ${u.reason}`);
|
|
@@ -1425,8 +1906,12 @@ function handleVerifyCommand({ taskId, action = 'report', risk, planPath, json =
|
|
|
1425
1906
|
}
|
|
1426
1907
|
// PASS is the only status that answers "verified". FAIL and INCONCLUSIVE are both findings, and
|
|
1427
1908
|
// collapsing INCONCLUSIVE into success would report a verdict over zero evidence as a pass.
|
|
1428
|
-
|
|
1429
|
-
|
|
1909
|
+
const exitCode = report.status === 'PASS' ? 0 : 1;
|
|
1910
|
+
if (action === 'report') appendVerificationRunRecord({ taskId, report, gitState: runGitState, reuse, fsImpl });
|
|
1911
|
+
// After the report is printed; never throws and changes nothing printed (088 C.1, IC-006).
|
|
1912
|
+
emitVerifyTelemetry({ report, metrics: engine.runner.metrics, ctx: { source: 'verb', taskId, risk: report.riskLevel, exitCode, v0: v0 ? 'on' : 'off' }, start: state.projectRoot || cwd, env, fsImpl });
|
|
1913
|
+
upkeepAfterVerify({ projectRoot: state.projectRoot });
|
|
1914
|
+
return finishRuntime(exitCode);
|
|
1430
1915
|
}
|
|
1431
1916
|
|
|
1432
1917
|
/** One line stating what the targeted tier selected, or why it ran the broad command instead. */
|
|
@@ -1445,6 +1930,8 @@ function sameSet(a , b ) {
|
|
|
1445
1930
|
}
|
|
1446
1931
|
|
|
1447
1932
|
module.exports = {
|
|
1933
|
+
verificationRunsRoot,
|
|
1934
|
+
verificationRunRecordFile,
|
|
1448
1935
|
VerificationContractRunner,
|
|
1449
1936
|
VerificationEngine,
|
|
1450
1937
|
loadVerificationRegistry,
|
|
@@ -1457,5 +1944,8 @@ module.exports = {
|
|
|
1457
1944
|
MAX_STREAM_CHARS,
|
|
1458
1945
|
REGISTRY_FILENAME,
|
|
1459
1946
|
handleVerifyCommand,
|
|
1947
|
+
readVerifyReuse,
|
|
1948
|
+
verifyStateRoot,
|
|
1460
1949
|
validateVerificationInput,
|
|
1950
|
+
saveFailedFirstFile,
|
|
1461
1951
|
};
|