@khoavu882/doflow 0.0.0-stage → 2.0.0-beta.3
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/LICENSE +21 -0
- package/README.md +132 -2
- package/bin/doflow.js +44 -0
- package/core/.antigravity-plugin/hooks.json +39 -0
- package/core/.antigravity-plugin/mcp_config.json +18 -0
- package/core/.antigravity-plugin/plugin.json +31 -0
- package/core/.claude-plugin/marketplace.json +20 -0
- package/core/.claude-plugin/plugin.json +21 -0
- package/core/.codex-plugin/marketplace.json +20 -0
- package/core/.codex-plugin/plugin.json +35 -0
- package/core/.plugin/plugin.json +19 -0
- package/core/harnesses/antigravity/hooks/pre-implementation-gate.sh +9 -0
- package/core/harnesses/antigravity/hooks/stop-check.sh +10 -0
- package/core/harnesses/claude/hooks/lib.sh +304 -0
- package/core/harnesses/claude/hooks/mcp-tool-guard.sh +20 -0
- package/core/harnesses/claude/hooks/permission-audit.sh +47 -0
- package/core/harnesses/claude/hooks/post-compact.sh +74 -0
- package/core/harnesses/claude/hooks/post-edit-lint.sh +36 -0
- package/core/harnesses/claude/hooks/post-tool-failure.sh +47 -0
- package/core/harnesses/claude/hooks/pre-bash-guard.sh +20 -0
- package/core/harnesses/claude/hooks/pre-compact.sh +43 -0
- package/core/harnesses/claude/hooks/pre-implement-gate.sh +21 -0
- package/core/harnesses/claude/hooks/session-end.sh +89 -0
- package/core/harnesses/claude/hooks/session-start.sh +5 -0
- package/core/harnesses/claude/hooks/skill-config-audit.sh +39 -0
- package/core/harnesses/claude/hooks/stop-check.sh +5 -0
- package/core/harnesses/claude/hooks/subagent-audit.sh +7 -0
- package/core/harnesses/claude/hooks/user-prompt-submit.sh +5 -0
- package/core/harnesses/claude/settings/keybindings.json +20 -0
- package/core/harnesses/claude/settings/settings.json +201 -0
- package/core/harnesses/claude/statusline/doflow-statusline.sh +49 -0
- package/core/harnesses/codex/agents/core-implementer.toml +7 -0
- package/core/harnesses/codex/agents/quality-guardian.toml +8 -0
- package/core/harnesses/codex/agents/research-writer.toml +8 -0
- package/core/harnesses/codex/agents/spec-analyst.toml +8 -0
- package/core/harnesses/codex/agents/system-architect.toml +9 -0
- package/core/harnesses/codex/config/config.toml +10 -0
- package/core/harnesses/codex/hooks/hooks.json +122 -0
- package/core/harnesses/codex/hooks/lib.sh +335 -0
- package/core/harnesses/codex/hooks/mcp-tool-guard.sh +18 -0
- package/core/harnesses/codex/hooks/post-edit-lint.impl.sh +52 -0
- package/core/harnesses/codex/hooks/post-edit-lint.sh +6 -0
- package/core/harnesses/codex/hooks/pre-bash-guard.sh +18 -0
- package/core/harnesses/codex/hooks/pre-compact.impl.sh +46 -0
- package/core/harnesses/codex/hooks/pre-compact.sh +9 -0
- package/core/harnesses/codex/hooks/pre-implement-gate.sh +19 -0
- package/core/harnesses/codex/hooks/session-end.impl.sh +92 -0
- package/core/harnesses/codex/hooks/session-end.sh +5 -0
- package/core/harnesses/codex/hooks/session-start.sh +5 -0
- package/core/harnesses/codex/hooks/stop-check.sh +8 -0
- package/core/harnesses/codex/hooks/subagent-audit.sh +8 -0
- package/core/harnesses/codex/hooks/user-prompt-submit.sh +6 -0
- package/core/harnesses/gemini/hooks/hooks.json +77 -0
- package/core/harnesses/gemini/hooks/lib.sh +302 -0
- package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +21 -0
- package/core/harnesses/gemini/hooks/post-edit-lint.sh +39 -0
- package/core/harnesses/gemini/hooks/pre-bash-guard.sh +21 -0
- package/core/harnesses/gemini/hooks/pre-compact.sh +38 -0
- package/core/harnesses/gemini/hooks/pre-implement-gate.sh +22 -0
- package/core/harnesses/gemini/hooks/session-end.impl.sh +89 -0
- package/core/harnesses/gemini/hooks/session-end.sh +5 -0
- package/core/harnesses/gemini/hooks/session-start.sh +5 -0
- package/core/harnesses/gemini/settings/adapter-defaults.json +4 -0
- package/core/harnesses/kiro/agents/doflow.json +58 -0
- package/core/harnesses/kiro/hooks/doflow.json +53 -0
- package/core/harnesses/kiro/hooks/lib.sh +324 -0
- package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +9 -0
- package/core/harnesses/kiro/hooks/pre-bash-guard.sh +9 -0
- package/core/harnesses/kiro/hooks/pre-implement-gate.sh +9 -0
- package/core/harnesses/kiro/hooks/session-start.sh +9 -0
- package/core/harnesses/kiro/hooks/stop-check.sh +43 -0
- package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +39 -0
- package/core/harnesses/shared/hooks/adapters/antigravity.js +111 -0
- package/core/harnesses/shared/hooks/adapters/arg-fields.js +19 -0
- package/core/harnesses/shared/hooks/adapters/gemini.js +114 -0
- package/core/harnesses/shared/hooks/hooks.json +47 -0
- package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +91 -0
- package/core/harnesses/shared/hooks/policies/capture-failure.sh +97 -0
- package/core/harnesses/shared/hooks/policies/deny-json.sh +29 -0
- package/core/harnesses/shared/hooks/policies/lib.sh +501 -0
- package/core/harnesses/shared/hooks/policies/mcp-policy.conf +21 -0
- package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +91 -0
- package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +328 -0
- package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +496 -0
- package/core/harnesses/shared/hooks/policies/prompt-nudge.jq +131 -0
- package/core/harnesses/shared/hooks/policies/session-context.sh +141 -0
- package/core/harnesses/shared/hooks/policies/stop-check.sh +264 -0
- package/core/harnesses/shared/hooks/policies/subagent-audit.sh +46 -0
- package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +235 -0
- package/core/harnesses/shared/hooks/stream-hook-runner.js +590 -0
- package/core/harnesses/shared/locator/doflow-run +87 -0
- package/core/registry/advice.json +101 -0
- package/core/registry/assets.json +792 -0
- package/core/registry/capabilities.json +120 -0
- package/core/registry/contracts.json +189 -0
- package/core/registry/external-tools.json +191 -0
- package/core/registry/harnesses.json +2062 -0
- package/core/registry/lifecycle.json +231 -0
- package/core/registry/mcp.json +18 -0
- package/core/registry/models.json +79 -0
- package/core/registry/readiness-templates.json +118 -0
- package/core/registry/routes.json +47 -0
- package/core/registry/storage.json +1057 -0
- package/core/registry/verification.json +258 -0
- package/core/registry/workflows.json +790 -0
- package/core/shared/agent-specs/core-implementer.md +73 -0
- package/core/shared/agent-specs/quality-guardian.md +28 -0
- package/core/shared/agent-specs/research-writer.md +31 -0
- package/core/shared/agent-specs/spec-analyst.md +33 -0
- package/core/shared/agent-specs/system-architect.md +29 -0
- package/core/shared/guidance/DOFLOW_CORE.md +8 -0
- package/core/shared/guidance/FLAGS.md +25 -0
- package/core/shared/guidance/PRINCIPLES.md +61 -0
- package/core/shared/guidance/VERSION +1 -0
- package/core/shared/guidance/mcp/MCP_Context7.md +18 -0
- package/core/shared/guidance/mcp/MCP_Sequential.md +17 -0
- package/core/shared/guidance/modes/MODE_Brainstorming.md +46 -0
- package/core/shared/guidance/modes/MODE_DeepResearch.md +69 -0
- package/core/shared/guidance/modes/MODE_Introspection.md +33 -0
- package/core/shared/guidance/modes/MODE_Orchestration.md +57 -0
- package/core/shared/guidance/modes/MODE_Task_Management.md +74 -0
- package/core/shared/guidance/pointers/claude-gemini.md +2 -0
- package/core/shared/guidance/pointers/codex.md +6 -0
- package/core/shared/guidance/references/ARTIFACT_FORMAT.md +373 -0
- package/core/shared/guidance/references/ARTIFACT_VOICE.md +104 -0
- package/core/shared/guidance/references/CONSTITUTION_BASE.md +47 -0
- package/core/shared/guidance/references/DIAGRAM_GUIDE.md +161 -0
- package/core/shared/guidance/references/DOFLOW_CHAIN.md +87 -0
- package/core/shared/guidance/references/EVIDENCE_LEDGER.md +93 -0
- package/core/shared/guidance/references/MODEL_SELECTION.md +74 -0
- package/core/shared/guidance/references/ORCHESTRATION.md +36 -0
- package/core/shared/guidance/references/RESEARCH_CONFIG.md +21 -0
- package/core/shared/guidance/references/WORKFLOW_HANDOFF.md +81 -0
- package/core/shared/guidance/rules/RULE_01_SAFETY.md +25 -0
- package/core/shared/guidance/rules/RULE_02_WORKFLOW.md +23 -0
- package/core/shared/guidance/rules/RULE_03_QUALITY.md +14 -0
- package/core/shared/guidance/rules/RULE_04_QUESTIONS.md +46 -0
- package/core/shared/scripts/doflow/assets/mermaid-theme.json +46 -0
- package/core/shared/scripts/doflow/bash/do-exec-paths.sh +167 -0
- package/core/shared/scripts/doflow/bash/do-git-state.sh +546 -0
- package/core/shared/scripts/doflow/bash/do-parallel-check.sh +208 -0
- package/core/shared/scripts/doflow/bash/do-paths.sh +347 -0
- package/core/shared/scripts/doflow/bash/do-prereqs.sh +66 -0
- package/core/shared/scripts/doflow/bash/do-review-package.sh +127 -0
- package/core/shared/scripts/doflow/bash/do-task-brief.sh +698 -0
- package/core/shared/scripts/doflow/bash/render-audit.sh +224 -0
- package/core/shared/scripts/doflow/bash/render-diagrams.sh +177 -0
- package/core/shared/scripts/doflow/bash/render-puml.sh +223 -0
- package/core/shared/scripts/doflow/bash/sync-context.sh +66 -0
- package/core/shared/scripts/doflow/bash/validate-artifacts.sh +526 -0
- package/core/shared/scripts/doflow/bin/doflow-run +592 -0
- package/core/shared/skills/do/SKILL.md +128 -0
- package/core/shared/skills/do/references/estimation.md +13 -0
- package/core/shared/skills/do/references/pm_routing.md +25 -0
- package/core/shared/skills/do/references/tool_matrix.md +52 -0
- package/core/shared/skills/do-brainstorm/SKILL.md +252 -0
- package/core/shared/skills/do-code-review/README.md +49 -0
- package/core/shared/skills/do-code-review/SKILL.md +333 -0
- package/core/shared/skills/do-code-review/assets/sample_c_clean.c +71 -0
- package/core/shared/skills/do-code-review/assets/sample_c_smells.c +66 -0
- package/core/shared/skills/do-code-review/assets/sample_cpp_shift.cpp +19 -0
- package/core/shared/skills/do-code-review/assets/sample_csharp_clean.cs +100 -0
- package/core/shared/skills/do-code-review/assets/sample_csharp_smells.cs +78 -0
- package/core/shared/skills/do-code-review/assets/sample_java_clean.java +55 -0
- package/core/shared/skills/do-code-review/assets/sample_java_smells.java +56 -0
- package/core/shared/skills/do-code-review/assets/sample_json_clean.json +7 -0
- package/core/shared/skills/do-code-review/assets/sample_markdown_clean.md +30 -0
- package/core/shared/skills/do-code-review/assets/sample_markdown_smells.md +29 -0
- package/core/shared/skills/do-code-review/assets/sample_shell_heredoc.sh +38 -0
- package/core/shared/skills/do-code-review/assets/sample_shell_smells.sh +15 -0
- package/core/shared/skills/do-code-review/assets/sample_typescript_strings.ts +36 -0
- package/core/shared/skills/do-code-review/assets/sample_yaml_smells.yaml +6 -0
- package/core/shared/skills/do-code-review/content-types/config.md +83 -0
- package/core/shared/skills/do-code-review/content-types/markdown.md +132 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_c_clean_quality.json +79 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_c_smells_quality.json +169 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_cpp_shift_quality.json +49 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_csharp_clean_quality.json +149 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_csharp_smells_quality.json +157 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_java_clean_quality.json +67 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_java_smells_quality.json +97 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_json_clean_quality.json +33 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_markdown_clean_quality.json +1 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_markdown_smells_quality.json +16 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_shell_heredoc_quality.json +54 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_shell_smells_quality.json +91 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_typescript_strings_quality.json +60 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_yaml_smells_quality.json +46 -0
- package/core/shared/skills/do-code-review/languages/c.md +103 -0
- package/core/shared/skills/do-code-review/languages/cpp.md +103 -0
- package/core/shared/skills/do-code-review/languages/csharp.md +97 -0
- package/core/shared/skills/do-code-review/languages/dart.md +108 -0
- package/core/shared/skills/do-code-review/languages/go.md +92 -0
- package/core/shared/skills/do-code-review/languages/java.md +103 -0
- package/core/shared/skills/do-code-review/languages/kotlin.md +88 -0
- package/core/shared/skills/do-code-review/languages/php.md +109 -0
- package/core/shared/skills/do-code-review/languages/python.md +94 -0
- package/core/shared/skills/do-code-review/languages/ruby.md +105 -0
- package/core/shared/skills/do-code-review/languages/rust.md +99 -0
- package/core/shared/skills/do-code-review/languages/shell.md +90 -0
- package/core/shared/skills/do-code-review/languages/swift.md +88 -0
- package/core/shared/skills/do-code-review/languages/typescript.md +97 -0
- package/core/shared/skills/do-code-review/review-policy.json +47 -0
- package/core/shared/skills/do-code-review/rules/universal.md +49 -0
- package/core/shared/skills/do-code-review/scripts/code_quality_checker.py +1696 -0
- package/core/shared/skills/do-code-review/scripts/doc_quality_checker.py +532 -0
- package/core/shared/skills/do-code-review/scripts/pr_analyzer.py +620 -0
- package/core/shared/skills/do-code-review/scripts/review_report_generator.py +505 -0
- package/core/shared/skills/do-constitution/SKILL.md +72 -0
- package/core/shared/skills/do-design/SKILL.md +212 -0
- package/core/shared/skills/do-diagnose/SKILL.md +156 -0
- package/core/shared/skills/do-diagnose/references/code_audit.md +15 -0
- package/core/shared/skills/do-diagnose/references/refactoring.md +12 -0
- package/core/shared/skills/do-diagnose/references/root_cause.md +17 -0
- package/core/shared/skills/do-document/SKILL.md +81 -0
- package/core/shared/skills/do-document/references/api-reference.md +42 -0
- package/core/shared/skills/do-document/references/deep_research.md +18 -0
- package/core/shared/skills/do-document/references/embedded-research.md +11 -0
- package/core/shared/skills/do-document/references/implementation-flow.md +105 -0
- package/core/shared/skills/do-document/references/index_generator.md +9 -0
- package/core/shared/skills/do-document/references/user-guide.md +22 -0
- package/core/shared/skills/do-execute-plan/SKILL.md +261 -0
- package/core/shared/skills/do-execute-plan/references/handoff_record.md +57 -0
- package/core/shared/skills/do-execute-plan/references/parallel_dispatch.md +92 -0
- package/core/shared/skills/do-execute-plan/references/readiness_gate.md +155 -0
- package/core/shared/skills/do-execute-plan/references/run_position.md +63 -0
- package/core/shared/skills/do-execute-plan/references/scaffold.md +441 -0
- package/core/shared/skills/do-execute-plan/references/task_attempts.md +41 -0
- package/core/shared/skills/do-flow/SKILL.md +186 -0
- package/core/shared/skills/do-flow/references/task_classes.md +134 -0
- package/core/shared/skills/do-git/SKILL.md +115 -0
- package/core/shared/skills/do-git/references/hotfix-ritual.md +44 -0
- package/core/shared/skills/do-git/references/lifecycle-policy.md +54 -0
- package/core/shared/skills/do-git/references/release-ritual.md +64 -0
- package/core/shared/skills/do-implement/SKILL.md +222 -0
- package/core/shared/skills/do-plan/SKILL.md +219 -0
- package/core/shared/skills/do-test/SKILL.md +113 -0
- package/core/shared/templates/doflow/constitution-template.md +28 -0
- package/core/shared/templates/doflow/data-model-template.md +169 -0
- package/core/shared/templates/doflow/design-template.md +193 -0
- package/core/shared/templates/doflow/external-contract-template.md +35 -0
- package/core/shared/templates/doflow/intent-template.md +45 -0
- package/core/shared/templates/doflow/plan-template.md +151 -0
- package/core/shared/templates/doflow/question-log-template.md +51 -0
- package/core/shared/templates/doflow/requirement-template.md +126 -0
- package/core/shared/templates/doflow/specs-template.md +55 -0
- package/core/shared/templates/doflow/state-template.md +59 -0
- package/core/shared/workflows/do-flow-chain.md +17 -0
- package/package.json +46 -4
- package/src/adapters/antigravity/index.ts +631 -0
- package/src/adapters/claude/index.ts +548 -0
- package/src/adapters/claude/mcp.ts +94 -0
- package/src/adapters/codex/agents.ts +208 -0
- package/src/adapters/codex/config.ts +272 -0
- package/src/adapters/codex/hooks.ts +203 -0
- package/src/adapters/codex/index.ts +707 -0
- package/src/adapters/codex/mcp.ts +271 -0
- package/src/adapters/codex/native-projection.ts +34 -0
- package/src/adapters/copilot/index.ts +413 -0
- package/src/adapters/copy-tree.ts +620 -0
- package/src/adapters/gemini/hooks.ts +210 -0
- package/src/adapters/gemini/index.ts +390 -0
- package/src/adapters/hook-commands.ts +60 -0
- package/src/adapters/index.ts +106 -0
- package/src/adapters/kiro/index.ts +310 -0
- package/src/adapters/mcp-entries.ts +331 -0
- package/src/adapters/opencode/index.ts +466 -0
- package/src/adapters/pi/index.ts +279 -0
- package/src/adapters/pi/json-members.ts +185 -0
- package/src/adapters/pi/mcp.ts +373 -0
- package/src/adapters/resolve-source.ts +19 -0
- package/src/cli/commands/install.ts +218 -0
- package/src/cli/commands/list-backups.ts +21 -0
- package/src/cli/commands/reconcile.ts +120 -0
- package/src/cli/commands/remove.ts +82 -0
- package/src/cli/commands/rollback.ts +172 -0
- package/src/cli/commands/self-update.ts +33 -0
- package/src/cli/commands/status.ts +138 -0
- package/src/cli/commands/tools.ts +92 -0
- package/src/cli/commands/update.ts +162 -0
- package/src/cli/index.ts +568 -0
- package/src/cli/runtime-commands.ts +281 -0
- package/src/cli/shared.ts +218 -0
- package/src/helper/bash-helper.ts +34 -0
- package/src/helper/git.ts +24 -0
- package/src/helper/guardrails.ts +49 -0
- package/src/helper/harness-paths.ts +140 -0
- package/src/helper/marker-merge.ts +116 -0
- package/src/helper/prompt.ts +61 -0
- package/src/helper/repo-root.ts +29 -0
- package/src/helper/settings-merge.ts +151 -0
- package/src/helper/settings-scope.ts +33 -0
- package/src/helper/toml.ts +244 -0
- package/src/install/backup-set.ts +149 -0
- package/src/install/backup.ts +724 -0
- package/src/install/context.ts +58 -0
- package/src/install/manifest.ts +145 -0
- package/src/install/mcp.ts +254 -0
- package/src/install/paths.ts +84 -0
- package/src/install/targets.ts +90 -0
- package/src/install/tool-lifecycle.ts +151 -0
- package/src/lifecycle/bash-availability.ts +25 -0
- package/src/lifecycle/index.ts +812 -0
- package/src/lifecycle/mcp-index.ts +28 -0
- package/src/lifecycle/policies.ts +73 -0
- package/src/lifecycle/view.ts +175 -0
- package/src/registry/index.ts +611 -0
- package/src/release/sync-plugin-versions.ts +66 -0
- package/src/runtime/advice/cli.ts +283 -0
- package/src/runtime/advice/client.ts +324 -0
- package/src/runtime/advice/config.ts +189 -0
- package/src/runtime/advice/doctor.ts +169 -0
- package/src/runtime/advice/endpoint-guard.ts +134 -0
- package/src/runtime/advice/kinds.ts +154 -0
- package/src/runtime/advice/providers/ollama-systemone.ts +209 -0
- package/src/runtime/advice/record.ts +166 -0
- package/src/runtime/advice/registry.ts +170 -0
- package/src/runtime/advice/strength.ts +66 -0
- package/src/runtime/brief-tools.ts +342 -0
- package/src/runtime/c4-project.ts +371 -0
- package/src/runtime/capability-router.ts +484 -0
- package/src/runtime/checkouts.ts +170 -0
- package/src/runtime/claims.ts +468 -0
- package/src/runtime/cli-result.ts +55 -0
- package/src/runtime/cli.ts +678 -0
- package/src/runtime/command-detect.ts +516 -0
- package/src/runtime/context-pack.ts +492 -0
- package/src/runtime/decision-register.ts +493 -0
- package/src/runtime/evidence-ledger.ts +338 -0
- package/src/runtime/failure/capture.ts +272 -0
- package/src/runtime/failure/classifier.ts +68 -0
- package/src/runtime/failure/cli.ts +178 -0
- package/src/runtime/failure/home.ts +64 -0
- package/src/runtime/failure/store.ts +226 -0
- package/src/runtime/feature-resolve.ts +79 -0
- package/src/runtime/freshness.ts +177 -0
- package/src/runtime/health.ts +863 -0
- package/src/runtime/history-compactor.ts +237 -0
- package/src/runtime/implementation-gate.ts +321 -0
- package/src/runtime/inventory/coverage.ts +218 -0
- package/src/runtime/inventory/identity.ts +65 -0
- package/src/runtime/inventory/index.ts +705 -0
- package/src/runtime/inventory/precedence.ts +134 -0
- package/src/runtime/inventory/read-scopes.ts +131 -0
- package/src/runtime/inventory/siblings.ts +192 -0
- package/src/runtime/knowledge/chunker.ts +124 -0
- package/src/runtime/knowledge/hybrid.ts +149 -0
- package/src/runtime/knowledge/index-store.ts +145 -0
- package/src/runtime/knowledge/retrieval.ts +194 -0
- package/src/runtime/leak-scan.ts +185 -0
- package/src/runtime/lifecycle/cli.ts +276 -0
- package/src/runtime/lifecycle/event-store.ts +340 -0
- package/src/runtime/lifecycle/fold.ts +353 -0
- package/src/runtime/lifecycle/followup.ts +432 -0
- package/src/runtime/lifecycle/goal.ts +249 -0
- package/src/runtime/lifecycle/intent-writer.ts +86 -0
- package/src/runtime/lifecycle/overview.ts +292 -0
- package/src/runtime/lifecycle/release.ts +241 -0
- package/src/runtime/lifecycle/report-store.ts +221 -0
- package/src/runtime/lifecycle/retention.ts +212 -0
- package/src/runtime/lifecycle/root.ts +71 -0
- package/src/runtime/lifecycle/status.ts +353 -0
- package/src/runtime/lifecycle/store-upkeep.ts +393 -0
- package/src/runtime/locator-resolve.ts +158 -0
- package/src/runtime/mask.ts +210 -0
- package/src/runtime/mcp-consultations.ts +412 -0
- package/src/runtime/mcp-reach.ts +157 -0
- package/src/runtime/model-router.ts +153 -0
- package/src/runtime/outcome.ts +467 -0
- package/src/runtime/reach.ts +148 -0
- package/src/runtime/readiness-record.ts +150 -0
- package/src/runtime/readiness.ts +435 -0
- package/src/runtime/recovery.ts +282 -0
- package/src/runtime/research-request.ts +231 -0
- package/src/runtime/retrieval-plan.ts +578 -0
- package/src/runtime/scaffold/artifacts.ts +217 -0
- package/src/runtime/scaffold/fingerprint.ts +56 -0
- package/src/runtime/scaffold/generate.ts +911 -0
- package/src/runtime/scaffold/languages.ts +245 -0
- package/src/runtime/storage/cli.ts +64 -0
- package/src/runtime/storage/engine.ts +249 -0
- package/src/runtime/storage/registry.ts +130 -0
- package/src/runtime/storage/roots.ts +63 -0
- package/src/runtime/storage/rules.ts +282 -0
- package/src/runtime/storage/upkeep.ts +243 -0
- package/src/runtime/storage/work-status.ts +326 -0
- package/src/runtime/task-attempts.ts +415 -0
- package/src/runtime/task-classifier.ts +550 -0
- package/src/runtime/task-scope.ts +130 -0
- package/src/runtime/task-state.ts +176 -0
- package/src/runtime/trace/indicators.ts +235 -0
- package/src/runtime/trace/ledger.ts +626 -0
- package/src/runtime/trace/render.ts +76 -0
- package/src/runtime/trace/views.ts +459 -0
- package/src/runtime/verification/contract-runner.ts +341 -0
- package/src/runtime/verification/engine.ts +1461 -0
- package/src/runtime/verification/registry.ts +130 -0
- package/src/runtime/verification/scope-bound.ts +418 -0
- package/src/runtime/verification/test-selection.ts +172 -0
- package/src/runtime/workflow-engine.ts +588 -0
- package/src/runtime/workflow-orchestrator.ts +953 -0
- package/src/runtime/worktree.ts +340 -0
- package/src/state/index.ts +193 -0
- package/src/state/lockfile.ts +136 -0
- package/src/state/run-lock.ts +408 -0
- package/types/node-commonjs.d.ts +68 -0
- package/types/runtime.d.ts +91 -0
|
@@ -0,0 +1,578 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Declared retrieval, and the report over it (feature 011, plan task B.1, design component C1;
|
|
7
|
+
* FR-010, FR-013, FR-014, FR-015).
|
|
8
|
+
*
|
|
9
|
+
* The distinction this module exists to make: **"the provider found nothing" and "the provider was
|
|
10
|
+
* never asked" are not the same answer**, and neither of them is "the provider answered but its
|
|
11
|
+
* index could not be located". Collapsing any pair of those is how a stage concludes there are no
|
|
12
|
+
* dependents when what actually happened is that no graph exists. So retrieval is declared before
|
|
13
|
+
* it runs, and every declared item is reported afterwards — including the ones the run never
|
|
14
|
+
* reached, which are reported as unreached rather than omitted.
|
|
15
|
+
*
|
|
16
|
+
* It mirrors `verification-contract-runner.js`'s declare-then-report split, and owns nothing else:
|
|
17
|
+
* it performs no retrieval, chooses no provider (it asks `capability-router` for each need), and
|
|
18
|
+
* does not decide whether an unreached item matters — that judgement belongs to the stage that
|
|
19
|
+
* declared it.
|
|
20
|
+
*
|
|
21
|
+
* **Freshness qualifies an answer; it never routes one.** FR-009 freezes routing, so a provider's
|
|
22
|
+
* index state is recorded against the need and reported, and is not consulted when resolving which
|
|
23
|
+
* provider a need goes to. `probeFreshness` is reused unchanged rather than reimplemented — this
|
|
24
|
+
* module owns the qualification, `health.ts` owns the measurement.
|
|
25
|
+
*
|
|
26
|
+
* Two shape decisions are load-bearing and are enforced structurally rather than by convention:
|
|
27
|
+
*
|
|
28
|
+
* 1. **One probe per distinct provider, at declare time** (design R8, plan D2). `probeFreshness`
|
|
29
|
+
* walks the project tree to `SCAN_FILE_LIMIT` (4000 files), so probing per *need* would pay
|
|
30
|
+
* one full scan per need for answers that are identical per provider. A plan of six needs
|
|
31
|
+
* across two providers pays two scans, not six.
|
|
32
|
+
* 2. **Freshness is stored once per provider and referenced, never copied onto a need** (plan
|
|
33
|
+
* D7). `needs[]` carries a provider id and nothing about that provider's index; a reader
|
|
34
|
+
* dereferences `providers{}`. The record is therefore structurally unable to represent two
|
|
35
|
+
* needs resolving to the same provider while disagreeing about its index.
|
|
36
|
+
*
|
|
37
|
+
* No state here is a number, a percentage or a confidence (NFR-001). The only integers this module
|
|
38
|
+
* emits are counts of records.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
const fs = require('node:fs');
|
|
42
|
+
const path = require('node:path');
|
|
43
|
+
const { CapabilityRouter } = require('./capability-router.ts');
|
|
44
|
+
const { EvidenceLedger, assertSafeTaskId } = require('./evidence-ledger.ts');
|
|
45
|
+
const { taskStoreDir } = require('./task-scope.ts');
|
|
46
|
+
const { probeFreshness } = require('./health.ts');
|
|
47
|
+
// One definition of the score refusal, applied at a second boundary. See the note on
|
|
48
|
+
// EVIDENCE_SCORE_FIELDS in cli.js: relevance is a property of a search, not of a fact, and a plan
|
|
49
|
+
// that recorded one would hand the next worker a confidence dressed as a measurement.
|
|
50
|
+
const { assertNoScoreFields } = require('./cli.ts');
|
|
51
|
+
const { finishRuntime, usageError } = require('./cli-result.ts');
|
|
52
|
+
const { REPO_ROOT } = require('../helper/repo-root.ts');
|
|
53
|
+
|
|
54
|
+
// ── the closed result vocabulary (design §4) ─────────────────────────────────────────────────
|
|
55
|
+
|
|
56
|
+
/** The provider was asked and answered with something; the ledger holds what it yielded. */
|
|
57
|
+
const RETRIEVED = 'RETRIEVED';
|
|
58
|
+
/** The provider was asked and answered with nothing — a negative finding, from a locatable index. */
|
|
59
|
+
const EMPTY = 'EMPTY';
|
|
60
|
+
/** The provider was never asked. The state this component exists to keep separate from EMPTY. */
|
|
61
|
+
const UNREACHED = 'UNREACHED';
|
|
62
|
+
/** It answered, but its index could not be located, so the answer carries no weight. */
|
|
63
|
+
const UNVERIFIED = 'UNVERIFIED';
|
|
64
|
+
|
|
65
|
+
const RESULTS = Object.freeze([RETRIEVED, EMPTY, UNREACHED, UNVERIFIED]);
|
|
66
|
+
|
|
67
|
+
// ── the freshness vocabulary, reused rather than reinvented (plan D1) ────────────────────────
|
|
68
|
+
//
|
|
69
|
+
// FRESH / STALE / UNKNOWN come from `probeFreshness` and are not respelled here. NOT_APPLICABLE is
|
|
70
|
+
// the runtime's existing word for "the alternative was not available, so nothing was missed"
|
|
71
|
+
// (trace-views.js) and covers a provider with no index concept at all — ripgrep and git read the
|
|
72
|
+
// working tree and history directly, so their answers are current by construction.
|
|
73
|
+
const NOT_APPLICABLE = 'NOT_APPLICABLE';
|
|
74
|
+
const FRESHNESS_STATES = Object.freeze(['FRESH', 'STALE', 'UNKNOWN', NOT_APPLICABLE]);
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Design §4's mapping table, as data. It maps only the *empty* answer: a provider that returned
|
|
78
|
+
* something is RETRIEVED whatever the age of its index, because the thing it returned is a
|
|
79
|
+
* locator a reader can check.
|
|
80
|
+
*
|
|
81
|
+
* `STALE` deliberately does not produce `UNVERIFIED` (design R10). A stale index answered from
|
|
82
|
+
* real data and its limitation is known and stated; an unlocatable index answered from nothing.
|
|
83
|
+
* Collapsing the two would repeat, one level down, exactly the empty-versus-unreached conflation
|
|
84
|
+
* this module exists to prevent — so staleness travels as a marker beside `EMPTY`, and the
|
|
85
|
+
* provider's `refresh` command travels with it so the remedy is one step away.
|
|
86
|
+
*/
|
|
87
|
+
const EMPTY_ANSWER_BY_FRESHNESS: Record<string, any> = Object.freeze({
|
|
88
|
+
UNKNOWN: UNVERIFIED,
|
|
89
|
+
STALE: EMPTY,
|
|
90
|
+
FRESH: EMPTY,
|
|
91
|
+
[NOT_APPLICABLE]: EMPTY,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* What an empty answer becomes, given the freshness of the index behind it.
|
|
96
|
+
* @param {string} freshnessState one of FRESHNESS_STATES
|
|
97
|
+
* @returns {string} one of RESULTS
|
|
98
|
+
*/
|
|
99
|
+
function resultForEmptyAnswer(freshnessState: any) {
|
|
100
|
+
const mapped = EMPTY_ANSWER_BY_FRESHNESS[freshnessState];
|
|
101
|
+
if (!mapped) {
|
|
102
|
+
// Fail closed rather than pick a default: an unrecognised freshness token means the record was
|
|
103
|
+
// written by something this table does not describe, and calling that a negative finding would
|
|
104
|
+
// be a verdict over an unread state.
|
|
105
|
+
return UNVERIFIED;
|
|
106
|
+
}
|
|
107
|
+
return mapped;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ── the record ────────────────────────────────────────────────────────────────────────────────
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Where a task's plan lives, under the invoking project's state root.
|
|
114
|
+
*
|
|
115
|
+
* The task id is validated by the evidence ledger's own rule, unchanged: it becomes a filename, so
|
|
116
|
+
* it must never be able to name a path.
|
|
117
|
+
* @param {string} projectRoot
|
|
118
|
+
* @param {string} taskId
|
|
119
|
+
* @param {string|null} [slug] the feature the task belongs to, when not the branch's (task-scope.js)
|
|
120
|
+
* @returns {string}
|
|
121
|
+
*/
|
|
122
|
+
function planPath(projectRoot: any, taskId: any, slug: any = null) {
|
|
123
|
+
const dir = taskStoreDir({ projectRoot, store: 'retrieval', taskId: assertSafeTaskId(taskId), slug });
|
|
124
|
+
return path.join(dir, `${taskId}.json`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @param {string} projectRoot
|
|
129
|
+
* @param {string} taskId
|
|
130
|
+
* @returns {Object|null} the record, or null when no plan was declared
|
|
131
|
+
*/
|
|
132
|
+
function readPlan(projectRoot: any, taskId: any, slug: any = null) {
|
|
133
|
+
const file = planPath(projectRoot, taskId, slug);
|
|
134
|
+
if (!fs.existsSync(file)) return null;
|
|
135
|
+
try {
|
|
136
|
+
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
137
|
+
} catch (error: any) {
|
|
138
|
+
throw new Error(`the retrieval plan at '${file}' is not readable: ${error.message}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* @param {string} projectRoot
|
|
144
|
+
* @param {string} taskId
|
|
145
|
+
* @param {Object} record
|
|
146
|
+
* @returns {string} the file written
|
|
147
|
+
*/
|
|
148
|
+
function writePlan(projectRoot: any, taskId: any, record: any, slug: any = null) {
|
|
149
|
+
const file = planPath(projectRoot, taskId, slug);
|
|
150
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
151
|
+
fs.writeFileSync(file, `${JSON.stringify(record, null, 2)}\n`, 'utf8');
|
|
152
|
+
return file;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* The `sourceMode` each provider id declares, as a lookup the plan builds once per declare.
|
|
157
|
+
*
|
|
158
|
+
* A provider id can appear under several capabilities (`native.rg`, `graphify.query`); `index` wins
|
|
159
|
+
* so a provider declared index-backed anywhere is never read as live.
|
|
160
|
+
*
|
|
161
|
+
* @param {Object} capabilities the router's `capabilities` map
|
|
162
|
+
* @returns {(providerId: string) => string|null} the declared mode, or null for an unknown id
|
|
163
|
+
*/
|
|
164
|
+
function sourceModeLookup(capabilities: any) {
|
|
165
|
+
const modes = new Map();
|
|
166
|
+
for (const capability of Object.values<any>(capabilities || {})) {
|
|
167
|
+
for (const provider of (capability && capability.providers) || []) {
|
|
168
|
+
if (!provider || !provider.id) continue;
|
|
169
|
+
if (modes.get(provider.id) === 'index') continue;
|
|
170
|
+
modes.set(provider.id, provider.sourceMode || null);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return (providerId: any) => (modes.has(providerId) ? modes.get(providerId) : null);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* One provider's index freshness, in the shape the record stores it.
|
|
178
|
+
*
|
|
179
|
+
* Called once per distinct provider per plan and never per need — that bound is the whole of R8's
|
|
180
|
+
* mitigation, and it is why this function is called from a loop over a provider set rather than
|
|
181
|
+
* from the loop over needs.
|
|
182
|
+
*
|
|
183
|
+
* @param {string} providerId
|
|
184
|
+
* @param {string} projectRoot the project whose source the index is compared against
|
|
185
|
+
* @param {(providerId: string) => string|null} [sourceModeOf] the provider's declared `sourceMode`;
|
|
186
|
+
* absent, every provider without an artifact layout is treated as unknown (fail closed)
|
|
187
|
+
* @returns {{state: string, artifact: string|null, refresh: string|null, label: string|null, reason: string|null}}
|
|
188
|
+
*/
|
|
189
|
+
function freshnessEntry(providerId: any, projectRoot: any, sourceModeOf: any = () => null) {
|
|
190
|
+
const probe = probeFreshness(providerId, { root: projectRoot });
|
|
191
|
+
if (!probe) {
|
|
192
|
+
// `probeFreshness` returns null for a provider it has no artifact layout for. What that means
|
|
193
|
+
// depends on how the provider answers: a `live` provider reads current files or commands, so
|
|
194
|
+
// there is no index to age; an `index` provider (or one the registry does not declare) answers
|
|
195
|
+
// from an artifact DoFlow cannot locate, so its freshness is UNKNOWN rather than trusted.
|
|
196
|
+
if (sourceModeOf(providerId) === 'live') {
|
|
197
|
+
return {
|
|
198
|
+
state: NOT_APPLICABLE,
|
|
199
|
+
artifact: null,
|
|
200
|
+
refresh: null,
|
|
201
|
+
label: null,
|
|
202
|
+
reason: 'this provider keeps no index DoFlow knows how to locate — a live filesystem or history '
|
|
203
|
+
+ 'scan is current by construction, so there is nothing whose age could be measured',
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
state: 'UNKNOWN',
|
|
208
|
+
artifact: null,
|
|
209
|
+
refresh: null,
|
|
210
|
+
label: null,
|
|
211
|
+
reason: 'index-backed provider with no artifact layout DoFlow knows; freshness cannot be established',
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
state: probe.state,
|
|
216
|
+
artifact: probe.artifact,
|
|
217
|
+
// Carried through from PROVIDER_ARTIFACTS so the report can state the remedy, not only the
|
|
218
|
+
// problem: an index that cannot be verified is actionable, and the action is one command.
|
|
219
|
+
refresh: probe.refresh || null,
|
|
220
|
+
label: probe.label || null,
|
|
221
|
+
reason: probe.reason || null,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* The freshness state a need's answer is qualified by, read from the plan's provider cache.
|
|
227
|
+
*
|
|
228
|
+
* Always a dereference, never a stored copy (plan D7).
|
|
229
|
+
* @param {Object} record
|
|
230
|
+
* @param {Object} need
|
|
231
|
+
* @returns {string} one of FRESHNESS_STATES
|
|
232
|
+
*/
|
|
233
|
+
function freshnessOf(record: any, need: any) {
|
|
234
|
+
if (!need.provider) return NOT_APPLICABLE;
|
|
235
|
+
const entry: any = record.providers && record.providers[need.provider];
|
|
236
|
+
return entry && entry.state ? entry.state : NOT_APPLICABLE;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* The ledger items a need produced: anything recorded for this task whose source names the
|
|
241
|
+
* provider the need resolved to, or the capability it resolved through.
|
|
242
|
+
*
|
|
243
|
+
* Both are checked because the evidence boundary requires `source.provider` and
|
|
244
|
+
* `source.capability` on every item but constrains neither to a registry id — a stage that
|
|
245
|
+
* recorded `grep` under `code.exact-search` still linked its find to the need that asked for it.
|
|
246
|
+
*
|
|
247
|
+
* @param {Object} need
|
|
248
|
+
* @param {Array<Object>} items
|
|
249
|
+
* @returns {Array<string>} evidence ids
|
|
250
|
+
*/
|
|
251
|
+
function evidenceIdsFor(need: any, items: any) {
|
|
252
|
+
return items
|
|
253
|
+
.filter((item: any) => {
|
|
254
|
+
const source = item.source || {};
|
|
255
|
+
if (need.provider && source.provider === need.provider) return true;
|
|
256
|
+
return Boolean(need.capability) && source.capability === need.capability;
|
|
257
|
+
})
|
|
258
|
+
.map((item: any) => item.id);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// ── actions ───────────────────────────────────────────────────────────────────────────────────
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* `--action declare` — record the needs and the provider each resolves to, before retrieval runs.
|
|
265
|
+
*
|
|
266
|
+
* Exits 0 even when a need resolves to nothing. A need no provider can answer is recorded as
|
|
267
|
+
* declared-unresolvable (`provider: null`) rather than dropped, and it surfaces as UNREACHED at
|
|
268
|
+
* report time, which is where the exit-1 contract lives. Failing the declare would stop the stage
|
|
269
|
+
* before it retrieves the needs that *are* resolvable.
|
|
270
|
+
*
|
|
271
|
+
* A declare replaces any previous plan for the task: design §5 keys the record by task id and
|
|
272
|
+
* stamps one `stage` and one `declaredAt` on it, so a second stage declaring a plan is declaring
|
|
273
|
+
* its own, not appending to somebody else's.
|
|
274
|
+
*
|
|
275
|
+
* @returns {number} exit code
|
|
276
|
+
*/
|
|
277
|
+
function declarePlan({ router, projectRoot, taskId, stage, intents, json, slug }: any) {
|
|
278
|
+
const needs = [];
|
|
279
|
+
for (const intent of intents) {
|
|
280
|
+
let resolution;
|
|
281
|
+
try {
|
|
282
|
+
resolution = router.resolveIntent(intent);
|
|
283
|
+
} catch (error: any) {
|
|
284
|
+
// A misspelled intent is a caller error, not an unresolvable need. Recording it as
|
|
285
|
+
// declared-unresolvable would file a typo as a machine limitation and hide it until report.
|
|
286
|
+
return usageError('retrieval-plan',
|
|
287
|
+
`${error.message}. Declared intents: ${Object.keys(router.routes).sort().join(', ')}`, json, error);
|
|
288
|
+
}
|
|
289
|
+
needs.push({
|
|
290
|
+
intent,
|
|
291
|
+
// Stored because it is what the evidence ledger records under `source.capability`, so the
|
|
292
|
+
// report can link a need to the ids it yielded without re-resolving the route.
|
|
293
|
+
capability: resolution.capability,
|
|
294
|
+
provider: resolution.selectedProvider ? resolution.selectedProvider.id : null,
|
|
295
|
+
result: UNREACHED,
|
|
296
|
+
evidenceIds: [],
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// R8, in one loop: the probe set is the set of DISTINCT providers, so cost is bounded by
|
|
301
|
+
// provider count and not by need count. Six needs across two providers pay two scans.
|
|
302
|
+
const providers: Record<string, any> = {};
|
|
303
|
+
const sourceModeOf: any = sourceModeLookup(router.capabilities);
|
|
304
|
+
for (const need of needs) {
|
|
305
|
+
if (!need.provider || providers[need.provider]) continue;
|
|
306
|
+
providers[need.provider] = freshnessEntry(need.provider, projectRoot, sourceModeOf);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const record = {
|
|
310
|
+
version: 1,
|
|
311
|
+
taskId,
|
|
312
|
+
stage: stage || null,
|
|
313
|
+
declaredAt: new Date().toISOString(),
|
|
314
|
+
providers,
|
|
315
|
+
needs,
|
|
316
|
+
};
|
|
317
|
+
const file = writePlan(projectRoot, taskId, record, slug);
|
|
318
|
+
|
|
319
|
+
const unresolvable = needs.filter((need) => !need.provider).map((need) => need.intent);
|
|
320
|
+
if (json) {
|
|
321
|
+
console.log(JSON.stringify({ action: 'declare', ...record, stateFile: file, unresolvable }, null, 2));
|
|
322
|
+
return finishRuntime(0);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
console.log(`\nDoFlow Retrieval Plan declared [Task: ${taskId}]:`);
|
|
326
|
+
console.log('═'.repeat(78));
|
|
327
|
+
console.log(`Stage: ${record.stage || 'not stated'}`);
|
|
328
|
+
console.log('─'.repeat(78));
|
|
329
|
+
console.log('Intent'.padEnd(26) + 'Provider'.padEnd(22) + 'Index');
|
|
330
|
+
for (const need of needs) {
|
|
331
|
+
console.log(
|
|
332
|
+
need.intent.padEnd(26)
|
|
333
|
+
+ (need.provider || 'none — unresolvable').padEnd(22)
|
|
334
|
+
+ freshnessOf(record, need),
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
printProviderIndexes(record);
|
|
338
|
+
if (unresolvable.length > 0) {
|
|
339
|
+
console.log('─'.repeat(78));
|
|
340
|
+
console.log(`Declared but unresolvable on this machine: ${unresolvable.join(', ')}`);
|
|
341
|
+
console.log(' No provider can answer these; they are recorded, not dropped, and will report as UNREACHED.');
|
|
342
|
+
}
|
|
343
|
+
console.log('─'.repeat(78));
|
|
344
|
+
console.log(`Declared ${needs.length} need(s) to ${file}`);
|
|
345
|
+
console.log('═'.repeat(78) + '\n');
|
|
346
|
+
return finishRuntime(0);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* `--action report` — emit every declared item with what it returned.
|
|
351
|
+
*
|
|
352
|
+
* How an item earns each result:
|
|
353
|
+
* - **RETRIEVED** — the ledger holds evidence for this task from the need's provider or
|
|
354
|
+
* capability. What it yielded is linked by id.
|
|
355
|
+
* - **EMPTY** / **UNVERIFIED** — the caller named the intent in `--need`, stating it was asked,
|
|
356
|
+
* and the ledger holds nothing from it. Which of the two it becomes is design §4's table.
|
|
357
|
+
* - **UNREACHED** — everything else. A need nothing recorded and nobody said was asked was not
|
|
358
|
+
* asked, and the report will not upgrade that to a negative finding on its own.
|
|
359
|
+
*
|
|
360
|
+
* That is why `--need` is meaningful on report and not only on declare: only the caller knows
|
|
361
|
+
* whether a lookup ran and came back empty. Guessing would be the empty-versus-unreached
|
|
362
|
+
* conflation this whole module exists to prevent, committed by the reporter.
|
|
363
|
+
*
|
|
364
|
+
* Exits 1 when any declared item is UNREACHED or UNVERIFIED. Every declared need is required — the
|
|
365
|
+
* record carries no optionality, and inventing one here would be an escape hatch from the exit
|
|
366
|
+
* code rather than a fact about the plan.
|
|
367
|
+
*
|
|
368
|
+
* @returns {number} exit code
|
|
369
|
+
*/
|
|
370
|
+
function reportPlan({ projectRoot, taskId, reached, json, slug }: any) {
|
|
371
|
+
let record;
|
|
372
|
+
try {
|
|
373
|
+
record = readPlan(projectRoot, taskId, slug);
|
|
374
|
+
} catch (error: any) {
|
|
375
|
+
return usageError('retrieval-plan', error.message, json, error);
|
|
376
|
+
}
|
|
377
|
+
if (!record) {
|
|
378
|
+
return usageError('retrieval-plan',
|
|
379
|
+
`no retrieval plan is declared for task '${taskId}' (looked in ${planPath(projectRoot, taskId, slug)}). `
|
|
380
|
+
+ 'Declare one with --action declare --need <intent> before the retrieval runs; a report over no '
|
|
381
|
+
+ 'plan would state that nothing was missed, which is not something an absent plan can establish', json);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const declared = new Set(record.needs.map((need: any) => need.intent));
|
|
385
|
+
const undeclared = [...reached].filter((intent) => !declared.has(intent)).sort();
|
|
386
|
+
if (undeclared.length > 0) {
|
|
387
|
+
return usageError('retrieval-plan',
|
|
388
|
+
`these intents are reported as reached but were never declared: ${undeclared.join(', ')}. `
|
|
389
|
+
+ `Declared: ${[...declared].sort().join(', ')}`, json);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const ledger = new EvidenceLedger({ repoRoot: projectRoot, slug });
|
|
393
|
+
try {
|
|
394
|
+
ledger.load(taskId);
|
|
395
|
+
} catch (error: any) {
|
|
396
|
+
return usageError('retrieval-plan', error.message, json, error);
|
|
397
|
+
}
|
|
398
|
+
const items = ledger.queryEvidence({ taskId });
|
|
399
|
+
|
|
400
|
+
for (const need of record.needs) {
|
|
401
|
+
need.evidenceIds = evidenceIdsFor(need, items);
|
|
402
|
+
if (need.evidenceIds.length > 0) need.result = RETRIEVED;
|
|
403
|
+
else if (reached.has(need.intent)) need.result = resultForEmptyAnswer(freshnessOf(record, need));
|
|
404
|
+
else need.result = UNREACHED;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
record.reportedAt = new Date().toISOString();
|
|
408
|
+
const file = writePlan(projectRoot, taskId, record, slug);
|
|
409
|
+
|
|
410
|
+
// Derived views, computed at emit and never stored on a need — a need holds a provider id and
|
|
411
|
+
// nothing about that provider's index (plan D7).
|
|
412
|
+
const byResult = (result: any) => record.needs.filter((need: any) => need.result === result).map((need: any) => need.intent);
|
|
413
|
+
const stale = record.needs.filter((need: any) => freshnessOf(record, need) === 'STALE').map((need: any) => need.intent);
|
|
414
|
+
const unreached = byResult(UNREACHED);
|
|
415
|
+
const unverified = byResult(UNVERIFIED);
|
|
416
|
+
const incomplete = unreached.length > 0 || unverified.length > 0;
|
|
417
|
+
|
|
418
|
+
if (json) {
|
|
419
|
+
console.log(JSON.stringify({
|
|
420
|
+
action: 'report',
|
|
421
|
+
...record,
|
|
422
|
+
stateFile: file,
|
|
423
|
+
summary: {
|
|
424
|
+
declared: record.needs.length,
|
|
425
|
+
retrieved: byResult(RETRIEVED).length,
|
|
426
|
+
empty: byResult(EMPTY).length,
|
|
427
|
+
unreached: unreached.length,
|
|
428
|
+
unverified: unverified.length,
|
|
429
|
+
},
|
|
430
|
+
unreached,
|
|
431
|
+
unverified,
|
|
432
|
+
stale,
|
|
433
|
+
}, null, 2));
|
|
434
|
+
return finishRuntime(incomplete ? 1 : 0);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
console.log(`\nDoFlow Retrieval Report [Task: ${taskId}]:`);
|
|
438
|
+
console.log('═'.repeat(78));
|
|
439
|
+
console.log(`Stage: ${record.stage || 'not stated'} Declared: ${record.declaredAt}`);
|
|
440
|
+
console.log('─'.repeat(78));
|
|
441
|
+
console.log('Intent'.padEnd(26) + 'Provider'.padEnd(22) + 'Index'.padEnd(16) + 'Result');
|
|
442
|
+
for (const need of record.needs) {
|
|
443
|
+
const freshness = freshnessOf(record, need);
|
|
444
|
+
console.log(
|
|
445
|
+
need.intent.padEnd(26)
|
|
446
|
+
+ (need.provider || 'none').padEnd(22)
|
|
447
|
+
+ freshness.padEnd(16)
|
|
448
|
+
+ need.result + (freshness === 'STALE' ? ' (stale index)' : ''),
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
printProviderIndexes(record);
|
|
452
|
+
console.log('─'.repeat(78));
|
|
453
|
+
// NFR-004: the skip is stated as prominently as the failure, on its own line and named.
|
|
454
|
+
if (unreached.length > 0) {
|
|
455
|
+
console.log(`UNREACHED — declared and never asked: ${unreached.join(', ')}`);
|
|
456
|
+
}
|
|
457
|
+
if (unverified.length > 0) {
|
|
458
|
+
console.log(`UNVERIFIED — answered with nothing from an index that could not be located: ${unverified.join(', ')}`);
|
|
459
|
+
console.log(' This is not a negative finding. Rebuild the index above, or record what you found by other means.');
|
|
460
|
+
}
|
|
461
|
+
if (!incomplete) console.log('Every declared need was reached and its answer is grounded.');
|
|
462
|
+
console.log(`Reported ${record.needs.length} declared need(s) to ${file}`);
|
|
463
|
+
console.log('═'.repeat(78) + '\n');
|
|
464
|
+
return finishRuntime(incomplete ? 1 : 0);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/** The provider index block both actions print: state, what was located, and the remedy. */
|
|
468
|
+
function printProviderIndexes(record: any) {
|
|
469
|
+
const entries = Object.entries<any>(record.providers || {});
|
|
470
|
+
if (entries.length === 0) return;
|
|
471
|
+
console.log('─'.repeat(78));
|
|
472
|
+
console.log('Provider indexes (probed once each, at declare time):');
|
|
473
|
+
for (const [providerId, entry] of entries) {
|
|
474
|
+
console.log(` ${providerId} — ${entry.state}${entry.label ? ` (${entry.label})` : ''}`);
|
|
475
|
+
if (entry.artifact) console.log(` at ${entry.artifact}`);
|
|
476
|
+
if (entry.reason) console.log(` ${entry.reason}`);
|
|
477
|
+
if (entry.refresh && entry.state !== 'FRESH') console.log(` Refresh: ${entry.refresh}`);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// ── the verb ──────────────────────────────────────────────────────────────────────────────────
|
|
482
|
+
|
|
483
|
+
/** `--need a,b --need c` and `--need a --need b` are the same list. */
|
|
484
|
+
function parseIntents(raw: any) {
|
|
485
|
+
const list = Array.isArray(raw) ? raw : (raw === undefined || raw === null ? [] : [raw]);
|
|
486
|
+
const out: any = [];
|
|
487
|
+
for (const entry of list) {
|
|
488
|
+
for (const token of String(entry).split(',')) {
|
|
489
|
+
const trimmed = token.trim();
|
|
490
|
+
if (trimmed !== '' && !out.includes(trimmed)) out.push(trimmed);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return out;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Handles `doflow retrieval-plan` — declare the information needs a stage intends to resolve, and
|
|
498
|
+
* report every declared item afterwards.
|
|
499
|
+
*
|
|
500
|
+
* @param {Object} options
|
|
501
|
+
* @param {string} options.taskId
|
|
502
|
+
* @param {'declare'|'report'|'status'} [options.action='report']
|
|
503
|
+
* @param {Array<string>|string} [options.need] intents, comma-separated or repeated
|
|
504
|
+
* @param {string} [options.stage] the stage id declaring the plan
|
|
505
|
+
* @param {boolean} [options.json=false]
|
|
506
|
+
* @param {string} [options.repoRoot] the DoFlow package root, holding the capability registries
|
|
507
|
+
* @param {string} [options.stateRoot] the project the task belongs to; its state and its index
|
|
508
|
+
* @returns {number} exit code
|
|
509
|
+
*/
|
|
510
|
+
function handleRetrievalPlanCommand(options: any = {}) {
|
|
511
|
+
const { taskId, action = 'report', need, stage, json = false, repoRoot, stateRoot, slug } = options;
|
|
512
|
+
|
|
513
|
+
// NFR-001, at the boundary where a caller's input first becomes a record. `bin/doflow.js` already
|
|
514
|
+
// refuses every score-shaped flag name on the command line; this is the same rule applied to the
|
|
515
|
+
// option object, so a second input surface cannot arrive later without inheriting the refusal.
|
|
516
|
+
try {
|
|
517
|
+
assertNoScoreFields(options, '');
|
|
518
|
+
} catch (error: any) {
|
|
519
|
+
return usageError('retrieval-plan', error.message, json, error);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// `'status'` is the CLI's shared `--action` default, owned by `tools`. Every read-action verb
|
|
523
|
+
// accepts it as an alias for its own default rather than reporting a usage error on an argument
|
|
524
|
+
// the caller never typed — `verify` and `claim` already do exactly this.
|
|
525
|
+
const resolvedAction = action === 'status' ? 'report' : action;
|
|
526
|
+
if (resolvedAction !== 'declare' && resolvedAction !== 'report') {
|
|
527
|
+
return usageError('retrieval-plan', `unknown --action '${action}'. Valid: declare, report (default)`, json);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// The project the task belongs to owns both the state and the index whose age is being measured.
|
|
531
|
+
// The package root owns the capability registries, and is a different directory in every install.
|
|
532
|
+
const projectRoot = stateRoot || process.cwd();
|
|
533
|
+
const packageRoot = repoRoot || REPO_ROOT;
|
|
534
|
+
|
|
535
|
+
try {
|
|
536
|
+
assertSafeTaskId(taskId);
|
|
537
|
+
} catch (error: any) {
|
|
538
|
+
return usageError('retrieval-plan', error.message, json, error);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const intents = parseIntents(need);
|
|
542
|
+
|
|
543
|
+
if (resolvedAction === 'declare') {
|
|
544
|
+
if (intents.length === 0) {
|
|
545
|
+
return usageError('retrieval-plan',
|
|
546
|
+
'--action declare needs at least one --need <intent>: a plan that declares nothing cannot '
|
|
547
|
+
+ 'later report that anything went unreached, which is the only thing it is for', json);
|
|
548
|
+
}
|
|
549
|
+
if (stage !== undefined && stage !== null && String(stage).trim() === '') {
|
|
550
|
+
return usageError('retrieval-plan', '--stage requires a value', json);
|
|
551
|
+
}
|
|
552
|
+
let router;
|
|
553
|
+
try {
|
|
554
|
+
router = new CapabilityRouter({ repoRoot: packageRoot });
|
|
555
|
+
} catch (error: any) {
|
|
556
|
+
return usageError('retrieval-plan', error.message, json, error);
|
|
557
|
+
}
|
|
558
|
+
return declarePlan({ router, projectRoot, taskId, stage, intents, json, slug });
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
return reportPlan({ projectRoot, taskId, reached: new Set(intents), json, slug });
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
module.exports = {
|
|
565
|
+
handleRetrievalPlanCommand,
|
|
566
|
+
resultForEmptyAnswer,
|
|
567
|
+
freshnessEntry,
|
|
568
|
+
sourceModeLookup,
|
|
569
|
+
planPath,
|
|
570
|
+
RESULTS,
|
|
571
|
+
RETRIEVED,
|
|
572
|
+
EMPTY,
|
|
573
|
+
UNREACHED,
|
|
574
|
+
UNVERIFIED,
|
|
575
|
+
NOT_APPLICABLE,
|
|
576
|
+
FRESHNESS_STATES,
|
|
577
|
+
EMPTY_ANSWER_BY_FRESHNESS,
|
|
578
|
+
};
|