@khoavu882/doflow 2.0.0-beta.5 → 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 -2
- 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/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-compact.sh +23 -27
- package/core/harnesses/claude/hooks/session-end.sh +23 -10
- package/core/harnesses/claude/hooks/skill-config-audit.sh +3 -0
- 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/post-edit-lint.impl.sh +3 -1
- package/core/harnesses/codex/hooks/pre-compact.impl.sh +29 -26
- package/core/harnesses/codex/hooks/session-end.impl.sh +23 -10
- package/core/harnesses/codex/hooks/subagent-audit.sh +1 -0
- 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 +1 -0
- package/core/harnesses/kiro/hooks/pre-bash-guard.sh +1 -0
- package/core/harnesses/kiro/hooks/pre-implement-gate.sh +1 -0
- package/core/harnesses/kiro/hooks/session-start.sh +1 -0
- package/core/harnesses/kiro/hooks/stop-check.sh +1 -0
- package/core/harnesses/pi/extensions/doflow-guard.js +8 -0
- package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +0 -1
- package/core/harnesses/shared/hooks/bridge/bridge.sh +102 -9
- package/core/harnesses/shared/hooks/bridge/envelope.jq +87 -18
- package/core/harnesses/shared/hooks/bridge/observe-model.sh +75 -0
- package/core/harnesses/shared/hooks/bridge/respond.sh +10 -2
- package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +28 -14
- package/core/harnesses/shared/hooks/policies/capture-failure.sh +48 -15
- package/core/harnesses/shared/hooks/policies/compact-text.sh +46 -0
- package/core/harnesses/shared/hooks/policies/contract.sh +3 -0
- package/core/harnesses/shared/hooks/policies/deny-json.sh +2 -11
- 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 +63 -40
- package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +3 -2
- package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +213 -12
- package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +114 -38
- package/core/harnesses/shared/hooks/policies/session-context.sh +30 -1
- package/core/harnesses/shared/hooks/policies/stop-check.sh +20 -9
- package/core/harnesses/shared/hooks/policies/subagent-audit.sh +5 -2
- package/core/harnesses/shared/hooks/policies/summary-store.sh +347 -0
- package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +60 -155
- package/core/harnesses/shared/hooks/stream-hook-runner.js +47 -8
- package/core/harnesses/shared/locator/doflow-run +2 -3
- package/core/registry/advice.json +3 -3
- package/core/registry/assets.json +55 -82
- package/core/registry/capabilities.json +12 -0
- package/core/registry/contracts.json +58 -31
- 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 +1442 -363
- package/core/registry/lifecycle.json +485 -304
- 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 +679 -31
- 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 +149 -34
- 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 +18 -17
- 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 +15 -58
- 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 +26 -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 +4 -7
- package/core/shared/skills/do-plan/SKILL.md +26 -15
- 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 +143 -66
- package/src/adapters/claude/index.js +24 -4
- package/src/adapters/codex/agents.js +25 -15
- package/src/adapters/codex/index.js +48 -13
- package/src/adapters/codex/native-projection.js +6 -2
- package/src/adapters/copilot/index.js +58 -23
- package/src/adapters/copy-tree.js +290 -44
- 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 +23 -12
- package/src/adapters/opencode/index.js +8 -8
- package/src/adapters/pi/hooks.js +2 -1
- package/src/adapters/pi/index.js +14 -7
- package/src/adapters/resolve-source.js +26 -2
- package/src/cli/commands/install.js +2 -1
- 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 +2 -1
- package/src/cli/index.js +322 -29
- package/src/cli/runtime-commands.js +209 -57
- 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 +20 -3
- package/src/lifecycle/hook-tier.js +160 -0
- package/src/lifecycle/index.js +412 -78
- package/src/lifecycle/retired-harnesses.js +318 -0
- package/src/lifecycle/view.js +35 -15
- package/src/registry/index.js +551 -26
- 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 +20 -11
- 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 +13 -7
- package/src/runtime/cli.js +196 -19
- package/src/runtime/config-home.js +42 -0
- package/src/runtime/context/arbitrate.js +767 -0
- package/src/runtime/context/delivery-store.js +4 -6
- package/src/runtime/context/emission-rules.js +118 -0
- package/src/runtime/context/identity.js +227 -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 +154 -78
- 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-ledger.js +34 -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 +33 -3
- 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 +2 -2
- 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 +46 -5
- package/src/runtime/research-request.js +50 -25
- package/src/runtime/retrieval-plan.js +10 -30
- package/src/runtime/retrieval-record.js +75 -5
- 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 +60 -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 +1 -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 +176 -55
- package/src/runtime/task-sources.js +147 -0
- package/src/runtime/task-state.js +374 -64
- 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/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 -10
- package/core/harnesses/gemini/hooks/post-edit-lint.sh +0 -39
- package/core/harnesses/gemini/hooks/pre-bash-guard.sh +0 -10
- package/core/harnesses/gemini/hooks/pre-compact.sh +0 -38
- package/core/harnesses/gemini/hooks/pre-implement-gate.sh +0 -10
- 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 -7
- package/core/harnesses/gemini/settings/adapter-defaults.json +0 -4
- 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 -251
- package/src/adapters/gemini/index.js +0 -429
package/src/runtime/readiness.js
CHANGED
|
@@ -7,6 +7,7 @@ const path = require('node:path');
|
|
|
7
7
|
const { parseYamlFile } = require('./capability-router.js');
|
|
8
8
|
const { resolveLocator, describeResolution } = require('./locator-resolve.js');
|
|
9
9
|
const { readPlan, bindEvidence, declaredIntentList } = require('./retrieval-record.js');
|
|
10
|
+
const { fileRootFor } = require('./freshness.js');
|
|
10
11
|
const { REPO_ROOT } = require('../helper/repo-root.js');
|
|
11
12
|
|
|
12
13
|
const READINESS_STATES = new Set([
|
|
@@ -64,6 +65,7 @@ class ReadinessEngine {
|
|
|
64
65
|
|
|
65
66
|
|
|
66
67
|
|
|
68
|
+
|
|
67
69
|
|
|
68
70
|
|
|
69
71
|
/**
|
|
@@ -72,6 +74,8 @@ class ReadinessEngine {
|
|
|
72
74
|
* @param {string} [options.projectRoot] the repository under work, which evidence locators name.
|
|
73
75
|
* Distinct from repoRoot on purpose: when the CLI runs inside another project, the templates
|
|
74
76
|
* come from the install and the locators must be resolved against that project, not the install.
|
|
77
|
+
* @param {string} [options.sourceRoot] the checkout the verb runs in, where a relative locator's file
|
|
78
|
+
* is read (IC-007, `fileRootFor`); differs from projectRoot in a linked worktree. Defaults to projectRoot.
|
|
75
79
|
* @param {string} [options.templatePath]
|
|
76
80
|
* @param {Object} [options.templates]
|
|
77
81
|
* @param {Object} [options.fsImpl]
|
|
@@ -80,6 +84,7 @@ class ReadinessEngine {
|
|
|
80
84
|
this.fsImpl = options.fsImpl || fs;
|
|
81
85
|
this.repoRoot = options.repoRoot || REPO_ROOT;
|
|
82
86
|
this.projectRoot = options.projectRoot || process.cwd();
|
|
87
|
+
this.sourceRoot = options.sourceRoot || this.projectRoot;
|
|
83
88
|
this.templatePath = options.templatePath || path.join(this.repoRoot, 'core', 'registry', 'readiness-templates.json');
|
|
84
89
|
|
|
85
90
|
if (options.templates) {
|
|
@@ -134,6 +139,22 @@ class ReadinessEngine {
|
|
|
134
139
|
action: 'Execute automated tests or record runtime observation reproducing the behavior.',
|
|
135
140
|
};
|
|
136
141
|
}
|
|
142
|
+
// These are satisfied by what the caller states, not by retrieved evidence (evaluateReadiness),
|
|
143
|
+
// so the action names the flag that states it (--scope / --invariants, cli/index.ts).
|
|
144
|
+
if (reqId === 'scope_clear' || reqId === 'scope_verified') {
|
|
145
|
+
return {
|
|
146
|
+
intent: 'locate-concept',
|
|
147
|
+
capability: 'code.semantic-search',
|
|
148
|
+
action: `State the scope boundary for requirement '${reqId}' (${reqDef.description}): pass --scope "<boundary>" when evaluating readiness.`,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
if (reqId === 'invariants_captured') {
|
|
152
|
+
return {
|
|
153
|
+
intent: 'locate-concept',
|
|
154
|
+
capability: 'code.semantic-search',
|
|
155
|
+
action: `State the invariants the change must preserve for requirement '${reqId}' (${reqDef.description}): pass --invariants "<invariants>" when evaluating readiness.`,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
137
158
|
return {
|
|
138
159
|
intent: 'locate-concept',
|
|
139
160
|
capability: 'code.semantic-search',
|
|
@@ -224,7 +245,10 @@ class ReadinessEngine {
|
|
|
224
245
|
const queryBound = Boolean(plan?.planId);
|
|
225
246
|
const binding = queryBound ? bindEvidence(plan, allTaskEvidence) : null;
|
|
226
247
|
const unboundIds = new Set(binding ? binding.unbound.map((item ) => item.id) : []);
|
|
248
|
+
// A superseded item is retired: it neither satisfies a requirement nor can it be unresolvable
|
|
249
|
+
// (the stale check below already skips it), so nothing else can be left that clears it.
|
|
227
250
|
const taskEvidence = allTaskEvidence.filter((item ) => item.freshness?.status === 'FRESH'
|
|
251
|
+
&& item.status !== 'superseded'
|
|
228
252
|
&& (!queryBound || !unboundIds.has(item.id)));
|
|
229
253
|
|
|
230
254
|
// An item accepted before FR-004 existed, or one whose target file has since shrunk, can be
|
|
@@ -246,7 +270,7 @@ class ReadinessEngine {
|
|
|
246
270
|
const unresolvableEvidence = [];
|
|
247
271
|
for (const item of taskEvidence) {
|
|
248
272
|
if (item.provenance !== 'extracted' || !item.locator || !item.locator.file) continue;
|
|
249
|
-
const resolution = resolveLocator({ locator: item.locator, repoRoot: this.projectRoot });
|
|
273
|
+
const resolution = resolveLocator({ locator: item.locator, repoRoot: fileRootFor(this.sourceRoot, this.projectRoot, item.locator) });
|
|
250
274
|
if (!resolution.resolved) {
|
|
251
275
|
unresolvableEvidence.push({
|
|
252
276
|
evidenceId: item.id,
|
|
@@ -296,7 +320,18 @@ class ReadinessEngine {
|
|
|
296
320
|
// not by any supported claim the task happens to hold (review R1 — a supported claim about an
|
|
297
321
|
// unrelated invariant satisfied the bug template's root-cause requirement).
|
|
298
322
|
else if (reqDef.requiresClaimStatus) {
|
|
299
|
-
|
|
323
|
+
// A `supported` claim counts only while at least one of its supporting items is live
|
|
324
|
+
// (active, FRESH, bound) in `taskEvidence`: support that was superseded, went stale or is
|
|
325
|
+
// unbound no longer backs it (107-B review C-1). Other statuses are not evidence-backed.
|
|
326
|
+
const liveEvidenceIds = new Set(taskEvidence.map((item ) => item.id));
|
|
327
|
+
const statusMatched = taskClaims.filter((c ) => c.status === reqDef.requiresClaimStatus);
|
|
328
|
+
const hasLiveSupport = (c ) => reqDef.requiresClaimStatus !== 'supported'
|
|
329
|
+
|| (c.supportingEvidence || []).some((id ) => liveEvidenceIds.has(id));
|
|
330
|
+
// A claim whose stored status still reads `supported` but whose support was retired is
|
|
331
|
+
// named as such, so the user is not told to add a supported claim they can already see.
|
|
332
|
+
const retired = statusMatched.filter((c ) => !hasLiveSupport(c)
|
|
333
|
+
&& (!reqDef.requiresClaimRole || c.role === reqDef.requiresClaimRole));
|
|
334
|
+
let matchingClaims = statusMatched.filter(hasLiveSupport);
|
|
300
335
|
if (reqDef.requiresClaimRole) {
|
|
301
336
|
const withRole = matchingClaims.filter((c ) => c.role === reqDef.requiresClaimRole);
|
|
302
337
|
if (withRole.length === 0 && matchingClaims.length > 0) {
|
|
@@ -309,6 +344,12 @@ class ReadinessEngine {
|
|
|
309
344
|
if (matchingClaims.length > 0) {
|
|
310
345
|
isSatisfied = true;
|
|
311
346
|
matchedEvidenceIds = matchingClaims.flatMap((c ) => c.supportingEvidence);
|
|
347
|
+
} else if (!reason && retired.length > 0) {
|
|
348
|
+
reasonCode = 'claim-support-retired';
|
|
349
|
+
reason = `${retired.length} claim(s) with status '${reqDef.requiresClaimStatus}'`
|
|
350
|
+
+ `${reqDef.requiresClaimRole ? ` and role '${reqDef.requiresClaimRole}'` : ''} have no live supporting `
|
|
351
|
+
+ `evidence (${retired.slice(0, 5).map((c ) => c.id).join(', ')}): each supporting item was superseded, `
|
|
352
|
+
+ 'went stale or is not bound to the task plan. Link a fresh item with `claim --action link` and re-evaluate.';
|
|
312
353
|
} else if (!reason) {
|
|
313
354
|
reason = `Requires at least one claim with status '${reqDef.requiresClaimStatus}'`
|
|
314
355
|
+ `${reqDef.requiresClaimRole ? ` and role '${reqDef.requiresClaimRole}'` : ''}.`;
|
|
@@ -476,7 +517,7 @@ class ReadinessEngine {
|
|
|
476
517
|
/** One live snapshot for readiness inspection and orchestration transitions. Reads never persist
|
|
477
518
|
* derived freshness or claim status; every invocation evaluates the current project again.
|
|
478
519
|
* `mode` defaults to 'workflow' — fail closed: the standalone exemption must be declared. */
|
|
479
|
-
function evaluateTaskReadiness({ taskProfile, repoRoot = REPO_ROOT, projectRoot = process.cwd(), treeRoot = projectRoot, mode = 'workflow' } ) {
|
|
520
|
+
function evaluateTaskReadiness({ taskProfile, repoRoot = REPO_ROOT, projectRoot = process.cwd(), treeRoot = projectRoot, sourceRoot = projectRoot, mode = 'workflow' } ) {
|
|
480
521
|
// Validated before any ledger work, not just inside stageEntryFor at the end — an invalid mode
|
|
481
522
|
// used to pay a full freshness/claims evaluation before erroring (review suggestion, 026).
|
|
482
523
|
if (!EXECUTION_MODES.has(mode)) {
|
|
@@ -487,11 +528,11 @@ function evaluateTaskReadiness({ taskProfile, repoRoot = REPO_ROOT, projectRoot
|
|
|
487
528
|
const { FreshnessValidator } = require('./freshness.js');
|
|
488
529
|
const ledger = new EvidenceLedger({ repoRoot: projectRoot });
|
|
489
530
|
ledger.load(taskProfile.taskId);
|
|
490
|
-
new FreshnessValidator({ repoRoot: projectRoot, treeRoot }).validateLedgerFreshness(ledger);
|
|
531
|
+
new FreshnessValidator({ repoRoot: projectRoot, treeRoot, sourceRoot }).validateLedgerFreshness(ledger);
|
|
491
532
|
const claims = new ClaimsManager({ evidenceLedger: ledger, repoRoot: projectRoot });
|
|
492
533
|
claims.load(taskProfile.taskId);
|
|
493
534
|
claims.evaluateAll();
|
|
494
|
-
const report = new ReadinessEngine({ repoRoot, projectRoot }).evaluateReadiness(taskProfile, ledger, claims);
|
|
535
|
+
const report = new ReadinessEngine({ repoRoot, projectRoot, sourceRoot }).evaluateReadiness(taskProfile, ledger, claims);
|
|
495
536
|
return { ...report, executionMode: mode, stageEntry: stageEntryFor(report.state, mode) };
|
|
496
537
|
}
|
|
497
538
|
|
|
@@ -8,13 +8,15 @@ const crypto = require('node:crypto');
|
|
|
8
8
|
const { acquireLock, updateTaskState, readTaskState } = require('./task-state.js');
|
|
9
9
|
const { EvidenceLedger, assertSafeTaskId } = require('./evidence-ledger.js');
|
|
10
10
|
const { ClaimsManager } = require('./claims.js');
|
|
11
|
-
const {
|
|
12
|
-
const { FreshnessValidator } = require('./freshness.js');
|
|
11
|
+
const { taskRecord } = require('./task-scope.js');
|
|
12
|
+
const { FreshnessValidator, fileRootFor } = require('./freshness.js');
|
|
13
13
|
const { resolveLocator } = require('./locator-resolve.js');
|
|
14
14
|
const { finishRuntime, usageError } = require('./cli-result.js');
|
|
15
15
|
|
|
16
16
|
const STATES = new Set(['OPEN', 'ANSWERED', 'UNRESOLVED']);
|
|
17
|
-
const REASONS = new Set(['detected-gap', 'user-request']);
|
|
17
|
+
const REASONS = new Set(['detected-gap', 'user-request', 'ideation']);
|
|
18
|
+
// Stage kinds an ideation request may attach to (IC-001): the feature's discovery and design stages.
|
|
19
|
+
const IDEATION_KINDS = new Set(['discovery', 'design']);
|
|
18
20
|
const safeId = (value , label ) => {
|
|
19
21
|
if (typeof value !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(value)) {
|
|
20
22
|
throw new Error(`${label} must be a safe non-empty identifier`);
|
|
@@ -22,7 +24,7 @@ const safeId = (value , label ) => {
|
|
|
22
24
|
return value;
|
|
23
25
|
};
|
|
24
26
|
|
|
25
|
-
function hasLocatedSource(item , projectRoot ) {
|
|
27
|
+
function hasLocatedSource(item , projectRoot , stateRoot = projectRoot) {
|
|
26
28
|
const locator = item.locator || {};
|
|
27
29
|
if (locator.uri !== undefined) {
|
|
28
30
|
if (typeof locator.uri !== 'string') return false;
|
|
@@ -32,33 +34,43 @@ function hasLocatedSource(item , projectRoot ) {
|
|
|
32
34
|
} catch { return false; }
|
|
33
35
|
}
|
|
34
36
|
return Boolean((locator.uri && typeof locator.uri === 'string')
|
|
35
|
-
|| (locator.file && resolveLocator({ locator, repoRoot: projectRoot }).resolved));
|
|
37
|
+
|| (locator.file && resolveLocator({ locator, repoRoot: fileRootFor(projectRoot, stateRoot, locator) }).resolved));
|
|
36
38
|
}
|
|
37
39
|
|
|
40
|
+
// The run, the requests, the evidence and the claims are all read and written under `projectRoot`,
|
|
41
|
+
// the task's state root (115 IC-004); `sourceRoot` is the checkout the verb runs in, where a relative
|
|
42
|
+
// evidence locator resolves and is hashed (IC-007). Both default to the same directory.
|
|
38
43
|
class ResearchRequestStore {
|
|
39
44
|
|
|
45
|
+
|
|
40
46
|
|
|
41
47
|
|
|
42
48
|
|
|
43
|
-
constructor({ projectRoot = process.cwd(), fsImpl = fs, slug = null } = {}) {
|
|
49
|
+
constructor({ projectRoot = process.cwd(), sourceRoot, fsImpl = fs, slug = null } = {}) {
|
|
44
50
|
this.projectRoot = projectRoot;
|
|
51
|
+
this.sourceRoot = sourceRoot || projectRoot;
|
|
45
52
|
this.fsImpl = fsImpl;
|
|
46
53
|
this.slug = slug;
|
|
47
54
|
this.stateDir = path.join(projectRoot, '.doflow', 'state', 'research');
|
|
48
55
|
}
|
|
49
56
|
|
|
50
|
-
/** The feature's namespace
|
|
57
|
+
/** The task's request file in the feature's namespace when it has one (task-scope.ts), else the flat store. */
|
|
58
|
+
record(taskId ) {
|
|
59
|
+
return taskRecord({ projectRoot: this.projectRoot, store: 'research', taskId: assertSafeTaskId(taskId), slug: this.slug, fsImpl: this.fsImpl });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Where the task's requests are written. */
|
|
51
63
|
file(taskId ) {
|
|
52
|
-
|
|
53
|
-
return path.join(dir, `${taskId}.json`);
|
|
64
|
+
return this.record(taskId).file;
|
|
54
65
|
}
|
|
55
66
|
|
|
67
|
+
/** The task's run, read where the orchestrator keeps it (115 IC-004). */
|
|
56
68
|
runFile(taskId ) {
|
|
57
|
-
return
|
|
69
|
+
return taskRecord({ projectRoot: this.projectRoot, store: 'orchestration', taskId: assertSafeTaskId(taskId), slug: this.slug, fsImpl: this.fsImpl }).read;
|
|
58
70
|
}
|
|
59
71
|
|
|
60
72
|
read(taskId ) {
|
|
61
|
-
const data = readTaskState(this.fsImpl, this.
|
|
73
|
+
const data = readTaskState(this.fsImpl, this.record(taskId).read);
|
|
62
74
|
if (!data) return { taskId, revision: 0, requests: [] };
|
|
63
75
|
if (data.taskId !== taskId || !Array.isArray(data.requests)
|
|
64
76
|
|| data.requests.some((r ) => !STATES.has(r.status))) {
|
|
@@ -70,7 +82,8 @@ class ResearchRequestStore {
|
|
|
70
82
|
// Re-read inside updateTaskState's lock: overlapping writers cannot lose a request.
|
|
71
83
|
change(taskId , update ) {
|
|
72
84
|
let result;
|
|
73
|
-
|
|
85
|
+
const { file, seed } = this.record(taskId);
|
|
86
|
+
updateTaskState({ fsImpl: this.fsImpl, file, prepare: seed, build: (disk ) => {
|
|
74
87
|
if (disk && (disk.taskId !== taskId || !Array.isArray(disk.requests))) {
|
|
75
88
|
throw new Error(`Invalid research state for task '${taskId}'`);
|
|
76
89
|
}
|
|
@@ -90,6 +103,7 @@ class ResearchRequestStore {
|
|
|
90
103
|
if (current?.type !== 'stage' || current.id !== stageId) {
|
|
91
104
|
throw new Error(`Current feature stage is '${current?.type === 'stage' ? current.id : 'none'}', not '${stageId}'`);
|
|
92
105
|
}
|
|
106
|
+
return current;
|
|
93
107
|
}
|
|
94
108
|
|
|
95
109
|
open({ taskId, stageId, question, reason, blocking } = {}) {
|
|
@@ -103,13 +117,20 @@ class ResearchRequestStore {
|
|
|
103
117
|
if (!this.fsImpl.existsSync(runFile)) throw new Error(`Task '${taskId}' has no active feature stage`);
|
|
104
118
|
const release = acquireLock(this.fsImpl, runFile);
|
|
105
119
|
try {
|
|
106
|
-
this.stageFor(taskId, stageId);
|
|
120
|
+
const stage = this.stageFor(taskId, stageId);
|
|
121
|
+
if (reason === 'ideation' && !IDEATION_KINDS.has(stage.kind)) {
|
|
122
|
+
throw new Error(`reason ideation is only valid on a feature discovery or design stage, not '${stage.kind ?? 'none'}'`);
|
|
123
|
+
}
|
|
107
124
|
return this.change(taskId, (requests ) => {
|
|
108
125
|
const existing = requests.find((r ) => r.stageId === stageId && r.question === question.trim() && r.status === 'OPEN');
|
|
109
126
|
if (existing) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
127
|
+
const upgradeBlocking = (blocking ?? reason === 'detected-gap') && !existing.blocking;
|
|
128
|
+
// A re-open as ideation makes the request ideation (so an OPEN one holds the stage); no
|
|
129
|
+
// re-open weakens it: an ideation request keeps its reason.
|
|
130
|
+
const nextReason = existing.reason === 'ideation' ? 'ideation' : reason === 'ideation' || upgradeBlocking ? reason : existing.reason;
|
|
131
|
+
if (upgradeBlocking || nextReason !== existing.reason) {
|
|
132
|
+
if (upgradeBlocking) existing.blocking = true;
|
|
133
|
+
existing.reason = nextReason;
|
|
113
134
|
existing.updatedAt = new Date().toISOString();
|
|
114
135
|
}
|
|
115
136
|
return existing;
|
|
@@ -142,7 +163,7 @@ class ResearchRequestStore {
|
|
|
142
163
|
if (!claimId || evidenceIds.length === 0) throw new Error('answered requires a claim and evidence IDs');
|
|
143
164
|
const ledger = new EvidenceLedger({ repoRoot: this.projectRoot, slug: this.slug });
|
|
144
165
|
ledger.load(taskId);
|
|
145
|
-
new FreshnessValidator({ repoRoot: this.projectRoot }).validateLedgerFreshness(ledger);
|
|
166
|
+
new FreshnessValidator({ repoRoot: this.projectRoot, treeRoot: this.sourceRoot, sourceRoot: this.sourceRoot }).validateLedgerFreshness(ledger);
|
|
146
167
|
const claims = new ClaimsManager({ repoRoot: this.projectRoot, evidenceLedger: ledger, slug: this.slug });
|
|
147
168
|
claims.load(taskId); claims.evaluateAll();
|
|
148
169
|
const claim = claims.getClaim(claimId);
|
|
@@ -153,7 +174,7 @@ class ResearchRequestStore {
|
|
|
153
174
|
const item = ledger.getEvidence(id);
|
|
154
175
|
if (!item || item.taskId !== taskId || item.provenance !== 'extracted'
|
|
155
176
|
|| item.status === 'superseded' || item.freshness?.status !== 'FRESH'
|
|
156
|
-
|| !hasLocatedSource(item, this.projectRoot)
|
|
177
|
+
|| !hasLocatedSource(item, this.sourceRoot, this.projectRoot)
|
|
157
178
|
|| !claim.supportingEvidence.includes(id)) {
|
|
158
179
|
throw new Error(`Evidence '${id}' is not fresh, located support for this claim and task`);
|
|
159
180
|
}
|
|
@@ -171,8 +192,12 @@ class ResearchRequestStore {
|
|
|
171
192
|
}
|
|
172
193
|
|
|
173
194
|
blockingGap(taskId , stageId ) {
|
|
174
|
-
const
|
|
175
|
-
|
|
195
|
+
const onStage = this.list(taskId).filter((r ) => r.stageId === stageId);
|
|
196
|
+
// An OPEN ideation request holds the stage whatever its blocking flag (IC-002): the research
|
|
197
|
+
// is either answered or recorded UNRESOLVED before the stage completes.
|
|
198
|
+
const openIdeation = onStage.find((r ) => r.reason === 'ideation' && r.status === 'OPEN');
|
|
199
|
+
if (openIdeation) return openIdeation;
|
|
200
|
+
for (const request of onStage.filter((r ) => r.blocking)) {
|
|
176
201
|
if (request.status !== 'ANSWERED') return request;
|
|
177
202
|
try {
|
|
178
203
|
this.validateAnswer(taskId, request);
|
|
@@ -186,7 +211,7 @@ class ResearchRequestStore {
|
|
|
186
211
|
validateAnswer(taskId , request ) {
|
|
187
212
|
const ledger = new EvidenceLedger({ repoRoot: this.projectRoot, slug: this.slug });
|
|
188
213
|
ledger.load(taskId);
|
|
189
|
-
new FreshnessValidator({ repoRoot: this.projectRoot }).validateLedgerFreshness(ledger);
|
|
214
|
+
new FreshnessValidator({ repoRoot: this.projectRoot, treeRoot: this.sourceRoot, sourceRoot: this.sourceRoot }).validateLedgerFreshness(ledger);
|
|
190
215
|
const claims = new ClaimsManager({ repoRoot: this.projectRoot, evidenceLedger: ledger, slug: this.slug });
|
|
191
216
|
claims.load(taskId); claims.evaluateAll();
|
|
192
217
|
const claim = claims.getClaim(request.claimId);
|
|
@@ -201,7 +226,7 @@ class ResearchRequestStore {
|
|
|
201
226
|
const item = ledger.getEvidence(id);
|
|
202
227
|
if (!item || item.taskId !== taskId || item.provenance !== 'extracted'
|
|
203
228
|
|| item.status === 'superseded' || item.freshness?.status !== 'FRESH'
|
|
204
|
-
|| !hasLocatedSource(item, this.projectRoot)
|
|
229
|
+
|| !hasLocatedSource(item, this.sourceRoot, this.projectRoot)
|
|
205
230
|
|| !claim.supportingEvidence.includes(id)) {
|
|
206
231
|
throw new Error(`Research evidence '${id}' is no longer fresh, located support`);
|
|
207
232
|
}
|
|
@@ -210,9 +235,9 @@ class ResearchRequestStore {
|
|
|
210
235
|
}
|
|
211
236
|
|
|
212
237
|
function handleResearchRequestCommand({ action = 'list', taskId, stageId, question, reason,
|
|
213
|
-
blocking, requestId, outcome, claimId, evidenceIds, gap, json = false, projectRoot, slug } = {}) {
|
|
238
|
+
blocking, requestId, outcome, claimId, evidenceIds, gap, json = false, projectRoot, sourceRoot, slug } = {}) {
|
|
214
239
|
try {
|
|
215
|
-
const store = new ResearchRequestStore({ projectRoot, slug });
|
|
240
|
+
const store = new ResearchRequestStore({ projectRoot, sourceRoot, slug });
|
|
216
241
|
let result;
|
|
217
242
|
switch (action) {
|
|
218
243
|
case 'open': result = store.open({ taskId, stageId, question, reason, blocking }); break;
|
|
@@ -228,4 +253,4 @@ function handleResearchRequestCommand({ action = 'list', taskId, stageId, questi
|
|
|
228
253
|
}
|
|
229
254
|
}
|
|
230
255
|
|
|
231
|
-
module.exports = { ResearchRequestStore, handleResearchRequestCommand };
|
|
256
|
+
module.exports = { ResearchRequestStore, handleResearchRequestCommand, REASONS };
|
|
@@ -42,7 +42,7 @@ const { CapabilityRouter } = require('./capability-router.js');
|
|
|
42
42
|
const { EvidenceLedger, assertSafeTaskId } = require('./evidence-ledger.js');
|
|
43
43
|
const { FreshnessValidator } = require('./freshness.js');
|
|
44
44
|
const { probeFreshness } = require('./health.js');
|
|
45
|
-
const { planPath, readPlan, updatePlan, moveCorruptPlanAside, mintPlanId, activeAttemptRef, resolveNeed, bindEvidence, deriveOutcome } = require('./retrieval-record.js');
|
|
45
|
+
const { planPath, readPlan, updatePlan, moveCorruptPlanAside, mintPlanId, activeAttemptRef, resolveNeed, appendRetrievalAttempt, bindEvidence, deriveOutcome } = require('./retrieval-record.js');
|
|
46
46
|
// One definition of the score refusal, applied at a second boundary. See the note on
|
|
47
47
|
// EVIDENCE_SCORE_FIELDS in src/runtime/evidence-fields.js: relevance is a property of a search, not of a fact, and a plan
|
|
48
48
|
// that recorded one would hand the next worker a confidence dressed as a measurement.
|
|
@@ -387,7 +387,7 @@ function reportPlan({ projectRoot, sourceRoot, taskId, reached, json, slug, repo
|
|
|
387
387
|
} catch (error ) {
|
|
388
388
|
return usageError('retrieval-plan', error.message, json, error);
|
|
389
389
|
}
|
|
390
|
-
new FreshnessValidator({ repoRoot: projectRoot, treeRoot: sourceRoot }).validateLedgerFreshness(ledger);
|
|
390
|
+
new FreshnessValidator({ repoRoot: projectRoot, treeRoot: sourceRoot, sourceRoot }).validateLedgerFreshness(ledger);
|
|
391
391
|
const items = ledger.queryEvidence({ taskId });
|
|
392
392
|
|
|
393
393
|
const isQueryBound = Boolean(record.planId);
|
|
@@ -730,35 +730,15 @@ function attemptPlan({
|
|
|
730
730
|
let savedAttempt = null;
|
|
731
731
|
let file ;
|
|
732
732
|
try {
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
if (!Array.isArray(targetNeed.attempts)) {
|
|
740
|
-
targetNeed.attempts = [];
|
|
741
|
-
}
|
|
742
|
-
const n = targetNeed.attempts.length + 1;
|
|
743
|
-
const role = provider === targetNeed.provider ? 'primary' : 'fallback';
|
|
744
|
-
const attempt = {
|
|
745
|
-
n,
|
|
746
|
-
provider,
|
|
747
|
-
capability: targetNeed.capability,
|
|
748
|
-
role,
|
|
749
|
-
status: finalStatus,
|
|
750
|
-
reason: finalReason,
|
|
751
|
-
note: note !== undefined && note !== null ? String(note) : null,
|
|
752
|
-
indexState,
|
|
753
|
-
attemptRef: activeRef,
|
|
754
|
-
evidenceIds: [],
|
|
755
|
-
recordedAt: new Date().toISOString(),
|
|
756
|
-
recordedBy: 'caller',
|
|
757
|
-
};
|
|
758
|
-
targetNeed.attempts.push(attempt);
|
|
759
|
-
savedAttempt = attempt;
|
|
760
|
-
return current;
|
|
733
|
+
const appended = appendRetrievalAttempt({
|
|
734
|
+
projectRoot, taskId, intent, provider, status: finalStatus, reason: finalReason,
|
|
735
|
+
note, indexState, slug, attemptRef: activeRef,
|
|
736
|
+
expectedCapability: declaredNeed.capability,
|
|
737
|
+
expectedPrimaryProvider: declaredNeed.provider,
|
|
738
|
+
registeredProviders,
|
|
761
739
|
});
|
|
740
|
+
file = appended.file;
|
|
741
|
+
savedAttempt = appended.attempt;
|
|
762
742
|
} catch (error ) {
|
|
763
743
|
return usageError('retrieval-plan', error.message, json, error);
|
|
764
744
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
const fs = require('node:fs');
|
|
6
6
|
const path = require('node:path');
|
|
7
7
|
const crypto = require('node:crypto');
|
|
8
|
-
const { taskStoreDir, resolveTaskScope } = require('./task-scope.js');
|
|
8
|
+
const { taskStoreDir, taskRecord, resolveTaskScope } = require('./task-scope.js');
|
|
9
9
|
const { assertSafeTaskId } = require('./evidence-ledger.js');
|
|
10
10
|
const { readTaskState, updateTaskState } = require('./task-state.js');
|
|
11
11
|
const { storeFile, activeAttempt } = require('./task-attempts.js');
|
|
@@ -36,8 +36,7 @@ function planPath(projectRoot , taskId , slug = null) {
|
|
|
36
36
|
* @returns {any|null}
|
|
37
37
|
*/
|
|
38
38
|
function readPlan(projectRoot , taskId , slug = null, fsImpl = fs) {
|
|
39
|
-
|
|
40
|
-
return readTaskState(fsImpl, file);
|
|
39
|
+
return readTaskState(fsImpl, taskRecord({ projectRoot, store: 'retrieval', taskId: assertSafeTaskId(taskId), slug, fsImpl }).read);
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
/**
|
|
@@ -50,8 +49,8 @@ function readPlan(projectRoot , taskId , slug = null, fsImpl =
|
|
|
50
49
|
* @returns {string} the written file path
|
|
51
50
|
*/
|
|
52
51
|
function updatePlan(projectRoot , taskId , slug , build , fsImpl = fs, prepare ) {
|
|
53
|
-
const file =
|
|
54
|
-
return updateTaskState({ fsImpl, file, build, prepare });
|
|
52
|
+
const { file, seed } = taskRecord({ projectRoot, store: 'retrieval', taskId: assertSafeTaskId(taskId), slug, fsImpl });
|
|
53
|
+
return updateTaskState({ fsImpl, file, build, prepare: () => { seed(); if (prepare) prepare(); } });
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
/** Move a syntactically unreadable plan aside before its replacement is written under the lock. */
|
|
@@ -128,6 +127,75 @@ function resolveNeed(plan , intent , taskId ) {
|
|
|
128
127
|
};
|
|
129
128
|
}
|
|
130
129
|
|
|
130
|
+
/** Resolve the plan's query binding and the task's active attempt in one reusable operation. */
|
|
131
|
+
function resolveRetrievalBinding({ projectRoot, taskId, intent, slug = null, fsImpl = fs, attemptRoot = null } ) {
|
|
132
|
+
const plan = readPlan(projectRoot, taskId, slug, fsImpl);
|
|
133
|
+
const resolved = resolveNeed(plan, intent, taskId);
|
|
134
|
+
if (!resolved.ok) return resolved;
|
|
135
|
+
return {
|
|
136
|
+
ok: true,
|
|
137
|
+
plan,
|
|
138
|
+
queryRef: resolved.queryRef,
|
|
139
|
+
need: resolved.need,
|
|
140
|
+
attemptRef: activeAttemptRef(projectRoot, taskId, slug, fsImpl, attemptRoot),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Append one validated provider attempt under the retrieval-plan state lock. */
|
|
145
|
+
function appendRetrievalAttempt({
|
|
146
|
+
projectRoot, taskId, intent, provider, status, reason, note = null, indexState,
|
|
147
|
+
slug = null, attemptRef = undefined, fsImpl = fs, expectedCapability = undefined,
|
|
148
|
+
expectedPrimaryProvider = undefined, registeredProviders = undefined,
|
|
149
|
+
} ) {
|
|
150
|
+
const allowedIndexStates = ['FRESH', 'STALE', 'UNKNOWN', 'NOT_APPLICABLE'];
|
|
151
|
+
if (!allowedIndexStates.includes(indexState)) {
|
|
152
|
+
throw new Error(`attempt indexState '${indexState}' is invalid; expected one of ${allowedIndexStates.join(', ')}`);
|
|
153
|
+
}
|
|
154
|
+
const allowedReasons = {
|
|
155
|
+
EMPTY: ['empty-answer', 'index-stale', 'index-unverifiable'],
|
|
156
|
+
UNREACHED: ['provider-unavailable', 'policy-bypass', 'provider-error', 'timeout'],
|
|
157
|
+
UNVERIFIED: ['index-unverifiable'],
|
|
158
|
+
};
|
|
159
|
+
if (status === 'RETRIEVED') throw new Error('RETRIEVED is derived from evidence and cannot be appended as a provider attempt');
|
|
160
|
+
if (!Object.prototype.hasOwnProperty.call(allowedReasons, status) || !allowedReasons[status].includes(reason)) {
|
|
161
|
+
throw new Error(`attempt reason '${reason}' is not valid for status '${status}'`);
|
|
162
|
+
}
|
|
163
|
+
if (!Array.isArray(registeredProviders) || !registeredProviders.includes(provider)) {
|
|
164
|
+
throw new Error(`provider '${provider}' is not registered for this capability`);
|
|
165
|
+
}
|
|
166
|
+
if (note !== null && String(note).length > 160) throw new Error('attempt note is longer than 160 characters');
|
|
167
|
+
|
|
168
|
+
let savedAttempt = null;
|
|
169
|
+
const file = updatePlan(projectRoot, taskId, slug, (disk ) => {
|
|
170
|
+
if (disk === null) throw new Error(`the retrieval plan for task ${taskId} was removed during the update; re-declare it`);
|
|
171
|
+
const current = disk;
|
|
172
|
+
const targetNeed = Array.isArray(current.needs) ? current.needs.find((need ) => need.intent === intent) : null;
|
|
173
|
+
if (!targetNeed) throw new Error(`need '${intent}' missing from disk plan during update`);
|
|
174
|
+
if (expectedCapability !== undefined && targetNeed.capability !== expectedCapability) {
|
|
175
|
+
throw new Error(`need '${intent}' capability changed during update`);
|
|
176
|
+
}
|
|
177
|
+
if (expectedPrimaryProvider !== undefined && targetNeed.provider !== expectedPrimaryProvider) {
|
|
178
|
+
throw new Error(`need '${intent}' provider changed during update`);
|
|
179
|
+
}
|
|
180
|
+
if (provider === undefined || provider === null || String(provider).trim() === '') throw new Error('attempt provider is required');
|
|
181
|
+
if (!Array.isArray(targetNeed.attempts)) targetNeed.attempts = [];
|
|
182
|
+
const n = targetNeed.attempts.reduce((max , prior ) => Math.max(max, Number.isInteger(prior?.n) ? prior.n : 0), 0) + 1;
|
|
183
|
+
const role = provider === targetNeed.provider ? 'primary' : 'fallback';
|
|
184
|
+
savedAttempt = {
|
|
185
|
+
n, provider, capability: targetNeed.capability, role, status, reason,
|
|
186
|
+
note: note !== undefined && note !== null ? String(note) : null,
|
|
187
|
+
indexState,
|
|
188
|
+
attemptRef: attemptRef === undefined ? activeAttemptRef(projectRoot, taskId, slug, fsImpl) : attemptRef,
|
|
189
|
+
evidenceIds: [],
|
|
190
|
+
recordedAt: new Date().toISOString(),
|
|
191
|
+
recordedBy: 'caller',
|
|
192
|
+
};
|
|
193
|
+
targetNeed.attempts.push(savedAttempt);
|
|
194
|
+
return current;
|
|
195
|
+
}, fsImpl);
|
|
196
|
+
return { file, attempt: savedAttempt };
|
|
197
|
+
}
|
|
198
|
+
|
|
131
199
|
/**
|
|
132
200
|
* Reads the 070 active attempt ref for a task, returning `${taskId}#${token}` or null.
|
|
133
201
|
*
|
|
@@ -216,6 +284,8 @@ module.exports = {
|
|
|
216
284
|
moveCorruptPlanAside,
|
|
217
285
|
mintPlanId,
|
|
218
286
|
resolveNeed,
|
|
287
|
+
resolveRetrievalBinding,
|
|
288
|
+
appendRetrievalAttempt,
|
|
219
289
|
activeAttemptRef,
|
|
220
290
|
declaredIntentList,
|
|
221
291
|
bindEvidence,
|