@khoavu882/doflow 2.0.0-beta.5 → 2.0.1
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 +16 -16
- package/bin/doflow.js +22 -12
- 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/mcp-tool-guard.sh +4 -1
- 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/pre-implement-gate.sh +4 -1
- 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 +26 -9
- 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/hooks.json +0 -10
- package/core/harnesses/codex/hooks/lib.sh +44 -25
- package/core/harnesses/codex/hooks/mcp-tool-guard.sh +4 -1
- 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/pre-implement-gate.sh +4 -1
- 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 +94 -0
- package/core/harnesses/copilot/hooks/doflow-mcp-tool-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 +51 -0
- package/core/harnesses/kiro/agents/doflow.json +1 -11
- package/core/harnesses/kiro/hooks/doflow.json +0 -10
- package/core/harnesses/kiro/hooks/lib.sh +26 -6
- package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +8 -3
- package/core/harnesses/kiro/hooks/pre-implement-gate.sh +8 -3
- 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 +11 -2
- package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +1 -2
- package/core/harnesses/shared/hooks/adapters/antigravity.js +1 -1
- package/core/harnesses/shared/hooks/bridge/bridge.sh +126 -23
- 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 +16 -2
- package/core/harnesses/shared/hooks/policies/capture-failure.sh +49 -16
- 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 +20 -7
- 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-policy.conf +1 -1
- package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +7 -8
- package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +132 -57
- package/core/harnesses/shared/hooks/policies/session-context.sh +30 -1
- package/core/harnesses/shared/hooks/policies/stop-check.sh +27 -15
- package/core/harnesses/shared/hooks/policies/subagent-audit.sh +6 -3
- 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 +58 -20
- 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 +35 -0
- package/core/registry/gates.json +32 -0
- package/core/registry/guidance-caps.json +40 -0
- package/core/registry/harnesses.json +1445 -366
- package/core/registry/intake.json +53 -0
- package/core/registry/lifecycle.json +599 -996
- 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/intake-category/files/category.json +8 -0
- package/core/registry/package-templates/intake-category/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 +272 -0
- package/core/registry/storage.json +687 -31
- package/core/registry/telemetry.json +236 -0
- package/core/registry/transcripts.json +142 -0
- package/core/registry/verification.json +76 -6
- 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 +3 -2
- package/core/shared/guidance/references/WORKFLOW_HANDOFF.md +19 -1
- 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 +49 -5
- package/core/shared/scripts/doflow/bin/doflow-run +152 -34
- package/core/shared/skills/do/SKILL.md +14 -12
- 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 +45 -97
- package/core/shared/skills/do-brainstorm/references/handoff_branches.md +36 -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 +48 -148
- 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 +23 -20
- package/core/shared/skills/do-diagnose/SKILL.md +18 -6
- package/core/shared/skills/do-document/SKILL.md +12 -1
- package/core/shared/skills/do-document/references/deep_research.md +1 -2
- package/core/shared/skills/do-document/references/embedded-research.md +19 -7
- package/core/shared/skills/do-document/references/implementation-flow.md +5 -6
- package/core/shared/skills/do-execute-plan/SKILL.md +19 -60
- 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 +16 -2
- 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 +10 -11
- package/core/shared/skills/do-flow/references/task_classes.md +5 -5
- package/core/shared/skills/do-git/SKILL.md +4 -0
- package/core/shared/skills/do-implement/SKILL.md +13 -14
- package/core/shared/skills/do-plan/SKILL.md +34 -20
- 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/shared/workflows/do-flow-chain.md +2 -0
- 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 +46 -5
- package/src/adapters/codex/agents.js +25 -15
- package/src/adapters/codex/hooks.js +39 -3
- package/src/adapters/codex/index.js +61 -13
- package/src/adapters/codex/native-projection.js +6 -2
- package/src/adapters/copilot/index.js +58 -23
- package/src/adapters/copy-tree.js +291 -46
- 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 +22 -3
- package/src/cli/commands/ui.js +99 -0
- package/src/cli/commands/update.js +2 -1
- package/src/cli/index.js +337 -32
- package/src/cli/runtime-commands.js +220 -60
- 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 +4 -16
- 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 +472 -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/pointer.js +67 -12
- 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 +158 -79
- 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 +52 -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 +624 -18
- package/src/runtime/history-compactor.js +13 -3
- package/src/runtime/implementation-gate.js +281 -26
- package/src/runtime/intake/engine.js +677 -0
- package/src/runtime/intake/index.js +136 -0
- package/src/runtime/intake/manifest.js +140 -0
- package/src/runtime/intake/taxonomy.js +206 -0
- 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 +88 -10
- package/src/runtime/lifecycle/event-store.js +8 -6
- package/src/runtime/lifecycle/followup.js +38 -7
- package/src/runtime/lifecycle/overview.js +26 -2
- 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 +487 -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/attendance.js +137 -0
- package/src/runtime/roadmap/facts.js +479 -0
- package/src/runtime/roadmap/gates.js +273 -0
- package/src/runtime/roadmap/loop.js +1719 -0
- package/src/runtime/roadmap/merge.js +390 -0
- package/src/runtime/roadmap/rows.js +1122 -0
- package/src/runtime/roadmap/seed.js +1063 -0
- package/src/runtime/roadmap/worktree-cleanup.js +147 -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 +85 -14
- 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 +373 -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/verb-view.js +267 -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 -54
- 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 +256 -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 +610 -67
- 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/claude/hooks/pre-bash-guard.sh +0 -10
- package/core/harnesses/codex/hooks/pre-bash-guard.sh +0 -10
- 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/harnesses/kiro/hooks/pre-bash-guard.sh +0 -24
- package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +0 -91
- package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +0 -378
- 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,209 @@
|
|
|
1
|
+
# emission.jq — one-pass renderer of the prompt segments and the hook response (078 IC-002, IC-004
|
|
2
|
+
# step 7). renderEmission() in src/runtime/context/arbitrate.ts is the reference; both must give
|
|
3
|
+
# expected.text, bytes, refused and dropped of test/fixtures/emission/render-corpus.json byte for
|
|
4
|
+
# byte (IC-022, RK7).
|
|
5
|
+
#
|
|
6
|
+
# Invocation (IC-004 step 7), one process:
|
|
7
|
+
# jq -n -r -f emission.jq --argjson plan "$PLAN" \
|
|
8
|
+
# --rawfile git <git-context.json> --rawfile summary <claimed summary> \
|
|
9
|
+
# --rawfile grant <claimed grant> --arg warning "$WARNING" --arg nudge "$NUDGE"
|
|
10
|
+
#
|
|
11
|
+
# All four names are always bound (jq refuses an undefined $name); emission.sh binds $warning with
|
|
12
|
+
# --arg, because the warning claim renames the file and reads it before the render. A file that does not
|
|
13
|
+
# exist is passed as /dev/null and its name listed in plan.absent; an empty existing file is not
|
|
14
|
+
# absent (an empty warning still renders, an empty git capture is "Not a git repository.").
|
|
15
|
+
#
|
|
16
|
+
# plan:
|
|
17
|
+
# actions IC-001 actions by segment; only git, summary, warning, grant and nudge render here
|
|
18
|
+
# maxBytes positive integer, the IC-002 byte budget
|
|
19
|
+
# harness the harness name (DOFLOW_AGENT); sessionTitle is added on every harness but
|
|
20
|
+
# "codex", whose envelope rejects unknown fields (DEC-030)
|
|
21
|
+
# title true when a title may be set (the payload carries no session_title)
|
|
22
|
+
# absent names among git, summary, grant, warning whose file does not exist (default [])
|
|
23
|
+
# summaryBytes the claimed summary file's full size, which can exceed the bounded read; it
|
|
24
|
+
# defaults to the UTF-8 size of $summary
|
|
25
|
+
# mode "response" (default) prints the hook response; "render" prints
|
|
26
|
+
# {text, bytes, refused, dropped, segments}, the renderEmission() result, for parity;
|
|
27
|
+
# "hook" (emission.sh, run with -r) prints one first line holding the dropped
|
|
28
|
+
# segment names joined with tabs (possibly empty), a second line holding the
|
|
29
|
+
# response digest (empty unless plan.digest), then the hook response
|
|
30
|
+
# digest hook mode: true computes response_digest (rider R1, 091); other modes ignore it
|
|
31
|
+
#
|
|
32
|
+
# Response bytes (jq's default pretty print): emission is
|
|
33
|
+
# {"hookSpecificOutput": {"hookEventName": "UserPromptSubmit", "additionalContext": ...}}
|
|
34
|
+
# with sessionTitle on every harness but Codex (DEC-030), when plan.title holds and the git action built a title
|
|
35
|
+
# ("{branch?unknown} — {sha?unknown}", or "no-git" outside a repository; none when the capture
|
|
36
|
+
# file is missing). Nothing rendered prints {} and a newline. Any error exits non-zero and the
|
|
37
|
+
# caller answers with the silent response.
|
|
38
|
+
#
|
|
39
|
+
# Counting (design R1): characters are code points (jq string length and slices), budgets are
|
|
40
|
+
# UTF-8 bytes (utf8bytelength). Portability: jq 1.6+ built with Oniguruma; utf8bytelength is the
|
|
41
|
+
# one builtin newer than 1.5. No index/rindex on strings (byte offsets in 1.6) and no 1.7-only
|
|
42
|
+
# builtin (trim, ltrim, rtrim, abs, pick, toarray, have_decnum). Every regex is anchored
|
|
43
|
+
# with \A or \z, never ^ or $, which Oniguruma reads per line.
|
|
44
|
+
|
|
45
|
+
def summary_cap: 4000;
|
|
46
|
+
def summary_read: 65536;
|
|
47
|
+
|
|
48
|
+
# Trailing newlines removed, as $(...) does.
|
|
49
|
+
def rstrip_nl: sub("\n+\\z"; "");
|
|
50
|
+
def no_cr: split("\r") | join("");
|
|
51
|
+
|
|
52
|
+
# `jq -r '<field> // empty'` in a $(...): null and false are empty, other scalars print as JSON.
|
|
53
|
+
def jq_field:
|
|
54
|
+
if . == null or . == false then ""
|
|
55
|
+
elif type == "string" then rstrip_nl
|
|
56
|
+
else tojson | rstrip_nl
|
|
57
|
+
end;
|
|
58
|
+
|
|
59
|
+
# A summary or grant record: \r removed, trailing newlines dropped, frontmatter keys from the lines
|
|
60
|
+
# between the first two `---` lines (first match wins, spaces after `key:` stripped), body from the
|
|
61
|
+
# first non-empty line after the second `---`, trailing newlines dropped. Fewer than two `---`
|
|
62
|
+
# lines give an empty body.
|
|
63
|
+
def parse_record:
|
|
64
|
+
(no_cr | rstrip_nl | split("\n"))
|
|
65
|
+
| reduce .[] as $line ({fences: 0, started: false, fields: {}, body: []};
|
|
66
|
+
if .fences < 2 and $line == "---" then .fences += 1
|
|
67
|
+
elif .fences == 1 then
|
|
68
|
+
($line | split(":")) as $parts
|
|
69
|
+
| if ($parts | length) > 1 and ($parts[0] | length) > 0 and (.fields | has($parts[0]) | not)
|
|
70
|
+
then .fields[$parts[0]] = ($parts[1:] | join(":") | sub("\\A +"; ""))
|
|
71
|
+
else . end
|
|
72
|
+
elif .fences >= 2 and (.started or $line != "") then .started = true | .body += [$line]
|
|
73
|
+
else . end)
|
|
74
|
+
| {fields: .fields, body: (.body | join("\n") | rstrip_nl)};
|
|
75
|
+
|
|
76
|
+
# A bracketed header value: `]` removed, then empty when it is "unknown".
|
|
77
|
+
def known: (. // "") | split("]") | join("") | if . == "unknown" then "" else . end;
|
|
78
|
+
|
|
79
|
+
def header_tail:
|
|
80
|
+
(.compacted_at | known) as $at | (.branch | known) as $branch
|
|
81
|
+
| (if $at != "" then ", compacted \($at)" else "" end)
|
|
82
|
+
+ (if $branch != "" then " on branch \($branch)" else "" end);
|
|
83
|
+
|
|
84
|
+
# The git capture as an object; anything unparseable or not an object reads as {}.
|
|
85
|
+
def capture($raw):
|
|
86
|
+
(try ($raw | fromjson) catch null) | if type == "object" then . else {} end;
|
|
87
|
+
|
|
88
|
+
def is_repo: (.is_git_repo | jq_field) == "true";
|
|
89
|
+
|
|
90
|
+
def render_git($raw; $missing):
|
|
91
|
+
if $missing then "Git context unavailable for this session."
|
|
92
|
+
else capture($raw)
|
|
93
|
+
| if is_repo | not then "Not a git repository."
|
|
94
|
+
else
|
|
95
|
+
((if (.commits | type) == "array" then .commits else [] end)
|
|
96
|
+
| map(jq_field) | map(select(. != "")) | .[0:5] | join(" · ")) as $commits
|
|
97
|
+
| (.stash_count | jq_field) as $stash
|
|
98
|
+
| "Git context — branch: \(.branch | jq_field | if . == "" then "unknown" else . end) | \(.sha | jq_field | if . == "" then "unknown" else . end)\n"
|
|
99
|
+
+ "Last commits: \(if $commits == "" then "none" else $commits end)\n"
|
|
100
|
+
+ "Uncommitted files: \(.uncommitted_count | jq_field | if . == "" then "0" else . end)"
|
|
101
|
+
+ (if ($stash | test("\\A[0-9]+\\z")) and ($stash | tonumber) > 0 then " | Stashed: \($stash)" else "" end)
|
|
102
|
+
end
|
|
103
|
+
end;
|
|
104
|
+
|
|
105
|
+
def git_title($raw; $missing):
|
|
106
|
+
if $missing then null
|
|
107
|
+
else capture($raw)
|
|
108
|
+
| if is_repo then
|
|
109
|
+
"\(.branch | jq_field | if . == "" then "unknown" else . end) — \(.sha | jq_field | if . == "" then "unknown" else . end)"
|
|
110
|
+
else "no-git" end
|
|
111
|
+
end;
|
|
112
|
+
|
|
113
|
+
# null when the body is empty: an empty summary renders nothing.
|
|
114
|
+
def render_summary($size):
|
|
115
|
+
parse_record
|
|
116
|
+
| if .body == "" then null
|
|
117
|
+
else (.body | length) as $n
|
|
118
|
+
| (if $n > summary_cap
|
|
119
|
+
then .body[0:summary_cap] + "\n[summary truncated: first \(summary_cap) of \(if $size > summary_read then "at least " else "" end)\($n) characters]"
|
|
120
|
+
else .body end) as $shown
|
|
121
|
+
| "\n\n[Prior session summary\(.fields | header_tail)]\n\($shown)"
|
|
122
|
+
end;
|
|
123
|
+
|
|
124
|
+
def render_grant: "[Granted summary from lineage \(.fields.from_lineage // "")\(.fields | header_tail)]\n\(.body)";
|
|
125
|
+
|
|
126
|
+
def render_pointer($max): "[Granted summary \(.fields.summary_id // "") withheld: \(.body | utf8bytelength) bytes exceed the \($max)-byte context budget]";
|
|
127
|
+
|
|
128
|
+
def join_order: ["git", "summary", "warning", "grant", "nudge"];
|
|
129
|
+
|
|
130
|
+
# IC-002 join: summary drops its leading blank line when nothing precedes it; grant and nudge gain
|
|
131
|
+
# one when something does.
|
|
132
|
+
def join_segments:
|
|
133
|
+
. as $p
|
|
134
|
+
| reduce (join_order[] | select($p[.] != null)) as $s ("";
|
|
135
|
+
if $s == "summary" then (if . == "" then . + ($p[$s] | .[2:]) else . + $p[$s] end)
|
|
136
|
+
elif $s == "grant" or $s == "nudge" then (if . == "" then "" else . + "\n\n" end) + $p[$s]
|
|
137
|
+
else . + $p[$s] end);
|
|
138
|
+
|
|
139
|
+
def render($plan; $in):
|
|
140
|
+
($plan.actions // {}) as $a
|
|
141
|
+
| $plan.maxBytes as $max
|
|
142
|
+
| if ($max | type) != "number" or $max <= 0 or ($max | floor) != $max then error("plan.maxBytes must be a positive integer") else . end
|
|
143
|
+
| ($plan.absent // []) as $absent
|
|
144
|
+
| def present($k): ($absent | map(select(. == $k)) | length) == 0;
|
|
145
|
+
({}
|
|
146
|
+
| if $a.git == "include" then .git = render_git($in.git; present("git") | not) else . end
|
|
147
|
+
| if $a.summary == "truncate" and present("summary")
|
|
148
|
+
then ($in.summary | render_summary($plan.summaryBytes // ($in.summary | utf8bytelength))) as $s
|
|
149
|
+
| if $s != null then .summary = $s else . end
|
|
150
|
+
else . end
|
|
151
|
+
| if $a.warning == "include" and present("warning") then .warning = "\n[Prior session warning: \($in.warning | rstrip_nl)]" else . end
|
|
152
|
+
| if $a.nudge == "include" and ($in.nudge | type) == "string" and $in.nudge != "" then .nudge = $in.nudge else . end
|
|
153
|
+
) as $optional
|
|
154
|
+
| (if ($a.grant == "whole" or $a.grant == "pointer") and present("grant")
|
|
155
|
+
then ($in.grant | parse_record) as $r
|
|
156
|
+
| ($r | render_grant) as $whole
|
|
157
|
+
| if $a.grant == "whole" and ($whole | utf8bytelength) <= $max
|
|
158
|
+
then {selected: {grant: $whole}, refused: false}
|
|
159
|
+
else {selected: {grant: ($r | render_pointer($max))}, refused: true} end
|
|
160
|
+
else {selected: {}, refused: false} end)
|
|
161
|
+
| . + {dropped: []}
|
|
162
|
+
| reduce ("git", "summary", "warning", "nudge") as $s (.;
|
|
163
|
+
if $optional[$s] == null then .
|
|
164
|
+
elif (.selected + {($s): $optional[$s]} | join_segments | utf8bytelength) <= $max then .selected[$s] = $optional[$s]
|
|
165
|
+
else .dropped += [$s] end)
|
|
166
|
+
| (.selected | join_segments) as $text
|
|
167
|
+
| {
|
|
168
|
+
text: (if $text == "" then null else $text end),
|
|
169
|
+
bytes: ($text | utf8bytelength),
|
|
170
|
+
refused: .refused,
|
|
171
|
+
dropped: .dropped,
|
|
172
|
+
segments: [join_order[] as $s | select(.selected[$s] != null) | $s]
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
def response($plan; $in; $result):
|
|
176
|
+
if $result.text == null then {}
|
|
177
|
+
else
|
|
178
|
+
(if $plan.harness != "codex" and $plan.title == true and ($plan.actions // {}).git == "include"
|
|
179
|
+
then git_title($in.git; ($plan.absent // []) | map(select(. == "git")) | length > 0) else null end) as $title
|
|
180
|
+
| {"hookSpecificOutput": ({"hookEventName": "UserPromptSubmit", "additionalContext": $result.text}
|
|
181
|
+
+ (if $title != null and $title != "" then {"sessionTitle": $title} else {} end))}
|
|
182
|
+
end;
|
|
183
|
+
|
|
184
|
+
# Rider R1 (091): the response exactly as jq's default pretty print writes it (indent 2, insertion
|
|
185
|
+
# order, the same string escaping as tojson), without the trailing newline. The response shape is
|
|
186
|
+
# fixed: {} or one hookSpecificOutput object of string values.
|
|
187
|
+
def pretty_response:
|
|
188
|
+
if . == {} then "{}"
|
|
189
|
+
else "{\n \"hookSpecificOutput\": {\n"
|
|
190
|
+
+ ([.hookSpecificOutput | to_entries[] | " \(.key | tojson): \(.value | tojson)"] | join(",\n"))
|
|
191
|
+
+ "\n }\n}"
|
|
192
|
+
end;
|
|
193
|
+
|
|
194
|
+
# 8 lowercase hex of h = (h*31 + c) mod 2^32 over the code points of the pretty response. Every step
|
|
195
|
+
# stays below 2^38, exact in doubles; mod and the nibbles use floor, not %, which casts to int on old jq.
|
|
196
|
+
def response_digest:
|
|
197
|
+
(pretty_response | explode | reduce .[] as $c (0; (. * 31 + $c) | . - ((. / 4294967296) | floor) * 4294967296)) as $h
|
|
198
|
+
| [268435456, 16777216, 1048576, 65536, 4096, 256, 16, 1]
|
|
199
|
+
| map(($h / . | floor) - (($h / . / 16) | floor) * 16)
|
|
200
|
+
| map("0123456789abcdef"[.:. + 1]) | join("");
|
|
201
|
+
|
|
202
|
+
{git: $git, summary: $summary, grant: $grant, warning: $warning, nudge: $nudge} as $in
|
|
203
|
+
| render($plan; $in) as $result
|
|
204
|
+
| ($plan.mode // "response") as $mode
|
|
205
|
+
| if $mode == "render" then $result
|
|
206
|
+
elif $mode == "hook" then
|
|
207
|
+
response($plan; $in; $result) as $response
|
|
208
|
+
| ($result.dropped | join("\t")), (if $plan.digest == true then $response | response_digest else "" end), $response
|
|
209
|
+
else response($plan; $in; $result) end
|
|
@@ -0,0 +1,383 @@
|
|
|
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
|
+
# emission.sh — Canonical Policy Library: the hook emission adapter and the emission ledger
|
|
4
|
+
# (feature 078, IC-004, IC-015; DEC-015, DEC-016, DEC-017).
|
|
5
|
+
#
|
|
6
|
+
# The prompt path's emission, from the state bits to the response bytes. arbitrate() in
|
|
7
|
+
# src/runtime/context/arbitrate.js is the reference for the rules; this file reads the compiled
|
|
8
|
+
# table (emission-rules.sh), claims through summary-store.sh and renders with one jq process running
|
|
9
|
+
# emission.jq. It starts no Node or doflow-run process.
|
|
10
|
+
#
|
|
11
|
+
# Sourcing this file defines functions only: no option, no variable, no trap, no output. bash 3.2
|
|
12
|
+
# safe and safe under the caller's `set -euo pipefail`. The caller has sourced lib.sh (which sources
|
|
13
|
+
# identity.sh), summary-store.sh and emission-rules.sh; a missing one is a fault.
|
|
14
|
+
#
|
|
15
|
+
# Store, under SESSION_DIR (lib.sh; session-env/sessions; empty with no config home, which prints
|
|
16
|
+
# the silent response with reason silent:no-home):
|
|
17
|
+
# <unitId>/injected one-shot bootstrap slot, created exclusively (set -C) by the first caller
|
|
18
|
+
# <unitId>/emitted the IC-015 ledger: <ISO-8601 UTC>\t<kind>\t<key>\t<outcome>\t<caller>, one
|
|
19
|
+
# line per (kind, key, outcome), appended only when that triple is absent
|
|
20
|
+
#
|
|
21
|
+
# Faults: any claim or render failure prints the silent response ({} and a newline) and records
|
|
22
|
+
# `doflow_capture_failure user-prompt-submit emission-<point>`, point one of deps, unit, slot,
|
|
23
|
+
# summary, grant, warning, render; a failed ledger append records emission-ledger and keeps the
|
|
24
|
+
# rendered response. A fault after the slot claim releases the slot this call created, so the next
|
|
25
|
+
# prompt bootstraps again (DEC-038); a fault in a later claim renames the summary, grant and warning
|
|
26
|
+
# already claimed back into place (`mv -n`, never over a newer file). A claim that fails because its
|
|
27
|
+
# source vanished (another caller won it) is no fault: that segment counts as absent and the
|
|
28
|
+
# bootstrap continues. A claimed summary or grant whose render fails is not restored.
|
|
29
|
+
#
|
|
30
|
+
# Ledger outcomes besides refused:mandatory-over-budget: `optional-dropped <key> over-budget`, one
|
|
31
|
+
# per optional segment the budget dropped (key: the summary id for summary, else the segment name;
|
|
32
|
+
# DEC-041). A dropped nudge leaves the session unmarked, so a later prompt can nudge.
|
|
33
|
+
|
|
34
|
+
# _doflow_em_capture <point>: one failure-capture line, never on stdout or stderr.
|
|
35
|
+
_doflow_em_capture() {
|
|
36
|
+
local dir
|
|
37
|
+
_DOFLOW_SRC=${BASH_SOURCE[0]//\\//}; case $_DOFLOW_SRC in */*) dir=${_DOFLOW_SRC%/*} ;; *) dir=. ;; esac
|
|
38
|
+
if declare -F doflow_capture_failure >/dev/null 2>&1; then
|
|
39
|
+
( doflow_capture_failure user-prompt-submit "emission-$1" ) >/dev/null 2>&1 || true
|
|
40
|
+
else
|
|
41
|
+
( . "$dir/capture-failure.sh" && doflow_capture_failure user-prompt-submit "emission-$1" ) >/dev/null 2>&1 || true
|
|
42
|
+
fi
|
|
43
|
+
return 0
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# _doflow_em_restore <claimed> <original>: rename a claimed file back unless a newer one took its
|
|
47
|
+
# place, in which case the claimed copy is the older and is removed.
|
|
48
|
+
_doflow_em_restore() {
|
|
49
|
+
[ -n "$1" ] && [ -e "$1" ] || return 0
|
|
50
|
+
mv -n "$1" "$2" 2>/dev/null || true
|
|
51
|
+
[ ! -e "$1" ] || rm -f "$1" 2>/dev/null || true
|
|
52
|
+
return 0
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# _doflow_em_release: undo this call's claims before a fault response, from the caller's locals
|
|
56
|
+
# (made, dir, sumfile, sumsrc, claimed_grant, grantsrc, warnclaimed, warnfile). The slot goes last.
|
|
57
|
+
_doflow_em_release() {
|
|
58
|
+
[ "$sumfile" = /dev/null ] || _doflow_em_restore "$sumfile" "$sumsrc"
|
|
59
|
+
[ -z "$claimed_grant" ] || _doflow_em_restore "$claimed_grant" "$grantsrc"
|
|
60
|
+
[ -z "$warnclaimed" ] || _doflow_em_restore "$warnclaimed" "$warnfile"
|
|
61
|
+
sumfile=/dev/null claimed_grant="" warnclaimed=""
|
|
62
|
+
if [ "$made" = 1 ]; then rm -f "$dir/injected" 2>/dev/null || true; made=0; fi
|
|
63
|
+
return 0
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
# _doflow_em_measure <response> <digest>: for the response printed as `printf '%s\n'`, DF_EMIT_BYTES
|
|
67
|
+
# (its byte length under LC_ALL=C, newline included) and DF_EMIT_DIGEST (the renderer's digest
|
|
68
|
+
# header line when it is 8 lowercase hex, else empty). Rider R1 (091): metadata for the hook line
|
|
69
|
+
# only; builtins only, no response byte leaves this function, and nothing reaches stdout.
|
|
70
|
+
_doflow_em_measure() {
|
|
71
|
+
local LC_ALL=C
|
|
72
|
+
DF_EMIT_BYTES=$(( ${#1} + 1 ))
|
|
73
|
+
DF_EMIT_DIGEST=""
|
|
74
|
+
case "${2:-}" in
|
|
75
|
+
[0123456789abcdef][0123456789abcdef][0123456789abcdef][0123456789abcdef][0123456789abcdef][0123456789abcdef][0123456789abcdef][0123456789abcdef]) DF_EMIT_DIGEST=$2 ;;
|
|
76
|
+
esac
|
|
77
|
+
return 0
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
# _doflow_em_silent <point>: the fault response.
|
|
81
|
+
_doflow_em_silent() {
|
|
82
|
+
_doflow_em_capture "$1"
|
|
83
|
+
DF_EMIT_REASON="fault:$1"
|
|
84
|
+
printf '{}\n'
|
|
85
|
+
return 0
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
# _doflow_em_seen <ledger file> <kind> <key> <outcome>: 0 when the triple is already recorded.
|
|
89
|
+
_doflow_em_seen() {
|
|
90
|
+
local ts kind key outcome caller
|
|
91
|
+
[ -f "$1" ] || return 1
|
|
92
|
+
{
|
|
93
|
+
while IFS=$'\t' read -r ts kind key outcome caller || [ -n "$ts" ]; do
|
|
94
|
+
if [ "$kind" = "$2" ] && [ "$key" = "$3" ] && [ "$outcome" = "$4" ]; then return 0; fi
|
|
95
|
+
ts=""
|
|
96
|
+
done
|
|
97
|
+
} 2>/dev/null <"$1"
|
|
98
|
+
return 1
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
# _doflow_em_ledger_add <unit dir> <kind> <key> <outcome> <caller>: append one IC-015 line unless
|
|
102
|
+
# (kind, key, outcome) is already recorded. Returns 1 when the line could not be written.
|
|
103
|
+
_doflow_em_ledger_add() {
|
|
104
|
+
local file="$1/emitted" ts
|
|
105
|
+
if _doflow_em_seen "$file" "$2" "$3" "$4"; then return 0; fi
|
|
106
|
+
mkdir -p "$1" 2>/dev/null || return 1
|
|
107
|
+
ts=$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null) || return 1
|
|
108
|
+
printf '%s\t%s\t%s\t%s\t%s\n' "$ts" "$2" "$3" "$4" "$5" >>"$file" 2>/dev/null || return 1
|
|
109
|
+
return 0
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
# _doflow_em_rule <event.segment> <vector>: prints nothing; sets DF_ACTION (skip on any failure).
|
|
113
|
+
_doflow_em_rule() {
|
|
114
|
+
DF_ACTION=skip
|
|
115
|
+
doflow_emission_rule "$1" "$2" || DF_ACTION=skip
|
|
116
|
+
return 0
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
# _doflow_em_actions: the prompt segments' actions from the caller's state locals (role, kind,
|
|
120
|
+
# bootstrapped, subagent, own, grant, refused, warning, nudge_state), vectors in IC-002 order.
|
|
121
|
+
_doflow_em_actions() {
|
|
122
|
+
_doflow_em_rule prompt.slot "$bootstrapped"; a_slot=$DF_ACTION
|
|
123
|
+
_doflow_em_rule prompt.git "$role:$bootstrapped"; a_git=$DF_ACTION
|
|
124
|
+
_doflow_em_rule prompt.summary "$role:$kind:$subagent:$bootstrapped:$own"; a_summary=$DF_ACTION
|
|
125
|
+
_doflow_em_rule prompt.warning "$role:$subagent:$bootstrapped:$warning"; a_warning=$DF_ACTION
|
|
126
|
+
_doflow_em_rule prompt.grant "$subagent:$grant:$refused"; a_grant=$DF_ACTION
|
|
127
|
+
_doflow_em_rule prompt.nudge "$role:$nudge_state"; a_nudge=$DF_ACTION
|
|
128
|
+
return 0
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
# doflow_emit_prompt <session_id> <cwd> <payload_title> <subagent> <nudge_text>
|
|
132
|
+
# IC-004 steps 2 to 8 for one UserPromptSubmit event; prints the response on stdout and returns 0.
|
|
133
|
+
# session_id the payload session_id; empty prints nothing (the 0fcd973 early exit)
|
|
134
|
+
# cwd the payload cwd
|
|
135
|
+
# payload_title the payload session_title; non-empty suppresses sessionTitle
|
|
136
|
+
# subagent non-empty when the payload is a native subagent prompt (agent_id or agent_type)
|
|
137
|
+
# nudge_text the nudge message when the classifier earned it, else empty; the session is
|
|
138
|
+
# marked nudged here, only once the nudge rendered (DEC-041)
|
|
139
|
+
# Reads DOFLOW_AGENT (harness; sessionTitle on every harness but codex, DEC-030),
|
|
140
|
+
# DOFLOW_CONTEXT_MAX_BYTES (budget, default DF_RULES_MAX_BYTES_DEFAULT or 16384), SESSION_DIR and the
|
|
141
|
+
# launcher variables through doflow_identity. Sets DF_ID_* (doflow_identity), DF_EMIT_REASON (emit,
|
|
142
|
+
# refused:mandatory-over-budget, silent:already-bootstrapped, silent:unchanged, silent:no-home or
|
|
143
|
+
# fault:<point>), DF_EMIT_BYTES and DF_EMIT_DIGEST (_doflow_em_measure for emit and refused:*, else 0
|
|
144
|
+
# and empty) and DF_EMIT_NUDGE (rendered or empty); when the nudge was rendered it calls
|
|
145
|
+
# `nudge_mark <session> nudged` and, when contract.sh is loaded, `doflow_decide advisory prompt-nudge`.
|
|
146
|
+
doflow_emit_prompt() {
|
|
147
|
+
local session="${1:-}" cwd="${2:-}" payload_title="${3:-}" nudge="${5:-}"
|
|
148
|
+
local subagent=false nudge_state=none role kind bootstrapped=false own=none grant=none refused=false
|
|
149
|
+
local warning=false legacy=false a_slot a_git a_summary a_warning a_grant a_nudge
|
|
150
|
+
local sessions dir unit max harness here renderer project="" warnfile="" warntext="" had_c=0 made=0
|
|
151
|
+
local gitfile sumfile=/dev/null grantfile=/dev/null claimed_grant="" absent="" plan out
|
|
152
|
+
local refusal=refused:mandatory-over-budget title=false sumsrc="" sumid="" grantsrc="" warnclaimed=""
|
|
153
|
+
local dropped rest seg key digest=false
|
|
154
|
+
DF_EMIT_REASON=""
|
|
155
|
+
DF_EMIT_BYTES=0
|
|
156
|
+
DF_EMIT_DIGEST=""
|
|
157
|
+
DF_EMIT_NUDGE=""
|
|
158
|
+
[ -n "$session" ] || return 0
|
|
159
|
+
[ -z "${4:-}" ] || subagent=true
|
|
160
|
+
[ -z "$nudge" ] || nudge_state=nudge
|
|
161
|
+
if ! declare -F doflow_identity doflow_emission_rule doflow_summary_peek doflow_summary_claim \
|
|
162
|
+
doflow_grant_peek doflow_grant_claim doflow_summary_adopt_legacy doflow_summary_legacy_present ensure_project_dir >/dev/null 2>&1; then
|
|
163
|
+
_doflow_em_silent deps
|
|
164
|
+
return 0
|
|
165
|
+
fi
|
|
166
|
+
_DOFLOW_SRC=${BASH_SOURCE[0]//\\//}; case $_DOFLOW_SRC in */*) here=${_DOFLOW_SRC%/*} ;; *) here=. ;; esac
|
|
167
|
+
# The renderer beside the entry policy when it set _DOFLOW_POLDIR, else beside this file.
|
|
168
|
+
renderer=$here/emission.jq
|
|
169
|
+
[ -z "${_DOFLOW_POLDIR:-}" ] || [ ! -f "$_DOFLOW_POLDIR/emission.jq" ] || renderer=$_DOFLOW_POLDIR/emission.jq
|
|
170
|
+
|
|
171
|
+
# Step 2: identity, and the unit whose slot and ledger this event uses. The session id names
|
|
172
|
+
# git-context.json's directory for every lineage kind, so it is a path segment or a fault (DEC-042).
|
|
173
|
+
if ! [[ $session =~ ^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$ ]]; then
|
|
174
|
+
_doflow_em_silent unit
|
|
175
|
+
return 0
|
|
176
|
+
fi
|
|
177
|
+
doflow_identity "$session" "$cwd"
|
|
178
|
+
role=$DF_ID_ROLE
|
|
179
|
+
kind=$DF_ID_KIND
|
|
180
|
+
unit=$DF_ID_UNIT
|
|
181
|
+
if ! [[ $unit =~ ^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$ ]]; then
|
|
182
|
+
_doflow_em_silent unit
|
|
183
|
+
return 0
|
|
184
|
+
fi
|
|
185
|
+
# IC-010: lib.sh's SESSION_DIR is empty with no config home; there is no store, so nothing is
|
|
186
|
+
# claimed, rendered or recorded (src/runtime/context/arbitrate.js answers silent:no-home too).
|
|
187
|
+
sessions="${SESSION_DIR:-}"
|
|
188
|
+
if [ -z "$sessions" ]; then
|
|
189
|
+
DF_EMIT_REASON=silent:no-home
|
|
190
|
+
printf '{}\n'
|
|
191
|
+
return 0
|
|
192
|
+
fi
|
|
193
|
+
dir="$sessions/$unit"
|
|
194
|
+
max="${DOFLOW_CONTEXT_MAX_BYTES:-}"
|
|
195
|
+
[[ $max =~ ^[1-9][0-9]{0,9}$ ]] || max="${DF_RULES_MAX_BYTES_DEFAULT:-16384}"
|
|
196
|
+
harness="${DOFLOW_AGENT:-}"
|
|
197
|
+
[[ $harness =~ ^[A-Za-z0-9._-]{0,40}$ ]] || harness=unknown
|
|
198
|
+
|
|
199
|
+
# Step 3: state bits. The project directory, the legacy file, the warning and the owner summary
|
|
200
|
+
# only matter before the bootstrap, so a later prompt reads none of them.
|
|
201
|
+
if [ -e "$dir/injected" ] || [ -L "$dir/injected" ]; then bootstrapped=true; fi
|
|
202
|
+
if [ "$bootstrapped" = false ]; then
|
|
203
|
+
if [ -n "$cwd" ]; then project=$(ensure_project_dir "$cwd" 2>/dev/null) || project=""; fi
|
|
204
|
+
if [ -n "$project" ]; then
|
|
205
|
+
if doflow_summary_legacy_present "$cwd"; then legacy=true; fi
|
|
206
|
+
warnfile="$project/uncommitted-warning.txt"
|
|
207
|
+
if [ -f "$warnfile" ]; then warning=true; fi
|
|
208
|
+
fi
|
|
209
|
+
doflow_summary_peek "$DF_ID_WORKTREE" "$DF_ID_RUN" "$DF_ID_LINEAGE" "$session"
|
|
210
|
+
own=$DF_SUMMARY_OWN
|
|
211
|
+
fi
|
|
212
|
+
# Grants are addressed to launched units only: local, solo and main lineages are shared by
|
|
213
|
+
# unrelated sessions (DEC-039).
|
|
214
|
+
if [ "$kind" = launched ]; then
|
|
215
|
+
doflow_grant_peek "$DF_ID_RUN" "$DF_ID_LINEAGE" "$max"
|
|
216
|
+
grant=$DF_GRANT
|
|
217
|
+
fi
|
|
218
|
+
if [ "$grant" = over ] && _doflow_em_seen "$dir/emitted" grant "$DF_GRANT_ID" "$refusal"; then refused=true; fi
|
|
219
|
+
|
|
220
|
+
# Step 4: the legacy adoption, then the owner summary bit again.
|
|
221
|
+
_doflow_em_rule legacy-adopt.legacy "$role:$kind:$subagent:$bootstrapped:$legacy"
|
|
222
|
+
if [ "$DF_ACTION" = adopt ]; then
|
|
223
|
+
doflow_summary_adopt_legacy "$cwd" "$DF_ID_WORKTREE" || true
|
|
224
|
+
doflow_summary_peek "$DF_ID_WORKTREE" "$DF_ID_RUN" "$DF_ID_LINEAGE" "$session"
|
|
225
|
+
own=$DF_SUMMARY_OWN
|
|
226
|
+
fi
|
|
227
|
+
|
|
228
|
+
# Step 5: one lookup per segment.
|
|
229
|
+
_doflow_em_actions
|
|
230
|
+
|
|
231
|
+
# Step 6: claims in the order slot, summary, grant, warning. EEXIST on the slot means another
|
|
232
|
+
# caller bootstrapped this unit: the segments are looked up again as bootstrapped, which makes
|
|
233
|
+
# every bootstrap-only one skip.
|
|
234
|
+
if [ "$a_slot" = claim ]; then
|
|
235
|
+
if ! mkdir -p "$dir" 2>/dev/null; then
|
|
236
|
+
_doflow_em_silent slot
|
|
237
|
+
return 0
|
|
238
|
+
fi
|
|
239
|
+
case $- in *C*) had_c=1 ;; esac
|
|
240
|
+
set -C
|
|
241
|
+
{ : >"$dir/injected"; } 2>/dev/null && made=1
|
|
242
|
+
[ "$had_c" = 1 ] || set +C
|
|
243
|
+
if [ "$made" = 0 ]; then
|
|
244
|
+
if [ -e "$dir/injected" ] || [ -L "$dir/injected" ]; then
|
|
245
|
+
bootstrapped=true
|
|
246
|
+
_doflow_em_actions
|
|
247
|
+
else
|
|
248
|
+
_doflow_em_silent slot
|
|
249
|
+
return 0
|
|
250
|
+
fi
|
|
251
|
+
fi
|
|
252
|
+
fi
|
|
253
|
+
# A lost claim (the source gone, or no longer a pending summary) makes that segment absent and the
|
|
254
|
+
# segments are looked up again; only a failure with the source still in place is a fault.
|
|
255
|
+
if [ "$a_summary" = truncate ]; then
|
|
256
|
+
if doflow_summary_claim "$DF_ID_WORKTREE" "$DF_ID_RUN" "$DF_ID_LINEAGE" "$unit" "$session"; then
|
|
257
|
+
sumfile=$DF_SUMMARY_CLAIMED
|
|
258
|
+
sumsrc=$DF_SUMMARY_SCOPE/summary.md
|
|
259
|
+
sumid=$DF_SUMMARY_SID
|
|
260
|
+
else
|
|
261
|
+
doflow_summary_peek "$DF_ID_WORKTREE" "$DF_ID_RUN" "$DF_ID_LINEAGE" "$session"
|
|
262
|
+
if [ "$DF_SUMMARY_OWN" = pending ]; then
|
|
263
|
+
_doflow_em_release
|
|
264
|
+
_doflow_em_silent summary
|
|
265
|
+
return 0
|
|
266
|
+
fi
|
|
267
|
+
own=none
|
|
268
|
+
_doflow_em_actions
|
|
269
|
+
fi
|
|
270
|
+
fi
|
|
271
|
+
if [ "$a_grant" = whole ]; then
|
|
272
|
+
grantsrc=$DF_GRANT_FILE
|
|
273
|
+
if doflow_grant_claim "$DF_ID_RUN" "$DF_ID_LINEAGE" "$unit" "$max"; then
|
|
274
|
+
grantfile=$DF_GRANT_CLAIMED
|
|
275
|
+
claimed_grant=$grantfile
|
|
276
|
+
grantsrc=$DF_GRANT_FILE
|
|
277
|
+
elif [ -n "$grantsrc" ] && [ -f "$grantsrc" ]; then
|
|
278
|
+
_doflow_em_release
|
|
279
|
+
_doflow_em_silent grant
|
|
280
|
+
return 0
|
|
281
|
+
else
|
|
282
|
+
grant=none
|
|
283
|
+
_doflow_em_actions
|
|
284
|
+
fi
|
|
285
|
+
elif [ "$a_grant" = pointer ]; then
|
|
286
|
+
# An over-budget grant is never claimed: it renders as its pointer from where it lies.
|
|
287
|
+
grantfile=$DF_GRANT_FILE
|
|
288
|
+
fi
|
|
289
|
+
# The warning is claimed by rename, so two first prompts deliver it once (DEC-040); read -d ''
|
|
290
|
+
# returns non-zero at EOF, which is accepted only once the claimed file is known to be readable.
|
|
291
|
+
if [ "$a_warning" = include ]; then
|
|
292
|
+
if mv "$warnfile" "$warnfile.claimed.$unit" 2>/dev/null; then
|
|
293
|
+
warnclaimed=$warnfile.claimed.$unit
|
|
294
|
+
if [ -r "$warnclaimed" ] && [ -f "$warnclaimed" ]; then
|
|
295
|
+
IFS= read -r -d '' warntext 2>/dev/null <"$warnclaimed" || true
|
|
296
|
+
else
|
|
297
|
+
_doflow_em_release
|
|
298
|
+
_doflow_em_silent warning
|
|
299
|
+
return 0
|
|
300
|
+
fi
|
|
301
|
+
elif [ -e "$warnfile" ] || [ -L "$warnfile" ]; then
|
|
302
|
+
_doflow_em_release
|
|
303
|
+
_doflow_em_silent warning
|
|
304
|
+
return 0
|
|
305
|
+
else
|
|
306
|
+
warning=false
|
|
307
|
+
_doflow_em_actions
|
|
308
|
+
fi
|
|
309
|
+
fi
|
|
310
|
+
|
|
311
|
+
# Nothing that renders: silence without a jq process.
|
|
312
|
+
case "$a_git $a_summary $a_warning $a_grant $a_nudge" in
|
|
313
|
+
*include* | *truncate* | *whole* | *pointer*) ;;
|
|
314
|
+
*)
|
|
315
|
+
if [ "$bootstrapped" = true ]; then DF_EMIT_REASON=silent:already-bootstrapped; else DF_EMIT_REASON=silent:unchanged; fi
|
|
316
|
+
printf '{}\n'
|
|
317
|
+
return 0
|
|
318
|
+
;;
|
|
319
|
+
esac
|
|
320
|
+
|
|
321
|
+
# Step 7: one jq process. A missing file is /dev/null and named in plan.absent.
|
|
322
|
+
gitfile="$sessions/$session/git-context.json"
|
|
323
|
+
if [ ! -f "$gitfile" ]; then gitfile=/dev/null; absent='"git"'; fi
|
|
324
|
+
[ "$sumfile" != /dev/null ] || absent="${absent:+$absent,}\"summary\""
|
|
325
|
+
[ "$grantfile" != /dev/null ] || absent="${absent:+$absent,}\"grant\""
|
|
326
|
+
[ "$a_warning" = include ] || absent="${absent:+$absent,}\"warning\""
|
|
327
|
+
[ -n "$payload_title" ] || title=true
|
|
328
|
+
plan="{\"actions\":{\"git\":\"$a_git\",\"summary\":\"$a_summary\",\"warning\":\"$a_warning\",\"grant\":\"$a_grant\",\"nudge\":\"$a_nudge\"},\"maxBytes\":$max,\"harness\":\"$harness\",\"title\":$title,\"absent\":[$absent]"
|
|
329
|
+
[ "$sumfile" = /dev/null ] || plan="$plan,\"summaryBytes\":${DF_SUMMARY_BYTES:-0}"
|
|
330
|
+
# Rider R1: the renderer hashes the response only when the bridge will write it (telemetry on, fd set).
|
|
331
|
+
[ "${DOFLOW_TELEMETRY:-}" = off ] || [ -z "${DOFLOW_DECISION_FD:-}" ] || digest=true
|
|
332
|
+
plan="$plan,\"digest\":$digest,\"mode\":\"hook\"}"
|
|
333
|
+
# Hook mode: the first line is the dropped segment names, tab-joined (possibly empty); the second
|
|
334
|
+
# is the response digest (empty unless plan.digest).
|
|
335
|
+
out=$(jq -n -r -f "$renderer" --argjson plan "$plan" --rawfile git "$gitfile" \
|
|
336
|
+
--rawfile summary "$sumfile" --rawfile grant "$grantfile" --arg warning "$warntext" \
|
|
337
|
+
--arg nudge "$nudge" 2>/dev/null) || out=""
|
|
338
|
+
[ "$sumfile" = /dev/null ] || rm -f "$sumfile" 2>/dev/null || true
|
|
339
|
+
[ -z "$claimed_grant" ] || rm -f "$claimed_grant" 2>/dev/null || true
|
|
340
|
+
[ -z "$warnclaimed" ] || rm -f "$warnclaimed" 2>/dev/null || true
|
|
341
|
+
sumfile=/dev/null claimed_grant="" warnclaimed=""
|
|
342
|
+
case "$out" in *$'\n'*$'\n'?*) ;; *) out="" ;; esac
|
|
343
|
+
if [ -z "$out" ]; then
|
|
344
|
+
_doflow_em_release
|
|
345
|
+
_doflow_em_silent render
|
|
346
|
+
return 0
|
|
347
|
+
fi
|
|
348
|
+
dropped=${out%%$'\n'*}
|
|
349
|
+
out=${out#*$'\n'}
|
|
350
|
+
digest=${out%%$'\n'*}
|
|
351
|
+
out=${out#*$'\n'}
|
|
352
|
+
|
|
353
|
+
# Step 8: the refusal ledger line (key: the grant's summary id), one optional-dropped line per
|
|
354
|
+
# dropped segment, then the nudge record.
|
|
355
|
+
DF_EMIT_REASON=emit
|
|
356
|
+
if [ "$a_grant" = pointer ]; then
|
|
357
|
+
DF_EMIT_REASON=$refusal
|
|
358
|
+
_doflow_em_ledger_add "$dir" grant "$DF_GRANT_ID" "$refusal" hook || _doflow_em_capture ledger
|
|
359
|
+
fi
|
|
360
|
+
rest=$dropped
|
|
361
|
+
while [ -n "$rest" ]; do
|
|
362
|
+
seg=${rest%%$'\t'*}
|
|
363
|
+
case "$rest" in *$'\t'*) rest=${rest#*$'\t'} ;; *) rest="" ;; esac
|
|
364
|
+
[ -n "$seg" ] || continue
|
|
365
|
+
key=$seg
|
|
366
|
+
[ "$seg" != summary ] || [ -z "$sumid" ] || key=$sumid
|
|
367
|
+
[ "$seg" != grant ] || [ -z "${DF_GRANT_ID:-}" ] || key=$DF_GRANT_ID
|
|
368
|
+
_doflow_em_ledger_add "$dir" optional-dropped "$key" over-budget hook || _doflow_em_capture ledger
|
|
369
|
+
done
|
|
370
|
+
if [ "$a_nudge" = include ]; then
|
|
371
|
+
case $'\t'"$dropped"$'\t' in
|
|
372
|
+
*$'\t'nudge$'\t'*) ;;
|
|
373
|
+
*)
|
|
374
|
+
DF_EMIT_NUDGE=rendered
|
|
375
|
+
if declare -F nudge_mark >/dev/null 2>&1; then nudge_mark "$session" nudged || true; fi
|
|
376
|
+
if declare -F doflow_decide >/dev/null 2>&1; then doflow_decide advisory prompt-nudge || true; fi
|
|
377
|
+
;;
|
|
378
|
+
esac
|
|
379
|
+
fi
|
|
380
|
+
_doflow_em_measure "$out" "$digest"
|
|
381
|
+
printf '%s\n' "$out"
|
|
382
|
+
return 0
|
|
383
|
+
}
|