@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
|
@@ -30,7 +30,12 @@ const REF_RE = /^[A-Za-z]+-?[0-9]+$/;
|
|
|
30
30
|
const DEC_RE = /^DEC-[0-9]{3,}$/;
|
|
31
31
|
const STAGES = ['discovery', 'design', 'planning', 'implementation', 'verification', 'review'];
|
|
32
32
|
/** Channel -> who decided. The caller names the channel; `decidedBy` follows from it. */
|
|
33
|
-
const CHANNEL_DECIDED_BY = { question: 'user', gate: 'user', prompt: 'user', default: 'agent', resolution: 'agent' };
|
|
33
|
+
const CHANNEL_DECIDED_BY = { question: 'user', gate: 'user', prompt: 'user', default: 'agent', resolution: 'agent', policy: 'autopilot' };
|
|
34
|
+
/** IC-005: `policy` records an evaluated gate, never a caller's claimed identity, so only the
|
|
35
|
+
* in-process `orchestrate decide-gate --policy` path writes it; every caller-facing path refuses it. */
|
|
36
|
+
const RESERVED_CHANNEL = 'policy';
|
|
37
|
+
const CHANNEL_RESERVED = `channel-reserved: ${RESERVED_CHANNEL} is set only by orchestrate decide-gate --policy`;
|
|
38
|
+
const isReservedChannel = (channel ) => typeof channel === 'string' && channel.trim() === RESERVED_CHANNEL;
|
|
34
39
|
const ITEM_KEYS = ['topic', 'statement', 'channel', 'stage', 'rationale', 'supersedes', 'refs', 'source'];
|
|
35
40
|
const REQUIRED_KEYS = ['topic', 'statement', 'channel', 'stage', 'rationale'];
|
|
36
41
|
|
|
@@ -204,7 +209,7 @@ function normalizeItem(raw , label ) {
|
|
|
204
209
|
}
|
|
205
210
|
const channel = raw.channel.trim();
|
|
206
211
|
if (!Object.prototype.hasOwnProperty.call(CHANNEL_DECIDED_BY, channel)) {
|
|
207
|
-
throw new DecisionUsageError(`${label}: channel '${channel}' must be one of ${Object.keys(CHANNEL_DECIDED_BY).join(', ')}`);
|
|
212
|
+
throw new DecisionUsageError(`${label}: channel '${channel}' must be one of ${Object.keys(CHANNEL_DECIDED_BY).filter((c) => c !== RESERVED_CHANNEL).join(', ')}`);
|
|
208
213
|
}
|
|
209
214
|
const stage = raw.stage.trim();
|
|
210
215
|
if (!STAGES.includes(stage)) throw new DecisionUsageError(`${label}: stage '${stage}' must be one of ${STAGES.join(', ')}`);
|
|
@@ -406,16 +411,17 @@ function runDecision({ action = 'list', projectRoot, slug = null, flags = {}, no
|
|
|
406
411
|
let resolved = null;
|
|
407
412
|
try {
|
|
408
413
|
if (!VALID_ACTIONS.includes(act)) throw new DecisionUsageError(`unknown --action '${action}'. Valid: ${VALID_ACTIONS.join(', ')}`);
|
|
414
|
+
if (isReservedChannel(flags.channel)) throw new DecisionUsageError(CHANNEL_RESERVED);
|
|
409
415
|
const feature = resolveActiveFeature({ projectRoot, slug });
|
|
410
416
|
if (feature.error) throw new DecisionUsageError(feature.message);
|
|
411
|
-
resolved = { featureDir: feature.
|
|
417
|
+
resolved = { featureDir: feature.featureFolder, slug: feature.paths.feature_slug };
|
|
412
418
|
let result ;
|
|
413
419
|
if (act === 'init') {
|
|
414
420
|
result = initRegister({ ...resolved, fsImpl });
|
|
415
421
|
} else if (act === 'list') {
|
|
416
422
|
result = listDecisions({ ...resolved, all: Boolean(flags.all), fsImpl });
|
|
417
423
|
} else if (act === 'compact') {
|
|
418
|
-
result = compactDecisions({ ...resolved, repoRoot: feature.
|
|
424
|
+
result = compactDecisions({ ...resolved, repoRoot: feature.featureRoot, paths: feature.paths, now, fsImpl });
|
|
419
425
|
} else {
|
|
420
426
|
let items ;
|
|
421
427
|
if (flags.batch) {
|
|
@@ -430,6 +436,7 @@ function runDecision({ action = 'list', projectRoot, slug = null, flags = {}, no
|
|
|
430
436
|
}
|
|
431
437
|
items = [single];
|
|
432
438
|
}
|
|
439
|
+
if (Array.isArray(items) && items.some((item ) => isReservedChannel(item?.channel))) throw new DecisionUsageError(CHANNEL_RESERVED);
|
|
433
440
|
result = addDecisions({ ...resolved, items, now, fsImpl });
|
|
434
441
|
}
|
|
435
442
|
return { exitCode: result.finding ? 1 : 0, result };
|
|
@@ -489,5 +496,7 @@ module.exports = {
|
|
|
489
496
|
archiveFile,
|
|
490
497
|
STAGES,
|
|
491
498
|
CHANNEL_DECIDED_BY,
|
|
499
|
+
CHANNEL_RESERVED,
|
|
500
|
+
isReservedChannel,
|
|
492
501
|
STATEMENT_MAX,
|
|
493
502
|
};
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Feature 082 C.1 (IC-007, IC-008 reservation, IC-009 drivers; DEC-002, DEC-003, DEC-018, DEC-019,
|
|
4
|
+
// DEC-033, DEC-038, DEC-040, DEC-047, DEC-059): two-section admission, reservations and drivers.
|
|
5
|
+
//
|
|
6
|
+
// Drivers (IC-009). Each invocation registers `{ pid, start, registeredAt }` in one locked write.
|
|
7
|
+
// Before it, outside the lock because `ps` is a process, every other driver with no `endedAt` and
|
|
8
|
+
// not yet marked lost is read with `alive`; the write applies those readings to the drivers it read
|
|
9
|
+
// only. `false` is lost `driver-lost`, `unknown` is lost `driver-liveness-unknown` (DEC-040), and a
|
|
10
|
+
// lost driver sets the run `resume-required` with that reason. Drivers are read, never signalled
|
|
11
|
+
// (DEC-047): nothing here sends a signal.
|
|
12
|
+
//
|
|
13
|
+
// Admission (IC-007). Outside every lock: 080 `inspect`, the 081 status of each prior dispatch's
|
|
14
|
+
// worker run, and the caller's `modeFor` per candidate (IC-022 lives with the loop, so admission
|
|
15
|
+
// never requires `loop.ts`). First section, one `mutateDispatch`: IC-007 steps 1 to 8, overlap from
|
|
16
|
+
// task-graph's exported `overlaps` (DEC-059) checked against every non-ended dispatch, not only the
|
|
17
|
+
// candidates of this pass (DEC-003, DEC-033). Then each admitted worktree is created outside the
|
|
18
|
+
// lock, and the second section, one `mutateDispatch` per dispatch, re-checks the slot and makes the
|
|
19
|
+
// in-process 080 `claim` with `reserveTokens` (IC-008), `execWorktree`, `epoch` and `owner`. A
|
|
20
|
+
// refusal or a throw drops the slot in the same write; `removeVerified` runs outside the lock. A
|
|
21
|
+
// budget refusal is a gap with cause `budget` (IC-027).
|
|
22
|
+
//
|
|
23
|
+
// Every write goes through `store.ts` (`updateTaskState`); this module makes no fs write call.
|
|
24
|
+
// Every function is synchronous, so a barrier race worker can call it directly.
|
|
25
|
+
|
|
26
|
+
const fs = require('node:fs');
|
|
27
|
+
const { inspect, claim } = require('../task-attempts.js');
|
|
28
|
+
const { overlaps } = require('../task-graph.js');
|
|
29
|
+
const { normalizeFile } = require('./envelope.js');
|
|
30
|
+
const { readTaskState } = require('../task-state.js');
|
|
31
|
+
const { procStart, alive } = require('../worker/process-tree.js');
|
|
32
|
+
const { storeFileFor: runStoreFileFor, readRuns } = require('../worker/run-store.js');
|
|
33
|
+
const { statusView } = require('../worker/command.js');
|
|
34
|
+
const { storeFileFor, readDispatch, mutateDispatch, newDispatchId, setRunState, DispatchStoreError } = require('./store.js');
|
|
35
|
+
const { managerFor, createAttemptWorktree } = require('./integrate.js');
|
|
36
|
+
const { copyGrant } = require('./grants.js');
|
|
37
|
+
const { resolveBinding } = require('../worker/models.js');
|
|
38
|
+
|
|
39
|
+
/** DEC-002: concurrency bounds and default. */
|
|
40
|
+
const CONCURRENCY_MIN = 1;
|
|
41
|
+
const CONCURRENCY_MAX = 4;
|
|
42
|
+
const CONCURRENCY_DEFAULT = 2;
|
|
43
|
+
/** IC-022's main-session modes: at most one such dispatch is non-ended (IC-007 step 7). */
|
|
44
|
+
const MAIN_SESSION_MODES = Object.freeze(['sequential', 'manual']);
|
|
45
|
+
const MODES = Object.freeze(['subagent', 'sequential', 'manual']);
|
|
46
|
+
/** 080 claim refusals that are a gap with cause `budget` (IC-007, IC-027). */
|
|
47
|
+
const BUDGET_REFUSALS = Object.freeze(['aggregate-exhausted', 'budget-exhausted', 'reservation-required']);
|
|
48
|
+
/** IC-009 lost reasons, by `alive` reading. */
|
|
49
|
+
const LOST_REASONS = Object.freeze({ false: 'driver-lost', unknown: 'driver-liveness-unknown' });
|
|
50
|
+
/** 081 statuses that mean a worker run is not gone (IC-007 step 8). */
|
|
51
|
+
const LIVE_WORKER_STATES = Object.freeze(['starting', 'running']);
|
|
52
|
+
const REASON_CHARS = 200;
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
function usage(message ) {
|
|
57
|
+
return new DispatchStoreError('usage', message);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const sameDriver = (a , b ) => !!a && !!b && a.pid === b.pid && (a.start ?? null) === (b.start ?? null)
|
|
61
|
+
&& (a.registeredAt ?? null) === (b.registeredAt ?? null);
|
|
62
|
+
|
|
63
|
+
const clip = (text ) => String(text ?? '').slice(0, REASON_CHARS);
|
|
64
|
+
|
|
65
|
+
/** One StepAction (IC-003). */
|
|
66
|
+
function action(name , d , reason , ms = 0) {
|
|
67
|
+
return { action: name, dispatchId: d.dispatchId ?? null, task: d.task ?? null, token: d.token ?? null, reason: clip(reason), ms };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ── drivers (IC-009) ─────────────────────────────────────────────────────────────────────────────
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* IC-009: registers this invocation's driver. Every other driver with no `endedAt` and no `lost`
|
|
74
|
+
* is read with `alive` outside the lock; the locked write marks each one read `false` or
|
|
75
|
+
* `unknown` lost (`lost` its reason) and sets the run `resume-required` with `resumeReason`.
|
|
76
|
+
* A driver registered after the read is left for the next reading. `aliveImpl` and `startImpl` are
|
|
77
|
+
* seams for an injected reading. Returns `{ driver, lost: [{ pid, start, reason }], state }`.
|
|
78
|
+
*/
|
|
79
|
+
function registerDriver({
|
|
80
|
+
file, pid = process.pid, now = new Date(), aliveImpl = alive, startImpl = procStart, fsImpl = fs,
|
|
81
|
+
} ) {
|
|
82
|
+
if (typeof file !== 'string') throw usage('registerDriver needs the dispatch store file');
|
|
83
|
+
if (!Number.isInteger(pid) || pid <= 0) throw usage(`driver pid '${pid}' is not a positive whole number`);
|
|
84
|
+
const driver = { pid, start: startImpl(pid) ?? null, registeredAt: now.toISOString(), endedAt: null };
|
|
85
|
+
|
|
86
|
+
// Outside the lock: the readings, `ps` being a process.
|
|
87
|
+
const before = readDispatch(file, fsImpl);
|
|
88
|
+
const readings = (before.drivers )
|
|
89
|
+
.filter((d) => d && !d.endedAt && !d.lost)
|
|
90
|
+
.map((d) => ({ driver: { pid: d.pid, start: d.start ?? null, registeredAt: d.registeredAt ?? null }, live: aliveImpl({ pid: d.pid, pgid: d.pid, start: d.start ?? null }) }));
|
|
91
|
+
|
|
92
|
+
return mutateDispatch(file, (store ) => {
|
|
93
|
+
const lost = [];
|
|
94
|
+
for (const { driver: read, live } of readings) {
|
|
95
|
+
if (live === true) continue;
|
|
96
|
+
const reason = live === false ? LOST_REASONS.false : LOST_REASONS.unknown;
|
|
97
|
+
const stored = (store.drivers ).find((d) => sameDriver(d, read));
|
|
98
|
+
if (!stored || stored.endedAt || stored.lost) continue;
|
|
99
|
+
stored.lost = reason;
|
|
100
|
+
lost.push({ pid: stored.pid, start: stored.start ?? null, reason });
|
|
101
|
+
}
|
|
102
|
+
store.drivers.push(driver);
|
|
103
|
+
if (lost.length && store.run && setRunState(store, 'resume-required')) store.run.resumeReason = lost[0].reason;
|
|
104
|
+
return { write: true, result: { driver: structuredClone(driver), lost, state: store.run ? store.run.state : null } };
|
|
105
|
+
}, fsImpl);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* IC-009: writes `endedAt` on this invocation's driver, add-only. With `endReason: 'threw'` (a pass
|
|
110
|
+
* that threw, RT-5) the same write records the reason and sets the run `resume-required`, reason
|
|
111
|
+
* `driver-threw`, unless it holds a state another writer owns, so IC-032 settles what the pass left
|
|
112
|
+
* in flight. `{ ended }`.
|
|
113
|
+
*/
|
|
114
|
+
function endDriver({ file, driver, now = new Date(), endReason = null, fsImpl = fs } ) {
|
|
115
|
+
if (!driver || !Number.isInteger(driver.pid)) throw usage('endDriver needs the driver registerDriver returned');
|
|
116
|
+
return mutateDispatch(file, (store ) => {
|
|
117
|
+
const stored = (store.drivers ).find((d) => sameDriver(d, driver));
|
|
118
|
+
if (!stored || stored.endedAt) return { write: false, result: { ended: false } };
|
|
119
|
+
stored.endedAt = now.toISOString();
|
|
120
|
+
if (endReason === 'threw') {
|
|
121
|
+
stored.endReason = 'threw';
|
|
122
|
+
if (store.run && setRunState(store, 'resume-required')) store.run.resumeReason = 'driver-threw';
|
|
123
|
+
}
|
|
124
|
+
return { write: true, result: { ended: true } };
|
|
125
|
+
}, fsImpl);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ── admission (IC-007) ───────────────────────────────────────────────────────────────────────────
|
|
129
|
+
|
|
130
|
+
/** `modeFor`'s answer as `{ mode, harness, selection }`, or `{ hold }` for a held task (106 IC-004). */
|
|
131
|
+
function modeOf(answer , task ) {
|
|
132
|
+
if (answer && typeof answer.hold === 'string') return { hold: answer.hold };
|
|
133
|
+
const out = typeof answer === 'string' ? { mode: answer } : (answer && typeof answer === 'object' ? answer : {});
|
|
134
|
+
if (!MODES.includes(out.mode)) throw usage(`modeFor('${task}') returned mode '${out.mode}', not one of ${MODES.join(', ')}`);
|
|
135
|
+
return { mode: out.mode, harness: out.mode === 'subagent' ? out.harness ?? null : null, selection: out.selection ?? null };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** IC-013 / IC-010: validate and copy an optional admission model snapshot. */
|
|
139
|
+
function modelOf(answer , task ) {
|
|
140
|
+
if (answer === undefined || answer === null) {
|
|
141
|
+
return { model: { tier: null, requested: null, source: null }, primary: null };
|
|
142
|
+
}
|
|
143
|
+
const object = (value ) => value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
144
|
+
const nullableString = (value ) => value === null || typeof value === 'string';
|
|
145
|
+
const model = answer.model;
|
|
146
|
+
const primary = answer.primary;
|
|
147
|
+
if (!object(answer) || !object(model) || !['light', 'standard', 'frontier'].includes(model.tier)
|
|
148
|
+
|| !nullableString(model.requested) || !['binding', 'harness-default'].includes(model.source)
|
|
149
|
+
|| (primary !== null && (!object(primary)
|
|
150
|
+
|| Object.keys(primary).sort().join(',') !== 'harness,model,state'
|
|
151
|
+
|| !nullableString(primary.harness) || !nullableString(primary.model)
|
|
152
|
+
|| typeof primary.state !== 'string' || primary.state.length === 0))) {
|
|
153
|
+
throw usage(`modelFor('${task}') returned an invalid model snapshot`);
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
model: { tier: model.tier, requested: model.requested, source: model.source },
|
|
157
|
+
primary: primary === null ? null : { harness: primary.harness, model: primary.model, state: primary.state },
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** IC-013 / IC-010: resolve record metadata without choosing launch argv or reading settings. */
|
|
162
|
+
function dispatchModelFor({ mode, harness, tier, bindings, primary } ) {
|
|
163
|
+
const worker = mode === 'subagent' ? harness : mode === 'sequential' ? primary?.harness : null;
|
|
164
|
+
const binding = worker ? resolveBinding({ worker, tier, bindings, descriptor: null }) : null;
|
|
165
|
+
const bound = binding?.source === 'binding';
|
|
166
|
+
return {
|
|
167
|
+
model: { tier, requested: bound ? binding.model : null, source: bound ? 'binding' : 'harness-default' },
|
|
168
|
+
primary: mode === 'sequential' && primary
|
|
169
|
+
? { harness: primary.harness, model: primary.model, state: primary.state }
|
|
170
|
+
: null,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** The 081 status of every worker run a dispatch of `store` names, read outside the lock. */
|
|
175
|
+
function workerStatuses(root , slug , store , ledgerFile , fsImpl , nowMs ) {
|
|
176
|
+
const named = (Object.values(store.dispatches) ).filter((d) => d.workerRunId);
|
|
177
|
+
const out = new Map ();
|
|
178
|
+
if (!named.length) return out;
|
|
179
|
+
const runs = readRuns(runStoreFileFor(root, slug), fsImpl);
|
|
180
|
+
const ledger = readTaskState(fsImpl, ledgerFile) || { tasks: {}, runs: {} };
|
|
181
|
+
for (const d of named) {
|
|
182
|
+
const run = runs[d.workerRunId];
|
|
183
|
+
out.set(d.workerRunId, run ? statusView(run, ledger, { platform: process.platform, nowMs }) : null);
|
|
184
|
+
}
|
|
185
|
+
return out;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** IC-007 step 8: a prior ended dispatch of `task` whose worker run is not gone, or null. */
|
|
189
|
+
function workerNotGone(store , task , statuses ) {
|
|
190
|
+
for (const d of Object.values(store.dispatches) ) {
|
|
191
|
+
if (d.task !== task || d.phase !== 'ended' || !d.workerRunId) continue;
|
|
192
|
+
const status = statuses.get(d.workerRunId);
|
|
193
|
+
const cleanup = d.cleanup ? d.cleanup.cleanup ?? d.cleanup : status ? status.cleanup : null;
|
|
194
|
+
if ((status && LIVE_WORKER_STATES.includes(status.state)) || (cleanup && cleanup.status === 'survivors')) return d;
|
|
195
|
+
}
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const isCandidate = (record ) => record.state === 'READY' || (record.state === 'FAILED' && record.claimable === true);
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* IC-007, phase 6 of IC-003. `ctx` is B.3's 080 context (`ledgerFile`, `roots`, `slug`, `env`,
|
|
203
|
+
* `cwd`, `fsImpl`); `root` the lifecycle root holding the dispatch store and worktrees; `driver` the
|
|
204
|
+
* `{ pid, start }` this pass registered; `by` the owner when a plan task names none; `modeFor(task,
|
|
205
|
+
* { record, store })` IC-022's choice, called outside the lock once per candidate, returning a mode
|
|
206
|
+
* or `{ mode, harness, selection }`. Optional `modelFor(task, { mode, harness, selection })`
|
|
207
|
+
* supplies IC-013 / IC-010 metadata beside that choice, outside every lock and never for a hold.
|
|
208
|
+
*
|
|
209
|
+
* Returns `{ outcome, admitted, skipped, refused, gaps, actions }`: `outcome` is `admitted` when a
|
|
210
|
+
* dispatch was claimed, else `none` or the reason nothing was admitted (`paused`, the run's state,
|
|
211
|
+
* `no-run`, `verification-undeclared`, `concurrency-full`). `actions` are IC-003 StepActions for the
|
|
212
|
+
* caller's step record.
|
|
213
|
+
*/
|
|
214
|
+
function admit({
|
|
215
|
+
root, slug, ctx, driver, by, modeFor, modelFor, env = process.env, now = () => new Date(), storeFile = null,
|
|
216
|
+
} ) {
|
|
217
|
+
if (typeof modeFor !== 'function') throw usage('admit needs modeFor, IC-022\'s mode choice');
|
|
218
|
+
if (!ctx || typeof ctx.ledgerFile !== 'string' || !Array.isArray(ctx.roots)) throw usage('admit needs ctx with ledgerFile and roots');
|
|
219
|
+
const fsImpl = ctx.fsImpl || fs;
|
|
220
|
+
const file = storeFile ?? storeFileFor(root, slug);
|
|
221
|
+
const who = driver ? { pid: driver.pid, start: driver.start ?? null } : { pid: process.pid, start: null };
|
|
222
|
+
const empty = (outcome , extra = {}) => ({ outcome, admitted: [], skipped: [], refused: [], gaps: [], actions: [], ...extra });
|
|
223
|
+
|
|
224
|
+
// Outside every lock: 080 inspect, 081 statuses and the mode per candidate.
|
|
225
|
+
const view = inspect({ file: ctx.ledgerFile, fsImpl, roots: ctx.roots, slug, env: ctx.env ?? env, cwd: ctx.cwd });
|
|
226
|
+
const pre = readDispatch(file, fsImpl);
|
|
227
|
+
const statuses = workerStatuses(root, slug, pre, ctx.ledgerFile, fsImpl, now().getTime());
|
|
228
|
+
const graph = { tasks: view.graph.tasks };
|
|
229
|
+
const filesOf = new Map (view.graph.tasks.map((t ) => [t.id, (t.files || []).map(normalizeFile).filter(Boolean)]));
|
|
230
|
+
const ownerOf = new Map (view.graph.tasks.map((t ) => [t.id, t.owner ?? null]));
|
|
231
|
+
const pairs = new Set (overlaps(graph).flatMap((p ) => [`${p.a}\u0000${p.b}`, `${p.b}\u0000${p.a}`]));
|
|
232
|
+
const modes = new Map ();
|
|
233
|
+
const models = new Map ();
|
|
234
|
+
for (const record of view.tasks) {
|
|
235
|
+
if (!isCandidate(record)) continue;
|
|
236
|
+
const choice = modeOf(modeFor(record.id, { record, store: pre }), record.id);
|
|
237
|
+
modes.set(record.id, choice);
|
|
238
|
+
if (choice.hold) continue;
|
|
239
|
+
models.set(record.id, modelOf(typeof modelFor === 'function' ? modelFor(record.id, choice) : null, record.id));
|
|
240
|
+
}
|
|
241
|
+
const manager = managerFor(root, slug, 'attempt', env);
|
|
242
|
+
|
|
243
|
+
// First section: IC-007 steps 1 to 8 and the admitted slots.
|
|
244
|
+
const first = mutateDispatch(file, (store ) => {
|
|
245
|
+
const run = store.run;
|
|
246
|
+
if (!run) return { write: false, result: empty('no-run') };
|
|
247
|
+
if (run.state !== 'running' || view.run.control !== 'running') {
|
|
248
|
+
return { write: false, result: empty(view.run.control === 'paused' ? 'paused' : run.state !== 'running' ? run.state : view.run.control) };
|
|
249
|
+
}
|
|
250
|
+
if (!run.verify || !Array.isArray(run.verify.argv)) return { write: false, result: empty('verification-undeclared') };
|
|
251
|
+
const concurrency = Number.isInteger(run.concurrency) ? run.concurrency : CONCURRENCY_DEFAULT;
|
|
252
|
+
const nonEnded = (Object.values(store.dispatches) ).filter((d) => d.phase !== 'ended');
|
|
253
|
+
if (nonEnded.length >= concurrency) return { write: false, result: empty('concurrency-full') };
|
|
254
|
+
|
|
255
|
+
// Every non-ended dispatch's task, the ones admitted below included.
|
|
256
|
+
const busy = nonEnded.map((d) => ({ task: d.task, dispatchId: d.dispatchId, mode: d.mode }));
|
|
257
|
+
// The write sets a candidate is checked against: every non-ended dispatch, not only this pass's (DEC-003).
|
|
258
|
+
const holders = busy.map((b) => ({ task: b.task, dispatchId: b.dispatchId }));
|
|
259
|
+
const admitted = [];
|
|
260
|
+
const skipped = [];
|
|
261
|
+
for (const record of view.tasks) {
|
|
262
|
+
const task = record.id;
|
|
263
|
+
const active = busy.find((b) => b.task === task);
|
|
264
|
+
if (active) { skipped.push({ task, reason: 'active-dispatch', dispatchId: active.dispatchId }); continue; }
|
|
265
|
+
if (!isCandidate(record)) continue;
|
|
266
|
+
if (modes.get(task)?.hold) { skipped.push({ task, reason: `held:${modes.get(task).hold}` }); continue; }
|
|
267
|
+
if (nonEnded.length + admitted.length >= concurrency) { skipped.push({ task, reason: 'concurrency' }); continue; }
|
|
268
|
+
const mine = filesOf.get(task) || [];
|
|
269
|
+
const clash = holders.find((b) => !mine.length || !(filesOf.get(b.task) || []).length || pairs.has(`${task}\u0000${b.task}`));
|
|
270
|
+
if (clash) { skipped.push({ task, reason: 'overlap', dispatchId: clash.dispatchId, with: clash.task }); continue; }
|
|
271
|
+
const choice = modes.get(task);
|
|
272
|
+
if (!choice) { skipped.push({ task, reason: 'no-mode' }); continue; }
|
|
273
|
+
const mainBusy = MAIN_SESSION_MODES.includes(choice.mode) && busy.find((b) => MAIN_SESSION_MODES.includes(b.mode));
|
|
274
|
+
if (mainBusy) { skipped.push({ task, reason: 'main-session-busy', dispatchId: mainBusy.dispatchId }); continue; }
|
|
275
|
+
const gone = workerNotGone(store, task, statuses);
|
|
276
|
+
if (gone) { skipped.push({ task, reason: 'worker-not-gone', dispatchId: gone.dispatchId }); continue; }
|
|
277
|
+
|
|
278
|
+
const dispatchId = newDispatchId();
|
|
279
|
+
const metadata = models.get(task);
|
|
280
|
+
store.dispatches[dispatchId] = {
|
|
281
|
+
dispatchId, dispatchRunId: run.dispatchRunId ?? null, parentRunKey: run.runKey ?? view.run.runKey ?? null,
|
|
282
|
+
task, token: null, epoch: null, phase: 'admitted', mode: choice.mode, harness: choice.harness,
|
|
283
|
+
selection: choice.selection, grant: copyGrant(run.spawnGrant ?? null, choice.mode),
|
|
284
|
+
model: { ...metadata.model, served: null },
|
|
285
|
+
...(choice.mode === 'sequential' && metadata.primary ? { primary: { ...metadata.primary } } : {}),
|
|
286
|
+
workerRunId: null, disposition: null, failureClass: null,
|
|
287
|
+
attempt: { worktree: manager.pathFor(dispatchId), branch: manager.branchFor(dispatchId), base: run.integration ? run.integration.head : null },
|
|
288
|
+
admittedAt: now().toISOString(), admittedBy: who,
|
|
289
|
+
};
|
|
290
|
+
if (MAIN_SESSION_MODES.includes(choice.mode) && run.held && Object.hasOwn(run.held, task)) delete run.held[task];
|
|
291
|
+
busy.push({ task, dispatchId, mode: choice.mode });
|
|
292
|
+
holders.push({ task, dispatchId });
|
|
293
|
+
admitted.push({ dispatchId, task, base: store.dispatches[dispatchId].attempt.base });
|
|
294
|
+
}
|
|
295
|
+
return { write: admitted.length > 0, result: { outcome: admitted.length ? 'admitted' : 'none', slots: admitted, skipped } };
|
|
296
|
+
}, fsImpl);
|
|
297
|
+
if (!first.slots) return first;
|
|
298
|
+
|
|
299
|
+
const result = { outcome: 'none', admitted: [], skipped: first.skipped, refused: [], gaps: [], actions: [] };
|
|
300
|
+
for (const s of first.skipped) result.actions.push(action('admit', { dispatchId: s.dispatchId, task: s.task }, `skipped:${s.reason}`));
|
|
301
|
+
|
|
302
|
+
for (const slot of first.slots) {
|
|
303
|
+
const started = Date.now();
|
|
304
|
+
const { dispatchId, task } = slot;
|
|
305
|
+
// Outside the lock: the attempt worktree (IC-016).
|
|
306
|
+
let made = null;
|
|
307
|
+
let madeError = null;
|
|
308
|
+
try {
|
|
309
|
+
if (slot.base) made = createAttemptWorktree({ root, slug, dispatchId, base: slot.base, env });
|
|
310
|
+
else madeError = 'integration-head-unknown';
|
|
311
|
+
} catch (error ) {
|
|
312
|
+
madeError = `worktree-failed:${error && (error.status ?? error.code) ? String(error.status ?? error.code) : 'error'}`;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Second section: re-check the slot, then the in-process 080 claim.
|
|
316
|
+
const second = mutateDispatch(file, (store ) => {
|
|
317
|
+
const d = store.dispatches[dispatchId];
|
|
318
|
+
if (!d || d.phase !== 'admitted') return { write: false, result: { kind: 'gone', phase: d ? d.phase : null } };
|
|
319
|
+
const drop = (reason , refusal = null) => {
|
|
320
|
+
delete store.dispatches[dispatchId];
|
|
321
|
+
return { write: true, result: { kind: 'refused', reason, refusal } };
|
|
322
|
+
};
|
|
323
|
+
if (madeError) return drop(madeError);
|
|
324
|
+
if (!store.run || store.run.state !== 'running') return drop(`run-${store.run ? store.run.state : 'missing'}`);
|
|
325
|
+
const reserve = Number.isInteger(store.run.reserveTokens) ? store.run.reserveTokens : null;
|
|
326
|
+
let claimed ;
|
|
327
|
+
try {
|
|
328
|
+
claimed = claim({
|
|
329
|
+
file: ctx.ledgerFile, fsImpl, roots: ctx.roots, slug, task, owner: ownerOf.get(task) || by, mode: d.mode,
|
|
330
|
+
epoch: view.run.epoch, reserveTokens: reserve, execWorktree: made.worktree,
|
|
331
|
+
env: ctx.env ?? env, cwd: ctx.cwd, now: now(),
|
|
332
|
+
});
|
|
333
|
+
} catch (error ) {
|
|
334
|
+
return drop(`error:${error && error.code ? error.code : 'throw'}`, { message: clip(error && error.message) });
|
|
335
|
+
}
|
|
336
|
+
if (!claimed || claimed.disposition !== 'claimed') return drop(claimed ? claimed.reason : 'no-result', claimed);
|
|
337
|
+
Object.assign(d, {
|
|
338
|
+
phase: 'claimed', token: claimed.token, epoch: claimed.epoch ?? view.run.epoch,
|
|
339
|
+
parentRunKey: claimed.runKey ?? d.parentRunKey, attempt: made, reservation: { tokens: reserve },
|
|
340
|
+
claimedAt: now().toISOString(), claimedBy: who,
|
|
341
|
+
});
|
|
342
|
+
return { write: true, result: { kind: 'claimed', dispatch: structuredClone(d) } };
|
|
343
|
+
}, fsImpl);
|
|
344
|
+
|
|
345
|
+
const ms = Date.now() - started;
|
|
346
|
+
if (second.kind === 'claimed') {
|
|
347
|
+
const d = second.dispatch;
|
|
348
|
+
result.admitted.push({ dispatchId, task, token: d.token, epoch: d.epoch, mode: d.mode, harness: d.harness, worktree: d.attempt.worktree });
|
|
349
|
+
result.actions.push(action('admit', d, `claimed:${d.mode}`, ms));
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
if (second.kind === 'gone') {
|
|
353
|
+
// A cancel or resume dropped the slot before its worktree existed: remove it here (RT-14).
|
|
354
|
+
if (made) manager.removeVerified(dispatchId);
|
|
355
|
+
result.actions.push(action('admit', { dispatchId, task }, `slot-moved:${second.phase ?? 'dropped'}`, ms));
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
// A dropped slot: its worktree is removed outside the lock.
|
|
359
|
+
const removal = made || madeError ? manager.removeVerified(dispatchId) : null;
|
|
360
|
+
const refused = { dispatchId, task, reason: second.reason, refusal: second.refusal, removal };
|
|
361
|
+
if (BUDGET_REFUSALS.includes(second.reason)) {
|
|
362
|
+
refused.gap = { task, kind: 'claim-refused', cause: 'budget', reason: second.reason };
|
|
363
|
+
result.gaps.push(refused.gap);
|
|
364
|
+
}
|
|
365
|
+
result.refused.push(refused);
|
|
366
|
+
result.actions.push(action('claim-refused', { dispatchId, task }, second.reason, ms));
|
|
367
|
+
}
|
|
368
|
+
result.outcome = result.admitted.length ? 'admitted' : 'none';
|
|
369
|
+
return result;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/** DEC-002: `value` as a concurrency, default 2; anything outside 1 to 4 is `usage`. */
|
|
373
|
+
function concurrencyOf(value ) {
|
|
374
|
+
if (value === undefined || value === null) return CONCURRENCY_DEFAULT;
|
|
375
|
+
const n = typeof value === 'string' && /^\d+$/.test(value) ? Number(value) : value;
|
|
376
|
+
if (!Number.isInteger(n) || n < CONCURRENCY_MIN || n > CONCURRENCY_MAX) throw usage(`--concurrency expects ${CONCURRENCY_MIN} to ${CONCURRENCY_MAX}, got '${value}'`);
|
|
377
|
+
return n;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
module.exports = {
|
|
381
|
+
registerDriver,
|
|
382
|
+
endDriver,
|
|
383
|
+
admit,
|
|
384
|
+
dispatchModelFor,
|
|
385
|
+
concurrencyOf,
|
|
386
|
+
CONCURRENCY_MIN,
|
|
387
|
+
CONCURRENCY_MAX,
|
|
388
|
+
CONCURRENCY_DEFAULT,
|
|
389
|
+
MAIN_SESSION_MODES,
|
|
390
|
+
BUDGET_REFUSALS,
|
|
391
|
+
LOST_REASONS,
|
|
392
|
+
};
|