@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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
+
case "${OSTYPE:-}" in msys*|cygwin*|win32*) if command -v jq >/dev/null 2>&1; then jq() { command jq -b "$@"; }; fi ;; esac # native jq.exe ends lines with CRLF unless -b
|
|
2
3
|
# Kiro front door: pre-bash-guard through the shared lifecycle bridge.
|
|
3
4
|
set -euo pipefail
|
|
4
5
|
export DOFLOW_AGENT="${DOFLOW_AGENT:-kiro}"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
+
case "${OSTYPE:-}" in msys*|cygwin*|win32*) if command -v jq >/dev/null 2>&1; then jq() { command jq -b "$@"; }; fi ;; esac # native jq.exe ends lines with CRLF unless -b
|
|
2
3
|
# Kiro front door: pre-implementation-gate through the shared lifecycle bridge.
|
|
3
4
|
set -euo pipefail
|
|
4
5
|
export DOFLOW_AGENT="${DOFLOW_AGENT:-kiro}"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
+
case "${OSTYPE:-}" in msys*|cygwin*|win32*) if command -v jq >/dev/null 2>&1; then jq() { command jq -b "$@"; }; fi ;; esac # native jq.exe ends lines with CRLF unless -b
|
|
2
3
|
# Kiro front door: session-context through the shared lifecycle bridge.
|
|
3
4
|
set -euo pipefail
|
|
4
5
|
export DOFLOW_AGENT="${DOFLOW_AGENT:-kiro}"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
+
case "${OSTYPE:-}" in msys*|cygwin*|win32*) if command -v jq >/dev/null 2>&1; then jq() { command jq -b "$@"; }; fi ;; esac # native jq.exe ends lines with CRLF unless -b
|
|
2
3
|
# Kiro Stop front door: translate the native payload, then render the bridge verdict for its engine.
|
|
3
4
|
set -uo pipefail
|
|
4
5
|
export DOFLOW_AGENT="${DOFLOW_AGENT:-kiro}"
|
|
@@ -138,6 +138,13 @@ export default function (pi) {
|
|
|
138
138
|
captureFailure(policy, 'bridge-missing');
|
|
139
139
|
return { block: true, reason: '[doflow] shared hooks lack the bridge; run: doflow update -t pi' };
|
|
140
140
|
}
|
|
141
|
+
let model;
|
|
142
|
+
try {
|
|
143
|
+
const modelId = ctx.model?.id;
|
|
144
|
+
if (typeof modelId === 'string' && modelId.length > 0) model = modelId;
|
|
145
|
+
} catch {
|
|
146
|
+
// A model getter must not prevent the tool call from reaching the bridge.
|
|
147
|
+
}
|
|
141
148
|
const payload = JSON.stringify({
|
|
142
149
|
type: event.type,
|
|
143
150
|
toolName: event.toolName,
|
|
@@ -146,6 +153,7 @@ export default function (pi) {
|
|
|
146
153
|
input: event.input,
|
|
147
154
|
cwd: ctx.cwd,
|
|
148
155
|
sessionId: ctx.sessionManager?.getSessionId?.() ?? null,
|
|
156
|
+
...(model ? { model } : {}),
|
|
149
157
|
});
|
|
150
158
|
if (Buffer.byteLength(payload) > LIMIT) throw Object.assign(new Error('bridge input exceeded limit'), { failurePoint: 'bridge-input-limit' });
|
|
151
159
|
return await bridgeCall(policy, payload, ctx);
|
|
@@ -22,7 +22,6 @@ tool's official docs (cross-checked against tool source where docs were incomple
|
|
|
22
22
|
|---|---|---|
|
|
23
23
|
| Claude Code | `PreToolUse` | `tool_name` Edit/Write/MultiEdit; `tool_input.file_path` |
|
|
24
24
|
| Codex | `PreToolUse` | `apply_patch` carries paths inside a unified diff under `tool_input.command` — the ONE decoder that must parse patch text |
|
|
25
|
-
| Gemini CLI | `BeforeTool` | also offers `BeforeToolSelection` (pre-selection restriction), which the gate does not use |
|
|
26
25
|
| GitHub Copilot | `preToolUse` | camelCase event names; `permissionRequest` is CLI-only, not fired in the cloud agent |
|
|
27
26
|
| OpenCode | `tool.execute.before` | a pluggable hook method, not a sub-event of the generic `event` hook |
|
|
28
27
|
| Pi | `tool_call` | The live-observed `cli-extension` pre-implementation gate row blocks. Project-interactive and user-interactive rows remain unknown; interactive loading and project-trust behavior were not observed. `tool_execution_start` is observation-only |
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
+
case "${OSTYPE:-}" in msys*|cygwin*|win32*) if command -v jq >/dev/null 2>&1; then jq() { command jq -b "$@"; }; fi ;; esac # native jq.exe ends lines with CRLF unless -b
|
|
2
3
|
# Shared lifecycle bridge. Sourcing defines functions only; it sets no shell options or traps.
|
|
3
|
-
|
|
4
|
+
_DOFLOW_SRC=${BASH_SOURCE[0]//\\//}; case $_DOFLOW_SRC in */*) _DOFLOW_BRIDGE_DIR=${_DOFLOW_SRC%/*} ;; *) _DOFLOW_BRIDGE_DIR=. ;; esac
|
|
4
5
|
|
|
5
6
|
doflow_bridge_failure() {
|
|
6
7
|
local policy="$1" point="$2"
|
|
@@ -47,13 +48,20 @@ doflow_bridge_effect() {
|
|
|
47
48
|
elif [ "$profile" = strict-1 ] && [ "$block" -eq 1 ] && [ "$policy" != stop-check ] && { [ "$outcome" = error ] || [ "$outcome" = unsupported ] || [ "$DF_BRIDGE_CODE" = bridge-spawn ] || [ "$DF_BRIDGE_CODE" = envelope-encode ]; }; then DF_BRIDGE_EFFECT=block
|
|
48
49
|
elif [ "$outcome" = error ] && [ "$DF_BRIDGE_CODE" != bridge-spawn ] && [ -n "$DF_BRIDGE_POINT" ] && [ "$profile" = compat-1 ]; then
|
|
49
50
|
case "$harness:$event" in
|
|
50
|
-
claude:PreToolUse|codex:PreToolUse|
|
|
51
|
+
claude:PreToolUse|codex:PreToolUse|copilot:PreToolUse|pi:tool_call) DF_BRIDGE_EFFECT=block ;;
|
|
51
52
|
antigravity:PreToolUse|antigravity:Stop) case "$DF_BRIDGE_CODE" in signal-*) DF_BRIDGE_EFFECT=proceed;; *) DF_BRIDGE_EFFECT=block;; esac ;;
|
|
52
53
|
kiro:PreToolUse) [ "$DF_BRIDGE_CODE" = exit-2-unmarked ] && DF_BRIDGE_EFFECT=block ;;
|
|
53
54
|
claude:Stop) [ "$DF_BRIDGE_CODE" = exit-2-unmarked ] && DF_BRIDGE_EFFECT=block ;;
|
|
54
|
-
codex:Stop|codex:stop|kiro:Stop|kiro:stop) [ "$DF_BRIDGE_CODE" = exit-2-unmarked ] && DF_BRIDGE_EFFECT=block || DF_BRIDGE_EFFECT=proceed ;;
|
|
55
|
+
codex:Stop|codex:stop|copilot:Stop|kiro:Stop|kiro:stop) [ "$DF_BRIDGE_CODE" = exit-2-unmarked ] && DF_BRIDGE_EFFECT=block || DF_BRIDGE_EFFECT=proceed ;;
|
|
55
56
|
esac
|
|
56
57
|
fi
|
|
58
|
+
# Copilot CLI: a guarded preToolUse policy that errors or cannot decide (jq or the policy script
|
|
59
|
+
# missing, an edit with no decodable target) denies whatever the fault profile; only a policy that
|
|
60
|
+
# answered allow or advisory lets the call through. Copilot-only: every other harness keeps the
|
|
61
|
+
# profile rules above byte for byte.
|
|
62
|
+
if [ "$block" -eq 1 ] && [ "$DF_BRIDGE_EFFECT" = proceed ] && [ "$harness:$event" = copilot:PreToolUse ] && [ "$policy" != stop-check ]; then
|
|
63
|
+
case "$outcome" in error|unsupported) DF_BRIDGE_EFFECT=block ;; esac
|
|
64
|
+
fi
|
|
57
65
|
}
|
|
58
66
|
|
|
59
67
|
doflow_bridge_drops_tool_streams() {
|
|
@@ -62,7 +70,7 @@ doflow_bridge_drops_tool_streams() {
|
|
|
62
70
|
*) return 1 ;;
|
|
63
71
|
esac
|
|
64
72
|
case "$2:$3" in
|
|
65
|
-
claude:PreToolUse|codex:PreToolUse|
|
|
73
|
+
claude:PreToolUse|codex:PreToolUse|copilot:PreToolUse|kiro:PreToolUse|antigravity:PreToolUse|antigravity:Stop|pi:tool_call) return 0 ;;
|
|
66
74
|
*) return 1 ;;
|
|
67
75
|
esac
|
|
68
76
|
}
|
|
@@ -74,20 +82,100 @@ doflow_bridge_drops_tool_stderr() {
|
|
|
74
82
|
doflow_bridge_drops_tool_streams "$1" "$2" "$3"
|
|
75
83
|
}
|
|
76
84
|
|
|
85
|
+
# One hook line (IC-004) for an allowlisted surface, from the encode tuple in DF_TUPLE, appended to
|
|
86
|
+
# <config>/state/runs/telemetry/<UTC date>.jsonl. Builtins only on the path that writes; every step
|
|
87
|
+
# returns 0, and nothing reaches stdout or stderr. Args: harness, normalised event, policy, and the
|
|
88
|
+
# policy's emit record (rider R1, user-prompt-submit only): its reason, byte count and digest are
|
|
89
|
+
# written only when each passes its pattern (a vocabulary member, digits, 8 lowercase hex or null),
|
|
90
|
+
# else all three are null, as they are on every other line.
|
|
91
|
+
doflow_bridge_hook_line() {
|
|
92
|
+
local d="" f="" day="" rest="" duration=null t1="" er=null eb=null eg=null r="" b="" g=""
|
|
93
|
+
[ "${DOFLOW_TELEMETRY:-}" != off ] || return 0
|
|
94
|
+
case "$DF_TUPLE" in ''|'{"dft":1}') return 0 ;; esac
|
|
95
|
+
case "$1:$3:$2" in
|
|
96
|
+
claude:pre-bash-guard:PreToolUse|codex:pre-bash-guard:PreToolUse|kiro:pre-bash-guard:PreToolUse|antigravity:pre-bash-guard:PreToolUse|pi:pre-bash-guard:tool_call|claude:user-prompt-submit:UserPromptSubmit|codex:user-prompt-submit:UserPromptSubmit) ;;
|
|
97
|
+
*) return 0 ;;
|
|
98
|
+
esac
|
|
99
|
+
if [ -n "${DOFLOW_CONFIG_DIR:-}" ]; then
|
|
100
|
+
d=$DOFLOW_CONFIG_DIR
|
|
101
|
+
else
|
|
102
|
+
d=${PWD:-}
|
|
103
|
+
case "$d" in /*) ;; *) return 0 ;; esac
|
|
104
|
+
while :; do
|
|
105
|
+
if [ -d "$d/.doflow" ]; then
|
|
106
|
+
case "$d/" in
|
|
107
|
+
*/.doflow/*) ;;
|
|
108
|
+
*) d=${d%/}/.doflow; break ;;
|
|
109
|
+
esac
|
|
110
|
+
fi
|
|
111
|
+
[ -n "$d" ] && [ "$d" != "/" ] || return 0
|
|
112
|
+
d=${d%/*}
|
|
113
|
+
done
|
|
114
|
+
fi
|
|
115
|
+
[ -d "$d/state" ] || return 0
|
|
116
|
+
day=${DF_TUPLE:17:10}
|
|
117
|
+
case "$day" in [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]) ;; *) return 0 ;; esac
|
|
118
|
+
rest=${DF_TUPLE#\{\"dft\":1,\"date\":\"??????????\",}
|
|
119
|
+
case "$rest" in \"timestamp\":*\}) ;; *) return 0 ;; esac
|
|
120
|
+
if [ -n "$_df_t0" ]; then
|
|
121
|
+
t1=${EPOCHREALTIME:-}; t1=${t1/[.,]/}; _df_t0=${_df_t0/[.,]/}
|
|
122
|
+
case "$t1$_df_t0" in ''|*[!0-9]*) ;; *) duration=$(( (t1 - _df_t0) / 1000 )); [ "$duration" -ge 0 ] || duration=null ;; esac
|
|
123
|
+
fi
|
|
124
|
+
if [ "$3" = user-prompt-submit ] && [ -n "${4:-}" ]; then
|
|
125
|
+
r=${4#*\"reason\":\"}; r=${r%%\"*}
|
|
126
|
+
b=${4#*\"bytes\":}; b=${b%%[,\}]*}
|
|
127
|
+
g=${4#*\"digest\":}; g=${g%%[,\}]*}
|
|
128
|
+
case "$r" in
|
|
129
|
+
emit|refused:mandatory-over-budget|silent:already-bootstrapped|silent:unchanged|silent:no-home|fault:deps|fault:unit|fault:slot|fault:summary|fault:grant|fault:warning|fault:render) ;;
|
|
130
|
+
*) r="" ;;
|
|
131
|
+
esac
|
|
132
|
+
# Explicit character lists: a locale's collation can put capitals inside a range such as [a-f].
|
|
133
|
+
case "$b" in ''|*[!0123456789]*|0?*|???????????*) b="" ;; esac
|
|
134
|
+
case "$g" in
|
|
135
|
+
null|\"[0123456789abcdef][0123456789abcdef][0123456789abcdef][0123456789abcdef][0123456789abcdef][0123456789abcdef][0123456789abcdef][0123456789abcdef]\") ;;
|
|
136
|
+
*) g="" ;;
|
|
137
|
+
esac
|
|
138
|
+
if [ -n "$r" ] && [ -n "$b" ] && [ -n "$g" ]; then er=\"$r\"; eb=$b; eg=$g; fi
|
|
139
|
+
fi
|
|
140
|
+
[ -d "$d/state/runs/telemetry" ] || mkdir "$d/state/runs/telemetry" 2>/dev/null || return 0
|
|
141
|
+
f=$d/state/runs/telemetry/$day.jsonl
|
|
142
|
+
[ ! -e "$f" ] || [ -f "$f" ] || return 0
|
|
143
|
+
{ printf '{"kind":"hook","harness":"%s","event":"%s","policy":"%s","verdict":"%s","duration_ms":%s,"emit_reason":%s,"emit_bytes":%s,"emit_digest":%s,%s\n' "$1" "$2" "$3" "$DF_BRIDGE_OUTCOME" "$duration" "$er" "$eb" "$eg" "$rest" >>"$f"; } 2>/dev/null || true
|
|
144
|
+
return 0
|
|
145
|
+
}
|
|
146
|
+
|
|
77
147
|
doflow_bridge() {
|
|
78
|
-
local harness="${1:-}" event="${2:-}" policy="${3:-}" response_event="" emit="native" payload="" encoded="" policy_path="" captured="" err="" code=0 policy_code=0 record="" reason="" decision_reason="" stdout="" status=0 decision="" line="" spawn_failed=0 encode_failed=0
|
|
148
|
+
local harness="${1:-}" event="${2:-}" policy="${3:-}" response_event="" emit="native" payload="" encoded="" policy_path="" captured="" err="" code=0 policy_code=0 record="" emit_record="" reason="" decision_reason="" stdout="" status=0 decision="" line="" spawn_failed=0 encode_failed=0 _df_t0=""
|
|
149
|
+
DF_TUPLE=""
|
|
150
|
+
if [ "${BASH_VERSINFO[0]:-0}" -ge 5 ] 2>/dev/null; then _df_t0=${EPOCHREALTIME:-}; fi
|
|
79
151
|
. "$_DOFLOW_BRIDGE_DIR/respond.sh"
|
|
80
152
|
doflow_bridge_native_event_var "$harness" "$event"; response_event=$DF_NE
|
|
81
153
|
if [ "${4:-}" = --emit ]; then emit="${5:-native}"; fi
|
|
82
154
|
payload="$(cat)"
|
|
83
155
|
if command -v jq >/dev/null 2>&1; then
|
|
84
|
-
encoded="$(printf '%s\n' "$payload" | DOFLOW_TASK_ID="${DOFLOW_TASK_ID:-}" DOFLOW_RUN_ID="${DOFLOW_RUN_ID:-}" DOFLOW_ATTEMPT="${DOFLOW_ATTEMPT:-}" DOFLOW_SESSION_ROLE="${DOFLOW_SESSION_ROLE:-}" jq -c --arg harness "$harness" --arg event "$event" --arg policy "$policy" --arg mode encode -f "$_DOFLOW_BRIDGE_DIR/envelope.jq" 2>/dev/null)"
|
|
156
|
+
encoded="$(printf '%s\n' "$payload" | DOFLOW_TASK_ID="${DOFLOW_TASK_ID:-}" DOFLOW_RUN_ID="${DOFLOW_RUN_ID:-}" DOFLOW_ATTEMPT="${DOFLOW_ATTEMPT:-}" DOFLOW_LINEAGE_ID="${DOFLOW_LINEAGE_ID:-}" DOFLOW_SESSION_ROLE="${DOFLOW_SESSION_ROLE:-}" DOFLOW_WORKTREE="${DOFLOW_WORKTREE:-}" jq -c --arg harness "$harness" --arg event "$event" --arg policy "$policy" --arg mode encode -f "$_DOFLOW_BRIDGE_DIR/envelope.jq" 2>/dev/null)" || encode_failed=1
|
|
157
|
+
# The telemetry tuple is the first line; without an envelope line after it there is no envelope.
|
|
158
|
+
case "$encoded" in
|
|
159
|
+
'{"dft"'*)
|
|
160
|
+
DF_TUPLE=${encoded%%$'\n'*}
|
|
161
|
+
case "$encoded" in *$'\n'*) encoded=${encoded#*$'\n'} ;; *) encoded="" ;; esac
|
|
162
|
+
;;
|
|
163
|
+
esac
|
|
164
|
+
if [ "$encode_failed" -eq 0 ]; then
|
|
85
165
|
case "$encoded" in \{*) payload="$encoded" ;; *) encode_failed=1 ;; esac
|
|
86
|
-
|
|
166
|
+
fi
|
|
87
167
|
if [ "$encode_failed" -eq 1 ]; then
|
|
88
168
|
encoded="$(jq -cn --arg harness "$harness" --arg event "$event" --arg policy "$policy" --arg mode minimal -f "$_DOFLOW_BRIDGE_DIR/envelope.jq" 2>/dev/null)" || encoded=""
|
|
89
169
|
case "$encoded" in \{*) payload="$encoded" ;; *) payload="{\"doflow_event\":{\"version\":1,\"harness\":\"$harness\",\"event\":\"$event\",\"operation\":\"tool\"}}" ;; esac
|
|
90
170
|
doflow_bridge_failure "$policy" envelope-encode
|
|
171
|
+
else
|
|
172
|
+
# Step observe-model (row 120 IC-005): an envelope whose identity names a model is recorded
|
|
173
|
+
# under the config home. It never changes the decision, the exit code or the streams.
|
|
174
|
+
case "$payload" in
|
|
175
|
+
*'"doflow_event":{'*'"model":"'*)
|
|
176
|
+
[ ! -f "$_DOFLOW_BRIDGE_DIR/observe-model.sh" ] || ( . "$_DOFLOW_BRIDGE_DIR/observe-model.sh" && doflow_observe_model "$harness" "$payload" ) >/dev/null 2>&1 </dev/null || true
|
|
177
|
+
;;
|
|
178
|
+
esac
|
|
91
179
|
fi
|
|
92
180
|
else
|
|
93
181
|
doflow_bridge_failure "$policy" dependency-missing:jq
|
|
@@ -113,7 +201,11 @@ doflow_bridge() {
|
|
|
113
201
|
case "$line" in
|
|
114
202
|
*$'\036'*)
|
|
115
203
|
local prefix="${line%%$'\036'*}"
|
|
116
|
-
record
|
|
204
|
+
# Rider R1: the emit record is telemetry, never the decision record.
|
|
205
|
+
case "${line#*$'\036'}" in
|
|
206
|
+
'{"v":1,"emit":'*) emit_record="${line#*$'\036'}" ;;
|
|
207
|
+
*) record="${line#*$'\036'}" ;;
|
|
208
|
+
esac
|
|
117
209
|
[ -z "$prefix" ] || err="$err$prefix"$'\n'
|
|
118
210
|
;;
|
|
119
211
|
*) err="$err$line"$'\n' ;;
|
|
@@ -125,6 +217,7 @@ doflow_bridge() {
|
|
|
125
217
|
if [ "$spawn_failed" -eq 1 ]; then DF_BRIDGE_OUTCOME=error; DF_BRIDGE_CODE=bridge-spawn; DF_BRIDGE_REASON=bridge-spawn; DF_BRIDGE_VERSION=0; DF_BRIDGE_POINT=bridge-spawn; fi
|
|
126
218
|
if [ -n "$DF_BRIDGE_POINT" ] && [ "$DF_BRIDGE_POINT" != decision-mismatch ] && [ "$DF_BRIDGE_POINT" != eval-error:signal-TERM ] && [ "$DF_BRIDGE_POINT" != eval-error:signal-INT ] && [ "$DF_BRIDGE_POINT" != eval-error:signal-PIPE ]; then doflow_bridge_failure "$policy" "$DF_BRIDGE_POINT"; fi
|
|
127
219
|
doflow_bridge_effect "$harness" "$event" "$policy" "$DF_BRIDGE_OUTCOME"
|
|
220
|
+
doflow_bridge_hook_line "$harness" "$response_event" "$policy" "$emit_record"
|
|
128
221
|
if [ "$DF_BRIDGE_EFFECT" = block ] && [ "$DF_BRIDGE_OUTCOME" != deny ]; then reason="[$policy] DoFlow policy error; details: doflow-run failure list"; fi
|
|
129
222
|
if [ "$DF_BRIDGE_OUTCOME" != deny ] && [ "$DF_BRIDGE_EFFECT" = proceed ]; then
|
|
130
223
|
case "$harness:$response_event:$DF_BRIDGE_OUTCOME" in
|
|
@@ -167,7 +260,7 @@ doflow_bridge() {
|
|
|
167
260
|
fi
|
|
168
261
|
case "$policy:$DF_BRIDGE_OUTCOME:$harness:$response_event:$DF_BRIDGE_CODE" in
|
|
169
262
|
session-context:error:*:*:*|user-prompt-submit:error:*:*:*|subagent-audit:error:*:*:*|session-context:unsupported:*:*:*|user-prompt-submit:unsupported:*:*:*|subagent-audit:unsupported:*:*:*) ;;
|
|
170
|
-
*:error:codex:Stop:exit-*|*:error:codex:Stop:signal-*|*:error:codex:stop:exit-*|*:error:codex:stop:signal-*|*:error:kiro:PreToolUse:exit-*|*:error:kiro:PreToolUse:signal-*|*:error:kiro:Stop:exit-*|*:error:kiro:Stop:signal-*|*:error:claude:Stop:exit-*|*:error:claude:Stop:signal-*) ;;
|
|
263
|
+
*:error:codex:Stop:exit-*|*:error:codex:Stop:signal-*|*:error:copilot:Stop:exit-*|*:error:copilot:Stop:signal-*|*:error:codex:stop:exit-*|*:error:codex:stop:signal-*|*:error:kiro:PreToolUse:exit-*|*:error:kiro:PreToolUse:signal-*|*:error:kiro:Stop:exit-*|*:error:kiro:Stop:signal-*|*:error:claude:Stop:exit-*|*:error:claude:Stop:signal-*) ;;
|
|
171
264
|
*)
|
|
172
265
|
if ! doflow_bridge_drops_tool_stderr "$policy" "$harness" "$response_event" && [ -n "$err" ]; then printf '%s' "$err" >&2; fi
|
|
173
266
|
;;
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
def nonempty($v): if ($v | type) == "string" and $v != "" then $v else null end;
|
|
2
|
+
def valid_model_id($v):
|
|
3
|
+
($v | type) == "string" and ($v | length) >= 1 and ($v | length) <= 120 and
|
|
4
|
+
($v | test("^[A-Za-z0-9][A-Za-z0-9 ._:/@()\\[\\]#+-]*$")) and
|
|
5
|
+
(($v | endswith(" ")) | not);
|
|
6
|
+
def model_identity($v):
|
|
7
|
+
if ($v | type) != "string" then {}
|
|
8
|
+
elif valid_model_id($v) then {model:$v}
|
|
9
|
+
else {modelUnparsed:true} end;
|
|
10
|
+
# Same reading as identity.ts and policies/identity.sh: ASCII-lowercased, main or worker kept, any other non-empty value is worker. Unset or empty stays null.
|
|
11
|
+
def norm_role($v): if ($v | type) == "string" and $v != "" then ($v | ascii_downcase) as $l | if $l == "main" or $l == "worker" then $l else "worker" end else null end;
|
|
2
12
|
def put($o; $k; $v): if $v == null then $o else $o + {($k): $v} end;
|
|
3
13
|
def event_name($fallback): if (.hook_event_name | type) == "string" then .hook_event_name else $fallback end;
|
|
4
14
|
def path_values($obj; $keys): first([$keys[] as $k | $obj[$k] | select(type == "string" and length > 0)][]) // null | if . == null then null else [.] end;
|
|
@@ -12,26 +22,31 @@ def event_operation($e):
|
|
|
12
22
|
elif $e == "Stop" or $e == "stop" then "stop"
|
|
13
23
|
elif $e == "SubagentStart" then "subagent-start"
|
|
14
24
|
elif $e == "SubagentStop" then "subagent-stop"
|
|
15
|
-
elif $e == "PreCompact"
|
|
16
|
-
elif $e == "PostToolUse"
|
|
25
|
+
elif $e == "PreCompact" then "compact"
|
|
26
|
+
elif $e == "PostToolUse" then "post-tool"
|
|
17
27
|
else "tool" end;
|
|
18
28
|
def canonical_tool($name; $harness):
|
|
19
29
|
if ($name | type) != "string" then {operation:"tool"}
|
|
20
|
-
elif (($harness == "claude" or $harness == "codex" or $harness == "kiro") and ($name | test("^mcp__"))) or (
|
|
21
|
-
elif (($harness == "claude" or $harness == "codex" or $harness == "kiro") and (["Edit","Write","MultiEdit","apply_patch"] | index($name)) != null) or ($harness == "
|
|
22
|
-
elif (($harness == "claude" or $harness == "codex" or $harness == "kiro"
|
|
30
|
+
elif (($harness == "claude" or $harness == "codex" or $harness == "kiro" or $harness == "copilot") and ($name | test("^mcp__"))) or ($harness == "antigravity" and ($name | test("^mcp_"))) or ($harness == "pi" and ($name | test("^mcp__"))) then {operation:"mcp",tool:$name}
|
|
31
|
+
elif (($harness == "claude" or $harness == "codex" or $harness == "kiro" or $harness == "copilot") and (["Edit","Write","MultiEdit","apply_patch"] | index($name)) != null) or ($harness == "antigravity" and (["write_to_file","replace_file_content","multi_replace_file_content","edit_file","write_file","create_file","replace_file","replace","Edit","Write","MultiEdit","apply_patch"] | index($name)) != null) or ($harness == "pi" and (["edit","write"] | index($name)) != null) then {operation:"edit",tool:$name}
|
|
32
|
+
elif (($harness == "claude" or $harness == "codex" or $harness == "kiro" or $harness == "copilot") and $name == "Bash") or ($harness == "antigravity" and (["run_command","run_shell_command","Bash","bash"] | index($name)) != null) or ($harness == "pi" and $name == "bash") then {operation:"command",tool:$name}
|
|
23
33
|
else {operation:"tool",tool:$name} end;
|
|
24
34
|
def decode_claude:
|
|
25
35
|
(canonical_tool(.tool_name // ""; "claude")) as $t |
|
|
26
|
-
if
|
|
27
|
-
|
|
36
|
+
(if event_name($event) == "SessionStart" then model_identity(.model) else {} end) as $model |
|
|
37
|
+
if .tool_name == null then {operation: event_operation($event), identity: (session_identity(.session_id) + $model)}
|
|
38
|
+
else {operation:$t.operation, tool:$t.tool, paths:(if $t.operation == "edit" then path_values(.tool_input; ["file_path"]) else null end), command:(if $t.operation == "command" then string_value(.tool_input; ["command"]) else null end), identity:(session_identity(.session_id) + $model)} end;
|
|
28
39
|
def decode_codex:
|
|
29
40
|
decode_claude as $d |
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
41
|
+
(model_identity(.model)) as $model |
|
|
42
|
+
(if .tool_name == "apply_patch" then $d + {patch:(string_value(.tool_input; ["command"]))} else $d end) |
|
|
43
|
+
.identity += $model;
|
|
44
|
+
# Copilot CLI payloads (normalised to the Claude shape by the front door) carry no model, so the
|
|
45
|
+
# envelope names none, whatever a payload happens to hold under .model.
|
|
46
|
+
def decode_copilot:
|
|
47
|
+
decode_claude as $d |
|
|
48
|
+
(if .tool_name == "apply_patch" then $d + {patch:(string_value(.tool_input; ["command"]))} else $d end) |
|
|
49
|
+
.identity |= del(.model, .modelUnparsed);
|
|
35
50
|
def decode_kiro:
|
|
36
51
|
(if ((.tool_name // "") | type) == "string" and ((.tool_name // "") | startswith("mcp_")) then {operation:"mcp",tool:.tool_name}
|
|
37
52
|
elif ((.tool_name // "") | type) == "string" and ((.tool_name // "") | test("^@[^/]+/.+")) then
|
|
@@ -40,21 +55,75 @@ def decode_kiro:
|
|
|
40
55
|
if .tool_name == null then {operation:event_operation($event),identity:session_identity(.session_id)}
|
|
41
56
|
else {operation:$t.operation,tool:$t.tool,paths:(if $t.operation == "edit" then path_values(.tool_input; ["file_path"]) else null end),command:(if $t.operation == "command" then string_value(.tool_input; ["command"]) else null end),identity:session_identity(.session_id)} end;
|
|
42
57
|
def decode_antigravity:
|
|
43
|
-
|
|
58
|
+
(model_identity(.modelName)) as $model |
|
|
59
|
+
(session_identity(if (.session_id | type) == "string" and .session_id != "" then .session_id else .conversationId end) + $model) as $identity |
|
|
60
|
+
if .toolCall == null then {operation:event_operation($event),identity:$identity}
|
|
44
61
|
else (canonical_tool(.toolCall.name // ""; "antigravity")) as $t |
|
|
45
|
-
{operation:$t.operation,tool:$t.tool,paths:(if $t.operation == "edit" then path_values(.toolCall.args; ["AbsolutePath","TargetFile","Path","file_path","path"]) else null end),command:(if $t.operation == "command" then string_value(.toolCall.args; ["CommandLine","command"]) else null end),identity
|
|
62
|
+
{operation:$t.operation,tool:$t.tool,paths:(if $t.operation == "edit" then path_values(.toolCall.args; ["AbsolutePath","TargetFile","Path","file_path","path"]) else null end),command:(if $t.operation == "command" then string_value(.toolCall.args; ["CommandLine","command"]) else null end),identity:$identity} end;
|
|
46
63
|
def decode_pi:
|
|
47
64
|
(canonical_tool(.toolName // ""; "pi")) as $t |
|
|
48
|
-
{operation:$t.operation,tool:$t.tool,paths:(if $t.operation == "edit" then path_values(.input; ["path"]) else null end),command:(if $t.operation == "command" then string_value(.input; ["command"]) else null end),identity:session_identity(.sessionId),correlation:(correlation(.toolCallId;.parentToolCallId))};
|
|
65
|
+
{operation:$t.operation,tool:$t.tool,paths:(if $t.operation == "edit" then path_values(.input; ["path"]) else null end),command:(if $t.operation == "command" then string_value(.input; ["command"]) else null end),identity:(session_identity(.sessionId) + model_identity(.model)),correlation:(correlation(.toolCallId;.parentToolCallId))};
|
|
66
|
+
# IC-005: only this closed vocabulary enters telemetry. The policy envelope keeps its command.
|
|
67
|
+
def command_segment_class:
|
|
68
|
+
gsub("^\\s+|\\s+$"; "") | sub("^([A-Za-z_][A-Za-z0-9_]*=[^\\s]*\\s+)+"; "") |
|
|
69
|
+
if test("^cd\\s+") then "none"
|
|
70
|
+
elif test("^(doflow-run|doflow) verify( |$)") then "verify.verb"
|
|
71
|
+
elif test("^(npm|pnpm|yarn|bun)( run)? test$|^node --test( --[^ ]+)*( test/?)?$|^pytest( -[^ ]+)*$|^go test \\./\\.\\.\\.$|^cargo test$") then "test.full"
|
|
72
|
+
elif test("^(npm|pnpm|yarn|bun)( run)? test |^node --test |^pytest |^go test |^cargo test ") then "test.scoped"
|
|
73
|
+
elif test("(^| )(jest|vitest|mocha|playwright test)( |$)|^(npm|pnpm|yarn|bun) run test:[^ ]+") then "test.other"
|
|
74
|
+
else "none" end;
|
|
75
|
+
def classify($cmd):
|
|
76
|
+
try (if ($cmd | type) != "string" then "none" else
|
|
77
|
+
["verify.verb", "test.full", "test.scoped", "test.other", "none"] as $rank |
|
|
78
|
+
[$cmd | splits("&&|\\|\\||;|\\|") | command_segment_class | . as $c | $rank | index($c)] |
|
|
79
|
+
(min // 4) as $best | $rank[$best]
|
|
80
|
+
end) catch "none";
|
|
81
|
+
# Telemetry tuple (IC-003): the first line in explicit `--arg mode encode`, read and stripped by
|
|
82
|
+
# bridge.sh. Every telemetry expression sits inside the `try`, so a fault here yields {dft:1} and
|
|
83
|
+
# never reaches the envelope; exactly one value comes out whatever the inside yields.
|
|
84
|
+
def dft_token($v): if ($v | type) == "string" and ($v | test("^[A-Za-z0-9][A-Za-z0-9._:-]{0,63}$")) then $v else null end;
|
|
85
|
+
def dft_id($v): if ($v | type) == "string" and ($v | test("^[A-Za-z0-9][A-Za-z0-9_-]{0,55}$")) then $v else null end;
|
|
86
|
+
def dft_tuple_base($native; $d):
|
|
87
|
+
if $ENV.DOFLOW_TELEMETRY == "off" then {dft:1} else
|
|
88
|
+
[try (now as $n | ($n | floor) as $s |
|
|
89
|
+
{dft:1, date:($s | strftime("%Y-%m-%d")),
|
|
90
|
+
timestamp:(($s | strftime("%Y-%m-%dT%H:%M:%S")) + "." + (((($n - $s) * 1000 | floor) + 1000 | tostring)[1:]) + "Z"),
|
|
91
|
+
session:dft_token($d.identity.session),
|
|
92
|
+
tool:(if $d.operation == "mcp" then null else dft_token($d.tool) end),
|
|
93
|
+
tool_call_id:dft_token(if $harness == "pi" then $native.toolCallId elif $harness == "claude" or $harness == "codex" then $native.tool_use_id else null end),
|
|
94
|
+
run:(dft_id($ENV.DOFLOW_RUN_ID) | if . == "local" then null else . end),
|
|
95
|
+
lineage:dft_id($ENV.DOFLOW_LINEAGE_ID),
|
|
96
|
+
attempt:($ENV.DOFLOW_ATTEMPT | if type == "string" and test("^[1-9][0-9]{0,3}$") then tonumber else null end),
|
|
97
|
+
role:norm_role($ENV.DOFLOW_SESSION_ROLE),
|
|
98
|
+
worker_run:($ENV.DOFLOW_WORKER_RUN | if type == "string" and test("\\Aw-[0-9a-f]{12}\\z") then . else null end)}) catch {dft:1}]
|
|
99
|
+
| if length == 1 then .[0] else {dft:1} end end;
|
|
100
|
+
def dft_tuple($native; $d):
|
|
101
|
+
dft_tuple_base($native; $d) |
|
|
102
|
+
[try (
|
|
103
|
+
if . == {dft:1} then . else
|
|
104
|
+
(if $ARGS.named.mode == "observe" then . + {duration_ms:null, tool_duration_ms:($native.duration_ms |
|
|
105
|
+
if type == "number" and . >= 0 and . <= 9007199254740991 and . == floor then . else null end)} else . + {tool_duration_ms:null} end) |
|
|
106
|
+
if ($policy == "pre-bash-guard" or $ARGS.named.mode == "observe") and $d.operation == "command"
|
|
107
|
+
then . + {test_class:classify($d.command)} else . + {test_class:null} end
|
|
108
|
+
end) catch {dft:1}] | if length == 1 then .[0] else {dft:1} end;
|
|
49
109
|
if ($ARGS.named.mode // "encode") == "minimal" then {doflow_event:{version:(1),harness:$harness,event:(event_name($event)),operation:event_operation($event)}} elif type != "object" then . else
|
|
50
110
|
. as $native |
|
|
51
111
|
($ENV.DOFLOW_TASK_ID | nonempty(.)) as $task |
|
|
52
112
|
($ENV.DOFLOW_RUN_ID | nonempty(.)) as $run |
|
|
53
113
|
($ENV.DOFLOW_ATTEMPT | try tonumber catch null) as $attempt |
|
|
54
|
-
(
|
|
55
|
-
(
|
|
114
|
+
($ENV.DOFLOW_LINEAGE_ID | nonempty(.)) as $lineage |
|
|
115
|
+
($ENV.DOFLOW_WORKTREE | nonempty(.)) as $worktree |
|
|
116
|
+
norm_role($ENV.DOFLOW_SESSION_ROLE) as $role |
|
|
117
|
+
(if $harness == "claude" then decode_claude elif $harness == "codex" then decode_codex elif $harness == "kiro" then decode_kiro elif $harness == "copilot" then decode_copilot elif $harness == "antigravity" then decode_antigravity elif $harness == "pi" then decode_pi else {operation:"tool"} end) as $d |
|
|
118
|
+
if $ARGS.named.mode == "observe" then
|
|
119
|
+
if $harness == "claude" and $d.operation == "command" and $native.hook_event_name == $event and
|
|
120
|
+
($event == "PostToolUse" or $event == "PostToolUseFailure")
|
|
121
|
+
then dft_tuple($native; $d) else {dft:1} end
|
|
122
|
+
else
|
|
56
123
|
({version:1,harness:$harness,event:(event_name($event)),operation:$d.operation,tool:$d.tool,paths:$d.paths,command:$d.command,patch:$d.patch,taskId:$task,
|
|
57
|
-
identity:($d.identity + {run:$run,attempt:$attempt,role:$role} | with_entries(select(.value != null)) | if length > 0 then . else null end),
|
|
124
|
+
identity:($d.identity + {run:$run,attempt:$attempt,lineage:$lineage,role:$role,worktree:$worktree} | with_entries(select(.value != null)) | if length > 0 then . else null end),
|
|
58
125
|
correlation:(($d.correlation // {}) | with_entries(select(.value != null)) | if length > 0 then . else null end) } | with_entries(select(.value != null))) as $envelope |
|
|
126
|
+
(if $ARGS.named.mode == "encode" then dft_tuple($native; $d) else empty end),
|
|
59
127
|
$native + {doflow_event:$envelope}
|
|
128
|
+
end
|
|
60
129
|
end
|
|
@@ -0,0 +1,75 @@
|
|
|
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
|
+
# observe-model.sh — the shared bridge step `observe-model` (row 120 IC-005).
|
|
4
|
+
#
|
|
5
|
+
# Defines doflow_observe_model <harness> <envelope>. Sourcing defines it only. bridge.sh calls it once
|
|
6
|
+
# per event, after the envelope is built, in a subshell with every stream discarded:
|
|
7
|
+
#
|
|
8
|
+
# ( . "<bridge-dir>/observe-model.sh" && doflow_observe_model "$harness" "$payload" ) >/dev/null 2>&1 </dev/null || true
|
|
9
|
+
#
|
|
10
|
+
# so it never changes the decision, the exit code or the output, and every I/O error is ignored.
|
|
11
|
+
#
|
|
12
|
+
# When the envelope carries identity.model and a session id it records the model in one file under
|
|
13
|
+
# lib.sh's STATE_DIR (<config home>/doflow/session-env):
|
|
14
|
+
# - main session (DOFLOW_SESSION_ROLE unset or main): primary/<cwd_hash cwd>/<harness>/<sessionKey>.json,
|
|
15
|
+
# where cwd is the payload's absolute `cwd`, else the hook's working directory;
|
|
16
|
+
# - worker (any other role, identity.sh's reading) with a launched identity (DOFLOW_RUN_ID other
|
|
17
|
+
# than `local`, DOFLOW_LINEAGE_ID and DOFLOW_ATTEMPT, identity.sh's patterns):
|
|
18
|
+
# worker-model/L.<run>.<lineage>.<attempt>.json. A worker without one writes nothing.
|
|
19
|
+
# sessionKey is the first 16 hex of the sha256 of the session id's bytes. The file holds exactly
|
|
20
|
+
# {"schema":1,"harness":"<id>","model":"<model-id>","event":"<event>","observedAt":"<UTC>","session":"<sessionKey>"}
|
|
21
|
+
# and is written only when absent or when its model differs: a tmp file in the same folder, then a
|
|
22
|
+
# rename, mode 0600. With no config home, no sha256 tool or a link in place of the file, nothing.
|
|
23
|
+
# The storage stores model-observations and worker-model-observations bound both folders.
|
|
24
|
+
|
|
25
|
+
_DOFLOW_SRC=${BASH_SOURCE[0]//\\//}; case $_DOFLOW_SRC in */*) _DOFLOW_OBSERVE_DIR=${_DOFLOW_SRC%/*} ;; *) _DOFLOW_OBSERVE_DIR=. ;; esac
|
|
26
|
+
|
|
27
|
+
doflow_observe_model() {
|
|
28
|
+
local harness="${1:-}" envelope="${2:-}" fields="" model="" model_json="" session="" cwd="" event="" now=""
|
|
29
|
+
local key="" dir="" file="" current="" tmp="" run="${DOFLOW_RUN_ID:-}" lineage="${DOFLOW_LINEAGE_ID:-}" attempt="${DOFLOW_ATTEMPT:-}"
|
|
30
|
+
case "$harness" in ''|*[!a-z0-9-]*) return 0 ;; esac
|
|
31
|
+
# lib.sh gives STATE_DIR (the config-home rule), cwd_hash and identity.sh's role reading.
|
|
32
|
+
. "$_DOFLOW_OBSERVE_DIR/../policies/lib.sh" || return 0
|
|
33
|
+
[ -n "${STATE_DIR:-}" ] || return 0
|
|
34
|
+
fields=$(printf '%s\n' "$envelope" | jq -r '
|
|
35
|
+
def text: type == "string" and length > 0 and (test("[[:cntrl:]]") | not);
|
|
36
|
+
.doflow_event as $e
|
|
37
|
+
| select(($e.identity.model | text) and ($e.identity.session | text))
|
|
38
|
+
| $e.identity.model, ($e.identity.model | tojson), $e.identity.session,
|
|
39
|
+
(if (.cwd | text) and (.cwd | startswith("/")) then .cwd else "-" end),
|
|
40
|
+
($e.event | tojson), (now | strftime("%Y-%m-%dT%H:%M:%SZ"))' 2>/dev/null) || return 0
|
|
41
|
+
{ IFS= read -r model && IFS= read -r model_json && IFS= read -r session && IFS= read -r cwd \
|
|
42
|
+
&& IFS= read -r event && IFS= read -r now; } <<<"$fields" || return 0
|
|
43
|
+
|
|
44
|
+
if command -v sha256sum >/dev/null 2>&1; then key=$(printf '%s' "$session" | sha256sum | cut -c1-16)
|
|
45
|
+
elif command -v shasum >/dev/null 2>&1; then key=$(printf '%s' "$session" | shasum -a 256 | cut -c1-16)
|
|
46
|
+
else return 0; fi
|
|
47
|
+
[[ $key =~ ^[0-9a-f]{16}$ ]] || return 0
|
|
48
|
+
|
|
49
|
+
_doflow_role_resolve
|
|
50
|
+
if [ "$_DF_ROLE" = main ]; then
|
|
51
|
+
case "$cwd" in /*) ;; *) cwd=$PWD ;; esac
|
|
52
|
+
dir="$STATE_DIR/primary/$(cwd_hash "$cwd")/$harness"
|
|
53
|
+
file="$dir/$key.json"
|
|
54
|
+
else
|
|
55
|
+
[ "$run" != local ] && [[ $run =~ ^[A-Za-z0-9][A-Za-z0-9_-]{0,55}$ ]] || return 0
|
|
56
|
+
[[ $lineage =~ ^[A-Za-z0-9][A-Za-z0-9_-]{0,55}$ ]] && [[ $attempt =~ ^[1-9][0-9]{0,3}$ ]] || return 0
|
|
57
|
+
dir="$STATE_DIR/worker-model"
|
|
58
|
+
file="$dir/L.$run.$lineage.$attempt.json"
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
[ ! -L "$file" ] || return 0
|
|
62
|
+
if [ -f "$file" ]; then
|
|
63
|
+
current=$(jq -r 'if (.model | type) == "string" then .model else empty end' "$file" 2>/dev/null) || current=""
|
|
64
|
+
[ "$current" != "$model" ] || return 0
|
|
65
|
+
fi
|
|
66
|
+
umask 077
|
|
67
|
+
mkdir -p "$dir" || return 0
|
|
68
|
+
tmp="$file.tmp.$$"
|
|
69
|
+
if printf '{"schema":1,"harness":"%s","model":%s,"event":%s,"observedAt":"%s","session":"%s"}\n' \
|
|
70
|
+
"$harness" "$model_json" "$event" "$now" "$key" >"$tmp" && mv -f "$tmp" "$file"; then
|
|
71
|
+
return 0
|
|
72
|
+
fi
|
|
73
|
+
rm -f "$tmp"
|
|
74
|
+
return 0
|
|
75
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
+
case "${OSTYPE:-}" in msys*|cygwin*|win32*) if command -v jq >/dev/null 2>&1; then jq() { command jq -b "$@"; }; fi ;; esac # native jq.exe ends lines with CRLF unless -b
|
|
2
3
|
# Native hook response renderers for the shared lifecycle bridge.
|
|
3
|
-
|
|
4
|
+
_DOFLOW_SRC=${BASH_SOURCE[0]//\\//}; case $_DOFLOW_SRC in */*) _DOFLOW_RESPOND_DIR=${_DOFLOW_SRC%/*} ;; *) _DOFLOW_RESPOND_DIR=. ;; esac
|
|
4
5
|
. "$_DOFLOW_RESPOND_DIR/../policies/deny-json.sh"
|
|
5
6
|
|
|
6
7
|
doflow_bridge_native_event_var() {
|
|
@@ -28,7 +29,6 @@ doflow_render_response() {
|
|
|
28
29
|
if [ "$outcome" = deny ]; then
|
|
29
30
|
case "$harness:$event" in
|
|
30
31
|
claude:PreToolUse|codex:PreToolUse) emit_pretooluse_deny_nested "$reason" || return $?; return 0 ;;
|
|
31
|
-
gemini:BeforeTool) emit_pretooluse_deny_flat "$reason"; return 0 ;;
|
|
32
32
|
kiro:PreToolUse) printf '%s' "$reason" >&2; return 2 ;;
|
|
33
33
|
antigravity:PreToolUse)
|
|
34
34
|
if command -v jq >/dev/null 2>&1; then jq -cn --arg reason "$reason" '{decision:"deny",reason:$reason}'
|
|
@@ -38,6 +38,14 @@ doflow_render_response() {
|
|
|
38
38
|
if command -v jq >/dev/null 2>&1; then jq -cn --arg reason "$reason" '{decision:"continue",reason:$reason}'
|
|
39
39
|
else printf '{"decision":"continue","reason":%s}\n' "$( _doflow_json_reason "$reason" )"; fi
|
|
40
40
|
return $? ;;
|
|
41
|
+
copilot:PreToolUse)
|
|
42
|
+
if command -v jq >/dev/null 2>&1; then jq -cn --arg reason "$reason" '{permissionDecision:"deny",permissionDecisionReason:$reason}'
|
|
43
|
+
else printf '{"permissionDecision":"deny","permissionDecisionReason":%s}\n' "$( _doflow_json_reason "$reason" )"; fi
|
|
44
|
+
return $? ;;
|
|
45
|
+
copilot:Stop)
|
|
46
|
+
if command -v jq >/dev/null 2>&1; then jq -cn --arg reason "$reason" '{decision:"block",reason:$reason}'
|
|
47
|
+
else printf '{"decision":"block","reason":%s}\n' "$( _doflow_json_reason "$reason" )"; fi
|
|
48
|
+
return $? ;;
|
|
41
49
|
pi:tool_call)
|
|
42
50
|
if command -v jq >/dev/null 2>&1; then jq -cn --arg reason "$reason" '{block:true,reason:$reason}'
|
|
43
51
|
else printf '{"block":true,"reason":%s}\n' "$( _doflow_json_reason "$reason" )"; fi
|
|
@@ -4,27 +4,36 @@
|
|
|
4
4
|
# regex<TAB>reason shown to the harness<TAB>exclude_regex (optional)
|
|
5
5
|
#
|
|
6
6
|
# Rules:
|
|
7
|
-
# - Lines beginning with # are comments (skipped)
|
|
7
|
+
# - Lines beginning with # are comments (skipped), except the marker
|
|
8
|
+
# #@exempt-next worker-worktree arms only the next pattern line for the
|
|
9
|
+
# hook's exact dispatch-worker reset/clean exemption. The worker-dropdb
|
|
10
|
+
# marker arms only the DROP row for one exact listed database invocation
|
|
11
|
+
# from the launch snapshot. Other rules still run.
|
|
8
12
|
# - Empty lines are skipped
|
|
9
13
|
# - Regex is POSIX Extended Regular Expression (ERE, as used by `grep -E`),
|
|
10
14
|
# matched case-insensitively against the full command string (RM and GIT run
|
|
11
15
|
# on a case-insensitive filesystem such as the macOS default), which
|
|
12
16
|
# includes any expanded heredocs or -m "..." arguments. Git and rm
|
|
13
17
|
# patterns use a command-context anchor:
|
|
14
|
-
# (^|[
|
|
18
|
+
# (^|[;&|(`])[[:space:]]*
|
|
15
19
|
# so they only fire when the dangerous text is an actual invocation,
|
|
16
20
|
# not when it appears as text inside a commit message or other argument.
|
|
17
21
|
# - Anchored patterns (those beginning with "(^|") are matched against the
|
|
18
22
|
# command with quoted text blanked out (pre-bash-guard.sh scrub_quotes),
|
|
19
23
|
# so echo "x && rm -rf /" or git commit -m "git reset --hard" is not a
|
|
20
24
|
# command. The argument of sh -c, bash -c, zsh -c and eval is NOT blanked
|
|
21
|
-
# (it executes).
|
|
22
|
-
#
|
|
25
|
+
# (it executes). SQL rules use the raw full text. Only the three shipped
|
|
26
|
+
# SQL rows may be skipped by the hook's positive raw-command text-only
|
|
27
|
+
# allow-list: literal commit/echo/printf/gh bodies or exact quoted heredocs
|
|
28
|
+
# to commit/cat/gh (including the Claude commit cat substitution).
|
|
29
|
+
# Other shapes retain the raw SQL verdict; dot-led file targets are excluded.
|
|
30
|
+
# - The git, rm and find lines use command positions at the start of a
|
|
23
31
|
# line or after ; & | ( or a backtick, then optional sudo/command/time/
|
|
24
32
|
# nohup/exec/env (with options, VAR=x and, per wrapper, the options that take
|
|
25
33
|
# a separate argument: sudo -u/-g/-C/-h/-p/-r/-t/-U/-D/-R and --user/--group
|
|
26
|
-
# root, xargs -n/-I/-L/-P/-s/-d/-E/-a, env -u/-C/-S)/xargs/eval/then/do/else
|
|
27
|
-
#
|
|
34
|
+
# root, xargs -n/-I/-L/-P/-s/-d/-E/-a, env -u/-C/-S)/xargs/eval/then/do/else
|
|
35
|
+
# or bare NAME=value assignments, then git/rm/find, optionally prefixed
|
|
36
|
+
# with \, /bin/ or /usr/bin/. "git rm" and "echo rm -rf /" are therefore
|
|
28
37
|
# not matched. pre-bash-guard.sh's hardcoded floor uses the same rule.
|
|
29
38
|
# Matching is case-insensitive, so an uppercase flag-only option that shares a
|
|
30
39
|
# letter with a value-taking one (sudo -H beside -h, xargs -p beside -P) is
|
|
@@ -34,6 +43,8 @@
|
|
|
34
43
|
# inside a flag cluster such as -uf), --force and a +refspec; the
|
|
35
44
|
# --force-with-lease form stays allowed, as the reason text advises, but a
|
|
36
45
|
# +refspec still blocks next to a lease (git push --force-with-lease origin +main).
|
|
46
|
+
# The clean rule also blocks -ff: double force can delete nested repos and
|
|
47
|
+
# is never eligible for the worker-worktree exemption.
|
|
37
48
|
# - The find rule blocks -delete, and -exec rm, off the root, a home directory
|
|
38
49
|
# or a system directory (among other start paths, find or \find or
|
|
39
50
|
# /usr/bin/find) when only -type, -maxdepth/-mindepth N, -mount, -xdev,
|
|
@@ -54,18 +65,21 @@
|
|
|
54
65
|
# Add project-specific patterns at the bottom of this file.
|
|
55
66
|
|
|
56
67
|
# ── Dangerous git operations ──────────────────────────────────────────────────
|
|
57
|
-
(^|[[:space:]]*(
|
|
58
|
-
(^|[[:space:]]*(
|
|
59
|
-
|
|
60
|
-
(^|[[:space:]]*(
|
|
68
|
+
(^|[;&|(`])[[:space:]]*((sudo([[:space:]]+(-[ugChprtUDR][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|--(user|group|host|prompt|role|type|chdir)[[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|xargs([[:space:]]+(-[nILPsdEa][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|env([[:space:]]+(-[uCS][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*|(command|time|nohup|exec|eval|then|do|else)([[:space:]]+(-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*)[[:space:]]+)*(\\|/(usr/)?bin/)?git([[:space:]]+(-[Cc][[:space:]]+[^[:space:];&|]+|--(git-dir|work-tree|namespace|exec-path)[[:space:]]+[^[:space:];&|]+|-[^[:space:];&|]+))*[[:space:]]+push([[:space:]]+[^[:space:];&|]+)*[[:space:]]+(--force|-[a-zA-Z]*f[a-zA-Z]*)([^-[:alnum:]]|$) Force push blocked — use --force-with-lease or confirm with user first
|
|
69
|
+
(^|[;&|(`])[[:space:]]*((sudo([[:space:]]+(-[ugChprtUDR][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|--(user|group|host|prompt|role|type|chdir)[[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|xargs([[:space:]]+(-[nILPsdEa][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|env([[:space:]]+(-[uCS][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*|(command|time|nohup|exec|eval|then|do|else)([[:space:]]+(-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*)[[:space:]]+)*(\\|/(usr/)?bin/)?git([[:space:]]+(-[Cc][[:space:]]+[^[:space:];&|]+|--(git-dir|work-tree|namespace|exec-path)[[:space:]]+[^[:space:];&|]+|-[^[:space:];&|]+))*[[:space:]]+push([[:space:]]+[^[:space:];&|]+)*[[:space:]]+\+[^[:space:];&|]+ Force push blocked — use --force-with-lease or confirm with user first
|
|
70
|
+
#@exempt-next worker-worktree
|
|
71
|
+
(^|[;&|(`])[[:space:]]*((sudo([[:space:]]+(-[ugChprtUDR][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|--(user|group|host|prompt|role|type|chdir)[[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|xargs([[:space:]]+(-[nILPsdEa][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|env([[:space:]]+(-[uCS][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*|(command|time|nohup|exec|eval|then|do|else)([[:space:]]+(-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*)[[:space:]]+)*(\\|/(usr/)?bin/)?git([[:space:]]+(-[Cc][[:space:]]+[^[:space:];&|]+|--(git-dir|work-tree|namespace|exec-path)[[:space:]]+[^[:space:];&|]+|-[^[:space:];&|]+))*[[:space:]]+reset --hard Destructive reset blocked — commit or stash changes before resetting
|
|
72
|
+
#@exempt-next worker-worktree
|
|
73
|
+
(^|[;&|(`])[[:space:]]*((sudo([[:space:]]+(-[ugChprtUDR][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|--(user|group|host|prompt|role|type|chdir)[[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|xargs([[:space:]]+(-[nILPsdEa][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|env([[:space:]]+(-[uCS][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*|(command|time|nohup|exec|eval|then|do|else)([[:space:]]+(-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*)[[:space:]]+)*(\\|/(usr/)?bin/)?git([[:space:]]+(-[Cc][[:space:]]+[^[:space:];&|]+|--(git-dir|work-tree|namespace|exec-path)[[:space:]]+[^[:space:];&|]+|-[^[:space:];&|]+))*[[:space:]]+(clean -fd|clean -ff) Irreversible clean blocked — use --dry-run to preview what would be deleted
|
|
61
74
|
|
|
62
75
|
# ── Filesystem catastrophes ───────────────────────────────────────────────────
|
|
63
|
-
(^|[;&|(`])[[:space:]]*((sudo([[:space:]]+(-[ugChprtUDR][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|--(user|group|host|prompt|role|type|chdir)[[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|xargs([[:space:]]+(-[nILPsdEa][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|env([[:space:]]+(-[uCS][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|(command|time|nohup|exec|eval|then|do|else)([[:space:]]+(-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*)[[:space:]]+)*(\\|/(usr/)?bin/)?rm([[:space:]]+[^[:space:];&|]+)*[[:space:]]+((-[a-zA-Z]*[rR][a-zA-Z]*|--recursive)([[:space:]]+[^[:space:];&|]+)*[[:space:]]+(/+\*{0,2})([[:space:];&|)`]|$)|(/+\*{0,2})([[:space:]]+[^[:space:];&|]+)*[[:space:]]+(-[a-zA-Z]*[rR][a-zA-Z]*|--recursive)([[:space:];&|)`]|$)) Catastrophic delete blocked — recursive rm of the root directory (/)
|
|
64
|
-
(^|[;&|(`])[[:space:]]*((sudo([[:space:]]+(-[ugChprtUDR][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|--(user|group|host|prompt|role|type|chdir)[[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|xargs([[:space:]]+(-[nILPsdEa][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|env([[:space:]]+(-[uCS][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|(command|time|nohup|exec|eval|then|do|else)([[:space:]]+(-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*)[[:space:]]+)*(\\|/(usr/)?bin/)?rm([[:space:]]+[^[:space:];&|]+)*[[:space:]]+((-[a-zA-Z]*[rR][a-zA-Z]*|--recursive)([[:space:]]+[^[:space:];&|]+)*[[:space:]]+((~[A-Za-z_][A-Za-z0-9_.-]*|~|\$HOME|\$\{HOME\})(/\*{0,2})?)([[:space:];&|)`]|$)|((~[A-Za-z_][A-Za-z0-9_.-]*|~|\$HOME|\$\{HOME\})(/\*{0,2})?)([[:space:]]+[^[:space:];&|]+)*[[:space:]]+(-[a-zA-Z]*[rR][a-zA-Z]*|--recursive)([[:space:];&|)`]|$)) Catastrophic delete blocked — recursive rm of the home directory
|
|
65
|
-
(^|[;&|(`])[[:space:]]*((sudo([[:space:]]+(-[ugChprtUDR][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|--(user|group|host|prompt|role|type|chdir)[[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|xargs([[:space:]]+(-[nILPsdEa][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|env([[:space:]]+(-[uCS][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|(command|time|nohup|exec|eval|then|do|else)([[:space:]]+(-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*)[[:space:]]+)*(\\|/(usr/)?bin/)?rm([[:space:]]+[^[:space:];&|]+)*[[:space:]]+((-[a-zA-Z]*[rR][a-zA-Z]*|--recursive)([[:space:]]+[^[:space:];&|]+)*[[:space:]]+(/(Users|home|etc|usr|bin|sbin|var|opt|System|Library|Applications|Volumes|private|root|boot|lib|dev|proc)(/\*{0,2})?)([[:space:];&|)`]|$)|(/(Users|home|etc|usr|bin|sbin|var|opt|System|Library|Applications|Volumes|private|root|boot|lib|dev|proc)(/\*{0,2})?)([[:space:]]+[^[:space:];&|]+)*[[:space:]]+(-[a-zA-Z]*[rR][a-zA-Z]*|--recursive)([[:space:];&|)`]|$)) Catastrophic delete blocked — recursive rm of a system directory
|
|
66
|
-
(^|[;&|(`])[[:space:]]*((sudo([[:space:]]+(-[ugChprtUDR][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|--(user|group|host|prompt|role|type|chdir)[[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|xargs([[:space:]]+(-[nILPsdEa][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|env([[:space:]]+(-[uCS][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|(command|time|nohup|exec|eval|then|do|else)([[:space:]]+(-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*)[[:space:]]+)*(\\|/(usr/)?bin/)?find([[:space:]]+-[HLP])*([[:space:]]+[^-[:space:];&|][^[:space:];&|]*)*[[:space:]]+(/+\*{0,2}|(~[A-Za-z_][A-Za-z0-9_.-]*|~|\$HOME|\$\{HOME\})(/\*{0,2})?|/(Users|home|etc|usr|bin|sbin|var|opt|System|Library|Applications|Volumes|private|root|boot|lib|dev|proc)(/\*{0,2})?)([[:space:]]+([^-[:space:];&|][^[:space:];&|]*|-(mount|xdev|depth|noleaf|follow|ignore_readdir_race)|-(type|maxdepth|mindepth)[[:space:]]+[^[:space:];&|]+))*[[:space:]]+(-delete([[:space:];&|)`]|$)|-exec(dir)?[[:space:]]+(\\|/(usr/)?bin/)?rm([[:space:];&|)`]|$)) Catastrophic delete blocked — find -delete or -exec rm from the root, home or a system directory with no name, path or time predicate to scope it
|
|
76
|
+
(^|[;&|(`])[[:space:]]*((sudo([[:space:]]+(-[ugChprtUDR][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|--(user|group|host|prompt|role|type|chdir)[[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|xargs([[:space:]]+(-[nILPsdEa][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|env([[:space:]]+(-[uCS][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*|(command|time|nohup|exec|eval|then|do|else)([[:space:]]+(-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*)[[:space:]]+)*(\\|/(usr/)?bin/)?rm([[:space:]]+[^[:space:];&|]+)*[[:space:]]+((-[a-zA-Z]*[rR][a-zA-Z]*|--recursive)([[:space:]]+[^[:space:];&|]+)*[[:space:]]+(/+\*{0,2})([[:space:];&|)`]|$)|(/+\*{0,2})([[:space:]]+[^[:space:];&|]+)*[[:space:]]+(-[a-zA-Z]*[rR][a-zA-Z]*|--recursive)([[:space:];&|)`]|$)) Catastrophic delete blocked — recursive rm of the root directory (/)
|
|
77
|
+
(^|[;&|(`])[[:space:]]*((sudo([[:space:]]+(-[ugChprtUDR][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|--(user|group|host|prompt|role|type|chdir)[[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|xargs([[:space:]]+(-[nILPsdEa][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|env([[:space:]]+(-[uCS][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*|(command|time|nohup|exec|eval|then|do|else)([[:space:]]+(-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*)[[:space:]]+)*(\\|/(usr/)?bin/)?rm([[:space:]]+[^[:space:];&|]+)*[[:space:]]+((-[a-zA-Z]*[rR][a-zA-Z]*|--recursive)([[:space:]]+[^[:space:];&|]+)*[[:space:]]+((~[A-Za-z_][A-Za-z0-9_.-]*|~|\$HOME|\$\{HOME\})(/\*{0,2})?)([[:space:];&|)`]|$)|((~[A-Za-z_][A-Za-z0-9_.-]*|~|\$HOME|\$\{HOME\})(/\*{0,2})?)([[:space:]]+[^[:space:];&|]+)*[[:space:]]+(-[a-zA-Z]*[rR][a-zA-Z]*|--recursive)([[:space:];&|)`]|$)) Catastrophic delete blocked — recursive rm of the home directory
|
|
78
|
+
(^|[;&|(`])[[:space:]]*((sudo([[:space:]]+(-[ugChprtUDR][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|--(user|group|host|prompt|role|type|chdir)[[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|xargs([[:space:]]+(-[nILPsdEa][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|env([[:space:]]+(-[uCS][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*|(command|time|nohup|exec|eval|then|do|else)([[:space:]]+(-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*)[[:space:]]+)*(\\|/(usr/)?bin/)?rm([[:space:]]+[^[:space:];&|]+)*[[:space:]]+((-[a-zA-Z]*[rR][a-zA-Z]*|--recursive)([[:space:]]+[^[:space:];&|]+)*[[:space:]]+(/(Users|home|etc|usr|bin|sbin|var|opt|System|Library|Applications|Volumes|private|root|boot|lib|dev|proc)(/\*{0,2})?)([[:space:];&|)`]|$)|(/(Users|home|etc|usr|bin|sbin|var|opt|System|Library|Applications|Volumes|private|root|boot|lib|dev|proc)(/\*{0,2})?)([[:space:]]+[^[:space:];&|]+)*[[:space:]]+(-[a-zA-Z]*[rR][a-zA-Z]*|--recursive)([[:space:];&|)`]|$)) Catastrophic delete blocked — recursive rm of a system directory
|
|
79
|
+
(^|[;&|(`])[[:space:]]*((sudo([[:space:]]+(-[ugChprtUDR][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|--(user|group|host|prompt|role|type|chdir)[[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|xargs([[:space:]]+(-[nILPsdEa][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|env([[:space:]]+(-[uCS][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*|(command|time|nohup|exec|eval|then|do|else)([[:space:]]+(-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*)[[:space:]]+)*(\\|/(usr/)?bin/)?find([[:space:]]+-[HLP])*([[:space:]]+[^-[:space:];&|][^[:space:];&|]*)*[[:space:]]+(/+\*{0,2}|(~[A-Za-z_][A-Za-z0-9_.-]*|~|\$HOME|\$\{HOME\})(/\*{0,2})?|/(Users|home|etc|usr|bin|sbin|var|opt|System|Library|Applications|Volumes|private|root|boot|lib|dev|proc)(/\*{0,2})?)([[:space:]]+([^-[:space:];&|][^[:space:];&|]*|-(mount|xdev|depth|noleaf|follow|ignore_readdir_race)|-(type|maxdepth|mindepth)[[:space:]]+[^[:space:];&|]+))*[[:space:]]+(-delete([[:space:];&|)`]|$)|-exec(dir)?[[:space:]]+(\\|/(usr/)?bin/)?rm([[:space:];&|)`]|$)) Catastrophic delete blocked — find -delete or -exec rm from the root, home or a system directory with no name, path or time predicate to scope it
|
|
67
80
|
|
|
68
81
|
# ── SQL destructive statements (no anchor — must catch inside psql heredocs) ──
|
|
82
|
+
#@exempt-next worker-dropdb
|
|
69
83
|
DROP[[:space:]]+(TABLE|DATABASE|SCHEMA)[[:space:]] Destructive DDL blocked — confirm with user before dropping
|
|
70
84
|
DELETE[[:space:]]+FROM[[:space:]]+[[:alnum:]_]+[[:space:]]*; Unscoped DELETE blocked — this would affect all rows, add a WHERE clause
|
|
71
85
|
TRUNCATE[[:space:]]+TABLE Irreversible truncate blocked — confirm with user before truncating
|