@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
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Retirement sweep (row 127 IC-003, design D2): release, delete or strip what a retired harness
|
|
7
|
+
* installed, driven only by the scope's ledger and the retired record in the registry. The retired
|
|
8
|
+
* harness has no adapter any more, so nothing here calls one.
|
|
9
|
+
*
|
|
10
|
+
* Every retired row gets exactly one change, first matching rule wins:
|
|
11
|
+
* 1. a live change in this run removes or supersedes its target -> drop-row
|
|
12
|
+
* 2. a non-retired ledger row has the same claim key -> release (file stays)
|
|
13
|
+
* 3. kind copy-tree-file | hooks-script: absent -> drop-row; bytes equal the row's fingerprint and the
|
|
14
|
+
* path resolves to itself -> delete; otherwise keep (named)
|
|
15
|
+
* 3a. a `removal.handover` entry (renderer + scope) at the replacement's own declared path: replacement
|
|
16
|
+
* planned in this run -> handover (it rewrites the span itself); installed but not planned -> defer
|
|
17
|
+
* (row and file stay, notice); otherwise on to rule 4. Decided by planHandovers before any live plan.
|
|
18
|
+
* 4. renderer in `markerSectionRenderers` -> strip-section (either start marker, see START_MARKERS); no
|
|
19
|
+
* markers -> drop-row; malformed -> keep
|
|
20
|
+
* 5. renderer = `hookCommands.renderer` -> strip-hooks (exact frozen commands only); unparseable -> keep;
|
|
21
|
+
* no matching command -> drop-row
|
|
22
|
+
* 6. anything else -> keep, `unknown row shape <kind>/<renderer>`
|
|
23
|
+
* Every action except `defer` gives the row up (retiredReleaseKeys). Actions that touch no file carry
|
|
24
|
+
* `release: true`, which keeps them out of the backup set like any other release; a handed-over file
|
|
25
|
+
* is backed up through the replacement's own change, which is the one write it gets.
|
|
26
|
+
*/
|
|
27
|
+
const fs = require('node:fs');
|
|
28
|
+
const path = require('node:path');
|
|
29
|
+
const crypto = require('node:crypto');
|
|
30
|
+
const { isRetired } = require('../registry/index.js');
|
|
31
|
+
const { resolveHarnessPaths } = require('../helper/harness-paths.js');
|
|
32
|
+
const { ownershipKey, atomicJsonWrite } = require('../state/index.js');
|
|
33
|
+
const { removeTree, readJson } = require('../adapters/copy-tree.js');
|
|
34
|
+
const { removeMarkedSection, markedSpan, MARKER_START } = require('../helper/marker-merge.js');
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
const WHOLE_FILE_KINDS = new Set(['copy-tree-file', 'hooks-script']);
|
|
56
|
+
/** Actions with no filesystem effect: the row is given up and nothing is written or deleted. */
|
|
57
|
+
const NO_FS_EFFECT = new Set (['release', 'handover', 'defer', 'drop-row', 'keep']);
|
|
58
|
+
/** The count each action adds to; every action has one, so the counts sum to the rows swept. */
|
|
59
|
+
const COUNTED = {
|
|
60
|
+
delete: 'deleted', 'strip-section': 'stripped', 'strip-hooks': 'stripped', release: 'released', handover: 'released',
|
|
61
|
+
'drop-row': 'dropped', defer: 'deferred', keep: 'kept',
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/** What one claim covers: a whole file, except an MCP entry, which is claimed on its own because
|
|
65
|
+
* several harnesses keep their servers side by side in one MCP file. The one definition: the
|
|
66
|
+
* lifecycle (src/lifecycle/index.js) imports it from here, since it requires this module. */
|
|
67
|
+
function claimKey(record ) {
|
|
68
|
+
return record.kind === 'mcp-server' ? `${record.target}\u0000${record.identity}` : record.target;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function sha256(bytes ) { return crypto.createHash('sha256').update(bytes).digest('hex'); }
|
|
72
|
+
|
|
73
|
+
/** Whether `target` is inside `scopeRoot` and resolves to that same place: no symlink between the
|
|
74
|
+
* scope root and the file (the file itself included) leads somewhere else. */
|
|
75
|
+
function resolvesInPlace(target , scopeRoot , fsImpl ) {
|
|
76
|
+
const rel = path.relative(scopeRoot, target);
|
|
77
|
+
if (!rel || rel === '..' || rel.startsWith(`..${path.sep}`) || path.isAbsolute(rel)) return false;
|
|
78
|
+
try { return fsImpl.realpathSync(target) === path.join(fsImpl.realpathSync(scopeRoot), rel); } catch { return false; }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** The `hooks` value with every handler whose command is in `commands` taken out, and any matcher
|
|
82
|
+
* group, event array or the key itself that this left empty. Containers that were already empty,
|
|
83
|
+
* and every handler not in the list, stay as they are. Returns the new document, or null when no
|
|
84
|
+
* handler matched. */
|
|
85
|
+
function stripHookCommands(doc , key , commands ) {
|
|
86
|
+
const hooks = doc[key];
|
|
87
|
+
let removed = false;
|
|
88
|
+
const nextHooks = {};
|
|
89
|
+
for (const [event, groups] of Object.entries(hooks)) {
|
|
90
|
+
if (!Array.isArray(groups)) { nextHooks[event] = groups; continue; }
|
|
91
|
+
let eventChanged = false;
|
|
92
|
+
const nextGroups = [];
|
|
93
|
+
for (const group of groups) {
|
|
94
|
+
if (!group || !Array.isArray(group.hooks)) { nextGroups.push(group); continue; }
|
|
95
|
+
const handlers = group.hooks.filter((handler ) => !commands.has(handler?.command));
|
|
96
|
+
if (handlers.length === group.hooks.length) { nextGroups.push(group); continue; }
|
|
97
|
+
eventChanged = true;
|
|
98
|
+
if (handlers.length) nextGroups.push({ ...group, hooks: handlers });
|
|
99
|
+
}
|
|
100
|
+
if (eventChanged) removed = true;
|
|
101
|
+
if (!eventChanged || nextGroups.length) nextHooks[event] = eventChanged ? nextGroups : groups;
|
|
102
|
+
}
|
|
103
|
+
if (!removed) return null;
|
|
104
|
+
const next = { ...doc };
|
|
105
|
+
if (Object.keys(nextHooks).length) next[key] = nextHooks;
|
|
106
|
+
else delete next[key];
|
|
107
|
+
return next;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** The hooks JSON at `target` parsed, or a reason it cannot be stripped. Absent file -> `absent`. */
|
|
111
|
+
function readHooksDoc(target , key , fsImpl ) {
|
|
112
|
+
const { exists, value, error } = readJson(target, { fsImpl });
|
|
113
|
+
if (!exists) return { absent: true };
|
|
114
|
+
if (error) return { error: 'not valid JSON' };
|
|
115
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return { error: 'not a JSON object' };
|
|
116
|
+
if (value[key] !== undefined && (!value[key] || typeof value[key] !== 'object' || Array.isArray(value[key]))) return { error: `'${key}' is not an object` };
|
|
117
|
+
return { doc: value };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** The start markers a retired section may open with: marker-merge's, and the short one the retired
|
|
121
|
+
* gemini adapter wrote (src/adapters/gemini/index.ts @ c35f681b), which its real sections carry. Both
|
|
122
|
+
* close with MARKER_END. Only this sweep reads the short one; live harnesses never see it. */
|
|
123
|
+
const START_MARKERS = [MARKER_START, '<!-- doflow:start -->'];
|
|
124
|
+
|
|
125
|
+
/** Marker state of a file, by the rules removeMarkedSection applies with START_MARKERS. */
|
|
126
|
+
function markerState(text ) { return markedSpan(text, START_MARKERS).state; }
|
|
127
|
+
|
|
128
|
+
/** Rule 3a's verdict for one retired row (IC-003, IC-007, design D7). */
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Rule 3a, decided before the live harnesses plan, because the replacement needs the verdict to plan
|
|
133
|
+
* its own change: `context.handovers` for each `to`. A retired row qualifies when a `removal.handover`
|
|
134
|
+
* entry names its renderer and this scope, its file is still there in place, and the file is the one
|
|
135
|
+
* the replacement itself declares for that surface here. Then: `to` planned in this run -> handover,
|
|
136
|
+
* `edited` when the whole file no longer has the bytes the row recorded; `to` installed in the scope
|
|
137
|
+
* (a ledger row of it, or its `targets` entry; a lock pins only harnesses the ledger holds, IC-004)
|
|
138
|
+
* -> defer. Rules 2 and 3 come first, as in classify. Returns the decisions by ownership key.
|
|
139
|
+
*/
|
|
140
|
+
function planHandovers({ registry, ledger, scope, scopeRoot, planned = [], fsImpl = fs } ) {
|
|
141
|
+
const decisions = new Map ();
|
|
142
|
+
const retiredRecords = Array.isArray(registry?.retired) ? registry.retired : [];
|
|
143
|
+
if (!retiredRecords.length) return decisions;
|
|
144
|
+
const rows = (ledger?.resources ?? []).filter((row ) => typeof row?.target === 'string' && typeof row?.harness === 'string');
|
|
145
|
+
const liveClaims = new Set (rows.filter((row ) => !isRetired(registry, row.harness)).map(claimKey));
|
|
146
|
+
const plannedIds = new Set (planned);
|
|
147
|
+
for (const retired of retiredRecords) {
|
|
148
|
+
const entries = (retired.removal?.handover ?? []).filter((entry ) => entry?.scope === scope);
|
|
149
|
+
if (!entries.length) continue;
|
|
150
|
+
for (const row of rows.filter((item ) => item.harness === retired.id)) {
|
|
151
|
+
const entry = entries.find((item ) => item.renderer === row.projection?.renderer);
|
|
152
|
+
if (!entry || liveClaims.has(claimKey(row)) || WHOLE_FILE_KINDS.has(row.kind)) continue;
|
|
153
|
+
const to = registry.harnesses?.find((harness ) => harness.id === entry.to);
|
|
154
|
+
if (!to?.paths?.[entry.surface]) continue;
|
|
155
|
+
let declared = null;
|
|
156
|
+
try { declared = resolveHarnessPaths({ [entry.surface]: to.paths[entry.surface] }, { scope, scopeRoot, homeDir: scopeRoot })[entry.surface]; } catch { continue; }
|
|
157
|
+
if (declared !== row.target || !fsImpl.existsSync(row.target) || !resolvesInPlace(row.target, scopeRoot, fsImpl)) continue;
|
|
158
|
+
const base = { to: to.id, target: row.target, fromKey: ownershipKey(row) };
|
|
159
|
+
if (plannedIds.has(to.id)) {
|
|
160
|
+
decisions.set(base.fromKey, { action: 'handover', ...base, edited: sha256(fsImpl.readFileSync(row.target)) !== row.fingerprint });
|
|
161
|
+
} else if (rows.some((item ) => item.harness === to.id) || ledger?.targets?.[to.id]) {
|
|
162
|
+
decisions.set(base.fromKey, { action: 'defer', ...base, edited: false });
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return decisions;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** What the replacement is told about each handover in this run: `context.handovers` per harness id. */
|
|
170
|
+
function handoversFor(decisions ) {
|
|
171
|
+
const byHarness = new Map ();
|
|
172
|
+
for (const { action, to, target, fromKey, edited } of decisions.values()) {
|
|
173
|
+
if (action !== 'handover') continue;
|
|
174
|
+
if (!byHarness.has(to)) byHarness.set(to, []);
|
|
175
|
+
byHarness.get(to) .push({ target, fromKey, edited });
|
|
176
|
+
}
|
|
177
|
+
return byHarness;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** Rule 3a in classify: the verdict planHandovers reached for this row before the live plans. */
|
|
181
|
+
function handoverAction(row , _retired , context ) {
|
|
182
|
+
const decided = context.handovers?.get(ownershipKey(row));
|
|
183
|
+
return decided ? { action: decided.action } : null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function classify(row , retired , context ) {
|
|
187
|
+
const { liveRemoved, superseded, liveClaims, scopeRoot, fsImpl } = context;
|
|
188
|
+
const key = claimKey(row);
|
|
189
|
+
const renderer = row.projection?.renderer;
|
|
190
|
+
// 1. A live change in this run already releases this row (a removal, or a prune naming it).
|
|
191
|
+
if (liveRemoved.has(key) || superseded.has(ownershipKey(row))) return { action: 'drop-row' };
|
|
192
|
+
// 2. A live harness still claims the file.
|
|
193
|
+
if (liveClaims.has(key)) return { action: 'release' };
|
|
194
|
+
// 3. Whole files DoFlow wrote: delete only its own unchanged bytes, at the path it recorded.
|
|
195
|
+
if (WHOLE_FILE_KINDS.has(row.kind)) {
|
|
196
|
+
if (!fsImpl.existsSync(row.target)) return { action: 'drop-row' };
|
|
197
|
+
if (!resolvesInPlace(row.target, scopeRoot, fsImpl)) return { action: 'keep', reason: 'resolves outside its recorded path' };
|
|
198
|
+
if (!fsImpl.statSync(row.target).isFile()) return { action: 'keep', reason: 'not a regular file' };
|
|
199
|
+
const current = sha256(fsImpl.readFileSync(row.target));
|
|
200
|
+
if (current !== row.fingerprint) return { action: 'keep', reason: 'changed since DoFlow wrote it' };
|
|
201
|
+
return { action: 'delete', fingerprint: current };
|
|
202
|
+
}
|
|
203
|
+
// 3a. Handover to the replacement (T-127-A5).
|
|
204
|
+
const handover = handoverAction(row, retired, context);
|
|
205
|
+
if (handover) return handover;
|
|
206
|
+
// 4. A marker section in a file the person also writes.
|
|
207
|
+
if (retired.removal.markerSectionRenderers.includes(renderer)) {
|
|
208
|
+
if (!fsImpl.existsSync(row.target)) return { action: 'drop-row' };
|
|
209
|
+
if (!resolvesInPlace(row.target, scopeRoot, fsImpl)) return { action: 'keep', reason: 'resolves outside its recorded path' };
|
|
210
|
+
const state = markerState(fsImpl.readFileSync(row.target, 'utf8'));
|
|
211
|
+
if (state === 'none') return { action: 'drop-row' };
|
|
212
|
+
if (state === 'malformed') return { action: 'keep', reason: 'malformed DoFlow markers' };
|
|
213
|
+
return { action: 'strip-section' };
|
|
214
|
+
}
|
|
215
|
+
// 5. Hook handlers in a shared JSON file: only the exact frozen commands.
|
|
216
|
+
const hookCommands = retired.removal.hookCommands;
|
|
217
|
+
if (renderer === hookCommands.renderer) {
|
|
218
|
+
const read = readHooksDoc(row.target, hookCommands.key, fsImpl);
|
|
219
|
+
if (read.absent) return { action: 'drop-row' };
|
|
220
|
+
if (read.error) return { action: 'keep', reason: read.error };
|
|
221
|
+
if (!resolvesInPlace(row.target, scopeRoot, fsImpl)) return { action: 'keep', reason: 'resolves outside its recorded path' };
|
|
222
|
+
if (read.doc[hookCommands.key] === undefined) return { action: 'drop-row' };
|
|
223
|
+
return stripHookCommands(read.doc, hookCommands.key, new Set(hookCommands.commands)) ? { action: 'strip-hooks' } : { action: 'drop-row' };
|
|
224
|
+
}
|
|
225
|
+
// 6. Nothing this sweep knows how to take back.
|
|
226
|
+
return { action: 'keep', reason: `unknown row shape ${row.kind ?? '(none)'}/${renderer ?? '(none)'}` };
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** One plan per retired harness that still has rows in this scope's ledger, in registry order. A ledger
|
|
230
|
+
* with no retired rows yields []. `livePlanChanges` are the live harnesses' planned changes, and
|
|
231
|
+
* `handovers` planHandovers' decisions for this same run. */
|
|
232
|
+
function planRetiredHarnesses({ registry, ledger, scope, scopeRoot, livePlanChanges = [], handovers = new Map(), fsImpl = fs } ) {
|
|
233
|
+
const retiredRecords = Array.isArray(registry?.retired) ? registry.retired : [];
|
|
234
|
+
if (!retiredRecords.length) return [];
|
|
235
|
+
const rows = (ledger?.resources ?? []).filter((row ) => typeof row?.target === 'string' && typeof row?.harness === 'string');
|
|
236
|
+
const liveRemoved = new Set ();
|
|
237
|
+
const superseded = new Set ();
|
|
238
|
+
for (const change of livePlanChanges) {
|
|
239
|
+
if (change?.operation !== 'remove' || typeof change.target !== 'string') continue;
|
|
240
|
+
liveRemoved.add(claimKey(change));
|
|
241
|
+
for (const key of change.supersedes ?? []) superseded.add(key);
|
|
242
|
+
}
|
|
243
|
+
const liveClaims = new Set (rows.filter((row ) => !isRetired(registry, row.harness)).map(claimKey));
|
|
244
|
+
const context = { registry, scope, scopeRoot, livePlanChanges, liveRemoved, superseded, liveClaims, handovers, fsImpl };
|
|
245
|
+
|
|
246
|
+
const plans = [];
|
|
247
|
+
for (const retired of retiredRecords) {
|
|
248
|
+
const own = rows.filter((row ) => row.harness === retired.id);
|
|
249
|
+
if (!own.length) continue;
|
|
250
|
+
const plan = {
|
|
251
|
+
harness: retired.id, replacement: retired.replacement, scopeRoot, changes: [],
|
|
252
|
+
counts: { deleted: 0, stripped: 0, released: 0, dropped: 0, deferred: 0, kept: 0 }, notices: [],
|
|
253
|
+
};
|
|
254
|
+
for (const row of own) {
|
|
255
|
+
const { action, reason, fingerprint } = classify(row, retired, context);
|
|
256
|
+
const change = {
|
|
257
|
+
harness: row.harness, retired: true, assetId: row.assetId, target: row.target,
|
|
258
|
+
ownershipKey: ownershipKey(row), ownershipIdentity: row.ownershipIdentity, action, operation: 'remove',
|
|
259
|
+
...(reason ? { reason } : {}),
|
|
260
|
+
...(NO_FS_EFFECT.has(action) ? { release: true } : {}),
|
|
261
|
+
...(fingerprint ? { fingerprint } : {}),
|
|
262
|
+
...(action === 'strip-hooks' ? { hookCommands: { key: retired.removal.hookCommands.key, commands: [...retired.removal.hookCommands.commands] } } : {}),
|
|
263
|
+
};
|
|
264
|
+
plan.changes.push(Object.freeze(change) );
|
|
265
|
+
plan.counts[COUNTED[action]] += 1;
|
|
266
|
+
if (action === 'keep') plan.notices.push(`kept ${path.relative(scopeRoot, row.target)}: ${reason}`);
|
|
267
|
+
if (action === 'defer') plan.notices.push(`${path.relative(scopeRoot, row.target)}: handed over on the next run that targets ${handovers.get(change.ownershipKey).to}`);
|
|
268
|
+
}
|
|
269
|
+
plans.push(plan);
|
|
270
|
+
}
|
|
271
|
+
return plans;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** Carry out the plans: JSON hook strips first, then marker sections, then whole-file deletes, so no
|
|
275
|
+
* settings file is ever left naming a script that is already gone. Each step re-checks what the plan
|
|
276
|
+
* proved and throws rather than act on a file that changed since; the caller's recovery restores. */
|
|
277
|
+
function applyRetiredHarnesses(plans , { fsImpl = fs } = {}) {
|
|
278
|
+
const all = plans.flatMap((plan) => plan.changes.map((change) => ({ change, scopeRoot: plan.scopeRoot })));
|
|
279
|
+
const guard = (change , scopeRoot ) => {
|
|
280
|
+
if (fsImpl.existsSync(change.target) && !resolvesInPlace(change.target, scopeRoot, fsImpl)) {
|
|
281
|
+
throw new Error(`Refusing to change retired ${change.harness} resource ${change.target}: it resolves outside its recorded path`);
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
for (const { change, scopeRoot } of all) {
|
|
285
|
+
if (change.action !== 'strip-hooks' || !change.hookCommands) continue;
|
|
286
|
+
guard(change, scopeRoot);
|
|
287
|
+
const read = readHooksDoc(change.target, change.hookCommands.key, fsImpl);
|
|
288
|
+
if (read.absent) continue;
|
|
289
|
+
if (read.error) throw new Error(`Refusing to strip retired ${change.harness} hooks from ${change.target}: ${read.error}`);
|
|
290
|
+
if (read.doc[change.hookCommands.key] === undefined) continue;
|
|
291
|
+
const next = stripHookCommands(read.doc, change.hookCommands.key, new Set(change.hookCommands.commands));
|
|
292
|
+
if (next) atomicJsonWrite(change.target, next, { fsImpl });
|
|
293
|
+
}
|
|
294
|
+
for (const { change, scopeRoot } of all) {
|
|
295
|
+
if (change.action !== 'strip-section') continue;
|
|
296
|
+
guard(change, scopeRoot);
|
|
297
|
+
removeMarkedSection(change.target, { startMarkers: START_MARKERS });
|
|
298
|
+
}
|
|
299
|
+
const deletes = all.filter(({ change }) => change.action === 'delete');
|
|
300
|
+
for (const { change, scopeRoot } of deletes) guard(change, scopeRoot);
|
|
301
|
+
removeTree({ changes: deletes.map(({ change }) => ({ operation: 'remove', target: change.target, fingerprint: change.fingerprint, relPath: change.target })), fsImpl });
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/** Every ledger row the plans give up: all of them except deferred ones. */
|
|
305
|
+
function retiredReleaseKeys(plans ) {
|
|
306
|
+
return new Set(plans.flatMap((plan) => plan.changes.filter((change) => change.action !== 'defer').map((change) => change.ownershipKey)));
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/** IC-003 output, for dry-run and apply alike: one summary line per retired harness, whose six counts
|
|
310
|
+
* sum to the rows it swept, then one line per kept or named item. No plans, no lines. */
|
|
311
|
+
function formatRetiredPlans(plans ) {
|
|
312
|
+
return plans.flatMap((plan) => [
|
|
313
|
+
`[RETIRED] ${plan.harness} → ${plan.replacement}: deleted ${plan.counts.deleted}, stripped ${plan.counts.stripped}, released ${plan.counts.released}, dropped ${plan.counts.dropped}, deferred ${plan.counts.deferred}, kept ${plan.counts.kept}`,
|
|
314
|
+
...plan.notices.map((notice) => ` ${notice}`),
|
|
315
|
+
]);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
module.exports = { planRetiredHarnesses, planHandovers, handoversFor, applyRetiredHarnesses, retiredReleaseKeys, formatRetiredPlans, claimKey };
|
package/src/lifecycle/view.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
|
|
4
4
|
// lifecycle-view.js — CLI-facing view over the registry/lifecycle path: computes a plan across
|
|
5
|
-
// every requested harness (claude
|
|
5
|
+
// every requested harness (claude, codex, opencode, pi, copilot, kiro, antigravity) from a loaded registry, a neutral ledger, and the
|
|
6
6
|
// Codex-native source locations under `repoRoot`, then renders/gates it for bin/doflow.js's
|
|
7
7
|
// install/update/remove/status commands.
|
|
8
8
|
const fs = require('node:fs');
|
|
@@ -13,16 +13,17 @@ const { createAdapterRegistry } = require('../adapters/index.js');
|
|
|
13
13
|
const { declaredHarnessPaths } = require('../helper/harness-paths.js');
|
|
14
14
|
const claudeAdapter = require('../adapters/claude/index.js');
|
|
15
15
|
const codexAdapter = require('../adapters/codex/index.js');
|
|
16
|
-
const { createGeminiAdapter } = require('../adapters/gemini/index.js');
|
|
17
16
|
const { createOpenCodeAdapter } = require('../adapters/opencode/index.js');
|
|
18
17
|
const { createPiAdapter } = require('../adapters/pi/index.js');
|
|
19
18
|
const { createCopilotAdapter } = require('../adapters/copilot/index.js');
|
|
20
19
|
const { createKiroAdapter } = require('../adapters/kiro/index.js');
|
|
21
20
|
const { createAntigravityAdapter } = require('../adapters/antigravity/index.js');
|
|
22
21
|
const { planLifecycle } = require('./index.js');
|
|
22
|
+
const { formatRetiredPlans } = require('./retired-harnesses.js');
|
|
23
|
+
const { resolveHookTier, hookTierWarnings } = require('./hook-tier.js');
|
|
23
24
|
const { stateRoot, readLedger, defaultLedger } = require('../state/index.js');
|
|
24
25
|
const { selectAssets } = require('../registry/index.js');
|
|
25
|
-
const { defaultLock, readLock, writeLock, diffLocks, removeLock, pinnedSelections } = require('../state/lockfile.js');
|
|
26
|
+
const { defaultLock, readLock, writeLock, diffLocks, removeLock, pinnedSelections, lockPackages } = require('../state/lockfile.js');
|
|
26
27
|
// Tolerant because the projected runtime under `.doflow/runtime/` ships bin/, src/ and
|
|
27
28
|
// core/registry/ but no package.json — see the `runtime.*` assets in core/registry/assets.json.
|
|
28
29
|
// A hard require here would make every Node-backed verb fail in an install, which is the exact
|
|
@@ -48,17 +49,18 @@ function codexConfigResources(repoRoot , fsImpl ) {
|
|
|
48
49
|
* mutation path until every native adapter has CLI-level parity.
|
|
49
50
|
* `registry` is loaded once per command (by the caller) and threaded through here rather than
|
|
50
51
|
* reloaded — the same registry also resolves every harness's MCP selection for that command. */
|
|
51
|
-
function registryLifecycleView({ registry, scope, targets, mcpSelections = {}, mcpAdoptable = {}, retainedMcpIds = [], operation, repoRoot, force = false, adopt = false, permissions = false, statusline = false, fsImpl = fs } ) {
|
|
52
|
+
function registryLifecycleView({ registry, scope, targets, mcpSelections = {}, mcpAdoptable = {}, retainedMcpIds = [], operation, repoRoot, force = false, adopt = false, permissions = false, statusline = false, hookHarnesses, fsImpl = fs } ) {
|
|
52
53
|
const lifecycleScope = codexScope(scope);
|
|
53
54
|
const scopeRoot = scope.global ? os.homedir() : path.resolve(scope.projectRoot);
|
|
54
55
|
const neutralStateRoot = stateRoot({ scope: lifecycleScope, projectRoot: scopeRoot, homeDir: scopeRoot });
|
|
55
56
|
const ledger = readLedger(neutralStateRoot) ?? defaultLedger({ scope: lifecycleScope, scopeRoot });
|
|
57
|
+
const tier = hookHarnesses ? { harnesses: hookHarnesses, workers: null } : resolveHookTier();
|
|
56
58
|
// Mirrors buildAdapterRegistry() in src/cli/shared.js — including the declared-paths wiring —
|
|
57
59
|
// and is kept as a literal construction here because the registry guard pins every declared
|
|
58
60
|
// harness into each adapter-registry call site by parsing it.
|
|
59
61
|
const declared = declaredHarnessPaths();
|
|
60
62
|
const adapters = createAdapterRegistry({ claude: claudeAdapter.createClaudeAdapter({ declaredPaths: declared.claude }),
|
|
61
|
-
codex: codexAdapter.createCodexAdapter({ declaredPaths: declared.codex }),
|
|
63
|
+
codex: codexAdapter.createCodexAdapter({ declaredPaths: declared.codex }),
|
|
62
64
|
opencode: createOpenCodeAdapter({ declaredPaths: declared.opencode }), pi: createPiAdapter({ declaredPaths: declared.pi }),
|
|
63
65
|
copilot: createCopilotAdapter({ declaredPaths: declared.copilot }), kiro: createKiroAdapter({ declaredPaths: declared.kiro }),
|
|
64
66
|
antigravity: createAntigravityAdapter({ declaredPaths: declared.antigravity }) });
|
|
@@ -68,19 +70,21 @@ function registryLifecycleView({ registry, scope, targets, mcpSelections = {}, m
|
|
|
68
70
|
codexAgentsSourceDir: path.join(repoRoot, 'core', 'harnesses', 'codex', 'agents'),
|
|
69
71
|
codexHooksSourceFile: path.join(repoRoot, 'core', 'harnesses', 'codex', 'hooks', 'hooks.json'),
|
|
70
72
|
codexHooksSourceDir: path.join(repoRoot, 'core', 'harnesses', 'codex', 'hooks'),
|
|
71
|
-
geminiHooksSourceFile: path.join(repoRoot, 'core', 'harnesses', 'gemini', 'hooks', 'hooks.json'),
|
|
72
|
-
geminiHooksSourceDir: path.join(repoRoot, 'core', 'harnesses', 'gemini', 'hooks'),
|
|
73
73
|
operation,
|
|
74
74
|
force,
|
|
75
75
|
adopt,
|
|
76
76
|
permissions,
|
|
77
77
|
statusline,
|
|
78
|
+
// maint-hooks: the person's hook tier (a caller may pass its own); every harness outside it gets content only.
|
|
79
|
+
hookHarnesses: tier.harnesses,
|
|
78
80
|
} });
|
|
79
|
-
|
|
81
|
+
const hookWarnings = operation === 'remove' ? [] : hookTierWarnings(registry, plan.targets.filter((t ) => !t.skipped).map((t ) => t.harness), tier);
|
|
82
|
+
return { registry, stateRoot: neutralStateRoot, ledger, plan, adapters, hookWarnings };
|
|
80
83
|
}
|
|
81
84
|
|
|
82
85
|
function printRegistryLifecycle(view , prefix = '[PLAN]') {
|
|
83
86
|
console.log(`${prefix} Registry lifecycle: ${view.plan.changes.length} native change(s), ${view.plan.conflicts.length} conflict(s), ${view.plan.prerequisites.length} prerequisite(s)`);
|
|
87
|
+
for (const warning of view.hookWarnings ?? []) console.log(`[WARN] ${warning}`);
|
|
84
88
|
for (const target of view.plan.targets) {
|
|
85
89
|
console.log(`${prefix} ${target.harness}: ${target.changes.length} change(s)${target.conflicts.length ? `; conflicts: ${target.conflicts.join('; ')}` : ''}`);
|
|
86
90
|
const hookChange = target.changes.find((change ) => change.nativeComponent === 'hooks');
|
|
@@ -89,19 +93,29 @@ function printRegistryLifecycle(view , prefix = '[PLAN]') {
|
|
|
89
93
|
}
|
|
90
94
|
for (const notice of target.notices ?? []) console.log(`${prefix} ${target.harness}: ${notice}`);
|
|
91
95
|
}
|
|
96
|
+
printRetiredPlans(view);
|
|
92
97
|
console.log(`${prefix} Neutral state: ${view.stateRoot}${readLedger(view.stateRoot) ? ' (existing ledger)' : ' (not yet created)'}`);
|
|
93
98
|
}
|
|
94
99
|
|
|
100
|
+
/** The retirement lines (row 127 IC-003), the same under --dry-run and after an apply: one
|
|
101
|
+
* `[RETIRED] <id> → <replacement>: deleted N, stripped N, released N, dropped N, deferred N, kept N`
|
|
102
|
+
* summary per retired harness the plan sweeps, then one line per kept or deferred item. Nothing
|
|
103
|
+
* when the ledger holds no retired row. */
|
|
104
|
+
function printRetiredPlans(view ) {
|
|
105
|
+
for (const line of formatRetiredPlans(view.plan.retiredHarnesses ?? [])) console.log(line);
|
|
106
|
+
}
|
|
107
|
+
|
|
95
108
|
/** Prints the plan's notices after a real run. Independent of whether anything changed: a no-op
|
|
96
109
|
* reinstall of a harness that has a gap must still say so. */
|
|
97
110
|
function printPlanNotices(view ) {
|
|
111
|
+
for (const warning of view.hookWarnings ?? []) console.log(`[WARN] ${warning}`);
|
|
98
112
|
for (const { harness, notice } of view.plan.notices) console.log(`[INFO] ${harness}: ${notice}`);
|
|
99
113
|
}
|
|
100
114
|
|
|
101
115
|
/** Harnesses whose native resources are reconciled through the registry/lifecycle path (all of
|
|
102
116
|
* them, as of this wiring). Kept as an explicit list — rather than reusing VALID from
|
|
103
117
|
* src/targets.js — so a future non-lifecycle target doesn't silently gain lifecycle behavior. */
|
|
104
|
-
const LIFECYCLE_HARNESSES = ['claude', 'codex', '
|
|
118
|
+
const LIFECYCLE_HARNESSES = ['claude', 'codex', 'opencode', 'pi', 'copilot', 'kiro', 'antigravity'];
|
|
105
119
|
|
|
106
120
|
function assertSafeRegistryPlan(view ) {
|
|
107
121
|
if (view.plan.safe) return;
|
|
@@ -117,13 +131,18 @@ function assertSafeRegistryPlan(view ) {
|
|
|
117
131
|
* as the ledger (as the run left it) still holds a resource of that harness, so a narrower later run
|
|
118
132
|
* never drops another harness's pin and a harness whose last resource is gone leaves it. MCP
|
|
119
133
|
* selections carry over for pinned harnesses and are replaced by the rows this run resolved, `[]`
|
|
120
|
-
* included. Assets are recomputed from the registry for every pinned harness.
|
|
121
|
-
*
|
|
134
|
+
* included. Assets are recomputed from the registry for every pinned harness. The previous lock's
|
|
135
|
+
* `packages` (122 IC-007) are not registry-derived, so they are carried forward unchanged, and a lock
|
|
136
|
+
* that holds packages stays as `targets: []` once no harness is pinned. `null` when no harness stays
|
|
137
|
+
* pinned and no package is recorded. Only live harnesses are pinned (row 127 IC-004): a previous pin
|
|
138
|
+
* of a retired id is dropped, even while the ledger still holds its rows. */
|
|
122
139
|
function lockDocument({ registry, scope, scopeRoot, previous = null, ledger, plannedTargets = [], mcpSelections = {}, sourceVersion = pkg.version, now = new Date() } ) {
|
|
123
140
|
const held = new Set((ledger?.resources ?? []).map((resource ) => resource?.harness));
|
|
141
|
+
const live = new Set(registry.harnesses.map((harness ) => harness.id));
|
|
124
142
|
const pinned = [...new Set([...(previous?.targets ?? []).map((entry ) => entry.harness), ...plannedTargets])]
|
|
125
|
-
.filter((harness) => held.has(harness)).sort();
|
|
126
|
-
|
|
143
|
+
.filter((harness) => held.has(harness) && live.has(harness)).sort();
|
|
144
|
+
const packages = lockPackages(previous);
|
|
145
|
+
if (!pinned.length && !packages.length) return null;
|
|
127
146
|
const assets = [];
|
|
128
147
|
for (const harness of pinned) {
|
|
129
148
|
for (const asset of selectAssets(registry, { harness })) {
|
|
@@ -139,6 +158,7 @@ function lockDocument({ registry, scope, scopeRoot, previous = null, ledger, pla
|
|
|
139
158
|
targets: pinned.map((harness) => ({ harness })),
|
|
140
159
|
assets,
|
|
141
160
|
mcpSelections: Object.fromEntries(pinned.filter((harness) => Array.isArray(rows[harness])).map((harness) => [harness, rows[harness]])),
|
|
161
|
+
...(packages.length ? { packages } : {}),
|
|
142
162
|
};
|
|
143
163
|
}
|
|
144
164
|
|
|
@@ -150,7 +170,7 @@ function lockDelta(previous , next ) {
|
|
|
150
170
|
if (!previous) return { changed: true, summary: 'created' };
|
|
151
171
|
const diff = diffLocks(previous, next);
|
|
152
172
|
if (diff.clean) return { changed: false, summary: 'unchanged' };
|
|
153
|
-
const count = ['targets', 'assets']
|
|
173
|
+
const count = ['targets', 'assets', 'packages']
|
|
154
174
|
.flatMap((section) => Object.values (diff[section]).map((list ) => list.length))
|
|
155
175
|
.reduce((sum, n) => sum + n, 0)
|
|
156
176
|
+ diff.mcpSelections.changed.length + (diff.meta.sourceVersion ? 1 : 0);
|
|
@@ -170,6 +190,6 @@ function recordLock(scopeArgs , next , { fsImpl = fs } = {}) {
|
|
|
170
190
|
}
|
|
171
191
|
|
|
172
192
|
module.exports = {
|
|
173
|
-
codexScope, registryLifecycleView, printRegistryLifecycle, printPlanNotices, LIFECYCLE_HARNESSES, assertSafeRegistryPlan,
|
|
193
|
+
codexScope, registryLifecycleView, printRegistryLifecycle, printRetiredPlans, printPlanNotices, LIFECYCLE_HARNESSES, assertSafeRegistryPlan,
|
|
174
194
|
lockDocument, lockDelta, recordLock,
|
|
175
195
|
};
|