@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
package/src/runtime/readiness.js
CHANGED
|
@@ -6,6 +6,8 @@ const fs = require('node:fs');
|
|
|
6
6
|
const path = require('node:path');
|
|
7
7
|
const { parseYamlFile } = require('./capability-router.js');
|
|
8
8
|
const { resolveLocator, describeResolution } = require('./locator-resolve.js');
|
|
9
|
+
const { readPlan, bindEvidence, declaredIntentList } = require('./retrieval-record.js');
|
|
10
|
+
const { fileRootFor } = require('./freshness.js');
|
|
9
11
|
const { REPO_ROOT } = require('../helper/repo-root.js');
|
|
10
12
|
|
|
11
13
|
const READINESS_STATES = new Set([
|
|
@@ -63,6 +65,7 @@ class ReadinessEngine {
|
|
|
63
65
|
|
|
64
66
|
|
|
65
67
|
|
|
68
|
+
|
|
66
69
|
|
|
67
70
|
|
|
68
71
|
/**
|
|
@@ -71,6 +74,8 @@ class ReadinessEngine {
|
|
|
71
74
|
* @param {string} [options.projectRoot] the repository under work, which evidence locators name.
|
|
72
75
|
* Distinct from repoRoot on purpose: when the CLI runs inside another project, the templates
|
|
73
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.
|
|
74
79
|
* @param {string} [options.templatePath]
|
|
75
80
|
* @param {Object} [options.templates]
|
|
76
81
|
* @param {Object} [options.fsImpl]
|
|
@@ -79,6 +84,7 @@ class ReadinessEngine {
|
|
|
79
84
|
this.fsImpl = options.fsImpl || fs;
|
|
80
85
|
this.repoRoot = options.repoRoot || REPO_ROOT;
|
|
81
86
|
this.projectRoot = options.projectRoot || process.cwd();
|
|
87
|
+
this.sourceRoot = options.sourceRoot || this.projectRoot;
|
|
82
88
|
this.templatePath = options.templatePath || path.join(this.repoRoot, 'core', 'registry', 'readiness-templates.json');
|
|
83
89
|
|
|
84
90
|
if (options.templates) {
|
|
@@ -133,6 +139,22 @@ class ReadinessEngine {
|
|
|
133
139
|
action: 'Execute automated tests or record runtime observation reproducing the behavior.',
|
|
134
140
|
};
|
|
135
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
|
+
}
|
|
136
158
|
return {
|
|
137
159
|
intent: 'locate-concept',
|
|
138
160
|
capability: 'code.semantic-search',
|
|
@@ -211,7 +233,23 @@ class ReadinessEngine {
|
|
|
211
233
|
hasBlocked = true;
|
|
212
234
|
}
|
|
213
235
|
|
|
214
|
-
const
|
|
236
|
+
const allTaskEvidence = evidenceLedger ? evidenceLedger.queryEvidence({ taskId }) : [];
|
|
237
|
+
let plan = null;
|
|
238
|
+
let unreadablePlan = null;
|
|
239
|
+
try {
|
|
240
|
+
plan = readPlan(this.projectRoot, taskId);
|
|
241
|
+
} catch (error ) {
|
|
242
|
+
if (error.code !== 'store-unreadable') throw error;
|
|
243
|
+
unreadablePlan = { unreadable: true, reason: error.message };
|
|
244
|
+
}
|
|
245
|
+
const queryBound = Boolean(plan?.planId);
|
|
246
|
+
const binding = queryBound ? bindEvidence(plan, allTaskEvidence) : null;
|
|
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.
|
|
250
|
+
const taskEvidence = allTaskEvidence.filter((item ) => item.freshness?.status === 'FRESH'
|
|
251
|
+
&& item.status !== 'superseded'
|
|
252
|
+
&& (!queryBound || !unboundIds.has(item.id)));
|
|
215
253
|
|
|
216
254
|
// An item accepted before FR-004 existed, or one whose target file has since shrunk, can be
|
|
217
255
|
// FRESH and still point at nothing. *Stale* (the file changed at all) and *unresolvable* (the
|
|
@@ -225,13 +263,14 @@ class ReadinessEngine {
|
|
|
225
263
|
// in evaluateClaim — it is not a claim that the file never changed. An item that genuinely went
|
|
226
264
|
// stale and was never superseded still forces NEEDS_EVIDENCE exactly as before.
|
|
227
265
|
const staleEvidence = (evidenceLedger ? evidenceLedger.getAllEvidence() : [])
|
|
228
|
-
.filter((item ) => item.taskId === taskId && item.
|
|
266
|
+
.filter((item ) => item.taskId === taskId && item.result !== 'EMPTY'
|
|
267
|
+
&& item.freshness?.status === 'STALE' && item.status !== 'superseded')
|
|
229
268
|
.map((item ) => ({ evidenceId: item.id, locator: item.locator, reason: 'file-modified-since-recorded' }));
|
|
230
269
|
|
|
231
270
|
const unresolvableEvidence = [];
|
|
232
271
|
for (const item of taskEvidence) {
|
|
233
272
|
if (item.provenance !== 'extracted' || !item.locator || !item.locator.file) continue;
|
|
234
|
-
const resolution = resolveLocator({ locator: item.locator, repoRoot: this.projectRoot });
|
|
273
|
+
const resolution = resolveLocator({ locator: item.locator, repoRoot: fileRootFor(this.sourceRoot, this.projectRoot, item.locator) });
|
|
235
274
|
if (!resolution.resolved) {
|
|
236
275
|
unresolvableEvidence.push({
|
|
237
276
|
evidenceId: item.id,
|
|
@@ -242,9 +281,30 @@ class ReadinessEngine {
|
|
|
242
281
|
}
|
|
243
282
|
}
|
|
244
283
|
|
|
284
|
+
const staleAbsence = [];
|
|
285
|
+
const staleAbsenceByRequirement = new Map ();
|
|
286
|
+
for (const item of allTaskEvidence) {
|
|
287
|
+
if (item.result !== 'EMPTY' || item.status !== 'active'
|
|
288
|
+
|| !['STALE', 'UNKNOWN'].includes(item.freshness?.status)
|
|
289
|
+
|| (queryBound && unboundIds.has(item.id))) continue;
|
|
290
|
+
for (const requirement of Array.isArray(item.establishes) ? item.establishes : []) {
|
|
291
|
+
const entry = { evidenceId: item.id, requirement, provider: item.source?.provider || null,
|
|
292
|
+
freshness: item.freshness.status, refresh: plan?.providers?.[item.source?.provider]?.refresh || null };
|
|
293
|
+
staleAbsence.push(entry);
|
|
294
|
+
const entries = staleAbsenceByRequirement.get(requirement) || [];
|
|
295
|
+
entries.push({ item, entry });
|
|
296
|
+
staleAbsenceByRequirement.set(requirement, entries);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const unboundEvidence = queryBound
|
|
301
|
+
? { count: binding.unbound.length, ids: binding.unbound.slice(0, 5).map((item ) => item.id) }
|
|
302
|
+
: undefined;
|
|
303
|
+
|
|
245
304
|
for (const [reqId, reqDef] of Object.entries (requirements)) {
|
|
246
305
|
let isSatisfied = false;
|
|
247
306
|
let reason = null;
|
|
307
|
+
let reasonCode = null;
|
|
248
308
|
let matchedEvidenceIds = [];
|
|
249
309
|
|
|
250
310
|
// Check verification plan requirement
|
|
@@ -260,7 +320,18 @@ class ReadinessEngine {
|
|
|
260
320
|
// not by any supported claim the task happens to hold (review R1 — a supported claim about an
|
|
261
321
|
// unrelated invariant satisfied the bug template's root-cause requirement).
|
|
262
322
|
else if (reqDef.requiresClaimStatus) {
|
|
263
|
-
|
|
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);
|
|
264
335
|
if (reqDef.requiresClaimRole) {
|
|
265
336
|
const withRole = matchingClaims.filter((c ) => c.role === reqDef.requiresClaimRole);
|
|
266
337
|
if (withRole.length === 0 && matchingClaims.length > 0) {
|
|
@@ -273,6 +344,12 @@ class ReadinessEngine {
|
|
|
273
344
|
if (matchingClaims.length > 0) {
|
|
274
345
|
isSatisfied = true;
|
|
275
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.';
|
|
276
353
|
} else if (!reason) {
|
|
277
354
|
reason = `Requires at least one claim with status '${reqDef.requiresClaimStatus}'`
|
|
278
355
|
+ `${reqDef.requiresClaimRole ? ` and role '${reqDef.requiresClaimRole}'` : ''}.`;
|
|
@@ -286,18 +363,18 @@ class ReadinessEngine {
|
|
|
286
363
|
// (review R1, P1 — reproduced through the public CLI).
|
|
287
364
|
else if (Array.isArray(reqDef.evidenceKinds) && reqDef.evidenceKinds.length > 0) {
|
|
288
365
|
const allowedKinds = new Set(reqDef.evidenceKinds);
|
|
366
|
+
const rawKindMatched = allTaskEvidence.filter((ev ) => allowedKinds.has(ev.kind));
|
|
289
367
|
const kindMatched = taskEvidence.filter((ev ) => allowedKinds.has(ev.kind));
|
|
290
368
|
const extracted = kindMatched.filter((ev ) => ev.provenance === 'extracted');
|
|
291
369
|
const bound = extracted.filter((ev ) => Array.isArray(ev.establishes) && ev.establishes.includes(reqId));
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
}
|
|
370
|
+
const observationOk = (ev ) => !reqDef.requiresObservation || (ev.observation
|
|
371
|
+
&& Number.isInteger(ev.observation.exitCode)
|
|
372
|
+
&& (reqDef.expectedExit !== 'zero' || ev.observation.exitCode === 0)
|
|
373
|
+
&& (reqDef.expectedExit !== 'nonzero' || ev.observation.exitCode !== 0));
|
|
374
|
+
const qualifies = (ev ) => ev.provenance === 'extracted'
|
|
375
|
+
&& Array.isArray(ev.establishes) && ev.establishes.includes(reqId)
|
|
376
|
+
&& observationOk(ev);
|
|
377
|
+
const qualifying = taskEvidence.filter((ev ) => allowedKinds.has(ev.kind) && qualifies(ev));
|
|
301
378
|
if (qualifying.length > 0) {
|
|
302
379
|
isSatisfied = true;
|
|
303
380
|
matchedEvidenceIds = qualifying.map((ev ) => ev.id);
|
|
@@ -319,6 +396,32 @@ class ReadinessEngine {
|
|
|
319
396
|
+ `${reqDef.expectedExit ? ` with a${reqDef.expectedExit === 'zero' ? ' passing (0)' : ' non-zero'} exit status` : ''} — `
|
|
320
397
|
+ 'record the execution itself, not a description of one.';
|
|
321
398
|
}
|
|
399
|
+
if (!isSatisfied && reqDef.required) {
|
|
400
|
+
const staleAbsences = staleAbsenceByRequirement.get(reqId) || [];
|
|
401
|
+
if (staleAbsences.length > 0) {
|
|
402
|
+
const { item, entry } = staleAbsences[0];
|
|
403
|
+
const refresh = entry.refresh || 'none declared';
|
|
404
|
+
reasonCode = 'stale-absence-unverified';
|
|
405
|
+
const prefix = `'${reqId}' rests only on an empty answer from ${entry.provider || 'unknown provider'}, whose index was ${entry.freshness} when it was recorded (${item.id}). An empty answer from that index cannot establish absence. `;
|
|
406
|
+
if (item.freshness?.staleReason === 'tree-changed-since-recorded'
|
|
407
|
+
|| item.freshness?.staleReason === 'tree-unverifiable') {
|
|
408
|
+
reason = `${prefix}the working tree changed since it was recorded; re-run the lookup and record the absence again.`;
|
|
409
|
+
} else if (item.freshness?.gitCommit === null) {
|
|
410
|
+
reason = `${prefix}absence cannot be established outside a git work tree; record a positive item.`;
|
|
411
|
+
} else {
|
|
412
|
+
reason = `${prefix}Run the declared refresh command (${refresh}) and record the absence again, or re-run the lookup with a live provider such as native.rg: doflow evidence --task-id ${taskId} --action add --absence --need <intent> --provider native.rg ...`;
|
|
413
|
+
}
|
|
414
|
+
} else if (queryBound) {
|
|
415
|
+
const unboundKind = rawKindMatched.filter((ev ) => unboundIds.has(ev.id));
|
|
416
|
+
const unboundQualifying = unboundKind.filter((ev ) =>
|
|
417
|
+
ev.freshness?.status === 'FRESH' && qualifies(ev));
|
|
418
|
+
if (unboundQualifying.length > 0) {
|
|
419
|
+
const ids = unboundQualifying.slice(0, 5).map((item ) => item.id).join(', ');
|
|
420
|
+
reasonCode = 'unbound-retrieval-evidence';
|
|
421
|
+
reason = `${unboundQualifying.length} item(s) of the right kind are not bound to a declared need of plan ${plan.planId} (${ids}), so they do not count. Re-record them with --need <intent>; declared: ${declaredIntentList(plan)}.`;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
322
425
|
}
|
|
323
426
|
// Scope verified / scope clear
|
|
324
427
|
else if (reqId === 'scope_clear' || reqId === 'scope_verified' || reqId === 'invariants_captured') {
|
|
@@ -347,6 +450,7 @@ class ReadinessEngine {
|
|
|
347
450
|
required: Boolean(reqDef.required),
|
|
348
451
|
satisfied: isSatisfied,
|
|
349
452
|
reason,
|
|
453
|
+
reasonCode,
|
|
350
454
|
evidenceIds: matchedEvidenceIds,
|
|
351
455
|
recommendedAction: isSatisfied ? null : this.suggestAction(reqId, reqDef, taskProfile),
|
|
352
456
|
});
|
|
@@ -383,6 +487,10 @@ class ReadinessEngine {
|
|
|
383
487
|
+ 'Re-record them against the tree as it stands.';
|
|
384
488
|
}
|
|
385
489
|
|
|
490
|
+
if (unreadablePlan) {
|
|
491
|
+
summary += ` Warning: retrieval plan is unreadable and was treated as absent: ${unreadablePlan.reason}`;
|
|
492
|
+
}
|
|
493
|
+
|
|
386
494
|
return {
|
|
387
495
|
taskId,
|
|
388
496
|
taskClass,
|
|
@@ -399,6 +507,9 @@ class ReadinessEngine {
|
|
|
399
507
|
evidenceCount: taskEvidence.length,
|
|
400
508
|
unresolvableEvidence,
|
|
401
509
|
staleEvidence,
|
|
510
|
+
staleAbsence,
|
|
511
|
+
...(unreadablePlan ? { retrievalPlan: unreadablePlan } : {}),
|
|
512
|
+
...(unboundEvidence ? { unboundEvidence } : {}),
|
|
402
513
|
};
|
|
403
514
|
}
|
|
404
515
|
}
|
|
@@ -406,7 +517,7 @@ class ReadinessEngine {
|
|
|
406
517
|
/** One live snapshot for readiness inspection and orchestration transitions. Reads never persist
|
|
407
518
|
* derived freshness or claim status; every invocation evaluates the current project again.
|
|
408
519
|
* `mode` defaults to 'workflow' — fail closed: the standalone exemption must be declared. */
|
|
409
|
-
function evaluateTaskReadiness({ taskProfile, repoRoot = REPO_ROOT, projectRoot = process.cwd(), mode = 'workflow' } ) {
|
|
520
|
+
function evaluateTaskReadiness({ taskProfile, repoRoot = REPO_ROOT, projectRoot = process.cwd(), treeRoot = projectRoot, sourceRoot = projectRoot, mode = 'workflow' } ) {
|
|
410
521
|
// Validated before any ledger work, not just inside stageEntryFor at the end — an invalid mode
|
|
411
522
|
// used to pay a full freshness/claims evaluation before erroring (review suggestion, 026).
|
|
412
523
|
if (!EXECUTION_MODES.has(mode)) {
|
|
@@ -417,11 +528,11 @@ function evaluateTaskReadiness({ taskProfile, repoRoot = REPO_ROOT, projectRoot
|
|
|
417
528
|
const { FreshnessValidator } = require('./freshness.js');
|
|
418
529
|
const ledger = new EvidenceLedger({ repoRoot: projectRoot });
|
|
419
530
|
ledger.load(taskProfile.taskId);
|
|
420
|
-
new FreshnessValidator({ repoRoot: projectRoot }).validateLedgerFreshness(ledger);
|
|
531
|
+
new FreshnessValidator({ repoRoot: projectRoot, treeRoot, sourceRoot }).validateLedgerFreshness(ledger);
|
|
421
532
|
const claims = new ClaimsManager({ evidenceLedger: ledger, repoRoot: projectRoot });
|
|
422
533
|
claims.load(taskProfile.taskId);
|
|
423
534
|
claims.evaluateAll();
|
|
424
|
-
const report = new ReadinessEngine({ repoRoot, projectRoot }).evaluateReadiness(taskProfile, ledger, claims);
|
|
535
|
+
const report = new ReadinessEngine({ repoRoot, projectRoot, sourceRoot }).evaluateReadiness(taskProfile, ledger, claims);
|
|
425
536
|
return { ...report, executionMode: mode, stageEntry: stageEntryFor(report.state, mode) };
|
|
426
537
|
}
|
|
427
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 };
|