@khoavu882/doflow 2.0.0-beta.5 → 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 -2
- 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/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-compact.sh +23 -27
- package/core/harnesses/claude/hooks/session-end.sh +23 -10
- package/core/harnesses/claude/hooks/skill-config-audit.sh +3 -0
- 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/post-edit-lint.impl.sh +3 -1
- package/core/harnesses/codex/hooks/pre-compact.impl.sh +29 -26
- package/core/harnesses/codex/hooks/session-end.impl.sh +23 -10
- package/core/harnesses/codex/hooks/subagent-audit.sh +1 -0
- 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 +1 -0
- package/core/harnesses/kiro/hooks/pre-bash-guard.sh +1 -0
- package/core/harnesses/kiro/hooks/pre-implement-gate.sh +1 -0
- package/core/harnesses/kiro/hooks/session-start.sh +1 -0
- package/core/harnesses/kiro/hooks/stop-check.sh +1 -0
- package/core/harnesses/pi/extensions/doflow-guard.js +8 -0
- package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +0 -1
- package/core/harnesses/shared/hooks/bridge/bridge.sh +102 -9
- package/core/harnesses/shared/hooks/bridge/envelope.jq +87 -18
- package/core/harnesses/shared/hooks/bridge/observe-model.sh +75 -0
- package/core/harnesses/shared/hooks/bridge/respond.sh +10 -2
- package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +28 -14
- package/core/harnesses/shared/hooks/policies/capture-failure.sh +48 -15
- package/core/harnesses/shared/hooks/policies/compact-text.sh +46 -0
- package/core/harnesses/shared/hooks/policies/contract.sh +3 -0
- package/core/harnesses/shared/hooks/policies/deny-json.sh +2 -11
- 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 +63 -40
- package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +3 -2
- package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +213 -12
- package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +114 -38
- package/core/harnesses/shared/hooks/policies/session-context.sh +30 -1
- package/core/harnesses/shared/hooks/policies/stop-check.sh +20 -9
- package/core/harnesses/shared/hooks/policies/subagent-audit.sh +5 -2
- package/core/harnesses/shared/hooks/policies/summary-store.sh +347 -0
- package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +60 -155
- package/core/harnesses/shared/hooks/stream-hook-runner.js +47 -8
- package/core/harnesses/shared/locator/doflow-run +2 -3
- package/core/registry/advice.json +3 -3
- package/core/registry/assets.json +55 -82
- package/core/registry/capabilities.json +12 -0
- package/core/registry/contracts.json +58 -31
- 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 +1442 -363
- package/core/registry/lifecycle.json +485 -304
- 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 +679 -31
- 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 +149 -34
- 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 +18 -17
- 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 +15 -58
- 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 +26 -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 +4 -7
- package/core/shared/skills/do-plan/SKILL.md +26 -15
- 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 +143 -66
- package/src/adapters/claude/index.js +24 -4
- package/src/adapters/codex/agents.js +25 -15
- package/src/adapters/codex/index.js +48 -13
- package/src/adapters/codex/native-projection.js +6 -2
- package/src/adapters/copilot/index.js +58 -23
- package/src/adapters/copy-tree.js +290 -44
- 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 +23 -12
- package/src/adapters/opencode/index.js +8 -8
- package/src/adapters/pi/hooks.js +2 -1
- package/src/adapters/pi/index.js +14 -7
- package/src/adapters/resolve-source.js +26 -2
- package/src/cli/commands/install.js +2 -1
- 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 +2 -1
- package/src/cli/index.js +322 -29
- package/src/cli/runtime-commands.js +209 -57
- 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 +20 -3
- package/src/lifecycle/hook-tier.js +160 -0
- package/src/lifecycle/index.js +412 -78
- package/src/lifecycle/retired-harnesses.js +318 -0
- package/src/lifecycle/view.js +35 -15
- package/src/registry/index.js +551 -26
- 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 +20 -11
- 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 +13 -7
- package/src/runtime/cli.js +196 -19
- package/src/runtime/config-home.js +42 -0
- package/src/runtime/context/arbitrate.js +767 -0
- package/src/runtime/context/delivery-store.js +4 -6
- package/src/runtime/context/emission-rules.js +118 -0
- package/src/runtime/context/identity.js +227 -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 +154 -78
- 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-ledger.js +34 -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 +33 -3
- 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 +2 -2
- 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 +46 -5
- package/src/runtime/research-request.js +50 -25
- package/src/runtime/retrieval-plan.js +10 -30
- package/src/runtime/retrieval-record.js +75 -5
- 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 +60 -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 +1 -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 +176 -55
- package/src/runtime/task-sources.js +147 -0
- package/src/runtime/task-state.js +374 -64
- 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/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 -10
- package/core/harnesses/gemini/hooks/post-edit-lint.sh +0 -39
- package/core/harnesses/gemini/hooks/pre-bash-guard.sh +0 -10
- package/core/harnesses/gemini/hooks/pre-compact.sh +0 -38
- package/core/harnesses/gemini/hooks/pre-implement-gate.sh +0 -10
- 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 -7
- package/core/harnesses/gemini/settings/adapter-defaults.json +0 -4
- 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 -251
- package/src/adapters/gemini/index.js +0 -429
|
@@ -93,11 +93,18 @@ function classify(bodyLines , kinds , canonicalPointer
|
|
|
93
93
|
return { comments, moved, pointer };
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
function writeAtomic(fsImpl , file , content ) {
|
|
96
|
+
function writeAtomic(fsImpl , file , content , unchangedSince ) {
|
|
97
97
|
fsImpl.mkdirSync(path.dirname(file), { recursive: true });
|
|
98
98
|
const tmp = `${file}.${process.pid}.tmp`;
|
|
99
99
|
try {
|
|
100
100
|
fsImpl.writeFileSync(tmp, content, 'utf8');
|
|
101
|
+
// The text `content` was built from is compared with the file just before the rename, so an edit
|
|
102
|
+
// made since it was read is kept instead of replaced (FU-1jbmm3). This narrows the window to the
|
|
103
|
+
// gap between this read and the rename; it cannot close it, which would take a lock on the
|
|
104
|
+
// artifact itself that its editors would also have to take.
|
|
105
|
+
if (unchangedSince !== undefined && fsImpl.readFileSync(file, 'utf8') !== unchangedSince) {
|
|
106
|
+
throw new Error(`${path.basename(file)} changed while it was being compacted`);
|
|
107
|
+
}
|
|
101
108
|
fsImpl.renameSync(tmp, file);
|
|
102
109
|
} catch (error ) {
|
|
103
110
|
try { fsImpl.rmSync(tmp, { force: true }); } catch { /* nothing to clean */ }
|
|
@@ -189,7 +196,7 @@ function compactArtifact({ fsImpl, featureDir, file, date }
|
|
|
189
196
|
const body = [blank];
|
|
190
197
|
for (const c of comments) body.push(...c, blank);
|
|
191
198
|
body.push(pointerLine, end === lines.length ? '' : blank);
|
|
192
|
-
writeAtomic(fsImpl, file, [...lines.slice(0, start + 1), ...body, ...lines.slice(end)].join('\n'));
|
|
199
|
+
writeAtomic(fsImpl, file, [...lines.slice(0, start + 1), ...body, ...lines.slice(end)].join('\n'), text);
|
|
193
200
|
return { artifact: name, path: rel, archive: archiveRel, lines: moved.length };
|
|
194
201
|
}
|
|
195
202
|
|
|
@@ -204,7 +211,10 @@ function compactArtifact({ fsImpl, featureDir, file, date }
|
|
|
204
211
|
* @param {string} [options.date] YYYY-MM-DD stamped on the archive chunk; defaults to today (UTC)
|
|
205
212
|
* @param {Object} [options.fsImpl] fs implementation (tests substitute one)
|
|
206
213
|
*
|
|
207
|
-
* Not locked: concurrent callers can interleave archive appends
|
|
214
|
+
* Not locked: concurrent callers can interleave archive appends, and an artifact edited between its
|
|
215
|
+
* read and its rewrite is detected just before the rename and left as the editor wrote it (the
|
|
216
|
+
* artifact is reported in `failed`; the moved block is already in the archive and a later run does
|
|
217
|
+
* not copy it twice), but an edit landing in the last instant before the rename is not. Call it through
|
|
208
218
|
* `compactDecisions` / `runDecision` in decision-register.js, which hold the register lock.
|
|
209
219
|
*
|
|
210
220
|
* Every artifact is attempted. One that cannot be compacted is left as it was and reported in
|
|
@@ -8,7 +8,7 @@ const { readReadinessRecord, newerVersionOf, RECORD_VERSION } = require('./readi
|
|
|
8
8
|
const { listCheckouts } = require('./checkouts.js');
|
|
9
9
|
const { readTaskState } = require('./task-state.js');
|
|
10
10
|
const { resolveActiveFeature } = require('./feature-resolve.js');
|
|
11
|
-
const { isSafeSlug } = require('./task-scope.js');
|
|
11
|
+
const { isSafeSlug, flatRecordIsOurs, readRegularFileText, MAX_RECORD_FILE_BYTES } = require('./task-scope.js');
|
|
12
12
|
const { REPO_ROOT } = require('../helper/repo-root.js');
|
|
13
13
|
|
|
14
14
|
// The implementation gate: which stage of a workflow needs a READY readiness record before it is
|
|
@@ -19,6 +19,15 @@ const { REPO_ROOT } = require('../helper/repo-root.js');
|
|
|
19
19
|
// orchestrator, `verify` and the edit hook (which prints the same bytes) say the same thing.
|
|
20
20
|
|
|
21
21
|
const GATE = 'doflow gate readiness-before-implementation';
|
|
22
|
+
const RUN_RECORD_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*\.json$/;
|
|
23
|
+
const RUN_NAMESPACE_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
24
|
+
/** Where a checkout keeps its run records, relative to the checkout; the one place the path is named. */
|
|
25
|
+
const RUN_STORE_REL = path.join('.doflow', 'state', 'orchestration');
|
|
26
|
+
|
|
27
|
+
/** The run store of the checkout at `root`. */
|
|
28
|
+
function runStoreDir(root ) {
|
|
29
|
+
return path.join(root, RUN_STORE_REL);
|
|
30
|
+
}
|
|
22
31
|
|
|
23
32
|
/** What clears a record a newer DoFlow wrote; `readiness` here refuses to overwrite it. */
|
|
24
33
|
const NEWER_NEXT = 'run this command with the DoFlow that wrote the record, or upgrade DoFlow here.';
|
|
@@ -55,13 +64,14 @@ function nextCommand({ taskId, slug, template } ) {
|
|
|
55
64
|
|
|
56
65
|
/**
|
|
57
66
|
* The refusal for one check code.
|
|
58
|
-
* @param {'missing'|'not-ready'|'wrong-template'|'unusable'|'ambiguous'} code
|
|
67
|
+
* @param {'missing'|'not-ready'|'wrong-template'|'unusable'|'ambiguous'|'store-conflict'|'ambiguous-run'} code
|
|
59
68
|
* @param {Object} options
|
|
60
69
|
* @param {string} options.taskId
|
|
61
70
|
* @param {string|null} [options.slug]
|
|
62
71
|
* @param {string} options.template the template the gated stage needs
|
|
63
72
|
* @param {Object|null} [options.record] the record read, for `not-ready` and `wrong-template`
|
|
64
|
-
* @param {Array<string>} [options.candidates] the files found, for `ambiguous
|
|
73
|
+
* @param {Array<string>} [options.candidates] the files found, for `ambiguous`; the worktree's then the
|
|
74
|
+
* main checkout's, for `store-conflict`
|
|
65
75
|
* @param {string|null} [options.detail] why the record cannot be used, for `unusable`
|
|
66
76
|
* @param {string|null} [options.file] the record's file, for `unusable` when a newer DoFlow wrote it
|
|
67
77
|
* @param {number|null} [options.newerVersion] that newer record version; `readiness` would refuse to
|
|
@@ -86,6 +96,8 @@ function refusalText(code , { taskId, slug = null, template, record = null,
|
|
|
86
96
|
return `${GATE}: task '${taskId}' has a readiness record that cannot be used (${detail}). ${tail}`;
|
|
87
97
|
case 'ambiguous':
|
|
88
98
|
return `${GATE}: task '${taskId}' has records in more than one other checkout (${candidates.join(', ')}). Next: run the command from the checkout that holds the one you mean, or run ${next} here. Nothing was changed.`;
|
|
99
|
+
case 'store-conflict':
|
|
100
|
+
return `${GATE}: task '${taskId}' has a readiness record in this worktree (${candidates[0]}) that differs from the main checkout's (${candidates[1]}). Next: keep one of the two and remove the other, then re-run. Nothing was changed.`;
|
|
89
101
|
case 'ambiguous-run':
|
|
90
102
|
// A readiness record written here cannot say which run is meant, so it is not offered.
|
|
91
103
|
return `${GATE}: task '${taskId}' has runs in more than one other checkout (${candidates.join(', ')}). Next: run verify from the checkout that holds the run you mean. Nothing was changed.`;
|
|
@@ -168,35 +180,269 @@ function runSlugOf(run , root ) {
|
|
|
168
180
|
return run && isSafeSlug(run.taskId) && isDir(folderOf(root, run.taskId)) ? run.taskId : null;
|
|
169
181
|
}
|
|
170
182
|
|
|
183
|
+
/** Whether a store directory can be a run namespace rather than a lock directory. */
|
|
184
|
+
function isRunNamespaceDir(name ) {
|
|
185
|
+
return RUN_NAMESPACE_PATTERN.test(name) && !name.includes('..') && !name.endsWith('.lock');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** The run record files under one checkout's store: flat `<id>.json` and `<slug>/<id>.json`, by
|
|
189
|
+
* path, without reading them. The one listing `findRunFile` and `orchestrationRuns` share.
|
|
190
|
+
* With `listing` (IC-001, the list action), a symbolic link is never followed: a linked run file is
|
|
191
|
+
* kept with `linked: true` so it can be reported as unreadable, and a linked namespace directory is
|
|
192
|
+
* not entered, so nothing outside the store root is read or named. */
|
|
193
|
+
function runFilesAt(root , { listing = false } = {}) {
|
|
194
|
+
const store = runStoreDir(root);
|
|
195
|
+
const files = [];
|
|
196
|
+
const take = (entry ) => entry.isFile() || entry.isSymbolicLink();
|
|
197
|
+
let entries ;
|
|
198
|
+
try { entries = fs.readdirSync(store, { withFileTypes: true }); } catch { return []; }
|
|
199
|
+
for (const entry of entries) {
|
|
200
|
+
if (take(entry) && RUN_RECORD_PATTERN.test(entry.name)) {
|
|
201
|
+
files.push({ file: path.join(store, entry.name), namespace: null, linked: entry.isSymbolicLink() });
|
|
202
|
+
} else if ((entry.isDirectory() || (!listing && entry.isSymbolicLink())) && isRunNamespaceDir(entry.name)) {
|
|
203
|
+
let nested ;
|
|
204
|
+
try { nested = fs.readdirSync(path.join(store, entry.name), { withFileTypes: true }); } catch { continue; }
|
|
205
|
+
for (const item of nested) {
|
|
206
|
+
if (take(item) && RUN_RECORD_PATTERN.test(item.name)) {
|
|
207
|
+
files.push({ file: path.join(store, entry.name, item.name), namespace: entry.name, linked: entry.isSymbolicLink() || item.isSymbolicLink() });
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return files.sort((a, b) => a.file < b.file ? -1 : a.file > b.file ? 1 : 0);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** The largest run file the list action and `readRunRecord` read; a bigger one is reported as `io` /
|
|
216
|
+
* refused (a journal is a few KiB). */
|
|
217
|
+
const MAX_RUN_FILE_BYTES = MAX_RECORD_FILE_BYTES;
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* One run's record, read the way the list action reads it (FU-16c2wz): no link is followed on the
|
|
221
|
+
* way from the store folder `storeDir` to the file or at the file, and the file must be a regular
|
|
222
|
+
* file of at most `MAX_RUN_FILE_BYTES`, so a FIFO or a device cannot block the read and a link
|
|
223
|
+
* cannot make it read outside the store. A run file that is not there is `null`; one that must not
|
|
224
|
+
* be read throws `store-unreadable` naming the file, and nothing of it is read.
|
|
225
|
+
* @param {Object} fsImpl
|
|
226
|
+
* @param {string} file the run file, under `storeDir`
|
|
227
|
+
* @param {string} storeDir the run store folder it belongs to
|
|
228
|
+
* @returns {Object|null} the parsed record
|
|
229
|
+
*/
|
|
230
|
+
function readRunRecord(fsImpl , file , storeDir ) {
|
|
231
|
+
if (typeof fsImpl.lstatSync !== 'function' || typeof fsImpl.openSync !== 'function') return readTaskState(fsImpl, file);
|
|
232
|
+
const text = readRunText(fsImpl, file, storeDir);
|
|
233
|
+
return text === null ? null : readTaskState({ existsSync: () => true, readFileSync: () => text }, file);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** `readRunRecord`'s guarded read without the parse: the run file's text, `null` when it is not
|
|
237
|
+
* there, `store-unreadable` when it must not be read. */
|
|
238
|
+
function readRunText(fsImpl , file , storeDir ) {
|
|
239
|
+
const relative = path.relative(storeDir, file);
|
|
240
|
+
const parts = relative === '' || relative.startsWith('..') || path.isAbsolute(relative) ? [] : relative.split(path.sep);
|
|
241
|
+
const refuse = () => {
|
|
242
|
+
const failure = new Error(`Run file '${file}' is not a regular file inside the run store (a link, FIFO, device or folder, or larger than ${MAX_RUN_FILE_BYTES} bytes). Nothing was read.`);
|
|
243
|
+
failure.code = 'store-unreadable';
|
|
244
|
+
throw failure;
|
|
245
|
+
};
|
|
246
|
+
let at = storeDir;
|
|
247
|
+
for (const part of parts.slice(0, -1)) {
|
|
248
|
+
at = path.join(at, part);
|
|
249
|
+
let stat ;
|
|
250
|
+
try { stat = fsImpl.lstatSync(at); } catch (error ) {
|
|
251
|
+
if (error.code === 'ENOENT' || error.code === 'ENOTDIR') return null;
|
|
252
|
+
throw error;
|
|
253
|
+
}
|
|
254
|
+
if (stat.isSymbolicLink()) refuse();
|
|
255
|
+
}
|
|
256
|
+
let stat ;
|
|
257
|
+
try { stat = fsImpl.lstatSync(file); } catch (error ) {
|
|
258
|
+
if (error.code === 'ENOENT' || error.code === 'ENOTDIR') return null;
|
|
259
|
+
throw error;
|
|
260
|
+
}
|
|
261
|
+
if (!stat.isFile()) refuse();
|
|
262
|
+
const text = readRegularFileText(file, fsImpl);
|
|
263
|
+
if (text === null) refuse();
|
|
264
|
+
return text;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** The run files under one checkout's store, read, with each malformed or unreadable file isolated. */
|
|
268
|
+
function runEntriesAt(root , origin ) {
|
|
269
|
+
return runFilesAt(root, { listing: true }).map(({ file, namespace, linked }) => {
|
|
270
|
+
let record = null;
|
|
271
|
+
let error = null;
|
|
272
|
+
const bytes = linked ? null : readRegularFileText(file);
|
|
273
|
+
if (bytes === null) error = 'io';
|
|
274
|
+
else {
|
|
275
|
+
try { record = JSON.parse(bytes); } catch { error = 'unparseable'; }
|
|
276
|
+
}
|
|
277
|
+
return {
|
|
278
|
+
file,
|
|
279
|
+
root,
|
|
280
|
+
origin,
|
|
281
|
+
slug: namespace || runSlugOf(record, root),
|
|
282
|
+
record,
|
|
283
|
+
error,
|
|
284
|
+
};
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* All orchestration journals in the current store and the other checkouts, when Git can list them.
|
|
290
|
+
* @param {{stateRoot: string, exec?: Function}} options
|
|
291
|
+
* @returns {{ok: true, entries: Array<Object>}|{ok: false, error: 'cannot-list-checkouts'}}
|
|
292
|
+
*/
|
|
293
|
+
function orchestrationRuns({ stateRoot, exec } ) {
|
|
294
|
+
const current = runEntriesAt(stateRoot, 'current');
|
|
295
|
+
const checkouts = listCheckouts({ cwd: stateRoot, ...(exec ? { exec } : {}) });
|
|
296
|
+
if (!checkouts.ok && checkouts.reason !== 'not-a-git-repository') {
|
|
297
|
+
return { ok: false, error: 'cannot-list-checkouts' };
|
|
298
|
+
}
|
|
299
|
+
if (!checkouts.ok || checkouts.sandbox) return { ok: true, entries: current };
|
|
300
|
+
|
|
301
|
+
const entries = [...current];
|
|
302
|
+
for (const root of checkouts.others) entries.push(...runEntriesAt(root, 'other'));
|
|
303
|
+
return { ok: true, entries };
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/** Whether a flat record names no feature (no `featureSlug`, no `slug`): such a run is any feature's
|
|
307
|
+
* (`flatRecordIsOurs`), so a namespaced copy of the same id in the same store shadows it. */
|
|
308
|
+
function namesNoFeature(record ) {
|
|
309
|
+
return Boolean(record) && typeof record === 'object'
|
|
310
|
+
&& ![record.featureSlug, record.slug].some((value) => typeof value === 'string' && value !== '');
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* IC-001/IC-003: one entry per logical run, the copy `status` reads, with `findRunFile`'s
|
|
315
|
+
* precedence. In one store, the namespaced `<slug>/<id>.json` wins over a flat `<id>.json` that
|
|
316
|
+
* names that feature (when the slug is the id, the flat file is the run's only path and wins).
|
|
317
|
+
* A flat file that names no feature, or that cannot be read or is a link, is keyed as `findRunFile`
|
|
318
|
+
* would first read it, the id as its slug, and stays its own entry. Across stores, the current
|
|
319
|
+
* store's copy of a key wins over every other checkout's; a current flat file `status` reads for
|
|
320
|
+
* any feature without a namespaced copy here (one naming no feature, or a link, which status refuses)
|
|
321
|
+
* also shadows the other checkouts' copies of that id. With no current copy, each other checkout
|
|
322
|
+
* that holds one keeps its entry (more than one is the case `status` refuses as ambiguous). A
|
|
323
|
+
* losing copy is dropped: `status` never reads it for this run.
|
|
324
|
+
* @param {Array<Object>} entries `orchestrationRuns(...).entries`
|
|
325
|
+
* @returns {Array<Object>} the winning entries, in input order
|
|
326
|
+
*/
|
|
327
|
+
function selectRunEntries(entries ) {
|
|
328
|
+
const stem = (entry ) => path.basename(entry.file, '.json');
|
|
329
|
+
const namespaceOf = (entry ) => {
|
|
330
|
+
const parts = path.relative(runStoreDir(entry.root), entry.file).split(path.sep);
|
|
331
|
+
return parts.length > 1 ? parts[0] : null;
|
|
332
|
+
};
|
|
333
|
+
const named = (entry ) => entry.record && !namesNoFeature(entry.record) && !entry.error;
|
|
334
|
+
const linked = (entry ) => {
|
|
335
|
+
try { return fs.lstatSync(entry.file).isSymbolicLink(); } catch { return false; }
|
|
336
|
+
};
|
|
337
|
+
const keyOf = (entry ) => {
|
|
338
|
+
const ns = namespaceOf(entry);
|
|
339
|
+
if (ns !== null) return `${ns}\0${stem(entry)}`;
|
|
340
|
+
if (named(entry) && entry.slug) return `${entry.slug}\0${stem(entry)}`;
|
|
341
|
+
return `${stem(entry)}\0${stem(entry)}`;
|
|
342
|
+
};
|
|
343
|
+
const rank = (entry ) => {
|
|
344
|
+
const ns = namespaceOf(entry);
|
|
345
|
+
if (ns === null) return named(entry) && entry.slug !== stem(entry) ? 1 : 0;
|
|
346
|
+
return ns === stem(entry) ? 2 : 0;
|
|
347
|
+
};
|
|
348
|
+
// Ids a current flat file answers for any feature: status reads it before any other checkout.
|
|
349
|
+
const wildcards = new Set(entries.filter((entry) => entry.origin === 'current' && namespaceOf(entry) === null
|
|
350
|
+
&& ((entry.record && namesNoFeature(entry.record)) || linked(entry))).map(stem));
|
|
351
|
+
const perRoot = new Map ();
|
|
352
|
+
for (const entry of entries) {
|
|
353
|
+
const local = `${entry.root}\0${keyOf(entry)}`;
|
|
354
|
+
const held = perRoot.get(local);
|
|
355
|
+
if (!held || rank(entry) < rank(held)) perRoot.set(local, entry);
|
|
356
|
+
}
|
|
357
|
+
const best = new Map ();
|
|
358
|
+
for (const entry of perRoot.values()) {
|
|
359
|
+
const key = keyOf(entry);
|
|
360
|
+
const held = best.get(key);
|
|
361
|
+
if (!held) best.set(key, [entry]);
|
|
362
|
+
else if (held[0].origin === 'current') continue;
|
|
363
|
+
else if (entry.origin === 'current') best.set(key, [entry]);
|
|
364
|
+
else held.push(entry);
|
|
365
|
+
}
|
|
366
|
+
const winners = new Set ();
|
|
367
|
+
for (const list of best.values()) {
|
|
368
|
+
for (const entry of list) {
|
|
369
|
+
if (entry.origin !== 'current' && wildcards.has(stem(entry))) continue;
|
|
370
|
+
winners.add(entry);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return entries.filter((entry) => winners.has(entry));
|
|
374
|
+
}
|
|
375
|
+
|
|
171
376
|
/**
|
|
172
377
|
* A task's run file: in this checkout, else in exactly one other checkout whose run belongs to the
|
|
173
|
-
* same feature.
|
|
174
|
-
*
|
|
378
|
+
* same feature. The run of a task id other than its feature's slug is `orchestration/<slug>/<id>.json`
|
|
379
|
+
* (115 C-1); a flat run of that id is read in its place only when it is this feature's or names none
|
|
380
|
+
* (D-3). Task ids repeat across features, so another checkout's run with the same id but another
|
|
381
|
+
* feature (or none) is not this task's and is never read or updated from here.
|
|
175
382
|
* @param {{stateRoot: string, taskId: string, slug: string|null, exec?: Function}} options
|
|
176
383
|
* @returns {{status: 'found'|'missing'|'ambiguous', file: string|null, root: string|null,
|
|
177
384
|
* origin: 'current'|'other'|null, candidates: Array<string>}}
|
|
178
385
|
*/
|
|
179
|
-
function findRunFile({ stateRoot, taskId, slug, exec } ) {
|
|
180
|
-
const
|
|
386
|
+
function findRunFile({ stateRoot, taskId, slug, exec, anyNamespace = false } ) {
|
|
387
|
+
const found = findRunFileFor({ stateRoot, taskId, slug, exec });
|
|
388
|
+
return anyNamespace && found.status === 'missing' ? findNamespacedRun({ stateRoot, taskId, exec }) : found;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* FU-wrnnaq: the run of `taskId` under whichever feature namespace holds it, for a caller that has
|
|
393
|
+
* no feature to name (`status` run from another branch). This is the run the list action shows for
|
|
394
|
+
* the id: this checkout's namespaced run first, else the other checkouts'; exactly one is the run,
|
|
395
|
+
* more than one is `ambiguous` (the feature must then be named), none is `missing`. `slug` is the
|
|
396
|
+
* namespace the run is in, for the caller to read it by.
|
|
397
|
+
* @returns {{status: 'found'|'missing'|'ambiguous', file: string|null, root: string|null,
|
|
398
|
+
* origin: 'current'|'other'|null, candidates: Array<string>, slug?: string, byFeature?: true}}
|
|
399
|
+
*/
|
|
400
|
+
function findNamespacedRun({ stateRoot, taskId, exec } ) {
|
|
401
|
+
const name = `${taskId}.json`;
|
|
181
402
|
const missing = { status: 'missing', file: null, root: null, origin: null, candidates: [] };
|
|
182
|
-
|
|
183
|
-
|
|
403
|
+
const inRoot = (root ) => runFilesAt(root).filter((entry) => entry.namespace !== null && path.basename(entry.file) === name);
|
|
404
|
+
const settle = (entries , root , origin ) => (
|
|
405
|
+
entries.length === 1
|
|
406
|
+
? { status: 'found', file: entries[0].file, root, origin, candidates: [], slug: entries[0].namespace }
|
|
407
|
+
: { ...missing, status: 'ambiguous', byFeature: true, candidates: entries.map((entry) => entry.file) });
|
|
408
|
+
const here = inRoot(stateRoot);
|
|
409
|
+
if (here.length > 0) return settle(here, stateRoot, 'current');
|
|
410
|
+
const checkouts = listCheckouts({ cwd: stateRoot, ...(exec ? { exec } : {}) });
|
|
411
|
+
if (!checkouts.ok || checkouts.sandbox) return missing;
|
|
412
|
+
const matches = checkouts.others.flatMap((root ) => inRoot(root).map((entry) => ({ ...entry, root })));
|
|
413
|
+
if (matches.length === 0) return missing;
|
|
414
|
+
return settle(matches, matches[0].root, 'other');
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function findRunFileFor({ stateRoot, taskId, slug, exec } ) {
|
|
418
|
+
const store = RUN_STORE_REL;
|
|
419
|
+
const flat = path.join(store, `${taskId}.json`);
|
|
420
|
+
const namespaced = slug && slug !== taskId ? path.join(store, slug, `${taskId}.json`) : null;
|
|
421
|
+
const missing = { status: 'missing', file: null, root: null, origin: null, candidates: [] };
|
|
422
|
+
const here = runFilesAt(stateRoot);
|
|
423
|
+
const localNamespaced = namespaced && here.find((entry) => path.relative(stateRoot, entry.file) === namespaced);
|
|
424
|
+
const localFlat = here.find((entry) => path.relative(stateRoot, entry.file) === flat);
|
|
425
|
+
if (localNamespaced) return { status: 'found', file: localNamespaced.file, root: stateRoot, origin: 'current', candidates: [] };
|
|
426
|
+
if (localFlat && (!namespaced || flatRecordIsOurs(localFlat.file, slug, fs, true))) {
|
|
427
|
+
return { status: 'found', file: localFlat.file, root: stateRoot, origin: 'current', candidates: [] };
|
|
184
428
|
}
|
|
185
429
|
if (!slug) return missing;
|
|
186
430
|
const checkouts = listCheckouts({ cwd: stateRoot, ...(exec ? { exec } : {}) });
|
|
187
431
|
if (!checkouts.ok || checkouts.sandbox) return missing;
|
|
188
|
-
const matches
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
432
|
+
const matches = [];
|
|
433
|
+
for (const root of checkouts.others) {
|
|
434
|
+
const files = runFilesAt(root);
|
|
435
|
+
const found = [namespaced, flat].filter(Boolean).map((rel ) => files.find((entry) => path.relative(root, entry.file) === rel)).find((entry ) => {
|
|
436
|
+
if (!entry) return false;
|
|
437
|
+
// The same safe read as every other run read: a linked run or namespace folder, a FIFO or a
|
|
438
|
+
// device in another checkout is never opened, and is not a match.
|
|
439
|
+
try { return runSlugOf(JSON.parse(readRunText(fs, entry.file, runStoreDir(root)) ), root) === slug; } catch { return false; }
|
|
440
|
+
});
|
|
441
|
+
if (found) matches.push({ file: found.file, root });
|
|
442
|
+
}
|
|
197
443
|
if (matches.length === 0) return missing;
|
|
198
|
-
if (matches.length > 1) return { ...missing, status: 'ambiguous', candidates: matches.map((
|
|
199
|
-
return { status: 'found', file:
|
|
444
|
+
if (matches.length > 1) return { ...missing, status: 'ambiguous', candidates: matches.map((m) => m.file) };
|
|
445
|
+
return { status: 'found', file: matches[0].file, root: matches[0].root, origin: 'other', candidates: [] };
|
|
200
446
|
}
|
|
201
447
|
|
|
202
448
|
/** The slug a refusal's next command names: one whose feature folder exists in this checkout or in
|
|
@@ -231,7 +477,7 @@ function unusableDetail(record , now ) {
|
|
|
231
477
|
* @param {string} options.template
|
|
232
478
|
* @param {Date} [options.now]
|
|
233
479
|
* @param {Function} [options.exec]
|
|
234
|
-
* @returns {{ok: boolean, code: 'ready'|'missing'|'not-ready'|'wrong-template'|'unusable'|'ambiguous',
|
|
480
|
+
* @returns {{ok: boolean, code: 'ready'|'missing'|'not-ready'|'wrong-template'|'unusable'|'ambiguous'|'store-conflict',
|
|
235
481
|
* record: Object|null, file: string|null, origin: string|null, candidates: Array<string>,
|
|
236
482
|
* detail: string|null, nextSlug: string|null, message: string|null}}
|
|
237
483
|
*/
|
|
@@ -254,6 +500,7 @@ function checkReadiness({ stateRoot, taskId, slug = null, template, now = new Da
|
|
|
254
500
|
}),
|
|
255
501
|
});
|
|
256
502
|
if (read.status === 'ambiguous') return result('ambiguous');
|
|
503
|
+
if (read.status === 'conflict') return result('store-conflict');
|
|
257
504
|
if (read.status === 'missing') return result('missing');
|
|
258
505
|
const detail = read.status === 'unreadable' ? read.detail : unusableDetail(read.record, now);
|
|
259
506
|
if (detail) return result('unusable', detail);
|
|
@@ -284,7 +531,9 @@ function verifyReadinessCheck({ stateRoot, taskId, slug = null, now = new Date()
|
|
|
284
531
|
};
|
|
285
532
|
}
|
|
286
533
|
if (found.status === 'missing') return { applies: false, reason: 'no-run' };
|
|
287
|
-
|
|
534
|
+
// Reads only through the run store's safe reader; a run file that must not be read is refused by name.
|
|
535
|
+
const run = readRunRecord(fs, found.file, runStoreDir(found.root));
|
|
536
|
+
if (!run) return { applies: false, reason: 'no-run' };
|
|
288
537
|
if (run.state === 'COMPLETED' || run.state === 'REJECTED') return { applies: false, reason: 'run-finished' };
|
|
289
538
|
const stageNodes = (run.program || []).filter((n ) => n.type === 'stage');
|
|
290
539
|
const stage = gatedStage(stageNodes);
|
|
@@ -309,6 +558,7 @@ function verifyReadinessCheck({ stateRoot, taskId, slug = null, now = new Date()
|
|
|
309
558
|
}
|
|
310
559
|
|
|
311
560
|
module.exports = {
|
|
561
|
+
unusableDetail,
|
|
312
562
|
gatedStage,
|
|
313
563
|
heldClasses,
|
|
314
564
|
refusalText,
|
|
@@ -317,5 +567,10 @@ module.exports = {
|
|
|
317
567
|
verifyReadinessCheck,
|
|
318
568
|
featureSlugFor,
|
|
319
569
|
runSlugOf,
|
|
570
|
+
orchestrationRuns,
|
|
571
|
+
selectRunEntries,
|
|
320
572
|
findRunFile,
|
|
573
|
+
RUN_STORE_REL,
|
|
574
|
+
runStoreDir,
|
|
575
|
+
readRunRecord,
|
|
321
576
|
};
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* States which of the report's findings the existing repair path can actually act on. That set is
|
|
9
9
|
* narrower than the set of findings, and the difference is not self-evident:
|
|
10
10
|
*
|
|
11
|
-
* $ doflow status -g → 321 pending changes across
|
|
11
|
+
* $ doflow status -g → 321 pending changes across several harnesses
|
|
12
12
|
* $ doflow reconcile -g --dry-run → 10 drifts for 1 harness
|
|
13
13
|
*
|
|
14
14
|
* Both numbers are internally consistent. `status` reads the ledger, which records what DoFlow
|
|
@@ -162,7 +162,7 @@ function planBasis(snapshot ) {
|
|
|
162
162
|
* source fingerprint, **and** the recorded ledger row equal to it too — which is right for its own
|
|
163
163
|
* purpose, planning a write that also refreshes the row. It means a change is emitted whenever the
|
|
164
164
|
* row alone is stale, and that happens routinely: the shared hooks asset projects one destination for
|
|
165
|
-
* claude, codex,
|
|
165
|
+
* claude, codex, pi, kiro and antigravity while ownership is recorded per harness, so a sibling's
|
|
166
166
|
* install legitimately refreshes bytes the other harnesses' rows still describe by their old hash.
|
|
167
167
|
* Reading such a change as divergence reported one file, identical bytes, under contradictory
|
|
168
168
|
* verdicts per harness, with a remedy offering to "restore" a file that was already correct.
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* first-match reading of that same order would get it backwards).
|
|
22
22
|
*
|
|
23
23
|
* A `merged` mode means the copies are combined rather than chosen between, so no single copy
|
|
24
|
-
* "wins". An `unestablished` mode means the order may be documented (
|
|
24
|
+
* "wins". An `unestablished` mode means the order may be documented (no shipped harness does today) or
|
|
25
25
|
* may not (`pi`, `antigravity` document neither), but either way the rule for choosing between
|
|
26
26
|
* copies is not — so no winner is named. Those two cases are separate states under IC-003 and the
|
|
27
27
|
* withheld reason says which: a documented order with an undocumented winner names the order it
|
|
@@ -96,7 +96,7 @@ function resolveShadowVerdict(presentScopes , precedence ) {
|
|
|
96
96
|
|
|
97
97
|
if (mode === 'unestablished') {
|
|
98
98
|
// IC-003 keeps these two states distinct: an order established with the mode explicitly
|
|
99
|
-
// unestablished
|
|
99
|
+
// unestablished is not the same finding as an order sought and found undocumented
|
|
100
100
|
// (`pi`, `antigravity`). Both withhold the verdict; only one of them knows the reading order.
|
|
101
101
|
const orderIsEstablished = Array.isArray(order) && order.length > 0;
|
|
102
102
|
return {
|
|
@@ -23,7 +23,7 @@ const path = require('node:path');
|
|
|
23
23
|
*
|
|
24
24
|
* **The bound on cost (R6).** Only the parent directories of assets already enumerated from the
|
|
25
25
|
* ledger are read — one level, never recursed into. A managed tree that nests (Kiro's steering tree
|
|
26
|
-
* mirrors `core/shared/guidance/`, `rules/`
|
|
26
|
+
* mirrors the core set of `core/shared/guidance/`, `rules/` included) is still covered completely
|
|
27
27
|
* without a walk, because every subdirectory holding a managed file is itself the parent of a
|
|
28
28
|
* managed resource and so is inspected on its own account. `mcp-builder/` and `skill-creator/`
|
|
29
29
|
* sitting beside a managed skills directory are never enumerated: neither is the parent of a
|
|
@@ -51,10 +51,12 @@ const path = require('node:path');
|
|
|
51
51
|
* *every* file it copies under an `applyTo: '**'` front-matter header, so every file landing in
|
|
52
52
|
* `.github/instructions/` is always applied. Asset `instructions.copilot`, native dir
|
|
53
53
|
* `instructions`.
|
|
54
|
-
* - `src/adapters/kiro/index.js` (header comment, lines 8-12) —
|
|
55
|
-
*
|
|
54
|
+
* - `src/adapters/kiro/index.js` (header comment, lines 8-12) — Kiro has no single-file
|
|
55
|
+
* instruction surface and loads every steering file without front-matter. DoFlow-produced
|
|
56
|
+
* steering holds only the core set (selected via `guidance-manifest`), but the directory is
|
|
57
|
+
* still loaded wholesale, so an unmanaged file there is operative. Asset
|
|
56
58
|
* `guidance.context-layer` for the `kiro` harness only, native dir `steering`; the same asset's
|
|
57
|
-
* `../.doflow/guidance` projection for
|
|
59
|
+
* `../.doflow/guidance` projection for the other harnesses is name-resolved by
|
|
58
60
|
* explicit import and is not in this set.
|
|
59
61
|
*
|
|
60
62
|
* This is the single place to change. If the registry later declares load semantics per projection,
|