@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,100 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
|
|
4
|
+
|
|
5
|
+
const fs = require('node:fs');
|
|
6
|
+
const { boundedAcquire, updateTaskState } = require('../task-state.js');
|
|
7
|
+
const { taskRecord } = require('../task-scope.js');
|
|
8
|
+
|
|
9
|
+
const MAX_DELIVERIES = 5;
|
|
10
|
+
const ACTUAL_UNMEASURED = 'no harness reports token usage to an external CLI; profile tokenTelemetry unverified';
|
|
11
|
+
|
|
12
|
+
function assertValidDeliveryEntry(entry ) {
|
|
13
|
+
if (!entry || typeof entry !== 'object') throw new Error('Delivery entry must be an object');
|
|
14
|
+
if (!entry.tokens || !entry.tokens.estimated) throw new Error('Delivery entry requires tokens.estimated');
|
|
15
|
+
if (typeof entry.tokens.estimated.value !== 'number' || entry.tokens.estimated.basis !== 'estimated:bytes/4') {
|
|
16
|
+
throw new Error('Delivery entry tokens.estimated must use estimated:bytes/4');
|
|
17
|
+
}
|
|
18
|
+
if (entry.tokens.actual?.basis?.startsWith('estimated:')) {
|
|
19
|
+
throw new Error('Delivery entry tokens.actual cannot use an estimated basis');
|
|
20
|
+
}
|
|
21
|
+
for (const expansion of entry.expansions || []) {
|
|
22
|
+
if (typeof expansion.reason !== 'string' || expansion.reason.trim() === '') {
|
|
23
|
+
throw new Error('Delivery expansion reason must not be empty');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function makeTokenRecord(bytes ) {
|
|
29
|
+
return {
|
|
30
|
+
estimated: { value: Math.ceil(bytes / 4), basis: 'estimated:bytes/4' },
|
|
31
|
+
actual: { unmeasured: ACTUAL_UNMEASURED },
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function createDeliveryEntry({ pack, form, harness = null, sources = {}, expansions = [], retention = null } ) {
|
|
36
|
+
const items = pack.deliveryAllItems || pack.deliveryItems || [];
|
|
37
|
+
const digests = Object.fromEntries(items.map((item ) => [item.id, item.digest]));
|
|
38
|
+
const mandatoryIds = items.filter((item ) => item.cls === 'mandatory').map((item ) => item.id);
|
|
39
|
+
const harnessId = harness || 'unstated';
|
|
40
|
+
const bytes = Number(pack.size?.totalBytes || 0);
|
|
41
|
+
const entry = {
|
|
42
|
+
key: `${pack.delivery.packDigest}.${form}.${harnessId}`,
|
|
43
|
+
packDigest: pack.delivery.packDigest,
|
|
44
|
+
form,
|
|
45
|
+
harness,
|
|
46
|
+
firstAt: new Date().toISOString(),
|
|
47
|
+
mode: pack.mode || 'full',
|
|
48
|
+
budget: { maxBytes: pack.budget.maxBytes, source: pack.budget.source },
|
|
49
|
+
since: pack.delivery.since || null,
|
|
50
|
+
deltaRefused: pack.delivery.deltaRefused || null,
|
|
51
|
+
retention: retention || { status: 'unknown', basis: 'contextProfile.retention is unverified' },
|
|
52
|
+
items: digests,
|
|
53
|
+
mandatoryIds,
|
|
54
|
+
sources,
|
|
55
|
+
pointers: pack.pointers || [],
|
|
56
|
+
bytes,
|
|
57
|
+
tokens: makeTokenRecord(bytes),
|
|
58
|
+
omissions: (pack.deliveryOmissions || []).map((item ) => ({ ...item })),
|
|
59
|
+
expansions: [...expansions],
|
|
60
|
+
};
|
|
61
|
+
assertValidDeliveryEntry(entry);
|
|
62
|
+
return entry;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function readDeliveryEntry({ projectRoot, taskId, slug = null, packDigest, fsImpl = fs } ) {
|
|
66
|
+
const file = taskRecord({ projectRoot, store: 'delivery', taskId, slug, fsImpl }).read;
|
|
67
|
+
try {
|
|
68
|
+
const disk = JSON.parse(fsImpl.readFileSync(file, 'utf8'));
|
|
69
|
+
return (Array.isArray(disk.deliveries) ? disk.deliveries : []).find((entry ) => entry.packDigest === packDigest) || null;
|
|
70
|
+
} catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Persist a delivery under the same bounded lock used by task state. */
|
|
76
|
+
function recordDelivery({ projectRoot, taskId, slug = null, entry, fsImpl = fs, acquire = boundedAcquire, supersedesKey = null } ) {
|
|
77
|
+
assertValidDeliveryEntry(entry);
|
|
78
|
+
if (typeof taskId !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(taskId) || taskId.includes('..')) {
|
|
79
|
+
throw new Error(`Invalid task id '${taskId}'`);
|
|
80
|
+
}
|
|
81
|
+
const { file, seed } = taskRecord({ projectRoot, store: 'delivery', taskId, slug, fsImpl });
|
|
82
|
+
const written = updateTaskState({
|
|
83
|
+
fsImpl,
|
|
84
|
+
file,
|
|
85
|
+
prepare: seed,
|
|
86
|
+
acquire: (impl , target ) => acquire(impl, target, { retries: 3, waitMs: 5 }),
|
|
87
|
+
build(disk ) {
|
|
88
|
+
const deliveries = Array.isArray(disk?.deliveries) ? disk.deliveries : [];
|
|
89
|
+
if (deliveries.some((existing ) => existing.key === entry.key)) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const current = supersedesKey ? deliveries.filter((existing ) => existing.key !== supersedesKey) : deliveries;
|
|
93
|
+
return { taskId, deliveries: [...current, entry].slice(-MAX_DELIVERIES) };
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
if (written === null) return null;
|
|
97
|
+
return { key: entry.key, recorded: true, reason: null };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
module.exports = { ACTUAL_UNMEASURED, assertValidDeliveryEntry, createDeliveryEntry, makeTokenRecord, readDeliveryEntry, recordDelivery };
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Rule compiler for the hook emission table (078 IC-003, DEC-005). Run directly by Node native
|
|
4
|
+
// type-stripping:
|
|
5
|
+
//
|
|
6
|
+
// node src/runtime/context/emission-rules.js --write rewrite the committed table
|
|
7
|
+
// node src/runtime/context/emission-rules.js --check exit 0 equal, 1 differs, 2 usage
|
|
8
|
+
//
|
|
9
|
+
// The table is compiled from EMISSION_RULES, never written by hand: each rule's finite domain is
|
|
10
|
+
// enumerated through INPUT_DOMAINS and ruleAction(), so every arm the hook reads is the action
|
|
11
|
+
// arbitrate() gives that vector. Every combination is written as its own literal arm; a pattern
|
|
12
|
+
// holds no glob, so the hook's lookup matches exactly what IC-002 lists and nothing else.
|
|
13
|
+
|
|
14
|
+
const crypto = require('node:crypto');
|
|
15
|
+
const fs = require('node:fs');
|
|
16
|
+
const path = require('node:path');
|
|
17
|
+
const { REPO_ROOT } = require('../../helper/repo-root.js');
|
|
18
|
+
const { EMISSION_RULES, INPUT_DOMAINS, ruleAction } = require('./arbitrate.js');
|
|
19
|
+
|
|
20
|
+
const TABLE_REL = 'core/harnesses/shared/hooks/policies/emission-rules.sh';
|
|
21
|
+
const RULES_VERSION = 1;
|
|
22
|
+
const MAX_BYTES_DEFAULT = 16384;
|
|
23
|
+
const SUMMARY_CAP = 4000;
|
|
24
|
+
const SUMMARY_READ = 65536;
|
|
25
|
+
|
|
26
|
+
/** Every value combination of the named inputs, in vector order. */
|
|
27
|
+
function combinations(inputs ) {
|
|
28
|
+
let out = [[]];
|
|
29
|
+
for (const name of inputs) {
|
|
30
|
+
const domain = INPUT_DOMAINS[name];
|
|
31
|
+
out = out.flatMap((prefix) => domain.map((value) => [...prefix, value]));
|
|
32
|
+
}
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** JSON with object keys sorted at every depth, so the digest does not depend on key order. */
|
|
37
|
+
function canonicalJson(value ) {
|
|
38
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(',')}]`;
|
|
39
|
+
if (value && typeof value === 'object') {
|
|
40
|
+
const entries = Object.keys(value ).sort()
|
|
41
|
+
.map((key) => `${JSON.stringify(key)}:${canonicalJson((value )[key])}`);
|
|
42
|
+
return `{${entries.join(',')}}`;
|
|
43
|
+
}
|
|
44
|
+
return JSON.stringify(value);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function rulesDigest(rules ) {
|
|
48
|
+
return crypto.createHash('sha256').update(canonicalJson(rules), 'utf8').digest('hex');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function compare(a , b ) {
|
|
52
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
/** Every non-skip combination of every rule, sorted by event, segment, vector. */
|
|
58
|
+
function compileArms(rules = EMISSION_RULES) {
|
|
59
|
+
const arms = [];
|
|
60
|
+
for (const r of rules) {
|
|
61
|
+
for (const values of combinations(r.inputs)) {
|
|
62
|
+
const action = ruleAction(r, values);
|
|
63
|
+
if (action !== 'skip') arms.push({ event: r.event, segment: r.segment, vector: values.join(':'), action });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return arms.sort((a, b) => compare(a.event, b.event) || compare(a.segment, b.segment) || compare(a.vector, b.vector));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** A value safe inside a single-quoted bash pattern, with no glob character. */
|
|
70
|
+
function literal(text ) {
|
|
71
|
+
if (!/^[A-Za-z0-9._:|-]+$/.test(text)) throw new TypeError(`emission-rules: ${JSON.stringify(text)} is not a plain token`);
|
|
72
|
+
return text;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** IC-003: the full text of `emission-rules.sh`. */
|
|
76
|
+
function compileEmissionTable(rules = EMISSION_RULES) {
|
|
77
|
+
const lines = [
|
|
78
|
+
'# Generated by node src/runtime/context/emission-rules.js --write. Do not edit.',
|
|
79
|
+
`# rules-digest: sha256:${rulesDigest(rules)}`,
|
|
80
|
+
`DF_RULES_VERSION=${RULES_VERSION}`,
|
|
81
|
+
`DF_RULES_MAX_BYTES_DEFAULT=${MAX_BYTES_DEFAULT}`,
|
|
82
|
+
`DF_RULES_SUMMARY_CAP=${SUMMARY_CAP}`,
|
|
83
|
+
`DF_RULES_SUMMARY_READ=${SUMMARY_READ}`,
|
|
84
|
+
'doflow_emission_rule() {',
|
|
85
|
+
" # $1 = event.segment, $2 = vector: input values joined with ':' in IC-002 order",
|
|
86
|
+
' case "$1|$2" in',
|
|
87
|
+
];
|
|
88
|
+
for (const arm of compileArms(rules)) {
|
|
89
|
+
lines.push(` '${literal(`${arm.event}.${arm.segment}|${arm.vector}`)}') DF_ACTION=${literal(arm.action)} ;;`);
|
|
90
|
+
}
|
|
91
|
+
lines.push(' *) DF_ACTION=skip ;;', ' esac', '}');
|
|
92
|
+
return `${lines.join('\n')}\n`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const USAGE = 'usage: node src/runtime/context/emission-rules.js --write|--check\n';
|
|
96
|
+
|
|
97
|
+
/** The CLI: 0 written or equal, 1 the committed table differs from the rules, 2 usage. */
|
|
98
|
+
function main(argv ) {
|
|
99
|
+
if (argv.length !== 1 || (argv[0] !== '--write' && argv[0] !== '--check')) {
|
|
100
|
+
process.stderr.write(USAGE);
|
|
101
|
+
return 2;
|
|
102
|
+
}
|
|
103
|
+
const target = path.join(REPO_ROOT, TABLE_REL);
|
|
104
|
+
const expected = compileEmissionTable();
|
|
105
|
+
if (argv[0] === '--write') {
|
|
106
|
+
fs.writeFileSync(target, expected);
|
|
107
|
+
return 0;
|
|
108
|
+
}
|
|
109
|
+
let actual ;
|
|
110
|
+
try { actual = fs.readFileSync(target, 'utf8'); } catch { actual = null; }
|
|
111
|
+
if (actual === expected) return 0;
|
|
112
|
+
process.stderr.write(`emission-rules: ${TABLE_REL} is ${actual === null ? 'missing' : 'stale'}; run node src/runtime/context/emission-rules.js --write\n`);
|
|
113
|
+
return 1;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
module.exports = { TABLE_REL, canonicalJson, compileArms, compileEmissionTable, rulesDigest };
|
|
117
|
+
|
|
118
|
+
if (require.main === module) process.exitCode = main(process.argv.slice(2));
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Session identity reader (078 IC-007). Run directly by Node native type-stripping.
|
|
4
|
+
//
|
|
5
|
+
// The TypeScript twin of `policies/identity.sh`: both read the launcher-set environment the same
|
|
6
|
+
// way, and test/fixtures/identity/cases.json is the corpus both must agree on (G38.6). Identity
|
|
7
|
+
// comes only from DOFLOW_RUN_ID, DOFLOW_ATTEMPT, DOFLOW_LINEAGE_ID, DOFLOW_SESSION_ROLE and
|
|
8
|
+
// DOFLOW_WORKTREE plus the payload's session_id and cwd; it is never inferred from git or from the
|
|
9
|
+
// harness's own idea of a main session.
|
|
10
|
+
|
|
11
|
+
const crypto = require('node:crypto');
|
|
12
|
+
const fs = require('node:fs');
|
|
13
|
+
const path = require('node:path');
|
|
14
|
+
const { configHome } = require('../config-home.js');
|
|
15
|
+
|
|
16
|
+
/** DEC-024: a launched unit id `L.<run>.<lineage>.<attempt>` stays inside the 128-character session-dirs match. */
|
|
17
|
+
const ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,55}$/;
|
|
18
|
+
const ATTEMPT_PATTERN = /^[1-9][0-9]{0,3}$/;
|
|
19
|
+
const DEFAULT_RUN = 'local';
|
|
20
|
+
const UNKNOWN_ROLE_LINE = '[doflow] unknown DOFLOW_SESSION_ROLE; treating session as worker\n';
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
let roleWarned = false;
|
|
47
|
+
|
|
48
|
+
/** `<config home>/doflow/session-env`, the root `policies/lib.sh` names STATE_DIR; null with no config home (IC-010), and the callers then skip session state. */
|
|
49
|
+
function stateDir(env = process.env) {
|
|
50
|
+
const { dir } = configHome(env);
|
|
51
|
+
return dir === null ? null : path.join(dir, 'session-env');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** `(cd "$p" && pwd -P)`: the physical path of an existing directory, or null. */
|
|
55
|
+
function physicalDir(p ) {
|
|
56
|
+
try {
|
|
57
|
+
if (!fs.statSync(p).isDirectory()) return null;
|
|
58
|
+
return fs.realpathSync.native(p);
|
|
59
|
+
} catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** `canonicalize_path` from policies/lib.sh: resolve the parent chain of an existing path, never its leaf. */
|
|
65
|
+
function canonicalizePath(p ) {
|
|
66
|
+
try {
|
|
67
|
+
fs.statSync(p); // `[[ -e ]]` follows symlinks, so a dangling link is "missing"
|
|
68
|
+
const dir = fs.realpathSync.native(path.dirname(p));
|
|
69
|
+
const base = path.basename(p);
|
|
70
|
+
return dir === '/' ? `/${base}` : `${dir}/${base}`;
|
|
71
|
+
} catch {
|
|
72
|
+
return p;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** `cwd_hash` from policies/lib.sh: first 16 hex chars of sha256 over the canonical path plus `echo`'s newline. */
|
|
77
|
+
function cwdHash(p ) {
|
|
78
|
+
let canonical = physicalDir(p) ?? canonicalizePath(p);
|
|
79
|
+
// bash hashes `pwd -P`, which Git Bash prints as /c/Users/x; hash the same spelling for a native C:\\Users\\x.
|
|
80
|
+
if (process.platform === 'win32') {
|
|
81
|
+
const drive = /^([A-Za-z]):[\\/]?(.*)$/.exec(canonical);
|
|
82
|
+
if (drive) canonical = `/${drive[1].toLowerCase()}${drive[2] ? `/${drive[2].replace(/\\/g, '/')}` : ''}`;
|
|
83
|
+
}
|
|
84
|
+
return crypto.createHash('sha256').update(`${canonical}\n`).digest('hex').slice(0, 16);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** `tr '[:upper:]' '[:lower:]'` on the role, ASCII only, then today's mapping: unset is main, unknown is worker. */
|
|
88
|
+
function readRole(raw ) {
|
|
89
|
+
const role = (raw || '').replace(/[A-Z]/g, (c) => c.toLowerCase());
|
|
90
|
+
if (role === '' || role === 'main') return { role: 'main', unknown: false };
|
|
91
|
+
if (role === 'worker') return { role: 'worker', unknown: false };
|
|
92
|
+
return { role: 'worker', unknown: true };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** DOFLOW_WORKTREE canonicalised when it names an absolute existing directory; else the payload cwd as given (cwdHash canonicalises for the store key). */
|
|
96
|
+
function readWorktree(raw , cwd ) {
|
|
97
|
+
if (raw && raw.startsWith('/')) {
|
|
98
|
+
const dir = physicalDir(raw);
|
|
99
|
+
if (dir !== null) return dir;
|
|
100
|
+
}
|
|
101
|
+
return cwd;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Create `sessions/<unitId>/bound` exclusively, holding the session id. True when this session owns
|
|
106
|
+
* the unit (just bound, or bound earlier by the same session); false when another session bound it,
|
|
107
|
+
* and false when it cannot be decided (the store is unwritable, `bound` is a symlink, which is never
|
|
108
|
+
* followed, or `bound` is still empty after one retry). Never throws: the caller treats false as an
|
|
109
|
+
* inherited session (L1-04).
|
|
110
|
+
*/
|
|
111
|
+
function bindUnit(env , unitId , sessionId ) {
|
|
112
|
+
const root = stateDir(env);
|
|
113
|
+
if (root === null) return false;
|
|
114
|
+
const dir = path.join(root, 'sessions', unitId);
|
|
115
|
+
const file = path.join(dir, 'bound');
|
|
116
|
+
try {
|
|
117
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
118
|
+
const fd = fs.openSync(file, 'wx');
|
|
119
|
+
try {
|
|
120
|
+
fs.writeSync(fd, `${sessionId}\n`);
|
|
121
|
+
} finally {
|
|
122
|
+
fs.closeSync(fd);
|
|
123
|
+
}
|
|
124
|
+
return true;
|
|
125
|
+
} catch (error ) {
|
|
126
|
+
if (error?.code !== 'EEXIST') return false;
|
|
127
|
+
}
|
|
128
|
+
// `IFS= read -r bound` of a regular file, not a symlink: its first line. An empty first line may
|
|
129
|
+
// be a concurrent binder between its create and its write, so it is read once more.
|
|
130
|
+
for (let tries = 0; tries < 2; tries++) {
|
|
131
|
+
let bound ;
|
|
132
|
+
try {
|
|
133
|
+
if (!fs.lstatSync(file).isFile()) return false;
|
|
134
|
+
bound = fs.readFileSync(file, 'utf8').split('\n')[0];
|
|
135
|
+
} catch {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
if (bound !== '') return bound === sessionId;
|
|
139
|
+
}
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** IC-007: the session identity from the environment and the payload's session id and cwd. */
|
|
144
|
+
function resolveIdentity(env , options ) {
|
|
145
|
+
const { cwd, sessionId, bind = false } = options;
|
|
146
|
+
const diagnostics = new Set ();
|
|
147
|
+
|
|
148
|
+
const { role, unknown } = readRole(env.DOFLOW_SESSION_ROLE);
|
|
149
|
+
if (unknown) {
|
|
150
|
+
diagnostics.add('role-unknown');
|
|
151
|
+
if (!roleWarned) {
|
|
152
|
+
roleWarned = true;
|
|
153
|
+
(options.warn ?? ((line ) => process.stderr.write(line)))(UNKNOWN_ROLE_LINE);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const rawRun = env.DOFLOW_RUN_ID || '';
|
|
158
|
+
const runValid = rawRun !== '' && rawRun !== DEFAULT_RUN && ID_PATTERN.test(rawRun);
|
|
159
|
+
if (rawRun !== '' && !runValid) diagnostics.add('identity-invalid');
|
|
160
|
+
const run = runValid ? rawRun : DEFAULT_RUN;
|
|
161
|
+
|
|
162
|
+
const rawAttempt = env.DOFLOW_ATTEMPT || '';
|
|
163
|
+
const attempt = ATTEMPT_PATTERN.test(rawAttempt) ? rawAttempt : '';
|
|
164
|
+
|
|
165
|
+
const rawLineage = env.DOFLOW_LINEAGE_ID || '';
|
|
166
|
+
const lineageValid = rawLineage !== '' && ID_PATTERN.test(rawLineage);
|
|
167
|
+
if (rawLineage !== '' && !lineageValid) diagnostics.add('identity-invalid');
|
|
168
|
+
let lineage ;
|
|
169
|
+
if (rawRun !== '' && !runValid) lineage = 'solo';
|
|
170
|
+
else if (rawLineage === '') lineage = role === 'main' ? 'main' : 'solo';
|
|
171
|
+
else lineage = lineageValid ? rawLineage : 'solo';
|
|
172
|
+
|
|
173
|
+
let lineageKind = 'solo';
|
|
174
|
+
if (runValid && lineageValid && attempt !== '') lineageKind = 'launched';
|
|
175
|
+
else if (run === DEFAULT_RUN && lineage === 'main') lineageKind = 'default-main';
|
|
176
|
+
|
|
177
|
+
let unitId = lineageKind === 'launched' ? `L.${run}.${lineage}.${attempt}` : sessionId;
|
|
178
|
+
if (lineageKind === 'launched' && bind) {
|
|
179
|
+
// An empty session id cannot bind: solo with an empty unit and no capture, as identity.sh.
|
|
180
|
+
// Any other failure to bind is an inherited session (DEC-044).
|
|
181
|
+
const mine = sessionId !== '' && bindUnit(env, unitId, sessionId);
|
|
182
|
+
if (!mine) {
|
|
183
|
+
if (sessionId !== '') diagnostics.add('identity-inherited');
|
|
184
|
+
lineageKind = 'solo';
|
|
185
|
+
lineage = 'solo';
|
|
186
|
+
unitId = sessionId;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
run,
|
|
192
|
+
attempt,
|
|
193
|
+
lineage,
|
|
194
|
+
role,
|
|
195
|
+
worktree: readWorktree(env.DOFLOW_WORKTREE, cwd),
|
|
196
|
+
lineageKind,
|
|
197
|
+
unitId,
|
|
198
|
+
session: sessionId,
|
|
199
|
+
diagnostics: [...diagnostics].sort(),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* The run ledger's identity fields (082 IC-034, DEC-042). Each value is tested on its own and
|
|
205
|
+
* omitted, never written, when absent or invalid: `run` by `resolveIdentity`'s `runValid` rule
|
|
206
|
+
* (non-empty, not `local`, `ID_PATTERN`), `lineage` by `ID_PATTERN`, `attempt` by `ATTEMPT_PATTERN`.
|
|
207
|
+
*/
|
|
208
|
+
function identityFields(env ) {
|
|
209
|
+
const out = {};
|
|
210
|
+
const run = env.DOFLOW_RUN_ID || '';
|
|
211
|
+
if (run !== '' && run !== DEFAULT_RUN && ID_PATTERN.test(run)) out.run = run;
|
|
212
|
+
const lineage = env.DOFLOW_LINEAGE_ID || '';
|
|
213
|
+
if (ID_PATTERN.test(lineage)) out.lineage = lineage;
|
|
214
|
+
const attempt = env.DOFLOW_ATTEMPT || '';
|
|
215
|
+
if (ATTEMPT_PATTERN.test(attempt)) out.attempt = attempt;
|
|
216
|
+
return out;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
module.exports = {
|
|
220
|
+
ATTEMPT_PATTERN,
|
|
221
|
+
ID_PATTERN,
|
|
222
|
+
UNKNOWN_ROLE_LINE,
|
|
223
|
+
cwdHash,
|
|
224
|
+
identityFields,
|
|
225
|
+
resolveIdentity,
|
|
226
|
+
stateDir,
|
|
227
|
+
};
|