@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
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
+
case "${OSTYPE:-}" in msys*|cygwin*|win32*) if command -v jq >/dev/null 2>&1; then jq() { command jq -b "$@"; }; fi ;; esac # native jq.exe ends lines with CRLF unless -b
|
|
3
|
+
_DOFLOW_SRC=${0//\\//}; case $_DOFLOW_SRC in */*) _DOFLOW_POLDIR=${_DOFLOW_SRC%/*} ;; *) _DOFLOW_POLDIR=. ;; esac
|
|
2
4
|
# stop-check.sh — Canonical Policy Library: Stop hook
|
|
3
5
|
#
|
|
4
6
|
# Two responsibilities:
|
|
@@ -24,14 +26,8 @@
|
|
|
24
26
|
#
|
|
25
27
|
# Canonical Policy Script Contract (design.md §4):
|
|
26
28
|
# env DOFLOW_PROJECT_DIR, DOFLOW_AGENT (both optional here)
|
|
27
|
-
# stdin a
|
|
28
|
-
#
|
|
29
|
-
# camelCase transcriptPath) — this script reads the union of known
|
|
30
|
-
# field names rather than assuming one, since a native front door
|
|
31
|
-
# (Claude/Codex/Kiro) execs this script directly with no payload
|
|
32
|
-
# translation of its own.
|
|
33
|
-
# exit 0 = allow the stop, non-zero = deny (block the stop) with the
|
|
34
|
-
# reason on stderr.
|
|
29
|
+
# stdin envelope v1 from a native front door via the shared lifecycle bridge.
|
|
30
|
+
# exit exit 2 + doflow_decide deny = deny; other faults per IC-011.
|
|
35
31
|
#
|
|
36
32
|
# Depends on lib.sh (require_jq, json_field, ensure_session_dir,
|
|
37
33
|
# run_with_timeout) being discoverable next to this script when it runs — see
|
|
@@ -48,18 +44,49 @@
|
|
|
48
44
|
|
|
49
45
|
set -euo pipefail
|
|
50
46
|
# shellcheck source=lib.sh
|
|
51
|
-
source "$
|
|
52
|
-
require_jq
|
|
53
|
-
|
|
47
|
+
source "$_DOFLOW_POLDIR/lib.sh"
|
|
54
48
|
INPUT=$(cat)
|
|
55
|
-
|
|
49
|
+
# Decision records stay off every stream unless a bridge supplied the fd.
|
|
50
|
+
POLICY_DIR=$_DOFLOW_POLDIR
|
|
51
|
+
if [ -f "$POLICY_DIR/contract.sh" ]; then
|
|
52
|
+
# shellcheck source=contract.sh
|
|
53
|
+
source "$POLICY_DIR/contract.sh"
|
|
54
|
+
doflow_decision_init stop-check 1
|
|
55
|
+
else
|
|
56
|
+
doflow_decision_init() { :; }
|
|
57
|
+
doflow_decide() { :; }
|
|
58
|
+
( . "$POLICY_DIR/capture-failure.sh"; doflow_capture_failure stop-check contract-missing ) >/dev/null 2>&1 || true
|
|
59
|
+
exit 0
|
|
60
|
+
fi
|
|
61
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
62
|
+
doflow_decide error dependency-missing:jq
|
|
63
|
+
( . "$POLICY_DIR/capture-failure.sh"; doflow_capture_failure stop-check dependency-missing:jq ) >/dev/null 2>&1 || true
|
|
64
|
+
fi
|
|
65
|
+
require_jq
|
|
66
|
+
SESSION_ID="" ENVELOPE_CLASS=missing
|
|
67
|
+
{ IFS= read -r -d '' ENVELOPE_CLASS; IFS= read -r -d '' SESSION_ID; } < <(
|
|
68
|
+
printf '%s' "$INPUT" | jq -j 'if ((. | type) != "object") or ((.doflow_event | type) != "object") or ((.doflow_event | has("version")) | not) then "missing" elif ((.doflow_event.version | type) == "number") and .doflow_event.version == 1 then "v1" else "unsupported" end, "\u0000", (.session_id // "" | if type == "string" then rtrimstr("\n") else tojson end), "\u0000"' 2>/dev/null
|
|
69
|
+
) || true
|
|
70
|
+
ENVELOPE_CLASS=${ENVELOPE_CLASS:-missing}
|
|
71
|
+
if [ ! -f "$POLICY_DIR/contract.sh" ]; then ENVELOPE_CLASS=missing; fi
|
|
72
|
+
if [ "$ENVELOPE_CLASS" = unsupported ] || [ "$ENVELOPE_CLASS" = missing ]; then
|
|
73
|
+
code=envelope-version
|
|
74
|
+
[ "$ENVELOPE_CLASS" = missing ] && code=envelope-missing
|
|
75
|
+
[ -f "$POLICY_DIR/contract.sh" ] || code=contract-missing
|
|
76
|
+
doflow_decide unsupported "$code"
|
|
77
|
+
if [ -f "$POLICY_DIR/contract.sh" ]; then
|
|
78
|
+
( . "$_DOFLOW_POLDIR/capture-failure.sh"; doflow_capture_failure stop-check "$code" ) >/dev/null 2>&1 || true
|
|
79
|
+
fi
|
|
80
|
+
exit 0
|
|
81
|
+
fi
|
|
56
82
|
|
|
57
83
|
TRANSCRIPT_PATH=$(json_field "$INPUT" ".transcript_path")
|
|
58
84
|
[ -z "$TRANSCRIPT_PATH" ] && TRANSCRIPT_PATH=$(json_field "$INPUT" ".transcriptPath")
|
|
59
85
|
|
|
60
86
|
# ── 1. Lint dispatch (only when we have a session-scoped edited-files queue) ──
|
|
61
87
|
|
|
62
|
-
|
|
88
|
+
# With no config home (SESSION_DIR empty) there is no queue to read; the stub check below still runs.
|
|
89
|
+
if [ -n "$SESSION_ID" ] && [ -n "$SESSION_DIR" ]; then
|
|
63
90
|
SESSION_PATH=$(ensure_session_dir "$SESSION_ID")
|
|
64
91
|
EDITED_FILES="$SESSION_PATH/edited-files.txt"
|
|
65
92
|
PROC_FILE="$SESSION_PATH/edited-files.txt.proc"
|
|
@@ -79,12 +106,14 @@ if [ -n "$SESSION_ID" ]; then
|
|
|
79
106
|
# The resolver for the leak-scan verb. Absent install -> the scan is
|
|
80
107
|
# skipped, like every other tool below: this hook has never required
|
|
81
108
|
# anything to be present.
|
|
109
|
+
# HOME may be unset (an absolute XDG_CONFIG_HOME alone gives a config home): its candidates drop out.
|
|
82
110
|
DOFLOW_RUN=""
|
|
111
|
+
claude_dir="${CLAUDE_CONFIG_DIR:-${HOME:+$HOME/.claude}}"
|
|
83
112
|
for candidate in \
|
|
84
|
-
"${
|
|
85
|
-
"$HOME/.doflow/scripts/doflow/bin/doflow-run"
|
|
113
|
+
"${claude_dir:+$claude_dir/scripts/doflow/bin/doflow-run}" \
|
|
114
|
+
"${HOME:+$HOME/.doflow/scripts/doflow/bin/doflow-run}"
|
|
86
115
|
do
|
|
87
|
-
[ -x "$candidate" ] && { DOFLOW_RUN="$candidate"; break; }
|
|
116
|
+
[ -n "$candidate" ] && [ -x "$candidate" ] && { DOFLOW_RUN="$candidate"; break; }
|
|
88
117
|
done
|
|
89
118
|
|
|
90
119
|
while IFS= read -r path; do
|
|
@@ -107,7 +136,7 @@ if [ -n "$SESSION_ID" ]; then
|
|
|
107
136
|
# before the {} reply is invalid hook output)
|
|
108
137
|
if [ ${#py_files[@]} -gt 0 ]; then
|
|
109
138
|
if command -v ruff &>/dev/null; then
|
|
110
|
-
nohup ruff format "${py_files[@]}" </dev/null >/dev/null 2>&1 &
|
|
139
|
+
nohup ruff format "${py_files[@]}" </dev/null >/dev/null 2>&1 3>&- 5>&- &
|
|
111
140
|
run_with_timeout 2 -- ruff check "${py_files[@]}" >&2 || true
|
|
112
141
|
fi
|
|
113
142
|
fi
|
|
@@ -115,7 +144,7 @@ if [ -n "$SESSION_ID" ]; then
|
|
|
115
144
|
# JS/TS: async fix only (eslint --fix rarely needs the caller's immediate attention)
|
|
116
145
|
if [ ${#js_files[@]} -gt 0 ]; then
|
|
117
146
|
if command -v eslint &>/dev/null; then
|
|
118
|
-
nohup eslint --fix "${js_files[@]}" </dev/null >/dev/null 2>&1 &
|
|
147
|
+
nohup eslint --fix "${js_files[@]}" </dev/null >/dev/null 2>&1 3>&- 5>&- &
|
|
119
148
|
fi
|
|
120
149
|
fi
|
|
121
150
|
|
|
@@ -129,7 +158,7 @@ if [ -n "$SESSION_ID" ]; then
|
|
|
129
158
|
# Java: async spotlessApply (slow — only if gradlew exists in cwd)
|
|
130
159
|
if [ "$java_has_files" = "true" ]; then
|
|
131
160
|
if [ -x "./gradlew" ]; then
|
|
132
|
-
nohup ./gradlew spotlessApply </dev/null >/dev/null 2>&1 &
|
|
161
|
+
nohup ./gradlew spotlessApply </dev/null >/dev/null 2>&1 3>&- 5>&- &
|
|
133
162
|
fi
|
|
134
163
|
fi
|
|
135
164
|
|
|
@@ -143,6 +172,7 @@ if [ -n "$SESSION_ID" ]; then
|
|
|
143
172
|
if [ -n "$DOFLOW_RUN" ]; then
|
|
144
173
|
leak_out="$(run_with_timeout 2 -- "$DOFLOW_RUN" leak-scan "${leak_args[@]}" 2>/dev/null || true)"
|
|
145
174
|
if [ -n "$leak_out" ] && printf '%s' "$leak_out" | grep -q ':[0-9]'; then
|
|
175
|
+
doflow_decide advisory internal-identifiers-found
|
|
146
176
|
printf 'doflow: internal identifiers found in shipped files (warning, not a block):\n%s\n' \
|
|
147
177
|
"$leak_out" >&2
|
|
148
178
|
fi
|
|
@@ -161,9 +191,10 @@ fi
|
|
|
161
191
|
|
|
162
192
|
# The payload's own last_assistant_message is the response being stopped on. A present key
|
|
163
193
|
# (even null or empty) is authoritative: the transcript is only read when the key is absent.
|
|
164
|
-
# jq filter for the transcript fallback: one JSON object per line, assistant text from
|
|
194
|
+
# jq filter for the transcript fallback: one JSON object per line, assistant text from four
|
|
165
195
|
# shapes — Claude Code (.message with text blocks), Codex rollout (response_item .payload with
|
|
166
|
-
# output_text blocks)
|
|
196
|
+
# output_text blocks), Copilot CLI events.jsonl (type assistant.message, .data.content a string) and
|
|
197
|
+
# the flat {role,content} line. A partial or foreign line yields "".
|
|
167
198
|
read -r -d '' LAST_ASSISTANT_JQ <<'JQ' || true
|
|
168
199
|
def text: if type == "string" then .
|
|
169
200
|
elif type == "array" then [ .[]? | objects | select(.type == "text" or .type == "output_text") | .text | strings ] | join("\n")
|
|
@@ -171,6 +202,7 @@ def text: if type == "string" then .
|
|
|
171
202
|
[ inputs | fromjson? | objects
|
|
172
203
|
| if ((.message | objects | .role) // null) == "assistant" then .message.content
|
|
173
204
|
elif .type == "response_item" and ((.payload | objects | .role) // null) == "assistant" then .payload.content
|
|
205
|
+
elif .type == "assistant.message" then .data.content
|
|
174
206
|
elif .role == "assistant" then .content
|
|
175
207
|
else empty end
|
|
176
208
|
| text | select(length > 0) ] | last // ""
|
|
@@ -248,16 +280,22 @@ if grep -qiE -- "$STUB_PATTERN" <<<"$STUB_TEXT" 2>/dev/null; then
|
|
|
248
280
|
# Block a given message once: the continuation that follows is judged on new text, and a
|
|
249
281
|
# harness with no stop_hook_active would otherwise be blocked on this one forever. State is
|
|
250
282
|
# per session, or per transcript when the harness sends no session_id; any failure here
|
|
251
|
-
# leaves the block in force only for this call.
|
|
283
|
+
# leaves the block in force only for this call. With no config home there is no state store, so
|
|
284
|
+
# this policy cannot remember a block and allows the stop.
|
|
285
|
+
[ -n "$SESSION_DIR" ] || exit 0
|
|
252
286
|
GUARD_KEY="$SESSION_ID"
|
|
253
287
|
[ -z "$GUARD_KEY" ] && GUARD_KEY="t$(printf '%s' "$TRANSCRIPT_PATH" | cksum | cut -d' ' -f1)"
|
|
254
|
-
GUARD_FILE="
|
|
288
|
+
GUARD_FILE=""
|
|
289
|
+
if [ -n "$SESSION_DIR" ]; then
|
|
290
|
+
GUARD_FILE="$(ensure_session_dir "$GUARD_KEY" 2>/dev/null)/stop-last-blocked" || GUARD_FILE=""
|
|
291
|
+
fi
|
|
255
292
|
MESSAGE_SUM=$(printf '%s' "$LAST_ASSISTANT_CONTENT" | cksum 2>/dev/null) || MESSAGE_SUM=""
|
|
256
293
|
if [ -n "$MESSAGE_SUM" ] && [ -n "$GUARD_FILE" ] && [ "$(cat "$GUARD_FILE" 2>/dev/null)" = "$MESSAGE_SUM" ]; then
|
|
257
294
|
exit 0
|
|
258
295
|
fi
|
|
259
296
|
[ -n "$MESSAGE_SUM" ] && [ -n "$GUARD_FILE" ] && { printf '%s\n' "$MESSAGE_SUM" > "$GUARD_FILE"; } 2>/dev/null || true
|
|
260
297
|
echo "[stop-check] The last response contains an unfinished-work marker (TODO/FIXME/stub comment or a not-implemented raise/throw)." >&2
|
|
298
|
+
doflow_decide deny unfinished-work
|
|
261
299
|
exit 2
|
|
262
300
|
fi
|
|
263
301
|
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
+
case "${OSTYPE:-}" in msys*|cygwin*|win32*) if command -v jq >/dev/null 2>&1; then jq() { command jq -b "$@"; }; fi ;; esac # native jq.exe ends lines with CRLF unless -b
|
|
3
|
+
_DOFLOW_SRC=${0//\\//}; case $_DOFLOW_SRC in */*) _DOFLOW_POLDIR=${_DOFLOW_SRC%/*} ;; *) _DOFLOW_POLDIR=. ;; esac
|
|
2
4
|
# subagent-audit.sh — Canonical Policy Library: SubagentStart + SubagentStop hook
|
|
3
5
|
# (pure observability, no deny path)
|
|
4
6
|
#
|
|
5
7
|
# 022-hooks-remaining-duplication: claude and codex's copies were byte-identical — moved here as
|
|
6
|
-
# the single canonical copy.
|
|
8
|
+
# the single canonical copy.
|
|
7
9
|
#
|
|
8
10
|
# Appends {timestamp, session_id, event, agent_type, agent_id} to a session-scoped audit log.
|
|
9
11
|
# Today there is zero record of which of the installed agents/ specialists actually run, how often, or
|
|
@@ -18,14 +20,45 @@
|
|
|
18
20
|
|
|
19
21
|
set -euo pipefail
|
|
20
22
|
# shellcheck source=lib.sh
|
|
21
|
-
source "$
|
|
22
|
-
require_jq
|
|
23
|
-
|
|
23
|
+
source "$_DOFLOW_POLDIR/lib.sh"
|
|
24
24
|
INPUT=$(cat)
|
|
25
|
-
|
|
25
|
+
POLICY_DIR=$_DOFLOW_POLDIR
|
|
26
|
+
if [ -f "$POLICY_DIR/contract.sh" ]; then
|
|
27
|
+
# shellcheck source=contract.sh
|
|
28
|
+
source "$POLICY_DIR/contract.sh"
|
|
29
|
+
doflow_decision_init subagent-audit 1
|
|
30
|
+
else
|
|
31
|
+
doflow_decision_init() { :; }
|
|
32
|
+
doflow_decide() { :; }
|
|
33
|
+
( . "$POLICY_DIR/capture-failure.sh"; doflow_capture_failure subagent-audit contract-missing ) >/dev/null 2>&1 || true
|
|
34
|
+
exit 0
|
|
35
|
+
fi
|
|
36
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
37
|
+
doflow_decide error dependency-missing:jq
|
|
38
|
+
( . "$POLICY_DIR/capture-failure.sh"; doflow_capture_failure subagent-audit dependency-missing:jq ) >/dev/null 2>&1 || true
|
|
39
|
+
fi
|
|
40
|
+
require_jq
|
|
41
|
+
SESSION_ID="" ENVELOPE_CLASS=missing
|
|
42
|
+
{ IFS= read -r -d '' ENVELOPE_CLASS; IFS= read -r -d '' SESSION_ID; } < <(
|
|
43
|
+
printf '%s' "$INPUT" | jq -j 'if ((. | type) != "object") or ((.doflow_event | type) != "object") or ((.doflow_event | has("version")) | not) then "missing" elif ((.doflow_event.version | type) == "number") and .doflow_event.version == 1 then "v1" else "unsupported" end, "\u0000", (.session_id // "" | if type == "string" then rtrimstr("\n") else tojson end), "\u0000"' 2>/dev/null
|
|
44
|
+
) || true
|
|
45
|
+
ENVELOPE_CLASS=${ENVELOPE_CLASS:-missing}
|
|
46
|
+
if [ ! -f "$POLICY_DIR/contract.sh" ]; then ENVELOPE_CLASS=missing; fi
|
|
47
|
+
if [ "$ENVELOPE_CLASS" = unsupported ] || [ "$ENVELOPE_CLASS" = missing ]; then
|
|
48
|
+
code=envelope-version
|
|
49
|
+
[ "$ENVELOPE_CLASS" = missing ] && code=envelope-missing
|
|
50
|
+
[ -f "$POLICY_DIR/contract.sh" ] || code=contract-missing
|
|
51
|
+
doflow_decide unsupported "$code"
|
|
52
|
+
if [ -f "$POLICY_DIR/contract.sh" ]; then
|
|
53
|
+
( . "$_DOFLOW_POLDIR/capture-failure.sh"; doflow_capture_failure subagent-audit "$code" ) >/dev/null 2>&1 || true
|
|
54
|
+
fi
|
|
55
|
+
exit 0
|
|
56
|
+
fi
|
|
26
57
|
|
|
27
58
|
# Guard: if we can't determine the session, nothing to do
|
|
28
59
|
[[ -z "$SESSION_ID" ]] && exit 0
|
|
60
|
+
# No config home (lib.sh leaves the state paths empty): no session store to write (086 IC-010).
|
|
61
|
+
[[ -n "$SESSION_DIR" ]] || exit 0
|
|
29
62
|
|
|
30
63
|
EVENT=$(json_field "$INPUT" ".hook_event_name")
|
|
31
64
|
AGENT_TYPE=$(json_field "$INPUT" ".agent_type")
|
|
@@ -41,6 +74,6 @@ jq -nc \
|
|
|
41
74
|
--arg agent_type "$AGENT_TYPE" \
|
|
42
75
|
--arg agent_id "$AGENT_ID" \
|
|
43
76
|
'{timestamp: $ts, session_id: $session, event: $event, agent_type: $agent_type, agent_id: $agent_id}' \
|
|
44
|
-
>> "$SESSION_PATH/subagent-audit.log" 2>/dev/null ||
|
|
77
|
+
>> "$SESSION_PATH/subagent-audit.log" 2>/dev/null || doflow_decide error audit-write-failed
|
|
45
78
|
|
|
46
79
|
exit 0
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# summary-store.sh — Canonical Policy Library: scoped compact summaries, grants and the legacy
|
|
3
|
+
# adopter (feature 078, IC-010, IC-011 store write, IC-012, IC-014; DEC-003, DEC-012, DEC-014).
|
|
4
|
+
#
|
|
5
|
+
# Layout under STATE_DIR (lib.sh: <config home>/doflow/session-env; empty with no config home, and
|
|
6
|
+
# then there is no store: peeks find nothing, writes, claims and adopts return 1):
|
|
7
|
+
# summaries/<worktreeKey>/<run>/<lineage>/summary.md one pending summary per scope
|
|
8
|
+
# summaries/<worktreeKey>/<run>/<lineage>/.summary.XXXXXX write temporary
|
|
9
|
+
# summaries/<worktreeKey>/<run>/<lineage>/.claimed.<unitId> claim temporary
|
|
10
|
+
# grants/<run>/<lineage>/<summaryId>.md grant addressed to a recipient
|
|
11
|
+
# grants/<run>/<lineage>/.claimed.<unitId>.<summaryId> claim temporary
|
|
12
|
+
# worktreeKey is cwd_hash of the identity worktree (DEC-027), so the caller must have sourced
|
|
13
|
+
# lib.sh, which defines cwd_hash, STATE_DIR and PROJECTS_DIR.
|
|
14
|
+
#
|
|
15
|
+
# Sourcing this file defines functions only: no option, no variable, no trap, no output. bash 3.2
|
|
16
|
+
# safe and safe under the caller's `set -euo pipefail`. Peek functions always return 0; write,
|
|
17
|
+
# claim and adopt return 0 when they did their work and 1 otherwise, so call them from `if` or
|
|
18
|
+
# with `||`. Results are DF_SUMMARY_* and DF_GRANT_* variables. A claimed file is left in place for
|
|
19
|
+
# the caller to pass to emission.jq (--rawfile summary or grant) and to remove after rendering.
|
|
20
|
+
|
|
21
|
+
# The id pattern of IC-007 (DEC-024) and the summary id pattern of IC-010.
|
|
22
|
+
_doflow_ss_id_ok() { [[ ${1:-} =~ ^[A-Za-z0-9][A-Za-z0-9_-]{0,55}$ ]]; }
|
|
23
|
+
_doflow_ss_summary_id_ok() { [[ ${1:-} =~ ^s-[0-9]{8}T[0-9]{6}Z-[A-Za-z0-9]{6}$ ]]; }
|
|
24
|
+
# A unit id (L.<run>.<lineage>.<attempt> or a harness session id) is one path segment.
|
|
25
|
+
_doflow_ss_unit_ok() { [[ ${1:-} =~ ^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$ ]]; }
|
|
26
|
+
|
|
27
|
+
# _doflow_ss_root: _DF_SS_ROOT = lib.sh's STATE_DIR; returns 1 when it is empty (no config home).
|
|
28
|
+
_doflow_ss_root() {
|
|
29
|
+
_DF_SS_ROOT="${STATE_DIR:-}"
|
|
30
|
+
[ -n "$_DF_SS_ROOT" ]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
# _doflow_ss_key <dir>: _DF_SS_KEY = cwd_hash <dir>, remembered for the last directory asked.
|
|
34
|
+
_doflow_ss_key() {
|
|
35
|
+
if [ -n "${_DF_SS_KEY_FOR:-}" ] && [ "$_DF_SS_KEY_FOR" = "$1" ] && [ -n "${_DF_SS_KEY:-}" ]; then
|
|
36
|
+
return 0
|
|
37
|
+
fi
|
|
38
|
+
_DF_SS_KEY=""
|
|
39
|
+
_DF_SS_KEY_FOR=""
|
|
40
|
+
[ -n "$1" ] || return 1
|
|
41
|
+
declare -F cwd_hash >/dev/null 2>&1 || return 1
|
|
42
|
+
_DF_SS_KEY=$(cwd_hash "$1" 2>/dev/null) || _DF_SS_KEY=""
|
|
43
|
+
[[ $_DF_SS_KEY =~ ^[0-9a-f]{16}$ ]] || { _DF_SS_KEY=""; return 1; }
|
|
44
|
+
_DF_SS_KEY_FOR=$1
|
|
45
|
+
return 0
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# doflow_summary_scope <worktree> <run> <lineage>
|
|
49
|
+
# DF_SUMMARY_SCOPE = summaries/<worktreeKey>/<run>/<lineage>, or empty (return 1) when run or
|
|
50
|
+
# lineage is not an id, or the lineage is `solo`, which has no scope directory.
|
|
51
|
+
doflow_summary_scope() {
|
|
52
|
+
DF_SUMMARY_SCOPE=""
|
|
53
|
+
_doflow_ss_id_ok "${2:-}" && _doflow_ss_id_ok "${3:-}" && [ "$3" != solo ] || return 1
|
|
54
|
+
_doflow_ss_key "${1:-}" || return 1
|
|
55
|
+
_doflow_ss_root || return 1
|
|
56
|
+
DF_SUMMARY_SCOPE="$_DF_SS_ROOT/summaries/$_DF_SS_KEY/$2/$3"
|
|
57
|
+
return 0
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# _doflow_ss_head <file>: reads the frontmatter the way emission.jq's parse_record does (\r removed,
|
|
61
|
+
# fields between the first two `---` lines, first match wins, leading spaces of a value dropped),
|
|
62
|
+
# never more than 65536 bytes. Sets _DF_SS_SESSION, _DF_SS_SID, and for the grant header
|
|
63
|
+
# _DF_SS_FROM (from_lineage), _DF_SS_AT (compacted_at) and _DF_SS_BRANCH (branch).
|
|
64
|
+
_doflow_ss_head() {
|
|
65
|
+
local LC_ALL=C line="" left=65536 fences=0 key value seen=" "
|
|
66
|
+
_DF_SS_SESSION=""
|
|
67
|
+
_DF_SS_SID=""
|
|
68
|
+
_DF_SS_FROM=""
|
|
69
|
+
_DF_SS_AT=""
|
|
70
|
+
_DF_SS_BRANCH=""
|
|
71
|
+
{
|
|
72
|
+
while [ "$left" -gt 0 ] && [ "$fences" -lt 2 ]; do
|
|
73
|
+
IFS= read -r -n "$left" line || [ -n "$line" ] || break
|
|
74
|
+
left=$((left - ${#line} - 1))
|
|
75
|
+
line=${line//$'\r'/}
|
|
76
|
+
if [ "$line" = "---" ]; then
|
|
77
|
+
fences=$((fences + 1))
|
|
78
|
+
elif [ "$fences" = 1 ] && [[ $line == ?*:* ]]; then
|
|
79
|
+
key=${line%%:*}
|
|
80
|
+
value=${line#*:}
|
|
81
|
+
while [ "${value# }" != "$value" ]; do value=${value# }; done
|
|
82
|
+
# The first match of a key wins: a wanted key is recorded in $seen once it is read.
|
|
83
|
+
case "$key" in
|
|
84
|
+
session_id | summary_id | from_lineage | compacted_at | branch)
|
|
85
|
+
case "$seen" in *" $key "*) key="" ;; esac ;;
|
|
86
|
+
*) key="" ;;
|
|
87
|
+
esac
|
|
88
|
+
case "$key" in
|
|
89
|
+
session_id) _DF_SS_SESSION=$value ;;
|
|
90
|
+
summary_id) _DF_SS_SID=$value ;;
|
|
91
|
+
from_lineage) _DF_SS_FROM=$value ;;
|
|
92
|
+
compacted_at) _DF_SS_AT=$value ;;
|
|
93
|
+
branch) _DF_SS_BRANCH=$value ;;
|
|
94
|
+
esac
|
|
95
|
+
[ -z "$key" ] || seen="$seen$key "
|
|
96
|
+
fi
|
|
97
|
+
line=""
|
|
98
|
+
done
|
|
99
|
+
} 2>/dev/null <"$1" || true
|
|
100
|
+
return 0
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# _doflow_ss_body_bytes <file>: _DF_SS_BODY = the UTF-8 byte length of the record body exactly as
|
|
104
|
+
# parse_record yields it (\r removed, from the first non-empty line after the second `---`,
|
|
105
|
+
# trailing newlines dropped). The caller has bounded the file's size.
|
|
106
|
+
_doflow_ss_body_bytes() {
|
|
107
|
+
local LC_ALL=C line="" fences=0 started=0 gap=0
|
|
108
|
+
_DF_SS_BODY=0
|
|
109
|
+
{
|
|
110
|
+
while IFS= read -r line || [ -n "$line" ]; do
|
|
111
|
+
line=${line//$'\r'/}
|
|
112
|
+
if [ "$fences" -lt 2 ]; then
|
|
113
|
+
[ "$line" != "---" ] || fences=$((fences + 1))
|
|
114
|
+
elif [ -z "$line" ]; then
|
|
115
|
+
[ "$started" = 0 ] || gap=$((gap + 1))
|
|
116
|
+
elif [ "$started" = 0 ]; then
|
|
117
|
+
started=1
|
|
118
|
+
_DF_SS_BODY=${#line}
|
|
119
|
+
else
|
|
120
|
+
_DF_SS_BODY=$((_DF_SS_BODY + gap + 1 + ${#line}))
|
|
121
|
+
gap=0
|
|
122
|
+
fi
|
|
123
|
+
line=""
|
|
124
|
+
done
|
|
125
|
+
} 2>/dev/null <"$1" || true
|
|
126
|
+
return 0
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
# _doflow_ss_size <file>: _DF_SS_SIZE = the file's size in bytes, or empty.
|
|
130
|
+
_doflow_ss_size() {
|
|
131
|
+
_DF_SS_SIZE=$(wc -c 2>/dev/null <"$1") || _DF_SS_SIZE=""
|
|
132
|
+
_DF_SS_SIZE=${_DF_SS_SIZE//[!0-9]/}
|
|
133
|
+
return 0
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
# doflow_summary_write <worktree> <run> <lineage> <attempt> <compacted_at> <session_id> <trigger>
|
|
137
|
+
# <branch> <role> <body>
|
|
138
|
+
# IC-011 step 3: mkdir -p the scope, mktemp .summary.XXXXXX in it, write the IC-010 record, mv it to
|
|
139
|
+
# summary.md (a newer compaction replaces the pending one). compacted_at is UTC ISO-8601 to the
|
|
140
|
+
# second (YYYY-MM-DDTHH:MM:SSZ). Sets DF_SUMMARY_ID and DF_SUMMARY_PATH. Returns 1 with nothing left
|
|
141
|
+
# behind on an invalid argument, an empty body or a failed step.
|
|
142
|
+
doflow_summary_write() {
|
|
143
|
+
local worktree="${1:-}" run="${2:-}" lineage="${3:-}" attempt="${4:-}" at="${5:-}"
|
|
144
|
+
local session="${6:-}" trigger="${7:-}" branch="${8:-}" role="${9:-}" body="${10:-}"
|
|
145
|
+
local tmp="" stamp suffix nl=$'\n' cr=$'\r'
|
|
146
|
+
DF_SUMMARY_ID=""
|
|
147
|
+
DF_SUMMARY_PATH=""
|
|
148
|
+
[ -n "$body" ] || return 1
|
|
149
|
+
[[ $at =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$ ]] || return 1
|
|
150
|
+
[ -z "$attempt" ] || [[ $attempt =~ ^[1-9][0-9]{0,3}$ ]] || return 1
|
|
151
|
+
doflow_summary_scope "$worktree" "$run" "$lineage" || return 1
|
|
152
|
+
# A frontmatter value is one line: an embedded line break would forge a key.
|
|
153
|
+
session=${session//[$nl$cr]/ }; trigger=${trigger//[$nl$cr]/ }; branch=${branch//[$nl$cr]/ }
|
|
154
|
+
role=${role//[$nl$cr]/ }; worktree=${worktree//[$nl$cr]/ }
|
|
155
|
+
mkdir -p "$DF_SUMMARY_SCOPE" 2>/dev/null || return 1
|
|
156
|
+
tmp=$(mktemp "$DF_SUMMARY_SCOPE/.summary.XXXXXX" 2>/dev/null) || return 1
|
|
157
|
+
suffix=${tmp##*.summary.}
|
|
158
|
+
stamp=${at//[-:]/}
|
|
159
|
+
DF_SUMMARY_ID="s-$stamp-$suffix"
|
|
160
|
+
if ! _doflow_ss_summary_id_ok "$DF_SUMMARY_ID" \
|
|
161
|
+
|| ! printf -- '---\ncompacted_at: %s\nsession_id: %s\ntrigger: %s\nbranch: %s\nrole: %s\nsummary_id: %s\nrun: %s\nlineage: %s\nattempt:%s\nworktree: %s\n---\n\n%s\n' \
|
|
162
|
+
"$at" "${session:-unknown}" "${trigger:-unknown}" "${branch:-unknown}" "${role:-main}" \
|
|
163
|
+
"$DF_SUMMARY_ID" "$run" "$lineage" "${attempt:+ $attempt}" "$worktree" "$body" >"$tmp" 2>/dev/null \
|
|
164
|
+
|| ! mv -f "$tmp" "$DF_SUMMARY_SCOPE/summary.md" 2>/dev/null; then
|
|
165
|
+
rm -f "$tmp" 2>/dev/null || true
|
|
166
|
+
DF_SUMMARY_ID=""
|
|
167
|
+
return 1
|
|
168
|
+
fi
|
|
169
|
+
DF_SUMMARY_PATH="$DF_SUMMARY_SCOPE/summary.md"
|
|
170
|
+
return 0
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
# doflow_summary_peek <worktree> <run> <lineage> <session_id>
|
|
174
|
+
# IC-012 owner steps 1 and 2, for the state vector: DF_SUMMARY_OWN is `none` (no regular,
|
|
175
|
+
# non-symlink summary.md in the scope), `own-write` (its frontmatter session_id is this session's)
|
|
176
|
+
# or `pending`; DF_SUMMARY_FILE is its path and DF_SUMMARY_SID its frontmatter summary_id (possibly
|
|
177
|
+
# empty) when not `none`. Always returns 0.
|
|
178
|
+
doflow_summary_peek() {
|
|
179
|
+
local f
|
|
180
|
+
DF_SUMMARY_OWN=none
|
|
181
|
+
DF_SUMMARY_FILE=""
|
|
182
|
+
DF_SUMMARY_SID=""
|
|
183
|
+
doflow_summary_scope "${1:-}" "${2:-}" "${3:-}" || return 0
|
|
184
|
+
f="$DF_SUMMARY_SCOPE/summary.md"
|
|
185
|
+
[ -f "$f" ] && [ ! -L "$f" ] || return 0
|
|
186
|
+
_doflow_ss_head "$f"
|
|
187
|
+
DF_SUMMARY_FILE=$f
|
|
188
|
+
DF_SUMMARY_SID=$_DF_SS_SID
|
|
189
|
+
if [ -n "${4:-}" ] && [ "$_DF_SS_SESSION" = "$4" ]; then DF_SUMMARY_OWN=own-write; else DF_SUMMARY_OWN=pending; fi
|
|
190
|
+
return 0
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
# doflow_summary_claim <worktree> <run> <lineage> <unit_id> <session_id>
|
|
194
|
+
# IC-012 owner steps 1 to 3: peek, then rename summary.md to .claimed.<unitId>; a summary written
|
|
195
|
+
# after the rename is a new summary.md and survives. The claimed copy is cut to its first 65536
|
|
196
|
+
# bytes. Sets DF_SUMMARY_OWN (as peek, `none` after a lost rename), DF_SUMMARY_CLAIMED (the claimed
|
|
197
|
+
# path, for --rawfile summary) and DF_SUMMARY_BYTES (its full size before the cut, for
|
|
198
|
+
# plan.summaryBytes). Returns 0 when this caller claimed it; the caller removes the claimed file.
|
|
199
|
+
doflow_summary_claim() {
|
|
200
|
+
local unit="${4:-}" claimed
|
|
201
|
+
DF_SUMMARY_CLAIMED=""
|
|
202
|
+
DF_SUMMARY_BYTES=0
|
|
203
|
+
doflow_summary_peek "${1:-}" "${2:-}" "${3:-}" "${5:-}"
|
|
204
|
+
[ "$DF_SUMMARY_OWN" = pending ] || return 1
|
|
205
|
+
_doflow_ss_unit_ok "$unit" || return 1
|
|
206
|
+
claimed="$DF_SUMMARY_SCOPE/.claimed.$unit"
|
|
207
|
+
if ! mv "$DF_SUMMARY_FILE" "$claimed" 2>/dev/null; then
|
|
208
|
+
DF_SUMMARY_OWN=none
|
|
209
|
+
return 1
|
|
210
|
+
fi
|
|
211
|
+
_doflow_ss_size "$claimed"
|
|
212
|
+
DF_SUMMARY_BYTES=${_DF_SS_SIZE:-0}
|
|
213
|
+
if [ "$DF_SUMMARY_BYTES" -gt 65536 ]; then
|
|
214
|
+
# The cut copy is private to this user, as the record it replaces (L1-11).
|
|
215
|
+
if ! { (umask 077; head -c 65536 "$claimed" >"$claimed.read") 2>/dev/null && mv -f "$claimed.read" "$claimed" 2>/dev/null; }; then
|
|
216
|
+
rm -f "$claimed" "$claimed.read" 2>/dev/null || true
|
|
217
|
+
return 1
|
|
218
|
+
fi
|
|
219
|
+
fi
|
|
220
|
+
DF_SUMMARY_CLAIMED=$claimed
|
|
221
|
+
return 0
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
# _doflow_ss_known <value>: _DF_SS_KNOWN = emission.jq's `known`: `]` removed, then empty when it
|
|
225
|
+
# is "unknown".
|
|
226
|
+
_doflow_ss_known() {
|
|
227
|
+
_DF_SS_KNOWN=${1//]/}
|
|
228
|
+
[ "$_DF_SS_KNOWN" != unknown ] || _DF_SS_KNOWN=""
|
|
229
|
+
return 0
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
# doflow_grant_peek <run> <lineage> <max_bytes>
|
|
233
|
+
# IC-012 grant steps 1 and 2: the oldest grants/<run>/<lineage>/<summaryId>.md by name among regular,
|
|
234
|
+
# non-symlink files whose name matches the summary id pattern. DF_GRANT is `none`, `fits` or `over`.
|
|
235
|
+
# It fits exactly when emission.jq would render it whole: the UTF-8 bytes of render_grant's segment
|
|
236
|
+
# "[Granted summary from lineage <from_lineage><header_tail>]\n<body>" are at most max_bytes, so a
|
|
237
|
+
# body that fits under a header that does not is `over` and is never claimed. DF_GRANT_FILE,
|
|
238
|
+
# DF_GRANT_ID and DF_GRANT_BYTES (the body's bytes, for the pointer; empty when the file is too large
|
|
239
|
+
# to be worth reading) describe it. Always returns 0.
|
|
240
|
+
doflow_grant_peek() {
|
|
241
|
+
local LC_ALL=C run="${1:-}" lineage="${2:-}" max="${3:-}" dir f name header tail=""
|
|
242
|
+
DF_GRANT=none
|
|
243
|
+
DF_GRANT_FILE=""
|
|
244
|
+
DF_GRANT_ID=""
|
|
245
|
+
DF_GRANT_BYTES=""
|
|
246
|
+
_doflow_ss_id_ok "$run" && _doflow_ss_id_ok "$lineage" || return 0
|
|
247
|
+
[[ $max =~ ^[1-9][0-9]{0,9}$ ]] || return 0
|
|
248
|
+
_doflow_ss_root || return 0
|
|
249
|
+
dir="$_DF_SS_ROOT/grants/$run/$lineage"
|
|
250
|
+
[ -d "$dir" ] && [ ! -L "$dir" ] || return 0
|
|
251
|
+
# Glob expansion is sorted (C collation here), and summary ids sort by time.
|
|
252
|
+
for f in "$dir"/s-*.md; do
|
|
253
|
+
name=${f##*/}
|
|
254
|
+
name=${name%.md}
|
|
255
|
+
_doflow_ss_summary_id_ok "$name" || continue
|
|
256
|
+
[ -f "$f" ] && [ ! -L "$f" ] || continue
|
|
257
|
+
DF_GRANT_FILE=$f
|
|
258
|
+
DF_GRANT_ID=$name
|
|
259
|
+
break
|
|
260
|
+
done
|
|
261
|
+
[ -n "$DF_GRANT_FILE" ] || return 0
|
|
262
|
+
_doflow_ss_size "$DF_GRANT_FILE"
|
|
263
|
+
# A frontmatter needs far less than 65536 bytes: anything larger than that plus the budget is over.
|
|
264
|
+
if [ -n "$_DF_SS_SIZE" ] && [ "$_DF_SS_SIZE" -le $((max + 65536)) ]; then
|
|
265
|
+
_doflow_ss_body_bytes "$DF_GRANT_FILE"
|
|
266
|
+
DF_GRANT_BYTES=$_DF_SS_BODY
|
|
267
|
+
# render_grant's header, from the same bounded head pass parse_record's fields come from.
|
|
268
|
+
_doflow_ss_head "$DF_GRANT_FILE"
|
|
269
|
+
_doflow_ss_known "$_DF_SS_AT"
|
|
270
|
+
[ -z "$_DF_SS_KNOWN" ] || tail=", compacted $_DF_SS_KNOWN"
|
|
271
|
+
_doflow_ss_known "$_DF_SS_BRANCH"
|
|
272
|
+
[ -z "$_DF_SS_KNOWN" ] || tail="$tail on branch $_DF_SS_KNOWN"
|
|
273
|
+
header="[Granted summary from lineage $_DF_SS_FROM$tail]"
|
|
274
|
+
if [ $((${#header} + 1 + DF_GRANT_BYTES)) -le "$max" ]; then DF_GRANT=fits; else DF_GRANT=over; fi
|
|
275
|
+
else
|
|
276
|
+
DF_GRANT=over
|
|
277
|
+
fi
|
|
278
|
+
return 0
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
# doflow_grant_claim <run> <lineage> <unit_id> <max_bytes>
|
|
282
|
+
# IC-012 grant step 3: peek; when the oldest grant fits, rename it to
|
|
283
|
+
# .claimed.<unitId>.<summaryId> and set DF_GRANT_CLAIMED (for --rawfile grant); the caller removes
|
|
284
|
+
# it after rendering. An `over` grant stays in place (DF_GRANT_FILE renders the pointer). One grant
|
|
285
|
+
# per event. Returns 0 only when this caller claimed a grant.
|
|
286
|
+
doflow_grant_claim() {
|
|
287
|
+
local unit="${3:-}" claimed
|
|
288
|
+
DF_GRANT_CLAIMED=""
|
|
289
|
+
doflow_grant_peek "${1:-}" "${2:-}" "${4:-}"
|
|
290
|
+
[ "$DF_GRANT" = fits ] || return 1
|
|
291
|
+
_doflow_ss_unit_ok "$unit" || return 1
|
|
292
|
+
claimed="${DF_GRANT_FILE%/*}/.claimed.$unit.$DF_GRANT_ID"
|
|
293
|
+
if ! mv "$DF_GRANT_FILE" "$claimed" 2>/dev/null; then
|
|
294
|
+
DF_GRANT=none
|
|
295
|
+
return 1
|
|
296
|
+
fi
|
|
297
|
+
DF_GRANT_CLAIMED=$claimed
|
|
298
|
+
return 0
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
# doflow_summary_adopt_legacy <cwd> <worktree> [check]
|
|
302
|
+
# IC-014, on action `adopt` only: the pre-078 project summary under projects/<cwd_hash cwd>/, a
|
|
303
|
+
# regular file and not a symlink, moves once into summaries/<worktreeKey>/local/main/summary.md
|
|
304
|
+
# with its frontmatter kept. When a summary is already pending there it is newer by construction
|
|
305
|
+
# and the old file is removed instead. The legacy file is first renamed to <old>.adopting.<pid>, so
|
|
306
|
+
# only one adopter proceeds (a lost rename means another adopter won and returns 0, DEC-043); then
|
|
307
|
+
# a hard link that refuses to replace, and the .adopting file is unlinked last, so a summary written
|
|
308
|
+
# meanwhile is never overwritten. Without hard links, `mv -n` is the fallback, taken only when the
|
|
309
|
+
# target is absent and verified afterwards. Nothing writes the old path. Returns 0 when a legacy
|
|
310
|
+
# file was adopted or retired, or another adopter took it. With a third argument `check` it only tests that the
|
|
311
|
+
# legacy file exists and changes nothing (doflow_summary_legacy_present). This function is the one
|
|
312
|
+
# place that names the legacy file (G38.4).
|
|
313
|
+
doflow_summary_adopt_legacy() {
|
|
314
|
+
local cwd="${1:-}" worktree="${2:-}" projects old new taken
|
|
315
|
+
[ -n "$cwd" ] || return 1
|
|
316
|
+
[ -n "$worktree" ] || worktree=$cwd
|
|
317
|
+
_doflow_ss_key "$cwd" || return 1
|
|
318
|
+
_doflow_ss_root || return 1
|
|
319
|
+
projects="${PROJECTS_DIR:-$_DF_SS_ROOT/projects}"
|
|
320
|
+
old="$projects/$_DF_SS_KEY/last-compact-summary.md"
|
|
321
|
+
[ -f "$old" ] && [ ! -L "$old" ] || return 1
|
|
322
|
+
[ "${3:-}" != check ] || return 0
|
|
323
|
+
doflow_summary_scope "$worktree" local main || return 1
|
|
324
|
+
new="$DF_SUMMARY_SCOPE/summary.md"
|
|
325
|
+
mkdir -p "$DF_SUMMARY_SCOPE" 2>/dev/null || return 1
|
|
326
|
+
taken="$old.adopting.$$"
|
|
327
|
+
mv "$old" "$taken" 2>/dev/null || return 0
|
|
328
|
+
# A link that refuses to replace; it fails when a summary is pending, which retires the legacy one.
|
|
329
|
+
if ! ln "$taken" "$new" 2>/dev/null && [ ! -e "$new" ] && [ ! -L "$new" ]; then
|
|
330
|
+
# No hard links on this filesystem: a rename that refuses to replace, then a check that $new is
|
|
331
|
+
# the adopted file (mv -n leaves the source in place when the target appeared meanwhile).
|
|
332
|
+
# The old path is never written again, so a failed rename leaves the .adopting file in place.
|
|
333
|
+
mv -n "$taken" "$new" 2>/dev/null || true
|
|
334
|
+
if [ -e "$taken" ] && [ ! -e "$new" ] && [ ! -L "$new" ]; then return 1; fi
|
|
335
|
+
fi
|
|
336
|
+
rm -f "$taken" 2>/dev/null || true
|
|
337
|
+
return 0
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
# doflow_summary_legacy_present <cwd>
|
|
341
|
+
# Returns 0 when the pre-078 project summary under projects/<cwd_hash cwd>/ exists as a regular file
|
|
342
|
+
# and not a symlink, and 1 otherwise. Runs adopt_legacy's check mode in a subshell, so it sets no
|
|
343
|
+
# variable, prints nothing and changes nothing.
|
|
344
|
+
doflow_summary_legacy_present() {
|
|
345
|
+
[ -n "${1:-}" ] || return 1
|
|
346
|
+
(doflow_summary_adopt_legacy "$1" "" check)
|
|
347
|
+
}
|