@khoavu882/doflow 0.0.0-stage → 2.0.0-beta.4
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 +1061 -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 +48 -4
- package/src/adapters/antigravity/index.js +631 -0
- package/src/adapters/claude/index.js +548 -0
- package/src/adapters/claude/mcp.js +94 -0
- package/src/adapters/codex/agents.js +208 -0
- package/src/adapters/codex/config.js +272 -0
- package/src/adapters/codex/hooks.js +203 -0
- package/src/adapters/codex/index.js +707 -0
- package/src/adapters/codex/mcp.js +271 -0
- package/src/adapters/codex/native-projection.js +34 -0
- package/src/adapters/copilot/index.js +413 -0
- package/src/adapters/copy-tree.js +620 -0
- package/src/adapters/gemini/hooks.js +210 -0
- package/src/adapters/gemini/index.js +390 -0
- package/src/adapters/hook-commands.js +60 -0
- package/src/adapters/index.js +106 -0
- package/src/adapters/kiro/index.js +310 -0
- package/src/adapters/mcp-entries.js +331 -0
- package/src/adapters/opencode/index.js +466 -0
- package/src/adapters/pi/index.js +279 -0
- package/src/adapters/pi/json-members.js +185 -0
- package/src/adapters/pi/mcp.js +373 -0
- package/src/adapters/resolve-source.js +19 -0
- package/src/cli/commands/install.js +218 -0
- package/src/cli/commands/list-backups.js +21 -0
- package/src/cli/commands/reconcile.js +120 -0
- package/src/cli/commands/remove.js +82 -0
- package/src/cli/commands/rollback.js +172 -0
- package/src/cli/commands/self-update.js +33 -0
- package/src/cli/commands/status.js +138 -0
- package/src/cli/commands/tools.js +92 -0
- package/src/cli/commands/update.js +162 -0
- package/src/cli/index.js +568 -0
- package/src/cli/runtime-commands.js +281 -0
- package/src/cli/shared.js +218 -0
- package/src/helper/bash-helper.js +34 -0
- package/src/helper/git.js +24 -0
- package/src/helper/guardrails.js +49 -0
- package/src/helper/harness-paths.js +140 -0
- package/src/helper/marker-merge.js +116 -0
- package/src/helper/prompt.js +61 -0
- package/src/helper/repo-root.js +29 -0
- package/src/helper/settings-merge.js +151 -0
- package/src/helper/settings-scope.js +33 -0
- package/src/helper/toml.js +244 -0
- package/src/install/backup-set.js +149 -0
- package/src/install/backup.js +724 -0
- package/src/install/context.js +58 -0
- package/src/install/manifest.js +145 -0
- package/src/install/mcp.js +254 -0
- package/src/install/paths.js +84 -0
- package/src/install/targets.js +90 -0
- package/src/install/tool-lifecycle.js +151 -0
- package/src/lifecycle/bash-availability.js +25 -0
- package/src/lifecycle/index.js +812 -0
- package/src/lifecycle/mcp-index.js +28 -0
- package/src/lifecycle/policies.js +73 -0
- package/src/lifecycle/view.js +175 -0
- package/src/registry/index.js +611 -0
- package/src/release/build-package.js +91 -0
- package/src/release/changelog-section.js +70 -0
- package/src/release/sync-plugin-versions.js +66 -0
- package/src/runtime/advice/cli.js +283 -0
- package/src/runtime/advice/client.js +324 -0
- package/src/runtime/advice/config.js +189 -0
- package/src/runtime/advice/doctor.js +169 -0
- package/src/runtime/advice/endpoint-guard.js +134 -0
- package/src/runtime/advice/kinds.js +154 -0
- package/src/runtime/advice/providers/ollama-systemone.js +209 -0
- package/src/runtime/advice/record.js +166 -0
- package/src/runtime/advice/registry.js +170 -0
- package/src/runtime/advice/strength.js +66 -0
- package/src/runtime/brief-tools.js +342 -0
- package/src/runtime/c4-project.js +371 -0
- package/src/runtime/capability-router.js +484 -0
- package/src/runtime/checkouts.js +170 -0
- package/src/runtime/claims.js +468 -0
- package/src/runtime/cli-result.js +55 -0
- package/src/runtime/cli.js +678 -0
- package/src/runtime/command-detect.js +516 -0
- package/src/runtime/context-pack.js +492 -0
- package/src/runtime/decision-register.js +493 -0
- package/src/runtime/evidence-ledger.js +338 -0
- package/src/runtime/failure/capture.js +272 -0
- package/src/runtime/failure/classifier.js +68 -0
- package/src/runtime/failure/cli.js +178 -0
- package/src/runtime/failure/home.js +64 -0
- package/src/runtime/failure/store.js +226 -0
- package/src/runtime/feature-resolve.js +79 -0
- package/src/runtime/freshness.js +177 -0
- package/src/runtime/health.js +863 -0
- package/src/runtime/history-compactor.js +237 -0
- package/src/runtime/implementation-gate.js +321 -0
- package/src/runtime/inventory/coverage.js +218 -0
- package/src/runtime/inventory/identity.js +65 -0
- package/src/runtime/inventory/index.js +705 -0
- package/src/runtime/inventory/precedence.js +134 -0
- package/src/runtime/inventory/read-scopes.js +131 -0
- package/src/runtime/inventory/siblings.js +192 -0
- package/src/runtime/knowledge/chunker.js +124 -0
- package/src/runtime/knowledge/hybrid.js +149 -0
- package/src/runtime/knowledge/index-store.js +145 -0
- package/src/runtime/knowledge/retrieval.js +194 -0
- package/src/runtime/leak-scan.js +185 -0
- package/src/runtime/lifecycle/cli.js +276 -0
- package/src/runtime/lifecycle/event-store.js +340 -0
- package/src/runtime/lifecycle/fold.js +353 -0
- package/src/runtime/lifecycle/followup.js +432 -0
- package/src/runtime/lifecycle/goal.js +249 -0
- package/src/runtime/lifecycle/intent-writer.js +86 -0
- package/src/runtime/lifecycle/overview.js +292 -0
- package/src/runtime/lifecycle/release.js +241 -0
- package/src/runtime/lifecycle/report-store.js +221 -0
- package/src/runtime/lifecycle/retention.js +212 -0
- package/src/runtime/lifecycle/root.js +71 -0
- package/src/runtime/lifecycle/status.js +353 -0
- package/src/runtime/lifecycle/store-upkeep.js +393 -0
- package/src/runtime/locator-resolve.js +158 -0
- package/src/runtime/mask.js +210 -0
- package/src/runtime/mcp-consultations.js +412 -0
- package/src/runtime/mcp-reach.js +157 -0
- package/src/runtime/model-router.js +153 -0
- package/src/runtime/outcome.js +467 -0
- package/src/runtime/reach.js +148 -0
- package/src/runtime/readiness-record.js +150 -0
- package/src/runtime/readiness.js +435 -0
- package/src/runtime/recovery.js +282 -0
- package/src/runtime/research-request.js +231 -0
- package/src/runtime/retrieval-plan.js +578 -0
- package/src/runtime/scaffold/artifacts.js +217 -0
- package/src/runtime/scaffold/fingerprint.js +56 -0
- package/src/runtime/scaffold/generate.js +911 -0
- package/src/runtime/scaffold/languages.js +245 -0
- package/src/runtime/storage/cli.js +64 -0
- package/src/runtime/storage/engine.js +249 -0
- package/src/runtime/storage/registry.js +130 -0
- package/src/runtime/storage/roots.js +63 -0
- package/src/runtime/storage/rules.js +282 -0
- package/src/runtime/storage/upkeep.js +243 -0
- package/src/runtime/storage/work-status.js +326 -0
- package/src/runtime/task-attempts.js +415 -0
- package/src/runtime/task-classifier.js +550 -0
- package/src/runtime/task-scope.js +130 -0
- package/src/runtime/task-state.js +176 -0
- package/src/runtime/trace/indicators.js +235 -0
- package/src/runtime/trace/ledger.js +626 -0
- package/src/runtime/trace/render.js +76 -0
- package/src/runtime/trace/views.js +459 -0
- package/src/runtime/verification/contract-runner.js +341 -0
- package/src/runtime/verification/engine.js +1461 -0
- package/src/runtime/verification/registry.js +130 -0
- package/src/runtime/verification/scope-bound.js +418 -0
- package/src/runtime/verification/test-selection.js +172 -0
- package/src/runtime/workflow-engine.js +588 -0
- package/src/runtime/workflow-orchestrator.js +953 -0
- package/src/runtime/worktree.js +340 -0
- package/src/state/index.js +193 -0
- package/src/state/lockfile.js +136 -0
- package/src/state/run-lock.js +408 -0
- package/types/node-commonjs.d.ts +68 -0
- package/types/runtime.d.ts +91 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
|
|
4
|
+
|
|
5
|
+
// ── Durable per-task state: locked, merged, versioned ────────────────────────────────────────────
|
|
6
|
+
//
|
|
7
|
+
// EvidenceLedger.save and ClaimsManager.save used to overwrite their whole JSON file with
|
|
8
|
+
// writeFileSync — no writer exclusion, no revision check. Two sessions whose read-modify-write
|
|
9
|
+
// operations overlapped both reported success and the final file held only the later writer's
|
|
10
|
+
// items (architecture review R4, reproduced with two ledger instances on one task). Atomic rename
|
|
11
|
+
// alone does not fix that: it prevents a torn file, not a lost update.
|
|
12
|
+
//
|
|
13
|
+
// This module is the one seam both stores persist through:
|
|
14
|
+
//
|
|
15
|
+
// - a per-file lock (`<file>.lock` directory — mkdirSync is atomic on every platform) makes the
|
|
16
|
+
// read-merge-write below a critical section across processes on this machine;
|
|
17
|
+
// - inside the lock, the writer re-reads the file and MERGES what is on disk into its own view
|
|
18
|
+
// before writing, so an overlapping writer's items survive;
|
|
19
|
+
// - the payload carries a monotonically increasing `revision`, so an overlapped-and-merged
|
|
20
|
+
// history is visible in the record rather than silent;
|
|
21
|
+
// - the write itself is tmp-file + rename, so an interrupted writer leaves the previous state
|
|
22
|
+
// intact plus an ignorable `*.tmp` — never a half-written file;
|
|
23
|
+
// - `readTaskState` validates the schema version and refuses a file from a future schema with
|
|
24
|
+
// the version named, instead of guessing at its shape.
|
|
25
|
+
|
|
26
|
+
const path = require('node:path');
|
|
27
|
+
const { isDeepStrictEqual } = require('node:util');
|
|
28
|
+
|
|
29
|
+
/** Three-way merge against the reader's snapshot. Unchanged local records never overwrite a
|
|
30
|
+
* newer disk version; concurrent edits to the same record must be retried after an explicit read. */
|
|
31
|
+
function mergeRecords(local , disk , baseline ) {
|
|
32
|
+
const merged = new Map(disk.map((item ) => [item.id, item]));
|
|
33
|
+
for (const item of local) {
|
|
34
|
+
const previous = baseline.get(item.id);
|
|
35
|
+
const current = merged.get(item.id);
|
|
36
|
+
if (isDeepStrictEqual(item, previous)) continue;
|
|
37
|
+
if (current && !isDeepStrictEqual(current, previous) && !isDeepStrictEqual(current, item)) {
|
|
38
|
+
throw new Error(`Concurrent update to '${item.id}'; reload the task and reapply the change. Nothing was written.`);
|
|
39
|
+
}
|
|
40
|
+
merged.set(item.id, item);
|
|
41
|
+
}
|
|
42
|
+
return [...merged.values()];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const SCHEMA_VERSION = 1;
|
|
46
|
+
const LOCK_STALE_MS = 10_000;
|
|
47
|
+
const LOCK_RETRIES = 250;
|
|
48
|
+
const LOCK_WAIT_MS = 20;
|
|
49
|
+
|
|
50
|
+
/** Synchronous sleep without spinning: Atomics.wait on a throwaway buffer. */
|
|
51
|
+
function sleep(ms ) {
|
|
52
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Makes one non-waiting attempt to take the cross-process lock for a state file. A lock older than
|
|
57
|
+
* LOCK_STALE_MS may be broken; a live holder is never waited on. Returns a release function or null.
|
|
58
|
+
* @param {Object} fsImpl
|
|
59
|
+
* @param {string} file
|
|
60
|
+
* @returns {(() => void)|null}
|
|
61
|
+
*/
|
|
62
|
+
function tryAcquireLock(fsImpl , file ) {
|
|
63
|
+
const lockDir = `${file}.lock`;
|
|
64
|
+
const release = () => { try { fsImpl.rmdirSync(lockDir); } catch { /* released is released */ } };
|
|
65
|
+
try {
|
|
66
|
+
fsImpl.mkdirSync(lockDir);
|
|
67
|
+
return release;
|
|
68
|
+
} catch (error ) {
|
|
69
|
+
if (error.code !== 'EEXIST') throw error;
|
|
70
|
+
}
|
|
71
|
+
let lock ;
|
|
72
|
+
try { lock = fsImpl.statSync(lockDir); } catch (error ) {
|
|
73
|
+
if (error.code !== 'ENOENT') return null;
|
|
74
|
+
try { fsImpl.mkdirSync(lockDir); return release; } catch (retryError ) {
|
|
75
|
+
if (retryError.code === 'EEXIST') return null;
|
|
76
|
+
throw retryError;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (Date.now() - lock.mtimeMs <= LOCK_STALE_MS) return null;
|
|
80
|
+
try { fsImpl.rmdirSync(lockDir); } catch (error ) {
|
|
81
|
+
if (error.code !== 'ENOENT') return null;
|
|
82
|
+
}
|
|
83
|
+
try { fsImpl.mkdirSync(lockDir); return release; } catch (error ) {
|
|
84
|
+
if (error.code === 'EEXIST') return null;
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Takes the cross-process lock for one state file, retrying for the existing bounded wait interval.
|
|
91
|
+
* A stale lock is broken because its holder died mid-write; the rename-based write keeps the file whole.
|
|
92
|
+
* @param {Object} fsImpl
|
|
93
|
+
* @param {string} file
|
|
94
|
+
* @returns {() => void}
|
|
95
|
+
*/
|
|
96
|
+
function acquireLock(fsImpl , file ) {
|
|
97
|
+
const lockDir = `${file}.lock`;
|
|
98
|
+
// The stale rule below is wall-clock, so the wait must be too: on a host where each attempt costs
|
|
99
|
+
// real time, counting attempts alone would outlive a live holder's staleness window and then
|
|
100
|
+
// break (and later release) its lock.
|
|
101
|
+
const deadline = Date.now() + LOCK_RETRIES * LOCK_WAIT_MS;
|
|
102
|
+
for (let attempt = 0; attempt < LOCK_RETRIES && (attempt === 0 || Date.now() < deadline); attempt += 1) {
|
|
103
|
+
try {
|
|
104
|
+
fsImpl.mkdirSync(lockDir);
|
|
105
|
+
return () => { try { fsImpl.rmdirSync(lockDir); } catch { /* released is released */ } };
|
|
106
|
+
} catch {
|
|
107
|
+
try {
|
|
108
|
+
if (Date.now() - fsImpl.statSync(lockDir).mtimeMs > LOCK_STALE_MS) {
|
|
109
|
+
fsImpl.rmdirSync(lockDir);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
} catch { /* the holder released between our mkdir and stat — retry immediately */ }
|
|
113
|
+
sleep(LOCK_WAIT_MS);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
throw new Error(`Could not lock '${file}' after ${(LOCK_RETRIES * LOCK_WAIT_MS) / 1000}s — another writer holds '${file}.lock'. `
|
|
117
|
+
+ 'If no other session is running, the lock is leftover from a crash younger than its stale timeout; retry shortly.');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Reads and validates one task-state file. Returns null when the file does not exist.
|
|
122
|
+
* @param {Object} fsImpl
|
|
123
|
+
* @param {string} file
|
|
124
|
+
* @returns {Object|null} the parsed payload
|
|
125
|
+
*/
|
|
126
|
+
function readTaskState(fsImpl , file ) {
|
|
127
|
+
if (!fsImpl.existsSync(file)) return null;
|
|
128
|
+
let data ;
|
|
129
|
+
try {
|
|
130
|
+
data = JSON.parse(fsImpl.readFileSync(file, 'utf8'));
|
|
131
|
+
} catch (error ) {
|
|
132
|
+
throw new Error(`Failed to load state file '${file}': ${error.message}. The write path is atomic `
|
|
133
|
+
+ '(tmp + rename), so a torn file means outside interference; recover it from version control '
|
|
134
|
+
+ 'or move it aside and re-record the task\'s items.');
|
|
135
|
+
}
|
|
136
|
+
if (data && typeof data === 'object' && data.version !== undefined && data.version !== SCHEMA_VERSION) {
|
|
137
|
+
throw new Error(`State file '${file}' has schema version ${data.version}; this runtime reads version ${SCHEMA_VERSION}. `
|
|
138
|
+
+ 'Refusing to guess at its shape — upgrade the runtime or migrate the file.');
|
|
139
|
+
}
|
|
140
|
+
return data;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* The one durable-update path: lock, re-read, merge, bump revision, atomic write.
|
|
145
|
+
*
|
|
146
|
+
* @param {Object} options
|
|
147
|
+
* @param {Object} options.fsImpl fs implementation (tests substitute one)
|
|
148
|
+
* @param {string} options.file absolute path of the state file
|
|
149
|
+
* @param {(diskPayload: Object|null) => Object} options.build called INSIDE the lock with what is
|
|
150
|
+
* currently on disk; must fold any disk items unknown to the caller into the caller's view and
|
|
151
|
+
* return the complete new payload (without `version`/`revision`, which are stamped here)
|
|
152
|
+
* @param {() => void} [options.prepare] called INSIDE the lock before the file is read, for a store
|
|
153
|
+
* that must inspect or set aside what is there under the same exclusion as the write
|
|
154
|
+
* @returns {string} the file path written
|
|
155
|
+
*/
|
|
156
|
+
function updateTaskState({ fsImpl, file, build, prepare } ) {
|
|
157
|
+
fsImpl.mkdirSync(path.dirname(file), { recursive: true });
|
|
158
|
+
const release = acquireLock(fsImpl, file);
|
|
159
|
+
try {
|
|
160
|
+
if (prepare) prepare();
|
|
161
|
+
const disk = readTaskState(fsImpl, file);
|
|
162
|
+
const payload = {
|
|
163
|
+
...build(disk),
|
|
164
|
+
version: SCHEMA_VERSION,
|
|
165
|
+
revision: (disk && Number.isInteger(disk.revision) ? disk.revision : 0) + 1,
|
|
166
|
+
};
|
|
167
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
168
|
+
fsImpl.writeFileSync(tmp, JSON.stringify(payload, null, 2), 'utf8');
|
|
169
|
+
fsImpl.renameSync(tmp, file);
|
|
170
|
+
return file;
|
|
171
|
+
} finally {
|
|
172
|
+
release();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
module.exports = { acquireLock, tryAcquireLock, updateTaskState, readTaskState, mergeRecords, SCHEMA_VERSION };
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Workflow indicators over the orchestration record (feature 033).
|
|
7
|
+
*
|
|
8
|
+
* Why this is not part of `stats`: a run-ledger record carries exactly `timestamp`, `verb`,
|
|
9
|
+
* `exit_code`, `duration_ms` and `arg_count`. It names no task, no stage and no class, so no amount of
|
|
10
|
+
* extending `stats` can answer "how long from discovery to plan" — the data is not in that file. The
|
|
11
|
+
* orchestrator writes a different one, per task, and that is what this reads.
|
|
12
|
+
*
|
|
13
|
+
* What it deliberately does not do:
|
|
14
|
+
*
|
|
15
|
+
* - No score, grade, percentage or confidence. The shipped guidance forbids expressing evidence that
|
|
16
|
+
* way, and one figure over stages that do unlike work would hide exactly what the report is for.
|
|
17
|
+
* - No git, pull-request or CI indicators. The record holds none of that data, and inventing a proxy
|
|
18
|
+
* would be worse than the gap.
|
|
19
|
+
* - No writes. The correctness of every figure here rests on the record being untouched by reading it.
|
|
20
|
+
*
|
|
21
|
+
* One honest limitation, stated in the output as well as here: the record marks when a stage
|
|
22
|
+
* *completed*, never when it began. A stage's figure is therefore the interval since the previous
|
|
23
|
+
* recorded event, which includes whatever time passed before that stage started. Fixing that needs the
|
|
24
|
+
* orchestrator to record starts, which this feature deliberately does not change.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
const fs = require('node:fs');
|
|
28
|
+
const path = require('node:path');
|
|
29
|
+
const { formatMs, finish } = require('./render.js');
|
|
30
|
+
|
|
31
|
+
const ORCHESTRATION_DIRNAME = 'orchestration';
|
|
32
|
+
|
|
33
|
+
/** Mirrors resolveRunsLocation's shape, for the sibling state directory this reads. */
|
|
34
|
+
function orchestrationDir(root ) {
|
|
35
|
+
return path.join(root, '.doflow', 'state', ORCHESTRATION_DIRNAME);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function parseAt(value ) {
|
|
39
|
+
const ms = Date.parse(value);
|
|
40
|
+
return Number.isNaN(ms) ? null : ms;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Every record in a directory, plus the names of the ones that did not parse. A file that cannot be
|
|
44
|
+
* read is counted and named rather than skipped: a report that quietly drops what it could not read
|
|
45
|
+
* overstates its own coverage, which is the defect `malformedLines` already guards against for runs. */
|
|
46
|
+
function readRecords(dir , fsImpl = fs) {
|
|
47
|
+
if (!fsImpl.existsSync(dir)) return { records: [], unreadable: [], exists: false };
|
|
48
|
+
const records = [];
|
|
49
|
+
const unreadable = [];
|
|
50
|
+
for (const name of fsImpl.readdirSync(dir).sort()) {
|
|
51
|
+
if (!name.endsWith('.json')) continue;
|
|
52
|
+
try {
|
|
53
|
+
const parsed = JSON.parse(fsImpl.readFileSync(path.join(dir, name), 'utf8'));
|
|
54
|
+
if (!parsed || typeof parsed !== 'object' || !parsed.taskId) {
|
|
55
|
+
unreadable.push({ file: name, reason: 'not an orchestration record' });
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
records.push(parsed);
|
|
59
|
+
} catch (err ) {
|
|
60
|
+
unreadable.push({ file: name, reason: err.message });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return { records, unreadable, exists: true };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** A stage row is "not work done" when the orchestrator backfilled it or imported it rather than
|
|
67
|
+
* executing it. Its duration measures when the record was written, so it is listed but never averaged. */
|
|
68
|
+
function isSynthetic(entry ) {
|
|
69
|
+
return Boolean(entry.backfilled) || (entry.executionStatus && entry.executionStatus !== 'completed');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function median(values ) {
|
|
73
|
+
if (!values.length) return null;
|
|
74
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
75
|
+
const mid = Math.floor(sorted.length / 2);
|
|
76
|
+
return sorted.length % 2 ? sorted[mid] : Math.round((sorted[mid - 1] + sorted[mid]) / 2);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** One run's rows, walking history in recorded order with a cursor so no interval is counted twice. */
|
|
80
|
+
function runRows(record ) {
|
|
81
|
+
const history = Array.isArray(record.history) ? record.history : [];
|
|
82
|
+
let cursor = parseAt(record.startedAt);
|
|
83
|
+
const stages = [];
|
|
84
|
+
const gates = [];
|
|
85
|
+
let reruns = 0;
|
|
86
|
+
|
|
87
|
+
for (const entry of history) {
|
|
88
|
+
const at = parseAt(entry.at);
|
|
89
|
+
// A negative elapsed can only mean out-of-order history. It is reported as the negative number it
|
|
90
|
+
// is rather than clamped, because hiding it would hide a corrupt record.
|
|
91
|
+
const elapsedMs = at !== null && cursor !== null ? at - cursor : null;
|
|
92
|
+
if (entry.action === 'complete-stage') {
|
|
93
|
+
stages.push({
|
|
94
|
+
stage: entry.node ?? null,
|
|
95
|
+
elapsedMs,
|
|
96
|
+
outcome: entry.outcome ?? null,
|
|
97
|
+
synthetic: isSynthetic(entry),
|
|
98
|
+
});
|
|
99
|
+
cursor = at ?? cursor;
|
|
100
|
+
} else if (entry.action === 'decide-gate') {
|
|
101
|
+
gates.push({
|
|
102
|
+
gate: entry.node ?? null,
|
|
103
|
+
elapsedMs,
|
|
104
|
+
decision: entry.detail ?? null,
|
|
105
|
+
forced: Boolean(entry.forced),
|
|
106
|
+
});
|
|
107
|
+
cursor = at ?? cursor;
|
|
108
|
+
} else if (entry.action === 'annotate') {
|
|
109
|
+
reruns += 1;
|
|
110
|
+
cursor = at ?? cursor;
|
|
111
|
+
} else {
|
|
112
|
+
cursor = at ?? cursor;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const started = parseAt(record.startedAt);
|
|
117
|
+
const updated = parseAt(record.updatedAt);
|
|
118
|
+
return {
|
|
119
|
+
taskId: record.taskId,
|
|
120
|
+
taskClass: record.taskClass ?? null,
|
|
121
|
+
state: record.state ?? null,
|
|
122
|
+
startedAt: record.startedAt ?? null,
|
|
123
|
+
updatedAt: record.updatedAt ?? null,
|
|
124
|
+
totalMs: started !== null && updated !== null ? updated - started : null,
|
|
125
|
+
reruns,
|
|
126
|
+
stages,
|
|
127
|
+
gates,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Median elapsed per stage id across runs of one class. Synthetic rows are excluded here and still
|
|
132
|
+
* listed in their run: averaging the moment a file was written into the figure most likely to be
|
|
133
|
+
* quoted would corrupt it. */
|
|
134
|
+
function aggregateStages(runs ) {
|
|
135
|
+
const perStage = new Map ();
|
|
136
|
+
for (const run of runs) {
|
|
137
|
+
for (const row of run.stages) {
|
|
138
|
+
if (row.synthetic || row.elapsedMs === null || !row.stage) continue;
|
|
139
|
+
if (!perStage.has(row.stage)) perStage.set(row.stage, []);
|
|
140
|
+
perStage.get(row.stage) .push(row.elapsedMs);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return [...perStage.entries()].map(([stage, values]) => ({
|
|
144
|
+
stage, runs: values.length, medianMs: median(values),
|
|
145
|
+
}));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Pure. Reads nothing and writes nothing; takes already-parsed records. */
|
|
149
|
+
function buildIndicators({ records = [], unreadable = [], exists = true } = {}) {
|
|
150
|
+
const runs = (records ).map(runRows);
|
|
151
|
+
const byClass = new Map ();
|
|
152
|
+
|
|
153
|
+
for (const run of runs) {
|
|
154
|
+
const key = run.taskClass || '(unclassified)';
|
|
155
|
+
if (!byClass.has(key)) byClass.set(key, { taskClass: key, runs: [], stageAggregate: [] });
|
|
156
|
+
byClass.get(key) .runs.push(run);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
for (const group of byClass.values()) group.stageAggregate = aggregateStages(group.runs);
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
view: 'indicators',
|
|
163
|
+
source: `.doflow/state/${ORCHESTRATION_DIRNAME}`,
|
|
164
|
+
exists,
|
|
165
|
+
runCount: runs.length,
|
|
166
|
+
unreadable,
|
|
167
|
+
byClass: [...byClass.values()],
|
|
168
|
+
// Printed as well as returned: a limitation a reader has to look up is one they will not read.
|
|
169
|
+
limits: [
|
|
170
|
+
'A stage figure is the interval since the previous recorded event, not pure execution time: the record marks completions, never starts.',
|
|
171
|
+
'A gate figure is a human deciding. It is charged to no stage and never aggregated.',
|
|
172
|
+
'Backfilled and imported stages are listed per run but excluded from every aggregate.',
|
|
173
|
+
'No git, pull-request or continuous-integration indicator is computable here: the orchestration record holds none of that data.',
|
|
174
|
+
],
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** One class block: its runs, each run's stage and gate rows, then the class median. */
|
|
179
|
+
function printClass(group ) {
|
|
180
|
+
console.log('─'.repeat(78));
|
|
181
|
+
console.log(`CLASS: ${group.taskClass} (${group.runs.length} run(s))`);
|
|
182
|
+
for (const run of group.runs) {
|
|
183
|
+
console.log(` ${run.taskId} state=${run.state} total ${formatMs(run.totalMs)} reruns ${run.reruns}`);
|
|
184
|
+
for (const row of run.stages) {
|
|
185
|
+
console.log(` stage ${String(row.stage).padEnd(24)} ${formatMs(row.elapsedMs).padStart(9)} outcome=${row.outcome ?? 'not recorded'}${row.synthetic ? ' [backfilled/imported]' : ''}`);
|
|
186
|
+
}
|
|
187
|
+
for (const row of run.gates) {
|
|
188
|
+
console.log(` wait ${String(row.gate).padEnd(24)} ${formatMs(row.elapsedMs).padStart(9)} decision=${row.decision ?? 'not recorded'}${row.forced ? ' [forced]' : ''}`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (!group.stageAggregate.length) return;
|
|
192
|
+
console.log(` median per stage (${group.taskClass}, backfilled excluded):`);
|
|
193
|
+
for (const agg of group.stageAggregate) {
|
|
194
|
+
console.log(` ${agg.stage.padEnd(24)} ${formatMs(agg.medianMs).padStart(9)} over ${agg.runs} run(s)`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function handleIndicatorsCommand({ json = false, projectRoot = process.cwd() } = {}) {
|
|
199
|
+
const dir = orchestrationDir(projectRoot);
|
|
200
|
+
const read = readRecords(dir);
|
|
201
|
+
const view = buildIndicators(read);
|
|
202
|
+
|
|
203
|
+
if (json) {
|
|
204
|
+
console.log(JSON.stringify(view, null, 2));
|
|
205
|
+
return finish(0);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
console.log('\nDoFlow Workflow Indicators');
|
|
209
|
+
console.log('═'.repeat(78));
|
|
210
|
+
if (!view.runCount) {
|
|
211
|
+
console.log(read.exists
|
|
212
|
+
? `No readable orchestration record under ${view.source}.`
|
|
213
|
+
: `No orchestration record has been written yet at ${view.source}.`);
|
|
214
|
+
if (view.unreadable.length) {
|
|
215
|
+
for (const u of view.unreadable) console.log(` unreadable: ${u.file} — ${u.reason}`);
|
|
216
|
+
}
|
|
217
|
+
console.log('═'.repeat(78) + '\n');
|
|
218
|
+
return finish(0);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
console.log(`Runs: ${view.runCount} across ${view.byClass.length} task class(es)`);
|
|
222
|
+
if (view.unreadable.length) {
|
|
223
|
+
console.log(`Unreadable: ${view.unreadable.length} record(s) — ${view.unreadable.map((u ) => u.file).join(', ')}`);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
for (const group of view.byClass) printClass(group);
|
|
227
|
+
|
|
228
|
+
console.log('─'.repeat(78));
|
|
229
|
+
console.log('What this does not measure:');
|
|
230
|
+
for (const limit of view.limits) console.log(` · ${limit}`);
|
|
231
|
+
console.log('═'.repeat(78) + '\n');
|
|
232
|
+
return finish(0);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
module.exports = { orchestrationDir, readRecords, buildIndicators, handleIndicatorsCommand };
|