@khoavu882/doflow 2.0.0-beta.5 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -11
- package/bin/doflow.js +21 -11
- package/core/.antigravity-plugin/plugin.json +1 -2
- package/core/.claude-plugin/plugin.json +1 -1
- package/core/.codex-plugin/plugin.json +1 -1
- package/core/.plugin/plugin.json +1 -1
- package/core/harnesses/claude/hooks/lib.sh +43 -24
- package/core/harnesses/claude/hooks/permission-audit.sh +3 -0
- package/core/harnesses/claude/hooks/post-compact.sh +35 -32
- package/core/harnesses/claude/hooks/post-edit-lint.sh +2 -0
- package/core/harnesses/claude/hooks/post-shell-observe.sh +48 -0
- package/core/harnesses/claude/hooks/post-tool-failure.sh +3 -0
- package/core/harnesses/claude/hooks/pre-compact.sh +23 -27
- package/core/harnesses/claude/hooks/session-end.sh +23 -10
- package/core/harnesses/claude/hooks/skill-config-audit.sh +3 -0
- package/core/harnesses/claude/settings/settings.json +18 -0
- package/core/harnesses/claude/statusline/doflow-statusline.sh +1 -0
- package/core/harnesses/codex/agents/core-implementer.toml +1 -0
- package/core/harnesses/codex/agents/delivery-lead.toml +10 -0
- package/core/harnesses/codex/agents/quality-guardian.toml +1 -1
- package/core/harnesses/codex/agents/research-writer.toml +1 -1
- package/core/harnesses/codex/agents/reviewer.toml +10 -0
- package/core/harnesses/codex/agents/spec-analyst.toml +1 -1
- package/core/harnesses/codex/agents/system-architect.toml +1 -1
- package/core/harnesses/codex/hooks/lib.sh +44 -25
- package/core/harnesses/codex/hooks/post-edit-lint.impl.sh +3 -1
- package/core/harnesses/codex/hooks/pre-compact.impl.sh +29 -26
- package/core/harnesses/codex/hooks/session-end.impl.sh +23 -10
- package/core/harnesses/codex/hooks/subagent-audit.sh +1 -0
- package/core/harnesses/copilot/hooks/doflow-lib.sh +93 -0
- package/core/harnesses/copilot/hooks/doflow-mcp-tool-guard.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-pre-bash-guard.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-pre-implement-gate.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-session-start.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-stop-check.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-subagent-start.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-subagent-stop.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow.json +57 -0
- package/core/harnesses/kiro/agents/doflow.json +1 -1
- package/core/harnesses/kiro/hooks/lib.sh +26 -6
- package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +1 -0
- package/core/harnesses/kiro/hooks/pre-bash-guard.sh +1 -0
- package/core/harnesses/kiro/hooks/pre-implement-gate.sh +1 -0
- package/core/harnesses/kiro/hooks/session-start.sh +1 -0
- package/core/harnesses/kiro/hooks/stop-check.sh +1 -0
- package/core/harnesses/pi/extensions/doflow-guard.js +8 -0
- package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +0 -1
- package/core/harnesses/shared/hooks/bridge/bridge.sh +102 -9
- package/core/harnesses/shared/hooks/bridge/envelope.jq +87 -18
- package/core/harnesses/shared/hooks/bridge/observe-model.sh +75 -0
- package/core/harnesses/shared/hooks/bridge/respond.sh +10 -2
- package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +28 -14
- package/core/harnesses/shared/hooks/policies/capture-failure.sh +48 -15
- package/core/harnesses/shared/hooks/policies/compact-text.sh +46 -0
- package/core/harnesses/shared/hooks/policies/contract.sh +3 -0
- package/core/harnesses/shared/hooks/policies/deny-json.sh +2 -11
- package/core/harnesses/shared/hooks/policies/emission-rules.sh +37 -0
- package/core/harnesses/shared/hooks/policies/emission.jq +209 -0
- package/core/harnesses/shared/hooks/policies/emission.sh +383 -0
- package/core/harnesses/shared/hooks/policies/identity.sh +177 -0
- package/core/harnesses/shared/hooks/policies/lib.sh +63 -40
- package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +3 -2
- package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +213 -12
- package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +114 -38
- package/core/harnesses/shared/hooks/policies/session-context.sh +30 -1
- package/core/harnesses/shared/hooks/policies/stop-check.sh +20 -9
- package/core/harnesses/shared/hooks/policies/subagent-audit.sh +5 -2
- package/core/harnesses/shared/hooks/policies/summary-store.sh +347 -0
- package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +60 -155
- package/core/harnesses/shared/hooks/stream-hook-runner.js +47 -8
- package/core/harnesses/shared/locator/doflow-run +2 -3
- package/core/registry/advice.json +3 -3
- package/core/registry/assets.json +55 -82
- package/core/registry/capabilities.json +12 -0
- package/core/registry/contracts.json +58 -31
- package/core/registry/contribution-points.json +32 -0
- package/core/registry/gates.json +32 -0
- package/core/registry/guidance-caps.json +40 -0
- package/core/registry/harnesses.json +1442 -363
- package/core/registry/lifecycle.json +485 -304
- package/core/registry/mcp.json +5 -1
- package/core/registry/models.json +7 -1
- package/core/registry/package-templates/README.md +19 -0
- package/core/registry/package-templates/agent/files/agent.md +9 -0
- package/core/registry/package-templates/agent/template.json +4 -0
- package/core/registry/package-templates/capability-provider/files/provider.json +9 -0
- package/core/registry/package-templates/capability-provider/template.json +11 -0
- package/core/registry/package-templates/debate-preset/files/preset.json +26 -0
- package/core/registry/package-templates/debate-preset/template.json +4 -0
- package/core/registry/package-templates/review-preset/files/preset.json +30 -0
- package/core/registry/package-templates/review-preset/template.json +4 -0
- package/core/registry/package-templates/skill/files/SKILL.md +8 -0
- package/core/registry/package-templates/skill/template.json +4 -0
- package/core/registry/package-templates/worker/files/worker.json +92 -0
- package/core/registry/package-templates/worker/template.json +11 -0
- package/core/registry/packages/doflow/doflow-package.json +15 -0
- package/core/registry/packages/doflow/presets/debate-3.json +26 -0
- package/core/registry/protocols.json +61 -0
- package/core/registry/routes.json +17 -0
- package/core/registry/rules.json +431 -0
- package/core/registry/settings.json +276 -0
- package/core/registry/storage.json +679 -31
- package/core/registry/telemetry.json +236 -0
- package/core/registry/transcripts.json +142 -0
- package/core/registry/verification.json +76 -1
- package/core/registry/workflows.json +55 -0
- package/core/shared/agent-specs/core-implementer.md +3 -0
- package/core/shared/agent-specs/delivery-lead.md +68 -0
- package/core/shared/agent-specs/quality-guardian.md +5 -1
- package/core/shared/agent-specs/research-writer.md +10 -3
- package/core/shared/agent-specs/reviewer.md +53 -0
- package/core/shared/agent-specs/spec-analyst.md +5 -6
- package/core/shared/agent-specs/system-architect.md +5 -1
- package/core/shared/guidance/DOFLOW_CORE.md +1 -5
- package/core/shared/guidance/modes/MODE_DeepResearch.md +1 -1
- package/core/shared/guidance/modes/MODE_Orchestration.md +4 -3
- package/core/shared/guidance/modes/MODE_Task_Management.md +14 -14
- package/core/shared/guidance/pointers/codex.md +1 -4
- package/core/shared/guidance/references/ARTIFACT_FORMAT.md +30 -16
- package/core/shared/guidance/references/ARTIFACT_VOICE.md +3 -2
- package/core/shared/guidance/references/CONSTITUTION_BASE.md +17 -15
- package/core/shared/guidance/references/DIAGRAM_GUIDE.md +1 -1
- package/core/shared/guidance/references/DOFLOW_CHAIN.md +17 -19
- package/core/shared/guidance/references/EVIDENCE_LEDGER.md +7 -8
- package/core/shared/guidance/references/MODEL_SELECTION.md +5 -3
- package/core/shared/guidance/references/ORCHESTRATION.md +1 -2
- package/core/shared/guidance/rules/RULES.md +45 -0
- package/core/shared/scripts/doflow/bash/do-args.sh +37 -0
- package/core/shared/scripts/doflow/bash/do-exec-paths.sh +4 -0
- package/core/shared/scripts/doflow/bash/do-git-state.sh +15 -4
- package/core/shared/scripts/doflow/bash/do-parallel-check.sh +4 -0
- package/core/shared/scripts/doflow/bash/do-paths.sh +40 -2
- package/core/shared/scripts/doflow/bash/do-prereqs.sh +2 -1
- package/core/shared/scripts/doflow/bash/do-review-package.sh +4 -0
- package/core/shared/scripts/doflow/bash/do-task-brief.sh +58 -16
- package/core/shared/scripts/doflow/bash/render-audit.sh +26 -2
- package/core/shared/scripts/doflow/bash/render-diagrams.sh +1 -0
- package/core/shared/scripts/doflow/bash/render-puml.sh +2 -1
- package/core/shared/scripts/doflow/bash/validate-artifacts.sh +48 -4
- package/core/shared/scripts/doflow/bin/doflow-run +149 -34
- package/core/shared/skills/do/SKILL.md +12 -10
- package/core/shared/skills/do/references/advice_calls.md +26 -0
- package/core/shared/skills/do/references/pm_routing.md +1 -2
- package/core/shared/skills/do/references/program_mode.md +31 -0
- package/core/shared/skills/do-brainstorm/SKILL.md +20 -74
- package/core/shared/skills/do-brainstorm/references/handoff_branches.md +35 -0
- package/core/shared/skills/do-brainstorm/references/intent_seed.md +21 -0
- package/core/shared/skills/do-brainstorm/references/round_log.md +17 -0
- package/core/shared/skills/do-code-review/SKILL.md +41 -144
- package/core/shared/skills/do-code-review/content-types/markdown.md +24 -2
- package/core/shared/skills/do-code-review/references/tools.md +90 -0
- package/core/shared/skills/do-code-review/scripts/doc_quality_checker.py +549 -7
- package/core/shared/skills/do-design/SKILL.md +18 -17
- package/core/shared/skills/do-diagnose/SKILL.md +14 -4
- package/core/shared/skills/do-document/SKILL.md +11 -1
- package/core/shared/skills/do-document/references/deep_research.md +1 -2
- package/core/shared/skills/do-document/references/embedded-research.md +18 -6
- package/core/shared/skills/do-document/references/implementation-flow.md +5 -6
- package/core/shared/skills/do-execute-plan/SKILL.md +15 -58
- package/core/shared/skills/do-execute-plan/references/handoff_record.md +7 -2
- package/core/shared/skills/do-execute-plan/references/parallel_dispatch.md +13 -0
- package/core/shared/skills/do-execute-plan/references/readiness_gate.md +26 -1
- package/core/shared/skills/do-execute-plan/references/run_position.md +13 -1
- package/core/shared/skills/do-execute-plan/references/scaffold.md +17 -9
- package/core/shared/skills/do-execute-plan/references/state_checkpoint.md +11 -0
- package/core/shared/skills/do-execute-plan/references/task_attempts.md +78 -24
- package/core/shared/skills/do-flow/SKILL.md +8 -9
- package/core/shared/skills/do-flow/references/task_classes.md +2 -2
- package/core/shared/skills/do-git/SKILL.md +4 -0
- package/core/shared/skills/do-implement/SKILL.md +4 -7
- package/core/shared/skills/do-plan/SKILL.md +26 -15
- package/core/shared/skills/do-test/SKILL.md +7 -4
- package/core/shared/templates/doflow/constitution-template.md +9 -3
- package/core/shared/templates/doflow/data-model-template.md +1 -1
- package/core/shared/templates/doflow/design-template.md +113 -16
- package/core/shared/templates/doflow/plan-template.md +11 -10
- package/core/shared/templates/doflow/question-log-template.md +14 -2
- package/core/shared/templates/doflow/requirement-template.md +1 -1
- package/core/worker-evidence/antigravity/checks.json +14 -0
- package/core/worker-evidence/codex/2026-10-18-cli-check.json +107 -0
- package/core/worker-evidence/codex/2026-10-18-live-run-edit.json +73 -0
- package/core/worker-evidence/codex/2026-10-18-live-run-read-only.json +62 -0
- package/core/worker-evidence/codex/2026-10-24-cli-check.json +126 -0
- package/core/worker-evidence/codex/checks.json +38 -0
- package/package.json +10 -6
- package/src/adapters/antigravity/index.js +143 -66
- package/src/adapters/claude/index.js +24 -4
- package/src/adapters/codex/agents.js +25 -15
- package/src/adapters/codex/index.js +48 -13
- package/src/adapters/codex/native-projection.js +6 -2
- package/src/adapters/copilot/index.js +58 -23
- package/src/adapters/copy-tree.js +290 -44
- package/src/adapters/guidance-manifest.js +71 -0
- package/src/adapters/guidance-pointer.js +19 -0
- package/src/adapters/hook-commands.js +6 -10
- package/src/adapters/index.js +1 -0
- package/src/adapters/kiro/index.js +23 -12
- package/src/adapters/opencode/index.js +8 -8
- package/src/adapters/pi/hooks.js +2 -1
- package/src/adapters/pi/index.js +14 -7
- package/src/adapters/resolve-source.js +26 -2
- package/src/cli/commands/install.js +2 -1
- package/src/cli/commands/reconcile.js +53 -9
- package/src/cli/commands/remove.js +2 -2
- package/src/cli/commands/rollback.js +6 -3
- package/src/cli/commands/self-update.js +18 -9
- package/src/cli/commands/status.js +21 -2
- package/src/cli/commands/ui.js +99 -0
- package/src/cli/commands/update.js +2 -1
- package/src/cli/index.js +322 -29
- package/src/cli/runtime-commands.js +209 -57
- package/src/cli/shared.js +0 -2
- package/src/helper/git.js +1 -1
- package/src/helper/guardrails.js +1 -1
- package/src/helper/marker-merge.js +31 -13
- package/src/helper/package-ids.js +37 -0
- package/src/helper/semver.js +94 -0
- package/src/install/backup.js +105 -15
- package/src/install/context.js +1 -1
- package/src/install/targets.js +31 -16
- package/src/lifecycle/coverage.js +20 -3
- package/src/lifecycle/hook-tier.js +160 -0
- package/src/lifecycle/index.js +412 -78
- package/src/lifecycle/retired-harnesses.js +318 -0
- package/src/lifecycle/view.js +35 -15
- package/src/registry/index.js +551 -26
- package/src/registry/worker-ids.js +45 -0
- package/src/release/build-package.js +0 -3
- package/src/release/docs-blocks.js +492 -0
- package/src/release/preflight.js +425 -0
- package/src/release/rehearse.js +401 -0
- package/src/release/release-workflow.js +733 -0
- package/src/release/sync-plugin-versions.js +43 -7
- package/src/runtime/advice/bench-gate.js +25 -0
- package/src/runtime/advice/cli.js +59 -16
- package/src/runtime/advice/client.js +32 -9
- package/src/runtime/advice/config.js +98 -133
- package/src/runtime/advice/doctor.js +1 -1
- package/src/runtime/advice/elevation.js +97 -0
- package/src/runtime/advice/endpoint-guard.js +9 -3
- package/src/runtime/advice/mode.js +37 -0
- package/src/runtime/advice/record.js +18 -5
- package/src/runtime/advice/registry.js +4 -1
- package/src/runtime/advice/shadow-config.js +229 -0
- package/src/runtime/advice/shadow-dispatcher.js +276 -0
- package/src/runtime/brief-rules.js +81 -0
- package/src/runtime/brief-tools.js +111 -34
- package/src/runtime/candidate.js +20 -11
- package/src/runtime/capabilities/executor.js +450 -0
- package/src/runtime/capabilities/loopback.js +41 -0
- package/src/runtime/capabilities/sanitizer.js +88 -0
- package/src/runtime/capability-router.js +135 -3
- package/src/runtime/checkouts.js +73 -11
- package/src/runtime/claims.js +13 -7
- package/src/runtime/cli.js +196 -19
- package/src/runtime/config-home.js +42 -0
- package/src/runtime/context/arbitrate.js +767 -0
- package/src/runtime/context/delivery-store.js +4 -6
- package/src/runtime/context/emission-rules.js +118 -0
- package/src/runtime/context/identity.js +227 -0
- package/src/runtime/context/primary-command.js +117 -0
- package/src/runtime/context/primary.js +344 -0
- package/src/runtime/context/summary-store.js +655 -0
- package/src/runtime/context-pack.js +154 -78
- package/src/runtime/decision-register.js +13 -4
- package/src/runtime/dispatch/admission.js +392 -0
- package/src/runtime/dispatch/attempt-end.js +274 -0
- package/src/runtime/dispatch/command.js +628 -0
- package/src/runtime/dispatch/controls.js +904 -0
- package/src/runtime/dispatch/envelope.js +276 -0
- package/src/runtime/dispatch/grants.js +310 -0
- package/src/runtime/dispatch/integrate.js +617 -0
- package/src/runtime/dispatch/loop.js +1835 -0
- package/src/runtime/dispatch/store.js +352 -0
- package/src/runtime/dispatch/verify-run.js +201 -0
- package/src/runtime/evidence-ledger.js +34 -7
- package/src/runtime/failure/capture.js +3 -1
- package/src/runtime/failure/cli.js +25 -9
- package/src/runtime/failure/home.js +57 -30
- package/src/runtime/failure/store.js +7 -2
- package/src/runtime/feature-pointer.js +174 -0
- package/src/runtime/feature-resolve.js +13 -2
- package/src/runtime/fleet/command.js +77 -0
- package/src/runtime/fleet/projects.js +178 -0
- package/src/runtime/fleet/tree.js +383 -0
- package/src/runtime/freshness.js +33 -3
- package/src/runtime/git-memo.js +46 -0
- package/src/runtime/guidance/ablation.js +427 -0
- package/src/runtime/health.js +565 -17
- package/src/runtime/history-compactor.js +13 -3
- package/src/runtime/implementation-gate.js +277 -22
- package/src/runtime/inventory/coverage.js +1 -1
- package/src/runtime/inventory/index.js +1 -1
- package/src/runtime/inventory/precedence.js +2 -2
- package/src/runtime/inventory/siblings.js +6 -4
- package/src/runtime/knowledge/code-corpus.js +277 -0
- package/src/runtime/knowledge/code-lexical.js +130 -0
- package/src/runtime/knowledge/code-retrieval.js +307 -0
- package/src/runtime/knowledge/code-validation.js +106 -0
- package/src/runtime/knowledge/gp3-gate.js +74 -0
- package/src/runtime/knowledge/graph-adapter.js +288 -0
- package/src/runtime/knowledge/graph-fallback.js +394 -0
- package/src/runtime/knowledge/hybrid.js +136 -8
- package/src/runtime/knowledge/retrieval.js +34 -1
- package/src/runtime/knowledge/rtk-compress.js +77 -0
- package/src/runtime/knowledge/semble-bridge.py +106 -0
- package/src/runtime/knowledge/semble-lifecycle.js +114 -0
- package/src/runtime/knowledge/semble-provider.js +365 -0
- package/src/runtime/lifecycle/cli.js +16 -6
- package/src/runtime/lifecycle/event-store.js +8 -6
- package/src/runtime/lifecycle/followup.js +6 -1
- package/src/runtime/lifecycle/overview.js +24 -1
- package/src/runtime/lifecycle/report-store.js +8 -10
- package/src/runtime/lifecycle/retention.js +95 -31
- package/src/runtime/lifecycle/root.js +9 -1
- package/src/runtime/lifecycle/status.js +34 -17
- package/src/runtime/lifecycle/store-upkeep.js +56 -7
- package/src/runtime/mask.js +134 -7
- package/src/runtime/mcp-consultations.js +40 -6
- package/src/runtime/model-router.js +79 -7
- package/src/runtime/models/catalogue.js +191 -0
- package/src/runtime/models/command.js +153 -0
- package/src/runtime/off-words.js +12 -0
- package/src/runtime/outcome.js +2 -2
- package/src/runtime/packages/command.js +1009 -0
- package/src/runtime/packages/contributions.js +116 -0
- package/src/runtime/packages/digest.js +132 -0
- package/src/runtime/packages/fetch.js +182 -0
- package/src/runtime/packages/grants.js +192 -0
- package/src/runtime/packages/index-file.js +134 -0
- package/src/runtime/packages/instruction.js +90 -0
- package/src/runtime/packages/manifest.js +331 -0
- package/src/runtime/packages/points.js +477 -0
- package/src/runtime/packages/programs.js +216 -0
- package/src/runtime/packages/scaffold.js +277 -0
- package/src/runtime/packages/source.js +90 -0
- package/src/runtime/packages/state.js +164 -0
- package/src/runtime/packages/store.js +449 -0
- package/src/runtime/packages/tar.js +106 -0
- package/src/runtime/packages/view.js +406 -0
- package/src/runtime/plan-check.js +59 -0
- package/src/runtime/plan-content.js +324 -0
- package/src/runtime/policy-init.js +118 -0
- package/src/runtime/profiles/doctor.js +96 -0
- package/src/runtime/profiles/resolve.js +244 -0
- package/src/runtime/profiles/rollback.js +254 -0
- package/src/runtime/profiles/sampler.js +41 -0
- package/src/runtime/readiness-record.js +16 -11
- package/src/runtime/readiness.js +46 -5
- package/src/runtime/research-request.js +50 -25
- package/src/runtime/retrieval-plan.js +10 -30
- package/src/runtime/retrieval-record.js +75 -5
- package/src/runtime/review/command.js +246 -0
- package/src/runtime/review/dominance.js +68 -0
- package/src/runtime/review/findings.js +434 -0
- package/src/runtime/review/identity.js +255 -0
- package/src/runtime/review/protocol-command.js +109 -0
- package/src/runtime/review/protocols.js +405 -0
- package/src/runtime/review/resolve-protocol.js +302 -0
- package/src/runtime/review/start.js +530 -0
- package/src/runtime/roadmap/facts.js +479 -0
- package/src/runtime/roadmap/gates.js +270 -0
- package/src/runtime/roadmap/loop.js +1636 -0
- package/src/runtime/roadmap/merge.js +381 -0
- package/src/runtime/roadmap/rows.js +1113 -0
- package/src/runtime/roadmap/seed.js +951 -0
- package/src/runtime/rules-report.js +336 -0
- package/src/runtime/scaffold/artifacts.js +60 -9
- package/src/runtime/scaffold/generate.js +21 -9
- package/src/runtime/settings/autopilot.js +106 -0
- package/src/runtime/settings/cli.js +280 -0
- package/src/runtime/settings/config-write.js +314 -0
- package/src/runtime/settings/files.js +271 -0
- package/src/runtime/settings/legacy.js +154 -0
- package/src/runtime/settings/lexical.js +56 -0
- package/src/runtime/settings/prepared-run.js +269 -0
- package/src/runtime/settings/project-layers.js +118 -0
- package/src/runtime/settings/refs.js +288 -0
- package/src/runtime/settings/registry.js +553 -0
- package/src/runtime/settings/resolve.js +713 -0
- package/src/runtime/settings/shared.js +37 -0
- package/src/runtime/settings/worker-billing.js +107 -0
- package/src/runtime/settings/write.js +269 -0
- package/src/runtime/slot-command.js +78 -0
- package/src/runtime/slots.js +671 -0
- package/src/runtime/source-version.js +1 -0
- package/src/runtime/state-root.js +217 -0
- package/src/runtime/storage/cli.js +12 -5
- package/src/runtime/storage/engine.js +9 -2
- package/src/runtime/storage/registry.js +14 -1
- package/src/runtime/storage/roots.js +66 -16
- package/src/runtime/storage/rules.js +82 -10
- package/src/runtime/storage/upkeep.js +10 -2
- package/src/runtime/storage/work-status.js +245 -26
- package/src/runtime/task-attempts.js +589 -69
- package/src/runtime/task-classifier.js +47 -11
- package/src/runtime/task-graph.js +373 -0
- package/src/runtime/task-run.js +778 -0
- package/src/runtime/task-scope.js +176 -55
- package/src/runtime/task-sources.js +147 -0
- package/src/runtime/task-state.js +374 -64
- package/src/runtime/team/admission.js +301 -0
- package/src/runtime/team/envelope.js +324 -0
- package/src/runtime/team/review-guard.js +156 -0
- package/src/runtime/trace/analyze.js +427 -0
- package/src/runtime/trace/baseline.js +770 -0
- package/src/runtime/trace/ledger.js +63 -140
- package/src/runtime/trace/otlp-export.js +568 -0
- package/src/runtime/trace/otlp-map.js +481 -0
- package/src/runtime/trace/spans.js +745 -0
- package/src/runtime/trace/telemetry.js +225 -0
- package/src/runtime/trace/views.js +1 -0
- package/src/runtime/transcripts/command.js +98 -0
- package/src/runtime/transcripts/cursor.js +326 -0
- package/src/runtime/transcripts/read.js +126 -0
- package/src/runtime/transcripts/readers/claude-jsonl.js +74 -0
- package/src/runtime/transcripts/readers/codex-rollout.js +70 -0
- package/src/runtime/transcripts/readers/copilot-events.js +90 -0
- package/src/runtime/transcripts/readers/kiro-jsonl.js +77 -0
- package/src/runtime/transcripts/readers/pi-jsonl.js +59 -0
- package/src/runtime/transcripts/registry.js +161 -0
- package/src/runtime/transcripts/stats.js +161 -0
- package/src/runtime/verification/check-key.js +628 -0
- package/src/runtime/verification/contract-runner.js +546 -11
- package/src/runtime/verification/engine.js +499 -9
- package/src/runtime/verification/impact.js +617 -0
- package/src/runtime/verification/inflight.js +205 -0
- package/src/runtime/verification/registry.js +176 -0
- package/src/runtime/verification/reuse.js +184 -0
- package/src/runtime/verification/telemetry-emit.js +254 -0
- package/src/runtime/verification/test-selection.js +67 -7
- package/src/runtime/worker/active.js +90 -0
- package/src/runtime/worker/cancel.js +222 -0
- package/src/runtime/worker/catalogue.js +122 -0
- package/src/runtime/worker/command.js +677 -0
- package/src/runtime/worker/evidence.js +288 -0
- package/src/runtime/worker/launch.js +288 -0
- package/src/runtime/worker/models.js +137 -0
- package/src/runtime/worker/parsers.js +218 -0
- package/src/runtime/worker/probes/index.js +706 -0
- package/src/runtime/worker/process-tree.js +415 -0
- package/src/runtime/worker/quota.js +43 -0
- package/src/runtime/worker/readiness.js +713 -0
- package/src/runtime/worker/run-store.js +166 -0
- package/src/runtime/worker/supervisor.js +541 -0
- package/src/runtime/worker/worktree-check.js +252 -0
- package/src/runtime/workflow-engine.js +5 -1
- package/src/runtime/workflow-orchestrator.js +578 -63
- package/src/runtime/worktree.js +149 -22
- package/src/state/index.js +1 -1
- package/src/state/lockfile.js +64 -10
- package/src/state/run-lock.js +1 -1
- package/types/node-commonjs.d.ts +106 -7
- package/core/harnesses/gemini/hooks/hooks.json +0 -77
- package/core/harnesses/gemini/hooks/lib.sh +0 -302
- package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +0 -10
- package/core/harnesses/gemini/hooks/post-edit-lint.sh +0 -39
- package/core/harnesses/gemini/hooks/pre-bash-guard.sh +0 -10
- package/core/harnesses/gemini/hooks/pre-compact.sh +0 -38
- package/core/harnesses/gemini/hooks/pre-implement-gate.sh +0 -10
- package/core/harnesses/gemini/hooks/session-end.impl.sh +0 -89
- package/core/harnesses/gemini/hooks/session-end.sh +0 -5
- package/core/harnesses/gemini/hooks/session-start.sh +0 -7
- package/core/harnesses/gemini/settings/adapter-defaults.json +0 -4
- package/core/shared/guidance/FLAGS.md +0 -25
- package/core/shared/guidance/rules/RULE_01_SAFETY.md +0 -25
- package/core/shared/guidance/rules/RULE_02_WORKFLOW.md +0 -23
- package/core/shared/guidance/rules/RULE_03_QUALITY.md +0 -14
- package/core/shared/guidance/rules/RULE_04_QUESTIONS.md +0 -46
- package/src/adapters/gemini/hooks.js +0 -251
- package/src/adapters/gemini/index.js +0 -429
|
@@ -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
|
+
}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
case $
|
|
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
|
|
3
4
|
# user-prompt-submit.sh — Canonical Policy Library: UserPromptSubmit hook
|
|
4
5
|
#
|
|
5
6
|
# 022-hooks-remaining-duplication: claude and codex's UserPromptSubmit policies share the same
|
|
6
|
-
# context-gathering behavior.
|
|
7
|
+
# context-gathering behavior.
|
|
7
8
|
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
9
|
+
# Emission (078 IC-004): this policy reads the payload, records its decisions and runs the nudge
|
|
10
|
+
# classifier; doflow_emit_prompt (emission.sh) decides and renders the response. On the first
|
|
11
|
+
# prompt of a bootstrap unit that is git context, the scoped prior summary and the prior warning
|
|
12
|
+
# in one nested envelope,
|
|
10
13
|
# {"hookSpecificOutput":{"hookEventName":"UserPromptSubmit","additionalContext":...}}
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
# already titled the session; Codex rejects unknown fields, so its object never does.
|
|
14
|
-
#
|
|
15
|
-
# On subsequent prompts: prints {} (clean, no token waste), unless the nudge below is earned.
|
|
14
|
+
# with sessionTitle on every harness but Codex unless the payload already carries session_title
|
|
15
|
+
# (DEC-030). Later prompts print {} unless the nudge below is earned.
|
|
16
16
|
#
|
|
17
17
|
# Once per session, the first prompt that reads as a plain code-change request (decided by
|
|
18
18
|
# prompt-nudge.jq against the rules in the workflow registry's `promptNudge` key) also carries a
|
|
@@ -22,7 +22,7 @@ case $0 in */*) _DOFLOW_POLDIR=${0%/*} ;; *) _DOFLOW_POLDIR=. ;; esac
|
|
|
22
22
|
# opt-out file, a subagent prompt, a /do-flow already in progress) resolves to silence; see the
|
|
23
23
|
# once-per-session marker in lib.sh. The nudge starts no node or doflow-run process.
|
|
24
24
|
#
|
|
25
|
-
# Multi-session safe:
|
|
25
|
+
# Multi-session safe: the adapter's one-shot slot is per bootstrap unit (DEC-007).
|
|
26
26
|
# Must complete in <100ms. Must NEVER output "decision: block".
|
|
27
27
|
|
|
28
28
|
set -euo pipefail
|
|
@@ -46,6 +46,16 @@ if ! command -v jq >/dev/null 2>&1; then
|
|
|
46
46
|
( . "$POLICY_DIR/capture-failure.sh"; doflow_capture_failure user-prompt-submit dependency-missing:jq ) >/dev/null 2>&1 || true
|
|
47
47
|
fi
|
|
48
48
|
require_jq
|
|
49
|
+
# The emission authority (feature 078): the scoped summary store, the compiled rule table and the
|
|
50
|
+
# adapter. A missing file leaves its functions
|
|
51
|
+
# undefined, which the adapter (or the check below) answers with the silent response and an
|
|
52
|
+
# emission-deps capture.
|
|
53
|
+
# shellcheck source=summary-store.sh
|
|
54
|
+
[ ! -f "$_DOFLOW_POLDIR/summary-store.sh" ] || . "$_DOFLOW_POLDIR/summary-store.sh"
|
|
55
|
+
# shellcheck source=emission-rules.sh
|
|
56
|
+
[ ! -f "$_DOFLOW_POLDIR/emission-rules.sh" ] || . "$_DOFLOW_POLDIR/emission-rules.sh"
|
|
57
|
+
# shellcheck source=emission.sh
|
|
58
|
+
[ ! -f "$_DOFLOW_POLDIR/emission.sh" ] || . "$_DOFLOW_POLDIR/emission.sh"
|
|
49
59
|
# One jq pass over the payload for every field this hook reads (each pass costs about 8 ms on a
|
|
50
60
|
# 20 KB prompt, and the budget is 100 ms), NUL-separated: session_id, cwd and session_title, null
|
|
51
61
|
# and false read as empty as json_field does, with any NUL inside a value dropped so no field can
|
|
@@ -77,7 +87,6 @@ fi
|
|
|
77
87
|
[[ -z "$SESSION_ID" ]] && exit 0
|
|
78
88
|
|
|
79
89
|
SESSION_PATH="$SESSION_DIR/$SESSION_ID"
|
|
80
|
-
INJECTED_FLAG="$SESSION_PATH/injected"
|
|
81
90
|
|
|
82
91
|
# ── Prompt nudge (IC-004 N1-N8) ───────────────────────────────────────────────
|
|
83
92
|
|
|
@@ -86,13 +95,22 @@ INJECTED_FLAG="$SESSION_PATH/injected"
|
|
|
86
95
|
# here can change the exit status or the first-prompt output. The prompt only ever travels to jq
|
|
87
96
|
# on stdin, with stderr discarded, and is never copied out of INPUT.
|
|
88
97
|
nudge_message() {
|
|
89
|
-
local root policy_dir here registry decision message
|
|
98
|
+
local root policy_dir here registry decision message role
|
|
90
99
|
[[ "${DOFLOW_AGENT:-}" == "claude" || "${DOFLOW_AGENT:-}" == "codex" ]] || return 0 # N1
|
|
91
|
-
|
|
100
|
+
# The role gate is the rule table's: a role whose prompt.nudge action is skip (a worker) is never
|
|
101
|
+
# nudged, and returning here keeps the classifier from running or marking it (L2-15).
|
|
102
|
+
role=$(doflow_session_role 2>/dev/null) || role=main
|
|
103
|
+
DF_ACTION=skip
|
|
104
|
+
declare -F doflow_emission_rule >/dev/null 2>&1 && doflow_emission_rule prompt.nudge "$role:nudge" 2>/dev/null || DF_ACTION=skip
|
|
105
|
+
[[ "$DF_ACTION" != "skip" ]] || return 0
|
|
92
106
|
nudge_safe_id "$SESSION_ID" || return 0 # N2
|
|
93
107
|
root=$(nudge_repo_root "$CWD") || return 0
|
|
94
108
|
[[ "$(nudge_setting "$root")" == "on" ]] || return 0 # N3
|
|
95
109
|
[[ ! -e "$STATE_DIR/nudge/$SESSION_ID" ]] || return 0 # N4 (IC-008's path, no process)
|
|
110
|
+
# The marker is written after the render (DEC-041), so a folder it cannot be written to would
|
|
111
|
+
# nudge every prompt: an unwritable marker folder (or state folder, before it exists) is silent.
|
|
112
|
+
if [[ -e "$STATE_DIR/nudge" ]]; then [[ -d "$STATE_DIR/nudge" && -w "$STATE_DIR/nudge" ]] || return 0
|
|
113
|
+
elif [[ -e "$STATE_DIR" ]]; then [[ -w "$STATE_DIR" ]] || return 0; fi
|
|
96
114
|
# N5: a subagent prompt carries agent_id or agent_type (read with the other fields above).
|
|
97
115
|
[[ -z "$SUBAGENT" ]] || return 0
|
|
98
116
|
# N6: the policy script's physical directory, found with built-ins (a subshell costs about 4 ms).
|
|
@@ -112,157 +130,44 @@ nudge_message() {
|
|
|
112
130
|
nudge_mark "$SESSION_ID" suppressed || true
|
|
113
131
|
return 0
|
|
114
132
|
fi
|
|
115
|
-
# N8:
|
|
116
|
-
#
|
|
133
|
+
# N8: a registry without a usable message leaves no marker. The `nudged` marker is written by
|
|
134
|
+
# doflow_emit_prompt once the nudge rendered, so a nudge the budget dropped stays unmarked (DEC-041).
|
|
117
135
|
message=$(jq -r '.promptNudge.message | strings' "$registry" 2>/dev/null) || return 0
|
|
118
136
|
[[ -n "$message" ]] || return 0
|
|
119
|
-
nudge_mark "$SESSION_ID" nudged || return 0
|
|
120
137
|
printf '%s' "$message"
|
|
121
138
|
return 0
|
|
122
139
|
}
|
|
123
140
|
|
|
124
|
-
#
|
|
125
|
-
#
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
141
|
+
# ── Emission: every prompt, handed to the authority (IC-004) ──────────────────
|
|
142
|
+
# The classifier runs first, where its gates allow, and returns the message unmarked.
|
|
143
|
+
# doflow_emit_prompt decides everything else (the bootstrap slot, git context, the
|
|
144
|
+
# scoped summary, the warning, a grant, the nudge's place in the budget), prints the response and
|
|
145
|
+
# marks the session nudged and records `advisory prompt-nudge` itself when the nudge was rendered.
|
|
146
|
+
|
|
147
|
+
# Rider R1 (091): the emission metadata for the bridge's hook line, as one record on the decision
|
|
148
|
+
# fd after the decision records: the reason, the byte count and the digest, never a response byte. The
|
|
149
|
+
# bridge validates each value before it writes it. Telemetry off or no fd writes nothing.
|
|
150
|
+
doflow_emit_record() {
|
|
151
|
+
local fd="${DOFLOW_DECISION_FD:-}" digest=null
|
|
152
|
+
[ "${DOFLOW_TELEMETRY:-}" != off ] && [ -n "${DF_EMIT_REASON:-}" ] || return 0
|
|
153
|
+
[ -z "${DF_EMIT_DIGEST:-}" ] || digest="\"$DF_EMIT_DIGEST\""
|
|
154
|
+
case "$fd" in
|
|
155
|
+
3|4|5|6|7|8|9)
|
|
156
|
+
{ printf '\036{"v":1,"emit":{"reason":"%s","bytes":%s,"digest":%s}}\n' "$DF_EMIT_REASON" "${DF_EMIT_BYTES:-0}" "$digest" >&"$fd"; } 2>/dev/null || :
|
|
157
|
+
;;
|
|
158
|
+
esac
|
|
159
|
+
return 0
|
|
130
160
|
}
|
|
131
161
|
|
|
132
|
-
# ── Subsequent prompts: no injection, unless the nudge is earned ──────────────
|
|
133
|
-
|
|
134
|
-
if [[ -f "$INJECTED_FLAG" ]]; then
|
|
135
|
-
NUDGE=$(nudge_message 2>/dev/null) || NUDGE=""
|
|
136
|
-
if [[ -n "$NUDGE" ]]; then
|
|
137
|
-
doflow_decide advisory prompt-nudge
|
|
138
|
-
emit_envelope "$NUDGE" ""
|
|
139
|
-
else
|
|
140
|
-
echo "{}"
|
|
141
|
-
fi
|
|
142
|
-
exit 0
|
|
143
|
-
fi
|
|
144
|
-
|
|
145
|
-
# ── Worker session: no bootstrap, nothing claimed ─────────────────────────────
|
|
146
|
-
# A worker never receives the git context, the prior compact summary or the prior warning, and
|
|
147
|
-
# leaves both files for the main session. It is marked injected so later prompts stay silent.
|
|
148
|
-
|
|
149
|
-
if [[ "$(doflow_session_role || echo main)" == "worker" ]]; then
|
|
150
|
-
ensure_session_dir "$SESSION_ID" >/dev/null || true
|
|
151
|
-
touch "$INJECTED_FLAG" 2>/dev/null || true
|
|
152
|
-
echo "{}"
|
|
153
|
-
exit 0
|
|
154
|
-
fi
|
|
155
|
-
|
|
156
|
-
# ── First prompt: build context ───────────────────────────────────────────────
|
|
157
|
-
|
|
158
|
-
GIT_CONTEXT_FILE="$SESSION_PATH/git-context.json"
|
|
159
|
-
|
|
160
|
-
# Fallback if session-start.sh didn't run or failed
|
|
161
|
-
if [[ ! -f "$GIT_CONTEXT_FILE" ]]; then
|
|
162
|
-
ensure_session_dir "$SESSION_ID" >/dev/null
|
|
163
|
-
touch "$INJECTED_FLAG"
|
|
164
|
-
CONTEXT="Git context unavailable for this session."
|
|
165
|
-
SESSION_TITLE=""
|
|
166
|
-
else
|
|
167
|
-
GIT_JSON=$(cat "$GIT_CONTEXT_FILE")
|
|
168
|
-
IS_GIT=$(json_field "$GIT_JSON" ".is_git_repo")
|
|
169
|
-
|
|
170
|
-
if [[ "$IS_GIT" == "true" ]]; then
|
|
171
|
-
BRANCH=$(json_field "$GIT_JSON" ".branch")
|
|
172
|
-
SHA=$(json_field "$GIT_JSON" ".sha")
|
|
173
|
-
UNCOMMITTED=$(json_field "$GIT_JSON" ".uncommitted_count")
|
|
174
|
-
STASH=$(json_field "$GIT_JSON" ".stash_count")
|
|
175
|
-
|
|
176
|
-
# Build commit list as a single line (· separated)
|
|
177
|
-
COMMITS=$(echo "$GIT_JSON" | jq -r '.commits[]? // empty' | head -5 | paste -sd ' · ' -)
|
|
178
|
-
|
|
179
|
-
CONTEXT="Git context — branch: ${BRANCH:-unknown} | ${SHA:-unknown}"$'\n'
|
|
180
|
-
CONTEXT+="Last commits: ${COMMITS:-none}"$'\n'
|
|
181
|
-
CONTEXT+="Uncommitted files: ${UNCOMMITTED:-0}"
|
|
182
|
-
[[ "${STASH:-0}" -gt 0 ]] && CONTEXT+=" | Stashed: ${STASH}"
|
|
183
|
-
|
|
184
|
-
SESSION_TITLE="${BRANCH:-unknown} — ${SHA:-unknown}"
|
|
185
|
-
else
|
|
186
|
-
CONTEXT="Not a git repository."
|
|
187
|
-
SESSION_TITLE="no-git"
|
|
188
|
-
fi
|
|
189
|
-
|
|
190
|
-
# ── Prior compact summary: injected once into the next session, then consumed ─
|
|
191
|
-
# post-compact.sh writes this file for the session that follows a compaction. The session
|
|
192
|
-
# that compacted already holds the summary, so it skips the file and leaves it in place.
|
|
193
|
-
# Any other session claims the file with an atomic rename first (a summary written meanwhile
|
|
194
|
-
# is a new file and survives), reads a bounded prefix of the claimed copy and removes it. No
|
|
195
|
-
# step here may fail the hook: on any error the summary is simply not injected.
|
|
196
|
-
|
|
197
|
-
PROJECT_DIR=$(ensure_project_dir "$CWD")
|
|
198
|
-
COMPACT_FILE="$PROJECT_DIR/last-compact-summary.md"
|
|
199
|
-
COMPACT_CLAIM="$PROJECT_DIR/.last-compact-summary.claimed.$SESSION_ID"
|
|
200
|
-
COMPACT_CAP=4000
|
|
201
|
-
COMPACT_READ=65536 # bytes: enough for the cap even at four bytes per character
|
|
202
|
-
compact_field() {
|
|
203
|
-
printf '%s\n' "$COMPACT_HEAD" | awk -v k="$1:" '/^---$/{n++; if (n == 2) exit; next} n == 1 && index($0, k) == 1 {sub(/^[^:]*:[ ]*/, ""); print; exit}' || true
|
|
204
|
-
}
|
|
205
|
-
# A native subagent payload (agent_id or agent_type) never claims the summary or the warning.
|
|
206
|
-
if [[ -z "$SUBAGENT" && -f "$COMPACT_FILE" && ! -L "$COMPACT_FILE" ]]; then
|
|
207
|
-
COMPACT_HEAD=$(head -c "$COMPACT_READ" "$COMPACT_FILE" 2>/dev/null | tr -d '\r') || COMPACT_HEAD=''
|
|
208
|
-
if [[ "$(compact_field session_id)" != "$SESSION_ID" ]] && mv "$COMPACT_FILE" "$COMPACT_CLAIM" 2>/dev/null; then
|
|
209
|
-
COMPACT_SIZE=$(wc -c < "$COMPACT_CLAIM" 2>/dev/null) || COMPACT_SIZE=0
|
|
210
|
-
COMPACT_HEAD=$(head -c "$COMPACT_READ" "$COMPACT_CLAIM" 2>/dev/null | tr -d '\r') || COMPACT_HEAD=''
|
|
211
|
-
rm -f "$COMPACT_CLAIM"
|
|
212
|
-
# Strip the YAML frontmatter (between the first two `---` lines) and the blank line after it;
|
|
213
|
-
# keep the summary body only.
|
|
214
|
-
COMPACT_BODY=$(printf '%s\n' "$COMPACT_HEAD" | awk '/^---$/ && n < 2 {n++; next} n >= 2 && (started || $0 != "") {started = 1; print}') || COMPACT_BODY=''
|
|
215
|
-
COMPACT_LEN=''
|
|
216
|
-
if [[ -n "$COMPACT_BODY" ]]; then
|
|
217
|
-
# --rawfile, not stdin or --arg: jq 1.7's raw stdin reader miscounts multibyte text across
|
|
218
|
-
# buffer boundaries, and --arg would hit the per-argument size limit on a large summary.
|
|
219
|
-
COMPACT_LEN=$(jq -n --rawfile b <(printf '%s' "$COMPACT_BODY") '$b | length' 2>/dev/null) || COMPACT_BODY=''
|
|
220
|
-
fi
|
|
221
|
-
if [[ -n "$COMPACT_BODY" && "$COMPACT_LEN" -gt "$COMPACT_CAP" ]]; then
|
|
222
|
-
COMPACT_BODY=$(jq -nr --rawfile b <(printf '%s' "$COMPACT_BODY") "\$b[0:$COMPACT_CAP]" 2>/dev/null) || COMPACT_BODY=''
|
|
223
|
-
COMPACT_MORE=''
|
|
224
|
-
[[ "$COMPACT_SIZE" -gt "$COMPACT_READ" ]] && COMPACT_MORE='at least '
|
|
225
|
-
[[ -n "$COMPACT_BODY" ]] && COMPACT_BODY+=$'\n'"[summary truncated: first ${COMPACT_CAP} of ${COMPACT_MORE}${COMPACT_LEN} characters]"
|
|
226
|
-
fi
|
|
227
|
-
if [[ -n "$COMPACT_BODY" ]]; then
|
|
228
|
-
COMPACT_AT=$(compact_field compacted_at); COMPACT_AT=${COMPACT_AT//]/}
|
|
229
|
-
COMPACT_BRANCH=$(compact_field branch); COMPACT_BRANCH=${COMPACT_BRANCH//]/}
|
|
230
|
-
COMPACT_HEADER="Prior session summary"
|
|
231
|
-
[[ -n "$COMPACT_AT" && "$COMPACT_AT" != "unknown" ]] && COMPACT_HEADER+=", compacted ${COMPACT_AT}"
|
|
232
|
-
[[ -n "$COMPACT_BRANCH" && "$COMPACT_BRANCH" != "unknown" ]] && COMPACT_HEADER+=" on branch ${COMPACT_BRANCH}"
|
|
233
|
-
CONTEXT+=$'\n\n'"[${COMPACT_HEADER}]"$'\n'"$COMPACT_BODY"
|
|
234
|
-
fi
|
|
235
|
-
fi
|
|
236
|
-
fi
|
|
237
|
-
|
|
238
|
-
# Check for uncommitted warning from prior session (one-time: delete after read —
|
|
239
|
-
# nothing else in the framework reassigns this cleanup, so it happens here)
|
|
240
|
-
if [[ -z "$SUBAGENT" && -f "$PROJECT_DIR/uncommitted-warning.txt" ]]; then
|
|
241
|
-
WARNING=$(cat "$PROJECT_DIR/uncommitted-warning.txt")
|
|
242
|
-
CONTEXT+=$'\n'"[Prior session warning: ${WARNING}]"
|
|
243
|
-
rm -f "$PROJECT_DIR/uncommitted-warning.txt"
|
|
244
|
-
fi
|
|
245
|
-
fi
|
|
246
|
-
|
|
247
|
-
# ── Write injected flag ────────────────────────────────────────────────────────
|
|
248
|
-
|
|
249
|
-
touch "$INJECTED_FLAG"
|
|
250
|
-
|
|
251
|
-
# ── Output JSON ───────────────────────────────────────────────────────────────
|
|
252
|
-
|
|
253
|
-
# The nudge, when earned, follows the context after a blank line.
|
|
254
162
|
NUDGE=$(nudge_message 2>/dev/null) || NUDGE=""
|
|
255
|
-
if
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
# titled the session.
|
|
262
|
-
TITLE_ARG=""
|
|
263
|
-
if [[ "${DOFLOW_AGENT:-}" != "codex" && -n "${SESSION_TITLE:-}" && -z "$PAYLOAD_TITLE" ]]; then
|
|
264
|
-
TITLE_ARG="$SESSION_TITLE"
|
|
163
|
+
if ! declare -F doflow_emit_prompt >/dev/null 2>&1; then
|
|
164
|
+
( . "$POLICY_DIR/capture-failure.sh"; doflow_capture_failure user-prompt-submit emission-deps ) >/dev/null 2>&1 || true
|
|
165
|
+
printf '{}\n'
|
|
166
|
+
DF_EMIT_REASON=fault:deps DF_EMIT_BYTES=0 DF_EMIT_DIGEST=""
|
|
167
|
+
doflow_emit_record
|
|
168
|
+
exit 0
|
|
265
169
|
fi
|
|
266
|
-
|
|
170
|
+
doflow_emit_prompt "$SESSION_ID" "$CWD" "$PAYLOAD_TITLE" "$SUBAGENT" "$NUDGE"
|
|
171
|
+
doflow_emit_record
|
|
267
172
|
|
|
268
173
|
exit 0
|