@khoavu882/doflow 2.0.0-beta.4 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -11
- package/bin/doflow.js +21 -11
- package/core/.antigravity-plugin/plugin.json +1 -3
- package/core/.claude-plugin/plugin.json +1 -1
- package/core/.codex-plugin/plugin.json +1 -1
- package/core/.plugin/plugin.json +1 -1
- package/core/harnesses/claude/hooks/lib.sh +43 -24
- package/core/harnesses/claude/hooks/mcp-tool-guard.sh +7 -17
- package/core/harnesses/claude/hooks/permission-audit.sh +3 -0
- package/core/harnesses/claude/hooks/post-compact.sh +35 -32
- package/core/harnesses/claude/hooks/post-edit-lint.sh +2 -0
- package/core/harnesses/claude/hooks/post-shell-observe.sh +48 -0
- package/core/harnesses/claude/hooks/post-tool-failure.sh +3 -0
- package/core/harnesses/claude/hooks/pre-bash-guard.sh +7 -17
- package/core/harnesses/claude/hooks/pre-compact.sh +23 -27
- package/core/harnesses/claude/hooks/pre-implement-gate.sh +7 -18
- package/core/harnesses/claude/hooks/session-end.sh +23 -10
- package/core/harnesses/claude/hooks/session-start.sh +5 -3
- package/core/harnesses/claude/hooks/skill-config-audit.sh +3 -0
- package/core/harnesses/claude/hooks/stop-check.sh +5 -3
- package/core/harnesses/claude/hooks/subagent-audit.sh +5 -5
- package/core/harnesses/claude/hooks/user-prompt-submit.sh +5 -3
- package/core/harnesses/claude/settings/settings.json +18 -0
- package/core/harnesses/claude/statusline/doflow-statusline.sh +1 -0
- package/core/harnesses/codex/agents/core-implementer.toml +1 -0
- package/core/harnesses/codex/agents/delivery-lead.toml +10 -0
- package/core/harnesses/codex/agents/quality-guardian.toml +1 -1
- package/core/harnesses/codex/agents/research-writer.toml +1 -1
- package/core/harnesses/codex/agents/reviewer.toml +10 -0
- package/core/harnesses/codex/agents/spec-analyst.toml +1 -1
- package/core/harnesses/codex/agents/system-architect.toml +1 -1
- package/core/harnesses/codex/hooks/lib.sh +44 -25
- package/core/harnesses/codex/hooks/mcp-tool-guard.sh +7 -15
- package/core/harnesses/codex/hooks/post-edit-lint.impl.sh +3 -1
- package/core/harnesses/codex/hooks/pre-bash-guard.sh +7 -15
- package/core/harnesses/codex/hooks/pre-compact.impl.sh +29 -26
- package/core/harnesses/codex/hooks/pre-implement-gate.sh +7 -16
- package/core/harnesses/codex/hooks/session-end.impl.sh +23 -10
- package/core/harnesses/codex/hooks/session-start.sh +5 -3
- package/core/harnesses/codex/hooks/stop-check.sh +6 -5
- package/core/harnesses/codex/hooks/subagent-audit.sh +18 -6
- package/core/harnesses/codex/hooks/user-prompt-submit.sh +5 -4
- package/core/harnesses/copilot/hooks/doflow-lib.sh +93 -0
- package/core/harnesses/copilot/hooks/doflow-mcp-tool-guard.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-pre-bash-guard.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-pre-implement-gate.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-session-start.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-stop-check.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-subagent-start.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-subagent-stop.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow.json +57 -0
- package/core/harnesses/kiro/agents/doflow.json +1 -1
- package/core/harnesses/kiro/hooks/lib.sh +26 -6
- package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +21 -5
- package/core/harnesses/kiro/hooks/pre-bash-guard.sh +21 -5
- package/core/harnesses/kiro/hooks/pre-implement-gate.sh +21 -5
- package/core/harnesses/kiro/hooks/session-start.sh +12 -5
- package/core/harnesses/kiro/hooks/stop-check.sh +22 -30
- package/core/harnesses/pi/extensions/doflow-guard.js +167 -0
- package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +27 -12
- package/core/harnesses/shared/hooks/adapters/antigravity.js +14 -57
- package/core/harnesses/shared/hooks/bridge/bridge.sh +277 -0
- package/core/harnesses/shared/hooks/bridge/envelope.jq +129 -0
- package/core/harnesses/shared/hooks/bridge/observe-model.sh +75 -0
- package/core/harnesses/shared/hooks/bridge/respond.sh +70 -0
- package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +28 -14
- package/core/harnesses/shared/hooks/policies/capture-failure.sh +51 -16
- package/core/harnesses/shared/hooks/policies/compact-text.sh +46 -0
- package/core/harnesses/shared/hooks/policies/contract.sh +78 -0
- package/core/harnesses/shared/hooks/policies/deny-json.sh +35 -9
- package/core/harnesses/shared/hooks/policies/emission-rules.sh +37 -0
- package/core/harnesses/shared/hooks/policies/emission.jq +209 -0
- package/core/harnesses/shared/hooks/policies/emission.sh +383 -0
- package/core/harnesses/shared/hooks/policies/identity.sh +177 -0
- package/core/harnesses/shared/hooks/policies/lib.sh +65 -40
- package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +48 -35
- package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +284 -33
- package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +180 -110
- package/core/harnesses/shared/hooks/policies/session-context.sh +66 -7
- package/core/harnesses/shared/hooks/policies/stop-check.sh +61 -23
- package/core/harnesses/shared/hooks/policies/subagent-audit.sh +39 -6
- package/core/harnesses/shared/hooks/policies/summary-store.sh +347 -0
- package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +97 -159
- package/core/harnesses/shared/hooks/stream-hook-runner.js +114 -187
- package/core/harnesses/shared/locator/doflow-run +2 -3
- package/core/registry/advice.json +3 -3
- package/core/registry/assets.json +72 -80
- package/core/registry/capabilities.json +12 -0
- package/core/registry/contracts.json +60 -33
- package/core/registry/contribution-points.json +32 -0
- package/core/registry/gates.json +32 -0
- package/core/registry/guidance-caps.json +40 -0
- package/core/registry/harnesses.json +1542 -277
- package/core/registry/lifecycle.json +2837 -37
- package/core/registry/mcp.json +5 -1
- package/core/registry/models.json +7 -1
- package/core/registry/package-templates/README.md +19 -0
- package/core/registry/package-templates/agent/files/agent.md +9 -0
- package/core/registry/package-templates/agent/template.json +4 -0
- package/core/registry/package-templates/capability-provider/files/provider.json +9 -0
- package/core/registry/package-templates/capability-provider/template.json +11 -0
- package/core/registry/package-templates/debate-preset/files/preset.json +26 -0
- package/core/registry/package-templates/debate-preset/template.json +4 -0
- package/core/registry/package-templates/review-preset/files/preset.json +30 -0
- package/core/registry/package-templates/review-preset/template.json +4 -0
- package/core/registry/package-templates/skill/files/SKILL.md +8 -0
- package/core/registry/package-templates/skill/template.json +4 -0
- package/core/registry/package-templates/worker/files/worker.json +92 -0
- package/core/registry/package-templates/worker/template.json +11 -0
- package/core/registry/packages/doflow/doflow-package.json +15 -0
- package/core/registry/packages/doflow/presets/debate-3.json +26 -0
- package/core/registry/protocols.json +61 -0
- package/core/registry/routes.json +17 -0
- package/core/registry/rules.json +431 -0
- package/core/registry/settings.json +276 -0
- package/core/registry/storage.json +701 -30
- package/core/registry/telemetry.json +236 -0
- package/core/registry/transcripts.json +142 -0
- package/core/registry/verification.json +76 -1
- package/core/registry/workflows.json +55 -0
- package/core/shared/agent-specs/core-implementer.md +3 -0
- package/core/shared/agent-specs/delivery-lead.md +68 -0
- package/core/shared/agent-specs/quality-guardian.md +5 -1
- package/core/shared/agent-specs/research-writer.md +10 -3
- package/core/shared/agent-specs/reviewer.md +53 -0
- package/core/shared/agent-specs/spec-analyst.md +5 -6
- package/core/shared/agent-specs/system-architect.md +5 -1
- package/core/shared/guidance/DOFLOW_CORE.md +1 -5
- package/core/shared/guidance/modes/MODE_DeepResearch.md +1 -1
- package/core/shared/guidance/modes/MODE_Orchestration.md +4 -3
- package/core/shared/guidance/modes/MODE_Task_Management.md +14 -14
- package/core/shared/guidance/pointers/codex.md +1 -4
- package/core/shared/guidance/references/ARTIFACT_FORMAT.md +30 -16
- package/core/shared/guidance/references/ARTIFACT_VOICE.md +3 -2
- package/core/shared/guidance/references/CONSTITUTION_BASE.md +17 -15
- package/core/shared/guidance/references/DIAGRAM_GUIDE.md +1 -1
- package/core/shared/guidance/references/DOFLOW_CHAIN.md +17 -19
- package/core/shared/guidance/references/EVIDENCE_LEDGER.md +7 -8
- package/core/shared/guidance/references/MODEL_SELECTION.md +5 -3
- package/core/shared/guidance/references/ORCHESTRATION.md +1 -2
- package/core/shared/guidance/rules/RULES.md +45 -0
- package/core/shared/scripts/doflow/bash/do-args.sh +37 -0
- package/core/shared/scripts/doflow/bash/do-exec-paths.sh +4 -0
- package/core/shared/scripts/doflow/bash/do-git-state.sh +15 -4
- package/core/shared/scripts/doflow/bash/do-parallel-check.sh +4 -0
- package/core/shared/scripts/doflow/bash/do-paths.sh +40 -2
- package/core/shared/scripts/doflow/bash/do-prereqs.sh +2 -1
- package/core/shared/scripts/doflow/bash/do-review-package.sh +4 -0
- package/core/shared/scripts/doflow/bash/do-task-brief.sh +58 -16
- package/core/shared/scripts/doflow/bash/render-audit.sh +26 -2
- package/core/shared/scripts/doflow/bash/render-diagrams.sh +1 -0
- package/core/shared/scripts/doflow/bash/render-puml.sh +2 -1
- package/core/shared/scripts/doflow/bash/validate-artifacts.sh +48 -4
- package/core/shared/scripts/doflow/bin/doflow-run +154 -35
- package/core/shared/skills/do/SKILL.md +12 -10
- package/core/shared/skills/do/references/advice_calls.md +26 -0
- package/core/shared/skills/do/references/pm_routing.md +1 -2
- package/core/shared/skills/do/references/program_mode.md +31 -0
- package/core/shared/skills/do-brainstorm/SKILL.md +20 -74
- package/core/shared/skills/do-brainstorm/references/handoff_branches.md +35 -0
- package/core/shared/skills/do-brainstorm/references/intent_seed.md +21 -0
- package/core/shared/skills/do-brainstorm/references/round_log.md +17 -0
- package/core/shared/skills/do-code-review/SKILL.md +41 -144
- package/core/shared/skills/do-code-review/content-types/markdown.md +24 -2
- package/core/shared/skills/do-code-review/references/tools.md +90 -0
- package/core/shared/skills/do-code-review/scripts/doc_quality_checker.py +549 -7
- package/core/shared/skills/do-design/SKILL.md +24 -23
- package/core/shared/skills/do-diagnose/SKILL.md +14 -4
- package/core/shared/skills/do-document/SKILL.md +11 -1
- package/core/shared/skills/do-document/references/deep_research.md +1 -2
- package/core/shared/skills/do-document/references/embedded-research.md +18 -6
- package/core/shared/skills/do-document/references/implementation-flow.md +5 -6
- package/core/shared/skills/do-execute-plan/SKILL.md +17 -59
- package/core/shared/skills/do-execute-plan/references/handoff_record.md +7 -2
- package/core/shared/skills/do-execute-plan/references/parallel_dispatch.md +13 -0
- package/core/shared/skills/do-execute-plan/references/readiness_gate.md +28 -1
- package/core/shared/skills/do-execute-plan/references/run_position.md +13 -1
- package/core/shared/skills/do-execute-plan/references/scaffold.md +17 -9
- package/core/shared/skills/do-execute-plan/references/state_checkpoint.md +11 -0
- package/core/shared/skills/do-execute-plan/references/task_attempts.md +78 -24
- package/core/shared/skills/do-flow/SKILL.md +8 -9
- package/core/shared/skills/do-flow/references/task_classes.md +2 -2
- package/core/shared/skills/do-git/SKILL.md +4 -0
- package/core/shared/skills/do-implement/SKILL.md +7 -9
- package/core/shared/skills/do-plan/SKILL.md +29 -19
- package/core/shared/skills/do-test/SKILL.md +7 -4
- package/core/shared/templates/doflow/constitution-template.md +9 -3
- package/core/shared/templates/doflow/data-model-template.md +1 -1
- package/core/shared/templates/doflow/design-template.md +113 -16
- package/core/shared/templates/doflow/plan-template.md +11 -10
- package/core/shared/templates/doflow/question-log-template.md +14 -2
- package/core/shared/templates/doflow/requirement-template.md +1 -1
- package/core/worker-evidence/antigravity/checks.json +14 -0
- package/core/worker-evidence/codex/2026-10-18-cli-check.json +107 -0
- package/core/worker-evidence/codex/2026-10-18-live-run-edit.json +73 -0
- package/core/worker-evidence/codex/2026-10-18-live-run-read-only.json +62 -0
- package/core/worker-evidence/codex/2026-10-24-cli-check.json +126 -0
- package/core/worker-evidence/codex/checks.json +38 -0
- package/package.json +10 -6
- package/src/adapters/antigravity/index.js +226 -113
- package/src/adapters/claude/index.js +39 -5
- package/src/adapters/codex/agents.js +25 -15
- package/src/adapters/codex/hooks.js +40 -5
- package/src/adapters/codex/index.js +100 -22
- package/src/adapters/codex/native-projection.js +6 -2
- package/src/adapters/copilot/index.js +58 -23
- package/src/adapters/copy-tree.js +294 -48
- package/src/adapters/guidance-manifest.js +71 -0
- package/src/adapters/guidance-pointer.js +19 -0
- package/src/adapters/hook-commands.js +6 -10
- package/src/adapters/index.js +1 -0
- package/src/adapters/kiro/index.js +38 -13
- package/src/adapters/opencode/index.js +8 -8
- package/src/adapters/pi/hooks.js +120 -0
- package/src/adapters/pi/index.js +48 -16
- package/src/adapters/resolve-source.js +26 -2
- package/src/cli/commands/install.js +3 -2
- package/src/cli/commands/reconcile.js +53 -9
- package/src/cli/commands/remove.js +2 -2
- package/src/cli/commands/rollback.js +6 -3
- package/src/cli/commands/self-update.js +18 -9
- package/src/cli/commands/status.js +21 -2
- package/src/cli/commands/ui.js +99 -0
- package/src/cli/commands/update.js +3 -2
- package/src/cli/index.js +338 -33
- package/src/cli/runtime-commands.js +243 -52
- package/src/cli/shared.js +0 -2
- package/src/helper/git.js +1 -1
- package/src/helper/guardrails.js +1 -1
- package/src/helper/marker-merge.js +31 -13
- package/src/helper/package-ids.js +37 -0
- package/src/helper/semver.js +94 -0
- package/src/install/backup.js +105 -15
- package/src/install/context.js +1 -1
- package/src/install/targets.js +31 -16
- package/src/lifecycle/coverage.js +74 -0
- package/src/lifecycle/hook-step.js +61 -0
- package/src/lifecycle/hook-tier.js +160 -0
- package/src/lifecycle/index.js +434 -86
- package/src/lifecycle/retired-harnesses.js +318 -0
- package/src/lifecycle/view.js +35 -15
- package/src/registry/index.js +686 -32
- package/src/registry/worker-ids.js +45 -0
- package/src/release/build-package.js +0 -3
- package/src/release/docs-blocks.js +492 -0
- package/src/release/preflight.js +425 -0
- package/src/release/rehearse.js +401 -0
- package/src/release/release-workflow.js +733 -0
- package/src/release/sync-plugin-versions.js +43 -7
- package/src/runtime/advice/bench-gate.js +25 -0
- package/src/runtime/advice/cli.js +59 -16
- package/src/runtime/advice/client.js +32 -9
- package/src/runtime/advice/config.js +98 -133
- package/src/runtime/advice/doctor.js +1 -1
- package/src/runtime/advice/elevation.js +97 -0
- package/src/runtime/advice/endpoint-guard.js +9 -3
- package/src/runtime/advice/mode.js +37 -0
- package/src/runtime/advice/record.js +18 -5
- package/src/runtime/advice/registry.js +4 -1
- package/src/runtime/advice/shadow-config.js +229 -0
- package/src/runtime/advice/shadow-dispatcher.js +276 -0
- package/src/runtime/brief-rules.js +81 -0
- package/src/runtime/brief-tools.js +111 -34
- package/src/runtime/candidate.js +514 -0
- package/src/runtime/capabilities/executor.js +450 -0
- package/src/runtime/capabilities/loopback.js +41 -0
- package/src/runtime/capabilities/sanitizer.js +88 -0
- package/src/runtime/capability-router.js +135 -3
- package/src/runtime/checkouts.js +73 -11
- package/src/runtime/claims.js +27 -12
- package/src/runtime/cli-result.js +11 -4
- package/src/runtime/cli.js +221 -277
- package/src/runtime/config-home.js +42 -0
- package/src/runtime/context/arbitrate.js +767 -0
- package/src/runtime/context/budget.js +203 -0
- package/src/runtime/context/compose.js +1367 -0
- package/src/runtime/context/delivery-store.js +100 -0
- package/src/runtime/context/emission-rules.js +118 -0
- package/src/runtime/context/identity.js +227 -0
- package/src/runtime/context/mandatory.js +594 -0
- package/src/runtime/context/pointer.js +142 -0
- package/src/runtime/context/primary-command.js +117 -0
- package/src/runtime/context/primary.js +344 -0
- package/src/runtime/context/summary-store.js +655 -0
- package/src/runtime/context-pack.js +1106 -245
- package/src/runtime/decision-register.js +13 -4
- package/src/runtime/dispatch/admission.js +392 -0
- package/src/runtime/dispatch/attempt-end.js +274 -0
- package/src/runtime/dispatch/command.js +628 -0
- package/src/runtime/dispatch/controls.js +904 -0
- package/src/runtime/dispatch/envelope.js +276 -0
- package/src/runtime/dispatch/grants.js +310 -0
- package/src/runtime/dispatch/integrate.js +617 -0
- package/src/runtime/dispatch/loop.js +1835 -0
- package/src/runtime/dispatch/store.js +352 -0
- package/src/runtime/dispatch/verify-run.js +201 -0
- package/src/runtime/evidence-fields.js +39 -0
- package/src/runtime/evidence-ledger.js +41 -7
- package/src/runtime/failure/capture.js +3 -1
- package/src/runtime/failure/cli.js +25 -9
- package/src/runtime/failure/home.js +57 -30
- package/src/runtime/failure/store.js +7 -2
- package/src/runtime/feature-pointer.js +174 -0
- package/src/runtime/feature-resolve.js +13 -2
- package/src/runtime/fleet/command.js +77 -0
- package/src/runtime/fleet/projects.js +178 -0
- package/src/runtime/fleet/tree.js +383 -0
- package/src/runtime/freshness.js +77 -10
- package/src/runtime/git-memo.js +46 -0
- package/src/runtime/guidance/ablation.js +427 -0
- package/src/runtime/health.js +565 -17
- package/src/runtime/history-compactor.js +13 -3
- package/src/runtime/implementation-gate.js +277 -22
- package/src/runtime/inventory/coverage.js +1 -1
- package/src/runtime/inventory/index.js +1 -1
- package/src/runtime/inventory/precedence.js +2 -2
- package/src/runtime/inventory/siblings.js +6 -4
- package/src/runtime/knowledge/code-corpus.js +277 -0
- package/src/runtime/knowledge/code-lexical.js +130 -0
- package/src/runtime/knowledge/code-retrieval.js +307 -0
- package/src/runtime/knowledge/code-validation.js +106 -0
- package/src/runtime/knowledge/gp3-gate.js +74 -0
- package/src/runtime/knowledge/graph-adapter.js +288 -0
- package/src/runtime/knowledge/graph-fallback.js +394 -0
- package/src/runtime/knowledge/hybrid.js +136 -8
- package/src/runtime/knowledge/retrieval.js +34 -1
- package/src/runtime/knowledge/rtk-compress.js +77 -0
- package/src/runtime/knowledge/semble-bridge.py +106 -0
- package/src/runtime/knowledge/semble-lifecycle.js +114 -0
- package/src/runtime/knowledge/semble-provider.js +365 -0
- package/src/runtime/lifecycle/cli.js +16 -6
- package/src/runtime/lifecycle/event-store.js +8 -6
- package/src/runtime/lifecycle/followup.js +6 -1
- package/src/runtime/lifecycle/overview.js +24 -1
- package/src/runtime/lifecycle/report-store.js +8 -10
- package/src/runtime/lifecycle/retention.js +95 -31
- package/src/runtime/lifecycle/root.js +9 -1
- package/src/runtime/lifecycle/status.js +34 -17
- package/src/runtime/lifecycle/store-upkeep.js +56 -7
- package/src/runtime/mask.js +134 -7
- package/src/runtime/mcp-consultations.js +40 -6
- package/src/runtime/model-router.js +79 -7
- package/src/runtime/models/catalogue.js +191 -0
- package/src/runtime/models/command.js +153 -0
- package/src/runtime/off-words.js +12 -0
- package/src/runtime/outcome.js +3 -3
- package/src/runtime/packages/command.js +1009 -0
- package/src/runtime/packages/contributions.js +116 -0
- package/src/runtime/packages/digest.js +132 -0
- package/src/runtime/packages/fetch.js +182 -0
- package/src/runtime/packages/grants.js +192 -0
- package/src/runtime/packages/index-file.js +134 -0
- package/src/runtime/packages/instruction.js +90 -0
- package/src/runtime/packages/manifest.js +331 -0
- package/src/runtime/packages/points.js +477 -0
- package/src/runtime/packages/programs.js +216 -0
- package/src/runtime/packages/scaffold.js +277 -0
- package/src/runtime/packages/source.js +90 -0
- package/src/runtime/packages/state.js +164 -0
- package/src/runtime/packages/store.js +449 -0
- package/src/runtime/packages/tar.js +106 -0
- package/src/runtime/packages/view.js +406 -0
- package/src/runtime/plan-check.js +59 -0
- package/src/runtime/plan-content.js +324 -0
- package/src/runtime/policy-init.js +118 -0
- package/src/runtime/profiles/doctor.js +96 -0
- package/src/runtime/profiles/resolve.js +244 -0
- package/src/runtime/profiles/rollback.js +254 -0
- package/src/runtime/profiles/sampler.js +41 -0
- package/src/runtime/readiness-record.js +16 -11
- package/src/runtime/readiness.js +127 -16
- package/src/runtime/research-request.js +50 -25
- package/src/runtime/retrieval-plan.js +402 -73
- package/src/runtime/retrieval-record.js +293 -0
- package/src/runtime/review/command.js +246 -0
- package/src/runtime/review/dominance.js +68 -0
- package/src/runtime/review/findings.js +434 -0
- package/src/runtime/review/identity.js +255 -0
- package/src/runtime/review/protocol-command.js +109 -0
- package/src/runtime/review/protocols.js +405 -0
- package/src/runtime/review/resolve-protocol.js +302 -0
- package/src/runtime/review/start.js +530 -0
- package/src/runtime/roadmap/facts.js +479 -0
- package/src/runtime/roadmap/gates.js +270 -0
- package/src/runtime/roadmap/loop.js +1636 -0
- package/src/runtime/roadmap/merge.js +381 -0
- package/src/runtime/roadmap/rows.js +1113 -0
- package/src/runtime/roadmap/seed.js +951 -0
- package/src/runtime/rules-report.js +336 -0
- package/src/runtime/scaffold/artifacts.js +63 -9
- package/src/runtime/scaffold/generate.js +21 -9
- package/src/runtime/settings/autopilot.js +106 -0
- package/src/runtime/settings/cli.js +280 -0
- package/src/runtime/settings/config-write.js +314 -0
- package/src/runtime/settings/files.js +271 -0
- package/src/runtime/settings/legacy.js +154 -0
- package/src/runtime/settings/lexical.js +56 -0
- package/src/runtime/settings/prepared-run.js +269 -0
- package/src/runtime/settings/project-layers.js +118 -0
- package/src/runtime/settings/refs.js +288 -0
- package/src/runtime/settings/registry.js +553 -0
- package/src/runtime/settings/resolve.js +713 -0
- package/src/runtime/settings/shared.js +37 -0
- package/src/runtime/settings/worker-billing.js +107 -0
- package/src/runtime/settings/write.js +269 -0
- package/src/runtime/slot-command.js +78 -0
- package/src/runtime/slots.js +671 -0
- package/src/runtime/source-version.js +290 -0
- package/src/runtime/state-root.js +217 -0
- package/src/runtime/storage/cli.js +12 -5
- package/src/runtime/storage/engine.js +9 -2
- package/src/runtime/storage/registry.js +14 -1
- package/src/runtime/storage/roots.js +66 -16
- package/src/runtime/storage/rules.js +82 -10
- package/src/runtime/storage/upkeep.js +10 -2
- package/src/runtime/storage/work-status.js +245 -26
- package/src/runtime/task-attempts.js +589 -69
- package/src/runtime/task-classifier.js +47 -11
- package/src/runtime/task-graph.js +373 -0
- package/src/runtime/task-run.js +778 -0
- package/src/runtime/task-scope.js +202 -56
- package/src/runtime/task-sources.js +147 -0
- package/src/runtime/task-state.js +440 -65
- package/src/runtime/team/admission.js +301 -0
- package/src/runtime/team/envelope.js +324 -0
- package/src/runtime/team/review-guard.js +156 -0
- package/src/runtime/trace/analyze.js +427 -0
- package/src/runtime/trace/baseline.js +770 -0
- package/src/runtime/trace/ledger.js +63 -140
- package/src/runtime/trace/otlp-export.js +568 -0
- package/src/runtime/trace/otlp-map.js +481 -0
- package/src/runtime/trace/spans.js +745 -0
- package/src/runtime/trace/telemetry.js +225 -0
- package/src/runtime/trace/views.js +1 -0
- package/src/runtime/transcripts/command.js +98 -0
- package/src/runtime/transcripts/cursor.js +326 -0
- package/src/runtime/transcripts/read.js +126 -0
- package/src/runtime/transcripts/readers/claude-jsonl.js +74 -0
- package/src/runtime/transcripts/readers/codex-rollout.js +70 -0
- package/src/runtime/transcripts/readers/copilot-events.js +90 -0
- package/src/runtime/transcripts/readers/kiro-jsonl.js +77 -0
- package/src/runtime/transcripts/readers/pi-jsonl.js +59 -0
- package/src/runtime/transcripts/registry.js +161 -0
- package/src/runtime/transcripts/stats.js +161 -0
- package/src/runtime/verification/check-key.js +628 -0
- package/src/runtime/verification/contract-runner.js +546 -11
- package/src/runtime/verification/engine.js +499 -9
- package/src/runtime/verification/impact.js +617 -0
- package/src/runtime/verification/inflight.js +205 -0
- package/src/runtime/verification/registry.js +176 -0
- package/src/runtime/verification/reuse.js +184 -0
- package/src/runtime/verification/telemetry-emit.js +254 -0
- package/src/runtime/verification/test-selection.js +67 -7
- package/src/runtime/worker/active.js +90 -0
- package/src/runtime/worker/cancel.js +222 -0
- package/src/runtime/worker/catalogue.js +122 -0
- package/src/runtime/worker/command.js +677 -0
- package/src/runtime/worker/evidence.js +288 -0
- package/src/runtime/worker/launch.js +288 -0
- package/src/runtime/worker/models.js +137 -0
- package/src/runtime/worker/parsers.js +218 -0
- package/src/runtime/worker/probes/index.js +706 -0
- package/src/runtime/worker/process-tree.js +415 -0
- package/src/runtime/worker/quota.js +43 -0
- package/src/runtime/worker/readiness.js +713 -0
- package/src/runtime/worker/run-store.js +166 -0
- package/src/runtime/worker/supervisor.js +541 -0
- package/src/runtime/worker/worktree-check.js +252 -0
- package/src/runtime/workflow-engine.js +5 -1
- package/src/runtime/workflow-orchestrator.js +578 -63
- package/src/runtime/worktree.js +149 -22
- package/src/state/index.js +1 -1
- package/src/state/lockfile.js +64 -10
- package/src/state/run-lock.js +1 -1
- package/types/node-commonjs.d.ts +106 -7
- package/core/.antigravity-plugin/hooks.json +0 -39
- package/core/harnesses/gemini/hooks/hooks.json +0 -77
- package/core/harnesses/gemini/hooks/lib.sh +0 -302
- package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +0 -21
- package/core/harnesses/gemini/hooks/post-edit-lint.sh +0 -39
- package/core/harnesses/gemini/hooks/pre-bash-guard.sh +0 -21
- package/core/harnesses/gemini/hooks/pre-compact.sh +0 -38
- package/core/harnesses/gemini/hooks/pre-implement-gate.sh +0 -22
- package/core/harnesses/gemini/hooks/session-end.impl.sh +0 -89
- package/core/harnesses/gemini/hooks/session-end.sh +0 -5
- package/core/harnesses/gemini/hooks/session-start.sh +0 -5
- package/core/harnesses/gemini/settings/adapter-defaults.json +0 -4
- package/core/harnesses/shared/hooks/adapters/arg-fields.js +0 -19
- package/core/harnesses/shared/hooks/adapters/gemini.js +0 -114
- package/core/harnesses/shared/hooks/hooks.json +0 -47
- package/core/shared/guidance/FLAGS.md +0 -25
- package/core/shared/guidance/rules/RULE_01_SAFETY.md +0 -25
- package/core/shared/guidance/rules/RULE_02_WORKFLOW.md +0 -23
- package/core/shared/guidance/rules/RULE_03_QUALITY.md +0 -14
- package/core/shared/guidance/rules/RULE_04_QUESTIONS.md +0 -46
- package/src/adapters/gemini/hooks.js +0 -210
- package/src/adapters/gemini/index.js +0 -390
package/src/runtime/health.js
CHANGED
|
@@ -22,11 +22,14 @@ const os = require('node:os');
|
|
|
22
22
|
const path = require('node:path');
|
|
23
23
|
const { execFileSync } = require('node:child_process');
|
|
24
24
|
const { CapabilityRouter } = require('./capability-router.js');
|
|
25
|
-
const { loadRegistry } = require('../registry/index.js');
|
|
25
|
+
const { loadRegistry, agentToolScopeView } = require('../registry/index.js');
|
|
26
26
|
const { evaluateReach } = require('./reach.js');
|
|
27
27
|
const { mcpReach } = require('./mcp-reach.js');
|
|
28
28
|
const { OUTCOME_LABELS } = require('./mcp-consultations.js');
|
|
29
29
|
const { REPO_ROOT } = require('../helper/repo-root.js');
|
|
30
|
+
const { readGrants, detectedProjectRoot } = require('./packages/grants.js');
|
|
31
|
+
const { readState, grantRevoked } = require('./packages/state.js');
|
|
32
|
+
const { transcriptsDoctorView } = require('./transcripts/registry.js');
|
|
30
33
|
|
|
31
34
|
/** Answered a probe. */
|
|
32
35
|
const HEALTHY = 'HEALTHY';
|
|
@@ -82,9 +85,10 @@ const SCAN_FILE_LIMIT = 4000;
|
|
|
82
85
|
* @param {Object} [options]
|
|
83
86
|
* @param {Function} [options.execFileImpl=execFileSync]
|
|
84
87
|
* @param {number} [options.timeoutMs=PROBE_TIMEOUT_MS]
|
|
88
|
+
* @param {boolean} [options.retryOnTimeout] run the probe once more after a timeout; defaults to true for git only
|
|
85
89
|
* @returns {{ok: boolean, output: string, durationMs: number, timedOut: boolean, error: string|null}}
|
|
86
90
|
*/
|
|
87
|
-
function runProbe(command , { execFileImpl = execFileSync, timeoutMs = PROBE_TIMEOUT_MS } = {})
|
|
91
|
+
function runProbe(command , { execFileImpl = execFileSync, timeoutMs = PROBE_TIMEOUT_MS, retryOnTimeout = path.basename(String(command[0])) === 'git' } = {}) {
|
|
88
92
|
const [bin, ...args] = command;
|
|
89
93
|
const started = Number(process.hrtime.bigint() / 1000000n);
|
|
90
94
|
try {
|
|
@@ -105,6 +109,9 @@ function runProbe(command , { execFileImpl = execFileSync, timeoutMs = PROBE
|
|
|
105
109
|
// repairs — a timeout usually means a first-run index build, a non-zero exit usually means a
|
|
106
110
|
// broken install — so they are reported apart rather than as one failure string.
|
|
107
111
|
const timedOut = error.killed === true || error.code === 'ETIMEDOUT' || error.signal === 'SIGTERM';
|
|
112
|
+
// A cold macOS git shim can outlast the first probe and answer at once the second time, which made the
|
|
113
|
+
// first `doflow doctor` exit 1 falsely (FU-93wtsf). Only the git probe is retried, once.
|
|
114
|
+
if (timedOut && retryOnTimeout) return runProbe(command, { execFileImpl, timeoutMs, retryOnTimeout: false });
|
|
108
115
|
return {
|
|
109
116
|
ok: false,
|
|
110
117
|
output: '',
|
|
@@ -243,6 +250,95 @@ function probeFreshness(providerId , { root = process.cwd(), homeDir = os.ho
|
|
|
243
250
|
};
|
|
244
251
|
}
|
|
245
252
|
|
|
253
|
+
async function probeLocalProvider(
|
|
254
|
+
provider ,
|
|
255
|
+
options = {}
|
|
256
|
+
) {
|
|
257
|
+
const timeoutMs = options.timeoutMs ?? 1500;
|
|
258
|
+
|
|
259
|
+
if (provider.kind === 'http') {
|
|
260
|
+
const targetUrl = provider.healthEndpoint || provider.endpoint;
|
|
261
|
+
const { probeHttpEndpoint } = require('./capabilities/executor.js');
|
|
262
|
+
const probeRes = await probeHttpEndpoint(targetUrl, {
|
|
263
|
+
timeoutMs,
|
|
264
|
+
fetchImpl: options.fetchImpl,
|
|
265
|
+
});
|
|
266
|
+
if (probeRes.ok) {
|
|
267
|
+
return {
|
|
268
|
+
id: provider.id,
|
|
269
|
+
name: provider.name || provider.id,
|
|
270
|
+
kind: 'http',
|
|
271
|
+
status: 'HEALTHY',
|
|
272
|
+
endpoint: targetUrl,
|
|
273
|
+
answered: true,
|
|
274
|
+
durationMs: probeRes.durationMs,
|
|
275
|
+
detail: `HTTP ${probeRes.status}`,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
if (probeRes.isUnavailable) {
|
|
279
|
+
return {
|
|
280
|
+
id: provider.id,
|
|
281
|
+
name: provider.name || provider.id,
|
|
282
|
+
kind: 'http',
|
|
283
|
+
status: 'UNAVAILABLE',
|
|
284
|
+
endpoint: targetUrl,
|
|
285
|
+
answered: false,
|
|
286
|
+
durationMs: probeRes.durationMs,
|
|
287
|
+
detail: `Local server at ${targetUrl} is not running`,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
return {
|
|
291
|
+
id: provider.id,
|
|
292
|
+
name: provider.name || provider.id,
|
|
293
|
+
kind: 'http',
|
|
294
|
+
status: 'UNHEALTHY',
|
|
295
|
+
endpoint: targetUrl,
|
|
296
|
+
answered: probeRes.status !== undefined,
|
|
297
|
+
durationMs: probeRes.durationMs,
|
|
298
|
+
detail: probeRes.status ? `HTTP status ${probeRes.status}` : (probeRes.error || 'HTTP request failed'),
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (provider.kind === 'cli') {
|
|
303
|
+
const binary = provider.binary || provider.program;
|
|
304
|
+
const isAvail = options.router ? options.router.isBinaryAvailable(binary) : true;
|
|
305
|
+
if (!isAvail) {
|
|
306
|
+
return {
|
|
307
|
+
id: provider.id,
|
|
308
|
+
name: provider.name || provider.id,
|
|
309
|
+
kind: 'cli',
|
|
310
|
+
status: 'UNAVAILABLE',
|
|
311
|
+
binary,
|
|
312
|
+
answered: false,
|
|
313
|
+
durationMs: null,
|
|
314
|
+
detail: `Binary '${binary}' not found on PATH`,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
const checkCommand = provider.checkCommand || [binary, '--version'];
|
|
318
|
+
const probe = runProbe(checkCommand, { execFileImpl: options.execFileImpl, timeoutMs });
|
|
319
|
+
return {
|
|
320
|
+
id: provider.id,
|
|
321
|
+
name: provider.name || provider.id,
|
|
322
|
+
kind: 'cli',
|
|
323
|
+
status: probe.ok ? 'HEALTHY' : 'UNHEALTHY',
|
|
324
|
+
binary,
|
|
325
|
+
answered: probe.ok,
|
|
326
|
+
durationMs: probe.durationMs,
|
|
327
|
+
detail: probe.ok ? banner(probe.output) : probe.error,
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return {
|
|
332
|
+
id: provider.id,
|
|
333
|
+
name: provider.name || provider.id,
|
|
334
|
+
kind: provider.kind || 'native',
|
|
335
|
+
status: 'UNVERIFIED',
|
|
336
|
+
answered: false,
|
|
337
|
+
durationMs: null,
|
|
338
|
+
detail: 'unsupported probe kind',
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
246
342
|
/**
|
|
247
343
|
* Probes one provider declared in `capabilities.json`.
|
|
248
344
|
* @param {Object} provider
|
|
@@ -257,10 +353,11 @@ function probeProvider(provider , { router, projectRoot = process.cwd(), exe
|
|
|
257
353
|
if (!provider || !provider.id) {
|
|
258
354
|
return { id: null, status: UNVERIFIED, detail: 'no provider declared' };
|
|
259
355
|
}
|
|
356
|
+
const binary = provider.binary || provider.program || null;
|
|
260
357
|
const result = {
|
|
261
358
|
id: provider.id,
|
|
262
359
|
name: provider.name || provider.id,
|
|
263
|
-
binary
|
|
360
|
+
binary,
|
|
264
361
|
present: null,
|
|
265
362
|
answered: false,
|
|
266
363
|
status: UNVERIFIED,
|
|
@@ -270,22 +367,32 @@ function probeProvider(provider , { router, projectRoot = process.cwd(), exe
|
|
|
270
367
|
freshness: null,
|
|
271
368
|
};
|
|
272
369
|
|
|
273
|
-
if (
|
|
274
|
-
result.present = router.isBinaryAvailable(
|
|
370
|
+
if (binary) {
|
|
371
|
+
result.present = router ? router.isBinaryAvailable(binary) : false;
|
|
275
372
|
if (!result.present) {
|
|
276
373
|
result.status = ABSENT;
|
|
277
|
-
result.detail = `binary '${
|
|
374
|
+
result.detail = `binary '${binary}' is not on PATH`;
|
|
375
|
+
return result;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (provider.kind === 'http' || provider.endpoint) {
|
|
380
|
+
const { validateLoopbackUrl } = require('./capabilities/loopback.js');
|
|
381
|
+
const valid = validateLoopbackUrl(provider.endpoint);
|
|
382
|
+
if (!valid.ok) {
|
|
383
|
+
result.status = UNHEALTHY;
|
|
384
|
+
result.detail = `Disallowed endpoint: ${valid.reason}`;
|
|
278
385
|
return result;
|
|
279
386
|
}
|
|
280
387
|
}
|
|
281
388
|
|
|
282
389
|
const command = Array.isArray(provider.checkCommand) && provider.checkCommand.length
|
|
283
390
|
? provider.checkCommand
|
|
284
|
-
: null;
|
|
391
|
+
: (provider.kind === 'cli' && binary ? [binary, '--version'] : null);
|
|
285
392
|
if (!command) {
|
|
286
393
|
result.status = UNVERIFIED;
|
|
287
|
-
result.detail =
|
|
288
|
-
? `binary '${
|
|
394
|
+
result.detail = binary
|
|
395
|
+
? `binary '${binary}' is present but the registry declares no probe command, so DoFlow cannot establish that it answers`
|
|
289
396
|
: 'declares neither a binary nor a probe command, so its function depends entirely on the project (see the project commands section)';
|
|
290
397
|
return result;
|
|
291
398
|
}
|
|
@@ -500,13 +607,29 @@ const byText = (a , b ) => (a < b ? -1 : a > b ? 1 : 0);
|
|
|
500
607
|
*/
|
|
501
608
|
function mcpConsultationsView({ projectRoot, fsImpl } ) {
|
|
502
609
|
const dir = path.join(projectRoot, '.doflow', 'state', 'orchestration');
|
|
503
|
-
|
|
504
|
-
|
|
610
|
+
// Runs live flat or in `<slug>/` (115: a task id other than its feature's slug is namespaced).
|
|
611
|
+
const listed = [];
|
|
612
|
+
const readRunFile = (rel , name , namespace ) => {
|
|
613
|
+
let run;
|
|
614
|
+
try { run = JSON.parse(fsImpl.readFileSync(path.join(dir, rel), 'utf8')); } catch { run = null; }
|
|
615
|
+
listed.push({ rel, name, namespace, run });
|
|
616
|
+
};
|
|
617
|
+
let entries = [];
|
|
618
|
+
try { entries = fsImpl.readdirSync(dir, { withFileTypes: true }); } catch { entries = []; }
|
|
619
|
+
for (const entry of entries) if (entry.name.endsWith('.json') && !entry.isDirectory()) readRunFile(entry.name, entry.name, '');
|
|
620
|
+
for (const entry of entries) {
|
|
621
|
+
if (!entry.isDirectory() || !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(entry.name) || entry.name.endsWith('.lock')) continue;
|
|
622
|
+
let inner = [];
|
|
623
|
+
try { inner = fsImpl.readdirSync(path.join(dir, entry.name), { withFileTypes: true }); } catch { inner = []; }
|
|
624
|
+
for (const file of inner) if (file.name.endsWith('.json') && !file.isDirectory()) readRunFile(`${entry.name}/${file.name}`, file.name, entry.name);
|
|
625
|
+
}
|
|
626
|
+
listed.sort((a, b) => byText(a.rel, b.rel));
|
|
627
|
+
// A flat run whose namespaced copy exists is that copy's seed, not a second run.
|
|
628
|
+
const files = listed.filter((one) => one.namespace !== '' || !listed.some((other) => other.namespace !== '' && other.name === one.name
|
|
629
|
+
&& (typeof one.run?.featureSlug !== 'string' || one.run.featureSlug === other.namespace)));
|
|
505
630
|
let unreadableRuns = 0;
|
|
506
631
|
const bySkill = new Map();
|
|
507
|
-
for (const name of files) {
|
|
508
|
-
let run;
|
|
509
|
-
try { run = JSON.parse(fsImpl.readFileSync(path.join(dir, name), 'utf8')); } catch { run = null; }
|
|
632
|
+
for (const { name, run } of files) {
|
|
510
633
|
if (!run || typeof run !== 'object' || Array.isArray(run)) { unreadableRuns += 1; continue; }
|
|
511
634
|
for (const item of Array.isArray(run.history) ? run.history : []) {
|
|
512
635
|
const record = item && item.consultations;
|
|
@@ -555,6 +678,68 @@ function mcpView({ registry, projectRoot, fsImpl, adapters } ) {
|
|
|
555
678
|
return { servers, ...(serversNote ? { serversNote } : {}), consultations: mcpConsultationsView({ projectRoot, fsImpl }) };
|
|
556
679
|
}
|
|
557
680
|
|
|
681
|
+
/**
|
|
682
|
+
* The doctor's `packages` block (122 IC-008, IC-009): the grants file path and mtime, every grant with its state,
|
|
683
|
+
* and the counts held in `state.json`. A trust or exempt grant a revocation covers (or any, when `state.json` is
|
|
684
|
+
* unreadable) is listed `revoked`. Read-only and not blocking; a failure degrades to a note.
|
|
685
|
+
*/
|
|
686
|
+
function packagesView({ projectRoot, fsImpl, env, homeDir, tmpDir } ) {
|
|
687
|
+
try {
|
|
688
|
+
// The trust root is the project detected from the directory reported on, never that directory itself (IC-008).
|
|
689
|
+
const home = homeDir ?? (typeof env?.HOME === 'string' && path.isAbsolute(env.HOME) ? env.HOME : os.homedir());
|
|
690
|
+
const grants = readGrants({ env, fsImpl, projectRoot: detectedProjectRoot({ cwd: projectRoot, homeDir: home, fsImpl }), tmpDir });
|
|
691
|
+
const state = readState({ homeDir, fsImpl });
|
|
692
|
+
const rows = grants.entries.map((entry ) => {
|
|
693
|
+
const revoked = entry.state === 'valid' && entry.grant !== 'fetch' && grantRevoked(state, entry);
|
|
694
|
+
const target = entry.grant === 'fetch' ? (entry.source.url || entry.source.name) : `${entry.package}@${entry.version}`;
|
|
695
|
+
return { line: entry.line, grant: entry.grant, target, state: revoked ? 'revoked' : entry.state, ...(revoked ? { reason: state.unreadable ? 'state-unreadable' : 'revoked' } : entry.reason ? { reason: entry.reason } : {}) };
|
|
696
|
+
});
|
|
697
|
+
return {
|
|
698
|
+
grants: { path: grants.path, noConfigHome: grants.noConfigHome, exists: grants.exists, mtime: grants.mtime, rows, findings: grants.findings },
|
|
699
|
+
suspendedProjected: state.state.suspended.filter((item ) => item.projected).map((item ) => item.package).sort(),
|
|
700
|
+
state: {
|
|
701
|
+
path: state.path, exists: state.exists, unreadable: state.unreadable, findings: state.findings,
|
|
702
|
+
revoked: state.state.revoked.length, suspended: state.state.suspended.length, removed: state.state.removed.length,
|
|
703
|
+
},
|
|
704
|
+
};
|
|
705
|
+
} catch (error ) {
|
|
706
|
+
return { note: `package grants unavailable: ${error.message}` };
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
function printPackagesSection(packages ) {
|
|
711
|
+
console.log('\n[Packages] — person grants and DoFlow state; not blocking');
|
|
712
|
+
if (packages.note) { console.log(` ${packages.note}`); return; }
|
|
713
|
+
const { grants, state } = packages;
|
|
714
|
+
if (grants.noConfigHome) console.log(' grants no config home (HOME and XDG_CONFIG_HOME unset): no grant can exist');
|
|
715
|
+
else if (!grants.exists) console.log(` grants none (no file at ${grants.path})`);
|
|
716
|
+
else {
|
|
717
|
+
console.log(` grants ${grants.path} (modified ${grants.mtime})`);
|
|
718
|
+
for (const row of grants.rows) console.log(` line ${String(row.line).padEnd(5)}${row.grant.padEnd(8)}${row.target.padEnd(28)}${row.state}${row.reason ? ` (${row.reason})` : ''}`);
|
|
719
|
+
for (const item of grants.findings) console.log(` ${item.code}: ${item.message}`);
|
|
720
|
+
}
|
|
721
|
+
if (state.unreadable) console.log(` state ${state.path} is unreadable: every package counts as revoked (run: doflow package --action verify --repair)`);
|
|
722
|
+
else if (state.revoked || state.suspended || state.removed) console.log(` state ${state.revoked} revoked, ${state.suspended} suspended, ${state.removed} removed (${state.path})`);
|
|
723
|
+
for (const id of packages.suspendedProjected ?? []) {
|
|
724
|
+
console.log(` suspended ${id} still has projected files (run: doflow package --action verify)`);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/** Whether the text report shows `[Packages]`: only when there is something to say. */
|
|
729
|
+
/** The `[Transcripts]` row (093 V.1): shown only when the registry is stale or drifted; never a finding. */
|
|
730
|
+
function printTranscriptsSection(transcripts ) {
|
|
731
|
+
if (transcripts.state === 'ok') return;
|
|
732
|
+
console.log('\n[Transcripts] — transcript locator registry; not blocking');
|
|
733
|
+
if (transcripts.state === 'drift') {
|
|
734
|
+
console.log(' transcripts.json drift: the transcript feature is off');
|
|
735
|
+
for (const error of transcripts.errors) console.log(` ${error}`);
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
for (const item of transcripts.stale) console.log(` ${item.harness.padEnd(18)}verifiedOn ${item.verifiedOn} (${item.ageDays} days ago): re-verify its locations`);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
const packagesWorthShowing = (packages ) => Boolean(packages.note || packages.grants.exists || packages.state.exists);
|
|
742
|
+
|
|
558
743
|
const MCP_STATE_MARK = Object.freeze({
|
|
559
744
|
selected: '✓ selected',
|
|
560
745
|
configured: '✓ configured',
|
|
@@ -602,7 +787,33 @@ function printMcpSections(mcp ) {
|
|
|
602
787
|
if (unreadableRuns) console.log(` ${unreadableRuns} run file(s) could not be read`);
|
|
603
788
|
}
|
|
604
789
|
|
|
605
|
-
|
|
790
|
+
/** The first release of each Node line that carries the 2026-10-21 security fix (CVE-2026-111497). */
|
|
791
|
+
const NODE_PATCHED_RELEASE = Object.freeze({ 24: [24, 22, 1], 26: [26, 12, 1] });
|
|
792
|
+
|
|
793
|
+
/**
|
|
794
|
+
* A warning when `version` (a `process.version` string) is a release of a Node line below that
|
|
795
|
+
* line's patched release; null for a patched release and for a line this table does not know.
|
|
796
|
+
* The floor in `bin/doflow.js` already refuses anything under 24.22.1, so in practice this fires for
|
|
797
|
+
* a Node 26 below 26.12.1; the 24 entry keeps the table complete for a caller that bypasses the bin. A line with no entry (25, 27) gets no warning. A prerelease of the
|
|
798
|
+
* patched release (26.12.1-rc.1) counts as below it.
|
|
799
|
+
*/
|
|
800
|
+
function nodeSecurityWarning(version ) {
|
|
801
|
+
const parts = /^v?(\d+)\.(\d+)\.(\d+)(-.*)?$/.exec(String(version));
|
|
802
|
+
if (!parts) return null;
|
|
803
|
+
const running = [Number(parts[1]), Number(parts[2]), Number(parts[3])];
|
|
804
|
+
const patched = NODE_PATCHED_RELEASE[running[0]];
|
|
805
|
+
if (!patched) return null;
|
|
806
|
+
const order = patched.reduce((result, part, i) => result || Math.sign(running[i] - part), 0);
|
|
807
|
+
const below = order < 0 || (order === 0 && Boolean(parts[4])); // a prerelease of the patched release sorts below it
|
|
808
|
+
if (!below) return null;
|
|
809
|
+
return {
|
|
810
|
+
kind: 'node-security-release',
|
|
811
|
+
subject: 'node',
|
|
812
|
+
detail: `Node ${String(version).replace(/^v/, '')} is below ${patched.join('.')}, the first ${running[0]}.x release with the fix for CVE-2026-111497 (2026-10-21 security release); upgrade Node`,
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
function buildHealthReport({ repoRoot, projectRoot = process.cwd(), router, execFileImpl, fsImpl = fs, detector, adapters, env = process.env, nodeVersion = process.version, homeDir, tmpDir, now } = {}) {
|
|
606
817
|
const root = repoRoot || REPO_ROOT;
|
|
607
818
|
const registry = loadRegistry({ repoRoot: root });
|
|
608
819
|
const activeRouter = router || new CapabilityRouter({ repoRoot: root, projectRoot });
|
|
@@ -665,7 +876,9 @@ function buildHealthReport({ repoRoot, projectRoot = process.cwd(), router, exec
|
|
|
665
876
|
.filter((row ) => row.state === 'NO-REACH')
|
|
666
877
|
.map((row ) => ({ kind: 'runtime-no-reach', subject: `${row.harness}:${row.scope}`, detail: `${row.reason}; fix: ${row.fix}` })),
|
|
667
878
|
];
|
|
879
|
+
const nodeWarning = nodeSecurityWarning(nodeVersion);
|
|
668
880
|
const warnings = [
|
|
881
|
+
...(nodeWarning ? [nodeWarning] : []),
|
|
669
882
|
...freshness
|
|
670
883
|
.filter((item) => item.state === 'STALE')
|
|
671
884
|
.map((item) => ({ kind: 'index-stale', subject: item.providerId, detail: `${item.label} is behind the working tree; refresh with: ${item.refresh}` })),
|
|
@@ -704,6 +917,8 @@ function buildHealthReport({ repoRoot, projectRoot = process.cwd(), router, exec
|
|
|
704
917
|
freshness,
|
|
705
918
|
project,
|
|
706
919
|
mcp: mcpView({ registry, projectRoot, fsImpl, adapters }),
|
|
920
|
+
packages: packagesView({ projectRoot, fsImpl, env, homeDir, tmpDir }),
|
|
921
|
+
transcripts: transcriptsDoctorView({ repoRoot: root, fsImpl, now }),
|
|
707
922
|
findings,
|
|
708
923
|
warnings,
|
|
709
924
|
summary: {
|
|
@@ -713,6 +928,8 @@ function buildHealthReport({ repoRoot, projectRoot = process.cwd(), router, exec
|
|
|
713
928
|
absent: Object.values(probes).filter((probe) => probe.status === ABSENT).length,
|
|
714
929
|
unverified: Object.values(probes).filter((probe) => probe.status === UNVERIFIED).length,
|
|
715
930
|
},
|
|
931
|
+
// IC-006: the read switch of doctor's own environment, only when set (additive key).
|
|
932
|
+
...(env.DOFLOW_UPKEEP === 'off' ? { upkeepSwitch: 'off' } : {}),
|
|
716
933
|
};
|
|
717
934
|
}
|
|
718
935
|
|
|
@@ -755,6 +972,121 @@ const FRESHNESS_MARK = Object.freeze({
|
|
|
755
972
|
UNKNOWN: '? UNKNOWN',
|
|
756
973
|
});
|
|
757
974
|
|
|
975
|
+
/** IC-014: read enabled state, bound tiers and unfiltered eligibility without writing or running worker probes. */
|
|
976
|
+
function workersDoctorView({
|
|
977
|
+
env = process.env, projectRoot, repoRoot = REPO_ROOT, descriptors, now,
|
|
978
|
+
} = {}) {
|
|
979
|
+
const reg = loadRegistry({ repoRoot });
|
|
980
|
+
const root = require('./lifecycle/root.js').projectRoot(projectRoot);
|
|
981
|
+
const { activeWorkers } = require('./worker/active.js');
|
|
982
|
+
// Readiness already requires health.ts; load it only after this module has initialized.
|
|
983
|
+
const { select, enabledOf, workerDescriptors } = require('./worker/readiness.js');
|
|
984
|
+
const { readBindings, resolveBinding } = require('./worker/models.js');
|
|
985
|
+
const active = activeWorkers({ registry: reg, env, projectRoot: root });
|
|
986
|
+
const bindings = readBindings({ env, projectRoot: root });
|
|
987
|
+
const entries = workerDescriptors({ descriptors, registry: reg });
|
|
988
|
+
const tiers = reg.dispatchTiers?.tiers ?? [];
|
|
989
|
+
const selection = select({
|
|
990
|
+
descriptors, registry: reg, env, cwd: projectRoot, root, now, needs: [], includeDisabled: true,
|
|
991
|
+
});
|
|
992
|
+
const rows = selection.candidates.map(({ harness, eligible, gaps } ) => {
|
|
993
|
+
const gap = gaps[0];
|
|
994
|
+
const descriptor = entries.find((entry ) => entry.harness === harness)?.worker;
|
|
995
|
+
const bound = tiers.flatMap((tier ) => {
|
|
996
|
+
const resolved = resolveBinding({ worker: harness, tier, bindings, descriptor });
|
|
997
|
+
return resolved.source === 'binding' ? [{ tier, flag: resolved.flag }] : [];
|
|
998
|
+
});
|
|
999
|
+
return {
|
|
1000
|
+
harness,
|
|
1001
|
+
enabled: enabledOf(active, harness),
|
|
1002
|
+
eligible,
|
|
1003
|
+
firstGap: gap ? { requirement: gap.requirement, result: gap.result, reason: gap.reason } : null,
|
|
1004
|
+
bound,
|
|
1005
|
+
};
|
|
1006
|
+
});
|
|
1007
|
+
return {
|
|
1008
|
+
state: active.state,
|
|
1009
|
+
origin: active.origin,
|
|
1010
|
+
inSession: active.inSession,
|
|
1011
|
+
reason: active.reason,
|
|
1012
|
+
dangling: active.dangling,
|
|
1013
|
+
bindings: { state: bindings.state, reason: bindings.reason },
|
|
1014
|
+
rows,
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
/** IC-005: read observed catalogue, login states, derived billing and enabled models without running probes. */
|
|
1019
|
+
function workerPoolDoctorView({
|
|
1020
|
+
env = process.env, projectRoot, repoRoot = REPO_ROOT,
|
|
1021
|
+
} = {}) {
|
|
1022
|
+
const { readCatalogue } = require('./models/catalogue.js');
|
|
1023
|
+
const { readAllowedModels } = require('./worker/models.js');
|
|
1024
|
+
const { readWorkerBilling, effectiveBilling } = require('./settings/worker-billing.js');
|
|
1025
|
+
const { HARNESS_IDS } = require('./worker/probes/index.js');
|
|
1026
|
+
const reg = loadRegistry({ repoRoot });
|
|
1027
|
+
const root = require('./lifecycle/root.js').projectRoot(projectRoot);
|
|
1028
|
+
|
|
1029
|
+
const catalogue = readCatalogue(root);
|
|
1030
|
+
const allowed = readAllowedModels({ projectRoot: root }) || [];
|
|
1031
|
+
const workerIds = reg.harnesses.map((h ) => h.id);
|
|
1032
|
+
const billingRecord = readWorkerBilling(env, { workerIds });
|
|
1033
|
+
|
|
1034
|
+
const observedModelIds = new Set ();
|
|
1035
|
+
const harnesses = [];
|
|
1036
|
+
const allHarnessIds = Array.from(new Set([...HARNESS_IDS, ...Object.keys(catalogue?.harnesses || {})]));
|
|
1037
|
+
|
|
1038
|
+
for (const id of allHarnessIds) {
|
|
1039
|
+
const catEntry = catalogue?.harnesses?.[id];
|
|
1040
|
+
const regEntry = reg.harnesses.find((h ) => h.id === id);
|
|
1041
|
+
const descBilling = regEntry?.worker?.billing ?? 'unknown';
|
|
1042
|
+
|
|
1043
|
+
if (catEntry) {
|
|
1044
|
+
for (const m of catEntry.offeredModels || []) {
|
|
1045
|
+
if (m && m.id) observedModelIds.add(m.id);
|
|
1046
|
+
}
|
|
1047
|
+
const effBilling = effectiveBilling(descBilling, id, billingRecord, catalogue);
|
|
1048
|
+
harnesses.push({
|
|
1049
|
+
harness: id,
|
|
1050
|
+
binary: catEntry.binary || id,
|
|
1051
|
+
installed: Boolean(catEntry.installed),
|
|
1052
|
+
loginState: catEntry.loginState,
|
|
1053
|
+
reloginHint: catEntry.reloginHint || null,
|
|
1054
|
+
detectedMode: catEntry.detectedMode,
|
|
1055
|
+
effectiveBilling: effBilling,
|
|
1056
|
+
offeredCount: (catEntry.offeredModels || []).length,
|
|
1057
|
+
});
|
|
1058
|
+
} else {
|
|
1059
|
+
const effBilling = effectiveBilling(descBilling, id, billingRecord, null);
|
|
1060
|
+
harnesses.push({
|
|
1061
|
+
harness: id,
|
|
1062
|
+
binary: id,
|
|
1063
|
+
installed: false,
|
|
1064
|
+
loginState: 'uncalibrated',
|
|
1065
|
+
reloginHint: null,
|
|
1066
|
+
detectedMode: 'unverified',
|
|
1067
|
+
effectiveBilling: effBilling,
|
|
1068
|
+
offeredCount: 0,
|
|
1069
|
+
});
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
let enabledCount = 0;
|
|
1074
|
+
for (const mId of observedModelIds) {
|
|
1075
|
+
if (allowed.includes(mId)) enabledCount++;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
return {
|
|
1079
|
+
state: catalogue ? 'synced' : 'unsynced',
|
|
1080
|
+
syncedAt: catalogue?.syncedAt || null,
|
|
1081
|
+
harnesses,
|
|
1082
|
+
models: {
|
|
1083
|
+
enabledCount,
|
|
1084
|
+
listedCount: observedModelIds.size,
|
|
1085
|
+
enabled: allowed,
|
|
1086
|
+
},
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
1089
|
+
|
|
758
1090
|
/**
|
|
759
1091
|
* Handles `doflow doctor` — installation and capability health.
|
|
760
1092
|
* @param {Object} options
|
|
@@ -765,11 +1097,159 @@ const FRESHNESS_MARK = Object.freeze({
|
|
|
765
1097
|
* installed harness's skills cannot reach a runtime
|
|
766
1098
|
*/
|
|
767
1099
|
async function handleDoctorCommand({ json = false, repoRoot, projectRoot = process.cwd(), ...rest } = {}) {
|
|
768
|
-
const
|
|
1100
|
+
const root = repoRoot || REPO_ROOT;
|
|
1101
|
+
const activeRouter = rest.router || new CapabilityRouter({ repoRoot: root, projectRoot });
|
|
1102
|
+
const report = buildHealthReport({ repoRoot: root, projectRoot, router: activeRouter, ...rest });
|
|
1103
|
+
|
|
1104
|
+
// Probe local capability providers asynchronously (REV-1)
|
|
1105
|
+
for (const capability of Object.values (activeRouter.capabilities || {})) {
|
|
1106
|
+
for (const provider of capability.providers || []) {
|
|
1107
|
+
if (!provider || !provider.id) continue;
|
|
1108
|
+
if (provider.kind === 'http' || provider.kind === 'cli') {
|
|
1109
|
+
const localRes = await probeLocalProvider(provider, {
|
|
1110
|
+
router: activeRouter,
|
|
1111
|
+
execFileImpl: rest.execFileImpl,
|
|
1112
|
+
fetchImpl: rest.fetchImpl,
|
|
1113
|
+
});
|
|
1114
|
+
const provIdx = report.providers.findIndex((p ) => p.id === provider.id);
|
|
1115
|
+
if (provIdx !== -1) {
|
|
1116
|
+
report.providers[provIdx] = { ...report.providers[provIdx], ...localRes };
|
|
1117
|
+
} else {
|
|
1118
|
+
report.providers.push(localRes);
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
// Refresh report.capabilities with updated probe states
|
|
1125
|
+
const probeMap = {};
|
|
1126
|
+
for (const p of report.providers) {
|
|
1127
|
+
probeMap[p.id] = p;
|
|
1128
|
+
}
|
|
1129
|
+
for (const cap of report.capabilities) {
|
|
1130
|
+
const capDef = activeRouter.capabilities[cap.capability];
|
|
1131
|
+
if (capDef && Array.isArray(capDef.providers)) {
|
|
1132
|
+
const declared = capDef.providers.map((p ) => probeMap[p.id]).filter(Boolean);
|
|
1133
|
+
const rolled = capabilityStatus(declared);
|
|
1134
|
+
cap.status = rolled.status;
|
|
1135
|
+
cap.activeProvider = rolled.activeProvider;
|
|
1136
|
+
cap.providers = declared.map((p ) => ({ id: p.id, status: p.status, detail: p.detail }));
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
// Refresh summary, findings, warnings, and effective identity
|
|
1141
|
+
report.summary.healthy = report.providers.filter((p ) => p.status === HEALTHY).length;
|
|
1142
|
+
report.summary.unhealthy = report.providers.filter((p ) => p.status === UNHEALTHY).length;
|
|
1143
|
+
report.summary.absent = report.providers.filter((p ) => p.status === ABSENT).length;
|
|
1144
|
+
report.summary.unverified = report.providers.filter((p ) => p.status === UNVERIFIED).length;
|
|
1145
|
+
report.summary.probed = report.providers.length;
|
|
1146
|
+
|
|
1147
|
+
const nonProviderFindings = report.findings.filter(
|
|
1148
|
+
(f ) => f.kind !== 'provider-unhealthy' && f.kind !== 'capability-degraded'
|
|
1149
|
+
);
|
|
1150
|
+
report.findings = [
|
|
1151
|
+
...nonProviderFindings,
|
|
1152
|
+
...report.providers
|
|
1153
|
+
.filter((p ) => p.status === UNHEALTHY)
|
|
1154
|
+
.map((p ) => ({ kind: 'provider-unhealthy', subject: p.id, detail: p.detail })),
|
|
1155
|
+
...report.capabilities
|
|
1156
|
+
.filter((c ) => c.status === 'DEGRADED')
|
|
1157
|
+
.map((c ) => ({ kind: 'capability-degraded', subject: c.capability, detail: 'every installed provider for this capability failed its probe' })),
|
|
1158
|
+
];
|
|
1159
|
+
|
|
1160
|
+
const nonProviderWarnings = report.warnings.filter(
|
|
1161
|
+
(w ) => w.kind !== 'provider-unverified'
|
|
1162
|
+
);
|
|
1163
|
+
report.warnings = [
|
|
1164
|
+
...nonProviderWarnings,
|
|
1165
|
+
...report.providers
|
|
1166
|
+
.filter((p ) => p.status === UNVERIFIED)
|
|
1167
|
+
.map((p ) => ({ kind: 'provider-unverified', subject: p.id, detail: p.detail })),
|
|
1168
|
+
];
|
|
1169
|
+
|
|
1170
|
+
report.identity.connectedCapabilities = report.capabilities
|
|
1171
|
+
.filter((c ) => c.activeProvider)
|
|
1172
|
+
.map((c ) => c.capability);
|
|
1173
|
+
|
|
769
1174
|
const { buildAdviceDoctor, printAdviceDoctor } = require('./advice/doctor.js');
|
|
1175
|
+
const { buildSettingsReport, printSettingsReport } = require('./settings/cli.js');
|
|
770
1176
|
const adviceSections = await buildAdviceDoctor({ repoRoot, projectRoot, fsImpl: rest.fsImpl });
|
|
1177
|
+
const settings = buildSettingsReport({ repoRoot, projectRoot, env: rest.env, fsImpl: rest.fsImpl });
|
|
771
1178
|
report.decisionModel = adviceSections.decisionModel;
|
|
772
1179
|
report.advice = adviceSections.advice;
|
|
1180
|
+
report.settings = settings;
|
|
1181
|
+
const { buildProfilesDoctor, printProfilesDoctor } = require('./profiles/doctor.js');
|
|
1182
|
+
const profilesSection = buildProfilesDoctor({ repoRoot, projectRoot, env: rest.env, fsImpl: rest.fsImpl });
|
|
1183
|
+
report.rolloutProfile = profilesSection;
|
|
1184
|
+
try {
|
|
1185
|
+
const { loadRuleLedger, getAblationCandidates } = require('./guidance/ablation.js');
|
|
1186
|
+
const ledger = loadRuleLedger({ repoRoot, fsImpl: rest.fsImpl });
|
|
1187
|
+
const candidates = getAblationCandidates(ledger);
|
|
1188
|
+
const neverAblateCount = ledger.rules.filter((r ) => r.neverAblate).length;
|
|
1189
|
+
report.guidance = {
|
|
1190
|
+
rulesCount: ledger.rules.length,
|
|
1191
|
+
neverAblateCount,
|
|
1192
|
+
candidateCount: candidates.length,
|
|
1193
|
+
};
|
|
1194
|
+
} catch (error ) {
|
|
1195
|
+
report.guidance = { state: 'unavailable', message: error?.message || String(error) };
|
|
1196
|
+
}
|
|
1197
|
+
try {
|
|
1198
|
+
const { AdmissionController, AdmissionError, resolveTeamSettings } = require('./team/admission.js');
|
|
1199
|
+
const { validateTaskBrief, validateTaskHandBack, quarantineInput, formatTaskBriefPrompt, EnvelopeError } = require('./team/envelope.js');
|
|
1200
|
+
const { assertReviewerSeparation, assertMechanicalPreChecks, ReviewGuardError, NeverReviewOwnCodeError, MechanicalPreChecksIncompleteError } = require('./team/review-guard.js');
|
|
1201
|
+
const teamController = new AdmissionController({ projectRoot, fsImpl: rest.fsImpl });
|
|
1202
|
+
const activeClaims = teamController.getActiveClaims();
|
|
1203
|
+
report.team = {
|
|
1204
|
+
state: 'ready',
|
|
1205
|
+
activeClaimsCount: activeClaims.length,
|
|
1206
|
+
writerCap: teamController.writerCap,
|
|
1207
|
+
maxAttempts: teamController.maxAttempts,
|
|
1208
|
+
_symbols: [
|
|
1209
|
+
AdmissionController,
|
|
1210
|
+
AdmissionError,
|
|
1211
|
+
resolveTeamSettings,
|
|
1212
|
+
validateTaskBrief,
|
|
1213
|
+
validateTaskHandBack,
|
|
1214
|
+
quarantineInput,
|
|
1215
|
+
formatTaskBriefPrompt,
|
|
1216
|
+
EnvelopeError,
|
|
1217
|
+
assertReviewerSeparation,
|
|
1218
|
+
assertMechanicalPreChecks,
|
|
1219
|
+
ReviewGuardError,
|
|
1220
|
+
NeverReviewOwnCodeError,
|
|
1221
|
+
MechanicalPreChecksIncompleteError,
|
|
1222
|
+
],
|
|
1223
|
+
};
|
|
1224
|
+
} catch (error ) {
|
|
1225
|
+
report.team = { state: 'unavailable', message: error?.message || String(error) };
|
|
1226
|
+
}
|
|
1227
|
+
try {
|
|
1228
|
+
report.agentToolScope = agentToolScopeView(loadRegistry({ repoRoot, sources: 'skip' }));
|
|
1229
|
+
} catch (error ) {
|
|
1230
|
+
report.agentToolScope = { state: 'unavailable', message: error?.message || String(error) };
|
|
1231
|
+
}
|
|
1232
|
+
// Read on use (row 120 A.4): no probe runs, nothing is written, and a fault never breaks the report.
|
|
1233
|
+
let primaryText ;
|
|
1234
|
+
try {
|
|
1235
|
+
const { primaryDoctorView, resolveForReport } = require('./context/primary-command.js');
|
|
1236
|
+
const resolved = resolveForReport({ env: rest.env ?? process.env, projectRoot, repoRoot });
|
|
1237
|
+
report.primary = primaryDoctorView(resolved);
|
|
1238
|
+
primaryText = `${report.primary.state} ${report.primary.harness ?? '-'}/${report.primary.model ?? '-'} (${report.primary.origin}), probe ${report.primary.probe}`;
|
|
1239
|
+
} catch (error ) {
|
|
1240
|
+
report.primary = { state: 'unavailable', message: error?.message || String(error) };
|
|
1241
|
+
primaryText = `unavailable (${report.primary.message})`;
|
|
1242
|
+
}
|
|
1243
|
+
try {
|
|
1244
|
+
report.workers = workersDoctorView({ env: rest.env ?? process.env, projectRoot, repoRoot });
|
|
1245
|
+
} catch (error ) {
|
|
1246
|
+
report.workers = { state: 'unavailable', message: error?.message || String(error) };
|
|
1247
|
+
}
|
|
1248
|
+
try {
|
|
1249
|
+
report.workerPool = workerPoolDoctorView({ env: rest.env ?? process.env, projectRoot, repoRoot });
|
|
1250
|
+
} catch (error ) {
|
|
1251
|
+
report.workerPool = { state: 'unavailable', message: error?.message || String(error) };
|
|
1252
|
+
}
|
|
773
1253
|
|
|
774
1254
|
if (json) {
|
|
775
1255
|
console.log(JSON.stringify(report, null, 2));
|
|
@@ -779,6 +1259,7 @@ async function handleDoctorCommand({ json = false, repoRoot, projectRoot = proce
|
|
|
779
1259
|
console.log('\nDoFlow System Diagnostics (doflow doctor)');
|
|
780
1260
|
console.log('═'.repeat(72));
|
|
781
1261
|
console.log('Health means a provider answered a probe, not that a binary exists on PATH.');
|
|
1262
|
+
if (report.upkeepSwitch === 'off') console.log('note: DOFLOW_UPKEEP=off in this environment: upkeep and lifecycle retention are skipped');
|
|
782
1263
|
|
|
783
1264
|
console.log('\n[Effective Identity]');
|
|
784
1265
|
const identity = report.identity;
|
|
@@ -789,6 +1270,11 @@ async function handleDoctorCommand({ json = false, repoRoot, projectRoot = proce
|
|
|
789
1270
|
console.log('\n[Harness Adapters]');
|
|
790
1271
|
for (const harness of report.harnesses) console.log(` ${harness.displayName.padEnd(28)} adapter PASS`);
|
|
791
1272
|
|
|
1273
|
+
console.log('\n[Agent Tool Scope]');
|
|
1274
|
+
if (Array.isArray(report.agentToolScope)) {
|
|
1275
|
+
for (const row of report.agentToolScope) console.log(` ${row.harness.padEnd(14)}${row.status === 'native' ? `native ${row.field}` : `unenforced projects no tool field: ${row.reason}`}`);
|
|
1276
|
+
} else console.log(` unavailable (${report.agentToolScope.message})`);
|
|
1277
|
+
|
|
792
1278
|
console.log('\n[Runtime Reach]');
|
|
793
1279
|
const reachLines = report.harnesses.flatMap((harness ) => harness.reach.map((row ) => {
|
|
794
1280
|
const detail = ({ REACHED: row.root, 'NO-REACH': `fix: ${row.fix}`, 'N/A': row.reason } )[row.state];
|
|
@@ -826,11 +1312,69 @@ async function handleDoctorCommand({ json = false, repoRoot, projectRoot = proce
|
|
|
826
1312
|
console.log(` test ${commandLine(commands.test)}`);
|
|
827
1313
|
if (commands.reason) console.log(` note ${commands.reason}`);
|
|
828
1314
|
|
|
1315
|
+
console.log('\n[Primary and Workers]');
|
|
1316
|
+
console.log(` primary: ${primaryText}`);
|
|
1317
|
+
if (report.workers.state === 'unavailable') {
|
|
1318
|
+
console.log(` worker: unavailable (${report.workers.message})`);
|
|
1319
|
+
} else {
|
|
1320
|
+
if (report.workers.bindings.state === 'invalid') {
|
|
1321
|
+
console.log(` workers.models: invalid (${report.workers.bindings.reason})`);
|
|
1322
|
+
}
|
|
1323
|
+
if (report.workers.state === 'invalid') {
|
|
1324
|
+
console.log(` workers.enabled: invalid (${report.workers.reason})`);
|
|
1325
|
+
}
|
|
1326
|
+
for (const row of report.workers.rows) {
|
|
1327
|
+
const position = row.enabled.position === null ? '' : ` #${row.enabled.position}`;
|
|
1328
|
+
const eligibility = row.eligible ? 'eligible' : `ineligible (${row.firstGap.requirement})`;
|
|
1329
|
+
const bound = row.bound.map((item ) => `${item.tier}(${item.flag})`).join(', ') || 'none';
|
|
1330
|
+
console.log(` worker ${row.harness}: ${row.enabled.state}${position}, ${eligibility}; bound tiers: ${bound}`);
|
|
1331
|
+
}
|
|
1332
|
+
for (const id of report.workers.dangling) {
|
|
1333
|
+
console.log(` worker ${id}: dangling (no registry or enabled package knows it)`);
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
console.log('\n[Worker Pool & Models]');
|
|
1338
|
+
if (report.workerPool.state === 'unavailable') {
|
|
1339
|
+
console.log(` worker pool: unavailable (${report.workerPool.message})`);
|
|
1340
|
+
} else if (report.workerPool.state === 'unsynced') {
|
|
1341
|
+
console.log(' No observed catalogue. Run `doflow models --action sync` to probe worker pool.');
|
|
1342
|
+
console.log(` Models: ${report.workerPool.models.enabledCount} enabled, ${report.workerPool.models.listedCount} listed`);
|
|
1343
|
+
} else {
|
|
1344
|
+
for (const h of report.workerPool.harnesses) {
|
|
1345
|
+
const binStatus = h.installed ? 'installed' : (h.loginState === 'uncalibrated' ? 'unknown' : 'missing');
|
|
1346
|
+
const hint = h.loginState === 'logged-out' && h.reloginHint ? ` (re-login: ${h.reloginHint})` : '';
|
|
1347
|
+
const billSource = h.effectiveBilling.source ? ` (${h.effectiveBilling.source})` : '';
|
|
1348
|
+
console.log(` ${h.harness.padEnd(14)} binary: ${binStatus.padEnd(10)} ${h.loginState.padEnd(12)} mode: ${h.detectedMode.padEnd(14)} billing: ${h.effectiveBilling.value}${billSource}${hint}`);
|
|
1349
|
+
}
|
|
1350
|
+
console.log(` Models: ${report.workerPool.models.enabledCount} enabled, ${report.workerPool.models.listedCount} listed`);
|
|
1351
|
+
}
|
|
1352
|
+
|
|
829
1353
|
printMcpSections(report.mcp);
|
|
1354
|
+
if (packagesWorthShowing(report.packages)) printPackagesSection(report.packages);
|
|
1355
|
+
printTranscriptsSection(report.transcripts);
|
|
830
1356
|
printAdviceDoctor(adviceSections);
|
|
1357
|
+
printProfilesDoctor(profilesSection);
|
|
1358
|
+
printSettingsReport(settings);
|
|
1359
|
+
if (report.guidance) {
|
|
1360
|
+
console.log('\n[Guidance Rules]');
|
|
1361
|
+
if (report.guidance.state === 'unavailable') {
|
|
1362
|
+
console.log(` guidance: unavailable (${report.guidance.message})`);
|
|
1363
|
+
} else {
|
|
1364
|
+
console.log(` ${report.guidance.rulesCount} registered rules (${report.guidance.neverAblateCount} protected invariants, ${report.guidance.candidateCount} ablation candidates)`);
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
if (report.team) {
|
|
1368
|
+
console.log('\n[Agent Teams & Coordination]');
|
|
1369
|
+
if (report.team.state === 'unavailable') {
|
|
1370
|
+
console.log(` team: unavailable (${report.team.message})`);
|
|
1371
|
+
} else {
|
|
1372
|
+
console.log(` writer cap: ${report.team.writerCap}, max attempts: ${report.team.maxAttempts}, active claims: ${report.team.activeClaimsCount}`);
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
831
1375
|
|
|
832
1376
|
if (report.warnings.length) {
|
|
833
|
-
console.log('\n[Warnings] — not blocking; a stale or unprobeable provider still degrades cleanly');
|
|
1377
|
+
console.log('\n[Warnings] — not blocking; a stale or unprobeable provider still degrades cleanly, and a Node below the security release of its line is named here');
|
|
834
1378
|
for (const warning of report.warnings) console.log(` ${warning.subject.padEnd(18)}${warning.detail}`);
|
|
835
1379
|
}
|
|
836
1380
|
|
|
@@ -853,11 +1397,15 @@ module.exports = {
|
|
|
853
1397
|
runProbe,
|
|
854
1398
|
probeProvider,
|
|
855
1399
|
probeProviders,
|
|
1400
|
+
probeLocalProvider,
|
|
856
1401
|
probeFreshness,
|
|
857
1402
|
PROVIDER_ARTIFACTS,
|
|
858
1403
|
newestSourceMtime,
|
|
859
1404
|
detectProjectCommands,
|
|
860
1405
|
buildHealthReport,
|
|
1406
|
+
nodeSecurityWarning,
|
|
1407
|
+
workersDoctorView,
|
|
1408
|
+
workerPoolDoctorView,
|
|
861
1409
|
handleDoctorCommand,
|
|
862
1410
|
PROBE_TIMEOUT_MS,
|
|
863
1411
|
};
|