@khoavu882/doflow 2.0.0-beta.3 → 2.0.0-beta.5
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/bin/doflow.js +2 -2
- package/core/.antigravity-plugin/plugin.json +0 -1
- 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/mcp-tool-guard.sh +7 -17
- package/core/harnesses/claude/hooks/pre-bash-guard.sh +7 -17
- package/core/harnesses/claude/hooks/pre-implement-gate.sh +7 -18
- package/core/harnesses/claude/hooks/session-start.sh +5 -3
- package/core/harnesses/claude/hooks/stop-check.sh +5 -3
- package/core/harnesses/claude/hooks/subagent-audit.sh +5 -5
- package/core/harnesses/claude/hooks/user-prompt-submit.sh +5 -3
- package/core/harnesses/codex/hooks/mcp-tool-guard.sh +7 -15
- package/core/harnesses/codex/hooks/pre-bash-guard.sh +7 -15
- package/core/harnesses/codex/hooks/pre-implement-gate.sh +7 -16
- package/core/harnesses/codex/hooks/session-start.sh +5 -3
- package/core/harnesses/codex/hooks/stop-check.sh +6 -5
- package/core/harnesses/codex/hooks/subagent-audit.sh +17 -6
- package/core/harnesses/codex/hooks/user-prompt-submit.sh +5 -4
- package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +7 -18
- package/core/harnesses/gemini/hooks/pre-bash-guard.sh +7 -18
- package/core/harnesses/gemini/hooks/pre-implement-gate.sh +7 -19
- package/core/harnesses/gemini/hooks/session-start.sh +5 -3
- package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +20 -5
- package/core/harnesses/kiro/hooks/pre-bash-guard.sh +20 -5
- package/core/harnesses/kiro/hooks/pre-implement-gate.sh +20 -5
- package/core/harnesses/kiro/hooks/session-start.sh +11 -5
- package/core/harnesses/kiro/hooks/stop-check.sh +21 -30
- package/core/harnesses/pi/extensions/doflow-guard.js +159 -0
- package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +27 -11
- package/core/harnesses/shared/hooks/adapters/antigravity.js +14 -57
- package/core/harnesses/shared/hooks/bridge/bridge.sh +184 -0
- package/core/harnesses/shared/hooks/bridge/envelope.jq +60 -0
- package/core/harnesses/shared/hooks/bridge/respond.sh +62 -0
- package/core/harnesses/shared/hooks/policies/capture-failure.sh +4 -2
- package/core/harnesses/shared/hooks/policies/contract.sh +75 -0
- package/core/harnesses/shared/hooks/policies/deny-json.sh +39 -4
- package/core/harnesses/shared/hooks/policies/lib.sh +4 -2
- package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +46 -34
- package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +75 -25
- package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +67 -73
- package/core/harnesses/shared/hooks/policies/session-context.sh +37 -7
- package/core/harnesses/shared/hooks/policies/stop-check.sh +42 -15
- package/core/harnesses/shared/hooks/policies/subagent-audit.sh +35 -5
- package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +40 -7
- package/core/harnesses/shared/hooks/stream-hook-runner.js +69 -181
- package/core/registry/assets.json +19 -0
- package/core/registry/contracts.json +2 -2
- package/core/registry/harnesses.json +196 -10
- package/core/registry/lifecycle.json +2626 -7
- package/core/registry/storage.json +110 -83
- package/core/registry/verification.json +1 -1
- package/core/shared/scripts/doflow/bash/render-puml.sh +2 -2
- package/core/shared/scripts/doflow/bin/doflow-run +10 -6
- package/core/shared/skills/do-design/SKILL.md +6 -6
- package/core/shared/skills/do-execute-plan/SKILL.md +11 -10
- package/core/shared/skills/do-execute-plan/references/readiness_gate.md +2 -0
- package/core/shared/skills/do-execute-plan/references/scaffold.md +2 -2
- package/core/shared/skills/do-implement/SKILL.md +3 -2
- package/core/shared/skills/do-plan/SKILL.md +7 -8
- package/package.json +4 -2
- package/src/adapters/antigravity/{index.ts → index.js} +141 -105
- package/src/adapters/claude/{index.ts → index.js} +63 -49
- package/src/adapters/claude/{mcp.ts → mcp.js} +16 -16
- package/src/adapters/codex/{agents.ts → agents.js} +24 -24
- package/src/adapters/codex/{config.ts → config.js} +49 -49
- package/src/adapters/codex/{hooks.ts → hooks.js} +62 -27
- package/src/adapters/codex/{index.ts → index.js} +148 -105
- package/src/adapters/codex/{mcp.ts → mcp.js} +38 -38
- package/src/adapters/codex/{native-projection.ts → native-projection.js} +5 -5
- package/src/adapters/copilot/{index.ts → index.js} +42 -42
- package/src/adapters/{copy-tree.ts → copy-tree.js} +74 -74
- package/src/adapters/gemini/{hooks.ts → hooks.js} +78 -37
- package/src/adapters/gemini/{index.ts → index.js} +96 -57
- package/src/adapters/{hook-commands.ts → hook-commands.js} +8 -8
- package/src/adapters/{index.ts → index.js} +13 -13
- package/src/adapters/kiro/{index.ts → index.js} +49 -35
- package/src/adapters/{mcp-entries.ts → mcp-entries.js} +55 -55
- package/src/adapters/opencode/{index.ts → index.js} +52 -52
- package/src/adapters/pi/hooks.js +119 -0
- package/src/adapters/pi/{index.ts → index.js} +64 -39
- package/src/adapters/pi/{json-members.ts → json-members.js} +23 -23
- package/src/adapters/pi/{mcp.ts → mcp.js} +70 -70
- package/src/adapters/{resolve-source.ts → resolve-source.js} +1 -1
- package/src/cli/commands/{install.ts → install.js} +25 -25
- package/src/cli/commands/{list-backups.ts → list-backups.js} +4 -4
- package/src/cli/commands/{reconcile.ts → reconcile.js} +19 -19
- package/src/cli/commands/{remove.ts → remove.js} +10 -10
- package/src/cli/commands/{rollback.ts → rollback.js} +17 -17
- package/src/cli/commands/{self-update.ts → self-update.js} +3 -3
- package/src/cli/commands/{status.ts → status.js} +24 -24
- package/src/cli/commands/{tools.ts → tools.js} +16 -16
- package/src/cli/commands/{update.ts → update.js} +20 -20
- package/src/cli/{index.ts → index.js} +43 -31
- package/src/cli/{runtime-commands.ts → runtime-commands.js} +97 -58
- package/src/cli/{shared.ts → shared.js} +40 -40
- package/src/helper/{bash-helper.ts → bash-helper.js} +7 -7
- package/src/helper/{git.ts → git.js} +2 -2
- package/src/helper/{guardrails.ts → guardrails.js} +10 -10
- package/src/helper/{harness-paths.ts → harness-paths.js} +24 -24
- package/src/helper/{marker-merge.ts → marker-merge.js} +4 -4
- package/src/helper/{prompt.ts → prompt.js} +5 -5
- package/src/helper/{repo-root.ts → repo-root.js} +2 -2
- package/src/helper/{settings-merge.ts → settings-merge.js} +27 -27
- package/src/helper/{settings-scope.ts → settings-scope.js} +3 -3
- package/src/helper/{toml.ts → toml.js} +48 -48
- package/src/install/{backup-set.ts → backup-set.js} +16 -16
- package/src/install/{backup.ts → backup.js} +65 -65
- package/src/install/{context.ts → context.js} +15 -15
- package/src/install/{manifest.ts → manifest.js} +27 -27
- package/src/install/{mcp.ts → mcp.js} +30 -30
- package/src/install/{paths.ts → paths.js} +15 -15
- package/src/install/{targets.ts → targets.js} +10 -10
- package/src/install/{tool-lifecycle.ts → tool-lifecycle.js} +20 -20
- package/src/lifecycle/{bash-availability.ts → bash-availability.js} +1 -1
- package/src/lifecycle/coverage.js +57 -0
- package/src/lifecycle/hook-step.js +61 -0
- package/src/lifecycle/{index.ts → index.js} +160 -146
- package/src/lifecycle/{mcp-index.ts → mcp-index.js} +1 -1
- package/src/lifecycle/{policies.ts → policies.js} +11 -11
- package/src/lifecycle/{view.ts → view.js} +28 -28
- package/src/registry/{index.ts → index.js} +195 -66
- package/src/release/build-package.js +91 -0
- package/src/release/changelog-section.js +70 -0
- package/src/release/{sync-plugin-versions.ts → sync-plugin-versions.js} +4 -4
- package/src/runtime/advice/{cli.ts → cli.js} +31 -31
- package/src/runtime/advice/{client.ts → client.js} +44 -44
- package/src/runtime/advice/{config.ts → config.js} +16 -16
- package/src/runtime/advice/{doctor.ts → doctor.js} +32 -32
- package/src/runtime/advice/{endpoint-guard.ts → endpoint-guard.js} +20 -20
- package/src/runtime/advice/{kinds.ts → kinds.js} +16 -16
- package/src/runtime/advice/providers/{ollama-systemone.ts → ollama-systemone.js} +21 -21
- package/src/runtime/advice/{record.ts → record.js} +19 -19
- package/src/runtime/advice/{registry.ts → registry.js} +16 -16
- package/src/runtime/advice/{strength.ts → strength.js} +7 -7
- package/src/runtime/{brief-tools.ts → brief-tools.js} +50 -50
- package/src/runtime/{c4-project.ts → c4-project.js} +27 -27
- package/src/runtime/candidate.js +505 -0
- package/src/runtime/{capability-router.ts → capability-router.js} +39 -39
- package/src/runtime/{checkouts.ts → checkouts.js} +21 -21
- package/src/runtime/{claims.ts → claims.js} +44 -35
- package/src/runtime/{cli-result.ts → cli-result.js} +15 -8
- package/src/runtime/{cli.ts → cli.js} +59 -292
- package/src/runtime/{command-detect.ts → command-detect.js} +47 -47
- package/src/runtime/context/budget.js +203 -0
- package/src/runtime/context/compose.js +1367 -0
- package/src/runtime/context/delivery-store.js +102 -0
- package/src/runtime/context/mandatory.js +594 -0
- package/src/runtime/context/pointer.js +142 -0
- package/src/runtime/context-pack.js +1277 -0
- package/src/runtime/{decision-register.ts → decision-register.js} +67 -67
- package/src/runtime/evidence-fields.js +39 -0
- package/src/runtime/{evidence-ledger.ts → evidence-ledger.js} +32 -25
- package/src/runtime/failure/{capture.ts → capture.js} +29 -29
- package/src/runtime/failure/{classifier.ts → classifier.js} +4 -4
- package/src/runtime/failure/{cli.ts → cli.js} +30 -30
- package/src/runtime/failure/{home.ts → home.js} +6 -6
- package/src/runtime/failure/{store.ts → store.js} +32 -32
- package/src/runtime/{feature-resolve.ts → feature-resolve.js} +6 -6
- package/src/runtime/{freshness.ts → freshness.js} +59 -22
- package/src/runtime/{health.ts → health.js} +72 -72
- package/src/runtime/{history-compactor.ts → history-compactor.js} +25 -25
- package/src/runtime/{implementation-gate.ts → implementation-gate.js} +30 -30
- package/src/runtime/inventory/{coverage.ts → coverage.js} +10 -10
- package/src/runtime/inventory/{identity.ts → identity.js} +2 -2
- package/src/runtime/inventory/{index.ts → index.js} +36 -36
- package/src/runtime/inventory/{precedence.ts → precedence.js} +2 -2
- package/src/runtime/inventory/{read-scopes.ts → read-scopes.js} +9 -9
- package/src/runtime/inventory/{siblings.ts → siblings.js} +15 -15
- package/src/runtime/knowledge/{chunker.ts → chunker.js} +12 -12
- package/src/runtime/knowledge/{hybrid.ts → hybrid.js} +17 -17
- package/src/runtime/knowledge/{index-store.ts → index-store.js} +23 -23
- package/src/runtime/knowledge/{retrieval.ts → retrieval.js} +27 -27
- package/src/runtime/{leak-scan.ts → leak-scan.js} +12 -12
- package/src/runtime/lifecycle/{cli.ts → cli.js} +32 -32
- package/src/runtime/lifecycle/{event-store.ts → event-store.js} +48 -48
- package/src/runtime/lifecycle/{fold.ts → fold.js} +41 -41
- package/src/runtime/lifecycle/{followup.ts → followup.js} +54 -54
- package/src/runtime/lifecycle/{goal.ts → goal.js} +50 -50
- package/src/runtime/lifecycle/{intent-writer.ts → intent-writer.js} +5 -5
- package/src/runtime/lifecycle/{overview.ts → overview.js} +47 -47
- package/src/runtime/lifecycle/{release.ts → release.js} +29 -29
- package/src/runtime/lifecycle/{report-store.ts → report-store.js} +25 -25
- package/src/runtime/lifecycle/{retention.ts → retention.js} +37 -37
- package/src/runtime/lifecycle/{root.ts → root.js} +9 -9
- package/src/runtime/lifecycle/{status.ts → status.js} +64 -64
- package/src/runtime/lifecycle/{store-upkeep.ts → store-upkeep.js} +46 -46
- package/src/runtime/{locator-resolve.ts → locator-resolve.js} +7 -7
- package/src/runtime/{mask.ts → mask.js} +22 -22
- package/src/runtime/{mcp-consultations.ts → mcp-consultations.js} +54 -54
- package/src/runtime/{mcp-reach.ts → mcp-reach.js} +24 -24
- package/src/runtime/{model-router.ts → model-router.js} +20 -20
- package/src/runtime/{outcome.ts → outcome.js} +27 -27
- package/src/runtime/{reach.ts → reach.js} +21 -21
- package/src/runtime/{readiness-record.ts → readiness-record.js} +12 -12
- package/src/runtime/{readiness.ts → readiness.js} +113 -43
- package/src/runtime/{recovery.ts → recovery.js} +15 -15
- package/src/runtime/{research-request.ts → research-request.js} +34 -34
- package/src/runtime/{retrieval-plan.ts → retrieval-plan.js} +453 -104
- package/src/runtime/retrieval-record.js +223 -0
- package/src/runtime/scaffold/{artifacts.ts → artifacts.js} +22 -19
- package/src/runtime/scaffold/{fingerprint.ts → fingerprint.js} +3 -3
- package/src/runtime/scaffold/{generate.ts → generate.js} +109 -109
- package/src/runtime/scaffold/{languages.ts → languages.js} +34 -34
- package/src/runtime/source-version.js +289 -0
- package/src/runtime/storage/{cli.ts → cli.js} +9 -9
- package/src/runtime/storage/{engine.ts → engine.js} +37 -37
- package/src/runtime/storage/{registry.ts → registry.js} +19 -19
- package/src/runtime/storage/{roots.ts → roots.js} +8 -8
- package/src/runtime/storage/{rules.ts → rules.js} +26 -26
- package/src/runtime/storage/{upkeep.ts → upkeep.js} +35 -35
- package/src/runtime/storage/{work-status.ts → work-status.js} +39 -39
- package/src/runtime/{task-attempts.ts → task-attempts.js} +62 -62
- package/src/runtime/{task-classifier.ts → task-classifier.js} +34 -34
- package/src/runtime/{task-scope.ts → task-scope.js} +40 -15
- package/src/runtime/{task-state.ts → task-state.js} +86 -21
- package/src/runtime/trace/{indicators.ts → indicators.js} +22 -22
- package/src/runtime/trace/{ledger.ts → ledger.js} +42 -42
- package/src/runtime/trace/{render.ts → render.js} +7 -7
- package/src/runtime/trace/{views.ts → views.js} +40 -40
- package/src/runtime/verification/{contract-runner.ts → contract-runner.js} +23 -23
- package/src/runtime/verification/{engine.ts → engine.js} +143 -143
- package/src/runtime/verification/{registry.ts → registry.js} +6 -6
- package/src/runtime/verification/{scope-bound.ts → scope-bound.js} +50 -50
- package/src/runtime/verification/{test-selection.ts → test-selection.js} +28 -28
- package/src/runtime/{workflow-engine.ts → workflow-engine.js} +50 -50
- package/src/runtime/{workflow-orchestrator.ts → workflow-orchestrator.js} +91 -91
- package/src/runtime/{worktree.ts → worktree.js} +31 -31
- package/src/state/{index.ts → index.js} +26 -26
- package/src/state/{lockfile.ts → lockfile.js} +20 -20
- package/src/state/{run-lock.ts → run-lock.js} +44 -44
- package/core/.antigravity-plugin/hooks.json +0 -39
- package/core/harnesses/shared/hooks/adapters/arg-fields.js +0 -19
- package/core/harnesses/shared/hooks/adapters/gemini.js +0 -114
- package/core/harnesses/shared/hooks/hooks.json +0 -47
- package/src/runtime/context-pack.ts +0 -492
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
+
case $0 in */*) _DOFLOW_POLDIR=${0%/*} ;; *) _DOFLOW_POLDIR=. ;; esac
|
|
2
3
|
# pre-implementation-gate.sh — Canonical Policy Library: PreToolUse(Edit|Write|...)
|
|
3
4
|
# backstop for the doflow implement gate. The HARD half of the one enforced
|
|
4
5
|
# gate (the other half is the prompt-level do-prereqs.sh inside
|
|
@@ -21,7 +22,7 @@
|
|
|
21
22
|
# - edit target is under agent-docs/ -> allow (editing the artifacts themselves)
|
|
22
23
|
# - edit target outside the repo -> allow
|
|
23
24
|
# It reads files with jq and lists checkouts with one git call; it never runs
|
|
24
|
-
# the DoFlow runtime.
|
|
25
|
+
# the DoFlow runtime. Missing or malformed v1 input is denied as out of step; other faults follow IC-011.
|
|
25
26
|
#
|
|
26
27
|
# Canonical Policy Script Contract (design.md §4):
|
|
27
28
|
# env DOFLOW_PROJECT_DIR (repo root, if the front door sets it),
|
|
@@ -30,17 +31,9 @@
|
|
|
30
31
|
# (each already read by the harness-specific dispatcher this policy
|
|
31
32
|
# replaces — kept so an unmodified front door still resolves its own
|
|
32
33
|
# harness's do-paths.sh resolver)
|
|
33
|
-
# stdin a
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
# tool_input.command; Kiro: field names undocumented) — this script
|
|
37
|
-
# reads the union of known shapes rather than assuming one, since a
|
|
38
|
-
# native front door (Claude/Codex/Kiro) execs this script directly
|
|
39
|
-
# with no payload translation of its own.
|
|
40
|
-
# exit 0 = allow, non-zero = deny with the reason on stderr. A front door
|
|
41
|
-
# translates this into whatever JSON/exit-code shape its harness
|
|
42
|
-
# natively expects — this script never speaks a harness-specific
|
|
43
|
-
# decision shape.
|
|
34
|
+
# stdin a native payload encoded as envelope v1 by the lifecycle bridge.
|
|
35
|
+
# exit exit 2 + doflow_decide deny = deny; other faults per IC-011. A front
|
|
36
|
+
# door translates this into its native response shape.
|
|
44
37
|
#
|
|
45
38
|
# Reconciliation note (022-normalize-hooks, Phase A): claude/gemini/codex/kiro
|
|
46
39
|
# each shipped a separately-adapted copy of this gate; antigravity's copy
|
|
@@ -58,74 +51,71 @@
|
|
|
58
51
|
|
|
59
52
|
set -uo pipefail
|
|
60
53
|
|
|
61
|
-
command -v jq >/dev/null 2>&1 || exit 0 # no jq -> cannot evaluate -> allow
|
|
62
|
-
|
|
63
54
|
INPUT=$(cat)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
# A host adapter that has already decoded its native event sends
|
|
67
|
-
# {"doflow_event": {"operation": "edit", "paths": [..], "projectRoot": "..", "taskId": ".."}}
|
|
68
|
-
# and this policy consumes it verbatim. The union decoder below remains the
|
|
69
|
-
# fallback for front doors that still forward raw native payloads; per-harness
|
|
70
|
-
# knowledge belongs in those adapters, and each one that starts sending the
|
|
71
|
-
# envelope retires its share of the union.
|
|
72
|
-
ENVELOPE_ROOT=""
|
|
73
|
-
ENVELOPE_TASK=""
|
|
74
|
-
ENVELOPE_OP=$(printf '%s' "$INPUT" | jq -r '.doflow_event.operation // empty' 2>/dev/null)
|
|
75
|
-
if [ -n "$ENVELOPE_OP" ]; then
|
|
76
|
-
[ "$ENVELOPE_OP" = "edit" ] || exit 0 # only edits are gated
|
|
77
|
-
FILES=$(printf '%s' "$INPUT" | jq -r '.doflow_event.paths[]? // empty' 2>/dev/null | sed '/^$/d' | sort -u)
|
|
78
|
-
[ -n "$FILES" ] || exit 0
|
|
79
|
-
ENVELOPE_ROOT=$(printf '%s' "$INPUT" | jq -r '.doflow_event.projectRoot // empty' 2>/dev/null)
|
|
80
|
-
ENVELOPE_TASK=$(printf '%s' "$INPUT" | jq -r '.doflow_event.taskId // empty' 2>/dev/null)
|
|
55
|
+
if [ -f "$_DOFLOW_POLDIR/contract.sh" ]; then
|
|
56
|
+
. "$_DOFLOW_POLDIR/contract.sh"
|
|
81
57
|
else
|
|
58
|
+
doflow_decision_init() { :; }
|
|
59
|
+
doflow_decide() { :; }
|
|
60
|
+
doflow_read_envelope() { DF_ENV_CLASS=missing; DF_ENV_CODE=contract-missing; }
|
|
61
|
+
( . "$_DOFLOW_POLDIR/capture-failure.sh"; doflow_capture_failure pre-implementation-gate contract-missing ) >/dev/null 2>&1 || true
|
|
62
|
+
if [[ "${DOFLOW_AGENT:-}" =~ ^[a-z]+$ ]]; then
|
|
63
|
+
printf '[pre-implementation-gate] DoFlow hooks out of step; run: doflow update -t %s\n' "$DOFLOW_AGENT" >&2
|
|
64
|
+
else
|
|
65
|
+
printf '[pre-implementation-gate] DoFlow hooks out of step; run: doflow update\n' >&2
|
|
66
|
+
fi
|
|
67
|
+
doflow_decide deny contract-missing
|
|
68
|
+
exit 2
|
|
69
|
+
fi
|
|
70
|
+
doflow_decision_init pre-implementation-gate 1
|
|
71
|
+
command -v jq >/dev/null 2>&1 || {
|
|
72
|
+
doflow_decide error dependency-missing:jq
|
|
73
|
+
( . "$_DOFLOW_POLDIR/capture-failure.sh"; doflow_capture_failure pre-implementation-gate dependency-missing:jq ) >/dev/null 2>&1 || true
|
|
74
|
+
exit 0
|
|
75
|
+
}
|
|
82
76
|
|
|
83
|
-
#
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
tool=$(printf '%s' "$INPUT" | jq -r "${field} // empty" 2>/dev/null)
|
|
87
|
-
[ -n "$tool" ] && break
|
|
88
|
-
done
|
|
89
|
-
|
|
90
|
-
is_patch_tool=false
|
|
91
|
-
case "$tool" in
|
|
92
|
-
Edit|Write|MultiEdit|edit_file|write_file|create_file|replace_file|replace) ;;
|
|
93
|
-
write_to_file|replace_file_content|multi_replace_file_content) ;;
|
|
94
|
-
apply_patch) is_patch_tool=true ;;
|
|
95
|
-
*) exit 0 ;;
|
|
96
|
-
esac
|
|
97
|
-
|
|
98
|
-
# ── Candidate files to gate ────────────────────────────────────────────────────
|
|
99
|
-
# Normal edit tools: one file, from the union of field names harnesses use.
|
|
100
|
-
# apply_patch (Codex): the tool has no file_path field at all — the paths are
|
|
101
|
-
# embedded in a unified diff under tool_input.command.
|
|
102
|
-
FILES=""
|
|
103
|
-
if [ "$is_patch_tool" = true ]; then
|
|
104
|
-
PATCH=$(printf '%s' "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
105
|
-
[ -n "$PATCH" ] || exit 0
|
|
106
|
-
# "#" (not the original codex script's "|") is the sed delimiter here: BSD
|
|
107
|
-
# sed (macOS, no GNU coreutils) misparses "s|...(Add|Update|Delete)...|...|"
|
|
108
|
-
# because it splits on every unescaped "|", including the ones meant as ERE
|
|
109
|
-
# alternation inside the pattern — a portability bug in the source script
|
|
110
|
-
# this reconciliation fixes rather than ports, since it silently broke
|
|
111
|
-
# apply_patch parsing under this exact invocation on any non-GNU-sed host.
|
|
112
|
-
FILES=$(printf '%s\n' "$PATCH" | sed -nE \
|
|
77
|
+
# Parse v1 apply_patch paths using the shared parser.
|
|
78
|
+
parse_patch_paths() {
|
|
79
|
+
printf '%s\n' "$1" | sed -nE \
|
|
113
80
|
-e 's#^\*\*\* (Add|Update|Delete) File: (.+)$#\2#p' \
|
|
114
81
|
-e 's#^\+\+\+ (a/|b/)?(.+)$#\2#p' \
|
|
115
|
-
| sed '/^\/dev\/null$/d' | sort -u
|
|
116
|
-
|
|
82
|
+
| sed '/^\/dev\/null$/d' | sort -u
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
DF_ENV_CLASS=missing DF_ENV_CODE=envelope-missing
|
|
86
|
+
doflow_read_envelope || true
|
|
87
|
+
|
|
88
|
+
# ── Envelope v1 ───────────────────────────────────────────────────────────────
|
|
89
|
+
ENVELOPE_ROOT=""
|
|
90
|
+
ENVELOPE_TASK=""
|
|
91
|
+
if [ "$DF_ENV_CLASS" = v1 ]; then
|
|
92
|
+
ENVELOPE_OP=$DF_ENV_OP
|
|
93
|
+
[ "$ENVELOPE_OP" = "edit" ] || exit 0 # only edits are gated
|
|
94
|
+
FILES=$DF_ENV_PATHS
|
|
95
|
+
ENVELOPE_ROOT=$DF_ENV_ROOT
|
|
96
|
+
ENVELOPE_TASK=$DF_ENV_TASK
|
|
97
|
+
if [ -z "$FILES" ] && [ -z "$DF_ENV_PATCH" ]; then
|
|
98
|
+
doflow_decide unsupported edit-without-target
|
|
99
|
+
( . "$_DOFLOW_POLDIR/capture-failure.sh"; doflow_capture_failure pre-implementation-gate edit-without-target ) >/dev/null 2>&1 || true
|
|
100
|
+
exit 0
|
|
101
|
+
fi
|
|
102
|
+
if [ -z "$FILES" ] && [ -n "$DF_ENV_PATCH" ]; then
|
|
103
|
+
FILES=$(parse_patch_paths "$DF_ENV_PATCH")
|
|
104
|
+
[ -n "$FILES" ] || exit 0
|
|
105
|
+
fi
|
|
106
|
+
elif [ "$DF_ENV_CLASS" = unsupported ]; then
|
|
107
|
+
doflow_decide unsupported envelope-version
|
|
108
|
+
( . "$_DOFLOW_POLDIR/capture-failure.sh"; doflow_capture_failure pre-implementation-gate unsupported:envelope-version ) >/dev/null 2>&1 || true
|
|
109
|
+
exit 0
|
|
117
110
|
else
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
FILES="$file"
|
|
111
|
+
if [ "${DF_ENV_CODE:-envelope-missing}" != contract-missing ]; then
|
|
112
|
+
( . "$_DOFLOW_POLDIR/capture-failure.sh"; doflow_capture_failure pre-implementation-gate envelope-missing ) >/dev/null 2>&1 || true
|
|
113
|
+
fi
|
|
114
|
+
if [[ "${DOFLOW_AGENT:-}" =~ ^[a-z]+$ ]]; then printf '[pre-implementation-gate] DoFlow hooks out of step; run: doflow update -t %s\n' "$DOFLOW_AGENT" >&2; else printf '[pre-implementation-gate] DoFlow hooks out of step; run: doflow update\n' >&2; fi
|
|
115
|
+
doflow_decide deny "${DF_ENV_CODE:-envelope-missing}"
|
|
116
|
+
exit 2
|
|
125
117
|
fi
|
|
126
118
|
|
|
127
|
-
fi # end legacy union decoder
|
|
128
|
-
|
|
129
119
|
# ── Resolve repo root ───────────────────────────────────────────────────────
|
|
130
120
|
# DOFLOW_PROJECT_DIR (explicit) beats the envelope's projectRoot beats git discovery.
|
|
131
121
|
ROOT="${DOFLOW_PROJECT_DIR:-$ENVELOPE_ROOT}"
|
|
@@ -384,13 +374,14 @@ PLANNED=false
|
|
|
384
374
|
# plan.md all exist. Classes that cannot be a feature skip this check.
|
|
385
375
|
if [ "$exempt" = false ] && [ -n "$FEATURE_ROOT" ] && [ "$PLANNED" = false ]; then
|
|
386
376
|
echo "[pre-implementation-gate] doflow gate: feature $feature_dir is missing requirement.md, design.md, or plan.md — run /do-brainstorm, /do-design, then /do-plan before editing source. (Edits under agent-docs/ are always allowed; skip the flow by removing the feature dir.)" >&2
|
|
377
|
+
doflow_decide deny missing-artifacts
|
|
387
378
|
exit 2
|
|
388
379
|
fi
|
|
389
380
|
|
|
390
381
|
# ── Readiness ────────────────────────────────────────────────────────────────
|
|
391
382
|
# The workflow registry the installed runtime reads: beside this policy in an
|
|
392
383
|
# install, the source tree's own, the project's install, the global install.
|
|
393
|
-
POLICY_DIR=$
|
|
384
|
+
POLICY_DIR=$_DOFLOW_POLDIR
|
|
394
385
|
REGISTRY=""
|
|
395
386
|
for candidate in \
|
|
396
387
|
"${POLICY_DIR:+$POLICY_DIR/../../../runtime/core/registry/workflows.json}" \
|
|
@@ -480,16 +471,19 @@ gate="doflow gate readiness-before-implementation: task '$task_id'"
|
|
|
480
471
|
rest="Next: $next, then gather what it lists until it reports READY. Nothing was changed."
|
|
481
472
|
if [ -z "$FOUND" ]; then
|
|
482
473
|
printf "%s has no readiness record for the '%s' template. %s\n" "$gate" "$STAGE_TEMPLATE" "$rest" >&2
|
|
474
|
+
doflow_decide deny readiness-missing
|
|
483
475
|
exit 2
|
|
484
476
|
fi
|
|
485
477
|
if [ "$record_state" != READY ]; then
|
|
486
478
|
printf "%s was last evaluated %s at %s against the '%s' template, not READY. %s\n" \
|
|
487
479
|
"$gate" "$record_state" "$record_at" "$STAGE_TEMPLATE" "$rest" >&2
|
|
480
|
+
doflow_decide deny readiness-not-ready
|
|
488
481
|
exit 2
|
|
489
482
|
fi
|
|
490
483
|
if [ "$record_class" != "$STAGE_TEMPLATE" ]; then
|
|
491
484
|
printf "%s has a READY record for the '%s' template, and this stage needs '%s'. %s\n" \
|
|
492
485
|
"$gate" "$record_class" "$STAGE_TEMPLATE" "$rest" >&2
|
|
486
|
+
doflow_decide deny readiness-wrong-template
|
|
493
487
|
exit 2
|
|
494
488
|
fi
|
|
495
489
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
+
case $0 in */*) _DOFLOW_POLDIR=${0%/*} ;; *) _DOFLOW_POLDIR=. ;; esac
|
|
2
3
|
# session-context.sh — Canonical Policy Library: SessionStart guard policy
|
|
3
4
|
#
|
|
4
5
|
# Fires when a harness starts a new session. Captures git state into a
|
|
@@ -11,24 +12,53 @@
|
|
|
11
12
|
# Canonical Policy Script Contract (design.md §4):
|
|
12
13
|
# env DOFLOW_PROJECT_DIR, DOFLOW_AGENT (both optional here; DOFLOW_AGENT
|
|
13
14
|
# only affects the "last_agent" field recorded in meta.json)
|
|
14
|
-
# stdin the
|
|
15
|
-
#
|
|
15
|
+
# stdin envelope v1 from the native SessionStart payload, including
|
|
16
|
+
# the session_id and cwd fields used by this policy.
|
|
16
17
|
# exit this policy never denies — it always exits 0 (side-effect only)
|
|
17
18
|
#
|
|
18
19
|
# Depends on lib.sh (require_jq, json_field, ensure_session_dir,
|
|
19
20
|
# ensure_project_dir, cwd_hash, run_with_timeout, $SESSIONS_LOG) being
|
|
20
21
|
# discoverable next to this script when it runs — a front door invoking this
|
|
21
22
|
# canonical script is responsible for making lib.sh resolvable via
|
|
22
|
-
#
|
|
23
|
+
# the sibling lib.sh, exactly as every harness's own SessionStart script
|
|
23
24
|
# already required before this policy moved to a shared location.
|
|
24
25
|
|
|
25
26
|
set -euo pipefail
|
|
26
27
|
# shellcheck source=lib.sh
|
|
27
|
-
source "$
|
|
28
|
-
require_jq
|
|
29
|
-
|
|
28
|
+
source "$_DOFLOW_POLDIR/lib.sh"
|
|
30
29
|
INPUT=$(cat)
|
|
31
|
-
|
|
30
|
+
POLICY_DIR=$_DOFLOW_POLDIR
|
|
31
|
+
if [ -f "$POLICY_DIR/contract.sh" ]; then
|
|
32
|
+
# shellcheck source=contract.sh
|
|
33
|
+
source "$POLICY_DIR/contract.sh"
|
|
34
|
+
doflow_decision_init session-context 1
|
|
35
|
+
else
|
|
36
|
+
doflow_decision_init() { :; }
|
|
37
|
+
doflow_decide() { :; }
|
|
38
|
+
( . "$POLICY_DIR/capture-failure.sh"; doflow_capture_failure session-context contract-missing ) >/dev/null 2>&1 || true
|
|
39
|
+
exit 0
|
|
40
|
+
fi
|
|
41
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
42
|
+
doflow_decide error dependency-missing:jq
|
|
43
|
+
( . "$POLICY_DIR/capture-failure.sh"; doflow_capture_failure session-context dependency-missing:jq ) >/dev/null 2>&1 || true
|
|
44
|
+
fi
|
|
45
|
+
require_jq
|
|
46
|
+
SESSION_ID="" ENVELOPE_CLASS=missing
|
|
47
|
+
{ IFS= read -r -d '' ENVELOPE_CLASS; IFS= read -r -d '' SESSION_ID; } < <(
|
|
48
|
+
printf '%s' "$INPUT" | jq -j 'if ((. | type) != "object") or ((.doflow_event | type) != "object") or ((.doflow_event | has("version")) | not) then "missing" elif ((.doflow_event.version | type) == "number") and .doflow_event.version == 1 then "v1" else "unsupported" end, "\u0000", (.session_id // "" | if type == "string" then rtrimstr("\n") else tojson end), "\u0000"' 2>/dev/null
|
|
49
|
+
) || true
|
|
50
|
+
ENVELOPE_CLASS=${ENVELOPE_CLASS:-missing}
|
|
51
|
+
if [ ! -f "$POLICY_DIR/contract.sh" ]; then ENVELOPE_CLASS=missing; fi
|
|
52
|
+
if [ "$ENVELOPE_CLASS" = unsupported ] || [ "$ENVELOPE_CLASS" = missing ]; then
|
|
53
|
+
code=envelope-version
|
|
54
|
+
[ "$ENVELOPE_CLASS" = missing ] && code=envelope-missing
|
|
55
|
+
[ -f "$POLICY_DIR/contract.sh" ] || code=contract-missing
|
|
56
|
+
doflow_decide unsupported "$code"
|
|
57
|
+
if [ -f "$POLICY_DIR/contract.sh" ]; then
|
|
58
|
+
( . "$_DOFLOW_POLDIR/capture-failure.sh"; doflow_capture_failure session-context "$code" ) >/dev/null 2>&1 || true
|
|
59
|
+
fi
|
|
60
|
+
exit 0
|
|
61
|
+
fi
|
|
32
62
|
CWD=$(json_field "$INPUT" ".cwd")
|
|
33
63
|
|
|
34
64
|
# Guard: if we can't determine the session, nothing to do
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
+
case $0 in */*) _DOFLOW_POLDIR=${0%/*} ;; *) _DOFLOW_POLDIR=. ;; esac
|
|
2
3
|
# stop-check.sh — Canonical Policy Library: Stop hook
|
|
3
4
|
#
|
|
4
5
|
# Two responsibilities:
|
|
@@ -24,14 +25,8 @@
|
|
|
24
25
|
#
|
|
25
26
|
# Canonical Policy Script Contract (design.md §4):
|
|
26
27
|
# env DOFLOW_PROJECT_DIR, DOFLOW_AGENT (both optional here)
|
|
27
|
-
# stdin a
|
|
28
|
-
#
|
|
29
|
-
# camelCase transcriptPath) — this script reads the union of known
|
|
30
|
-
# field names rather than assuming one, since a native front door
|
|
31
|
-
# (Claude/Codex/Kiro) execs this script directly with no payload
|
|
32
|
-
# translation of its own.
|
|
33
|
-
# exit 0 = allow the stop, non-zero = deny (block the stop) with the
|
|
34
|
-
# reason on stderr.
|
|
28
|
+
# stdin envelope v1 from a native front door via the shared lifecycle bridge.
|
|
29
|
+
# exit exit 2 + doflow_decide deny = deny; other faults per IC-011.
|
|
35
30
|
#
|
|
36
31
|
# Depends on lib.sh (require_jq, json_field, ensure_session_dir,
|
|
37
32
|
# run_with_timeout) being discoverable next to this script when it runs — see
|
|
@@ -48,11 +43,41 @@
|
|
|
48
43
|
|
|
49
44
|
set -euo pipefail
|
|
50
45
|
# shellcheck source=lib.sh
|
|
51
|
-
source "$
|
|
52
|
-
require_jq
|
|
53
|
-
|
|
46
|
+
source "$_DOFLOW_POLDIR/lib.sh"
|
|
54
47
|
INPUT=$(cat)
|
|
55
|
-
|
|
48
|
+
# Decision records stay off every stream unless a bridge supplied the fd.
|
|
49
|
+
POLICY_DIR=$_DOFLOW_POLDIR
|
|
50
|
+
if [ -f "$POLICY_DIR/contract.sh" ]; then
|
|
51
|
+
# shellcheck source=contract.sh
|
|
52
|
+
source "$POLICY_DIR/contract.sh"
|
|
53
|
+
doflow_decision_init stop-check 1
|
|
54
|
+
else
|
|
55
|
+
doflow_decision_init() { :; }
|
|
56
|
+
doflow_decide() { :; }
|
|
57
|
+
( . "$POLICY_DIR/capture-failure.sh"; doflow_capture_failure stop-check contract-missing ) >/dev/null 2>&1 || true
|
|
58
|
+
exit 0
|
|
59
|
+
fi
|
|
60
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
61
|
+
doflow_decide error dependency-missing:jq
|
|
62
|
+
( . "$POLICY_DIR/capture-failure.sh"; doflow_capture_failure stop-check dependency-missing:jq ) >/dev/null 2>&1 || true
|
|
63
|
+
fi
|
|
64
|
+
require_jq
|
|
65
|
+
SESSION_ID="" ENVELOPE_CLASS=missing
|
|
66
|
+
{ IFS= read -r -d '' ENVELOPE_CLASS; IFS= read -r -d '' SESSION_ID; } < <(
|
|
67
|
+
printf '%s' "$INPUT" | jq -j 'if ((. | type) != "object") or ((.doflow_event | type) != "object") or ((.doflow_event | has("version")) | not) then "missing" elif ((.doflow_event.version | type) == "number") and .doflow_event.version == 1 then "v1" else "unsupported" end, "\u0000", (.session_id // "" | if type == "string" then rtrimstr("\n") else tojson end), "\u0000"' 2>/dev/null
|
|
68
|
+
) || true
|
|
69
|
+
ENVELOPE_CLASS=${ENVELOPE_CLASS:-missing}
|
|
70
|
+
if [ ! -f "$POLICY_DIR/contract.sh" ]; then ENVELOPE_CLASS=missing; fi
|
|
71
|
+
if [ "$ENVELOPE_CLASS" = unsupported ] || [ "$ENVELOPE_CLASS" = missing ]; then
|
|
72
|
+
code=envelope-version
|
|
73
|
+
[ "$ENVELOPE_CLASS" = missing ] && code=envelope-missing
|
|
74
|
+
[ -f "$POLICY_DIR/contract.sh" ] || code=contract-missing
|
|
75
|
+
doflow_decide unsupported "$code"
|
|
76
|
+
if [ -f "$POLICY_DIR/contract.sh" ]; then
|
|
77
|
+
( . "$_DOFLOW_POLDIR/capture-failure.sh"; doflow_capture_failure stop-check "$code" ) >/dev/null 2>&1 || true
|
|
78
|
+
fi
|
|
79
|
+
exit 0
|
|
80
|
+
fi
|
|
56
81
|
|
|
57
82
|
TRANSCRIPT_PATH=$(json_field "$INPUT" ".transcript_path")
|
|
58
83
|
[ -z "$TRANSCRIPT_PATH" ] && TRANSCRIPT_PATH=$(json_field "$INPUT" ".transcriptPath")
|
|
@@ -107,7 +132,7 @@ if [ -n "$SESSION_ID" ]; then
|
|
|
107
132
|
# before the {} reply is invalid hook output)
|
|
108
133
|
if [ ${#py_files[@]} -gt 0 ]; then
|
|
109
134
|
if command -v ruff &>/dev/null; then
|
|
110
|
-
nohup ruff format "${py_files[@]}" </dev/null >/dev/null 2>&1 &
|
|
135
|
+
nohup ruff format "${py_files[@]}" </dev/null >/dev/null 2>&1 3>&- 5>&- &
|
|
111
136
|
run_with_timeout 2 -- ruff check "${py_files[@]}" >&2 || true
|
|
112
137
|
fi
|
|
113
138
|
fi
|
|
@@ -115,7 +140,7 @@ if [ -n "$SESSION_ID" ]; then
|
|
|
115
140
|
# JS/TS: async fix only (eslint --fix rarely needs the caller's immediate attention)
|
|
116
141
|
if [ ${#js_files[@]} -gt 0 ]; then
|
|
117
142
|
if command -v eslint &>/dev/null; then
|
|
118
|
-
nohup eslint --fix "${js_files[@]}" </dev/null >/dev/null 2>&1 &
|
|
143
|
+
nohup eslint --fix "${js_files[@]}" </dev/null >/dev/null 2>&1 3>&- 5>&- &
|
|
119
144
|
fi
|
|
120
145
|
fi
|
|
121
146
|
|
|
@@ -129,7 +154,7 @@ if [ -n "$SESSION_ID" ]; then
|
|
|
129
154
|
# Java: async spotlessApply (slow — only if gradlew exists in cwd)
|
|
130
155
|
if [ "$java_has_files" = "true" ]; then
|
|
131
156
|
if [ -x "./gradlew" ]; then
|
|
132
|
-
nohup ./gradlew spotlessApply </dev/null >/dev/null 2>&1 &
|
|
157
|
+
nohup ./gradlew spotlessApply </dev/null >/dev/null 2>&1 3>&- 5>&- &
|
|
133
158
|
fi
|
|
134
159
|
fi
|
|
135
160
|
|
|
@@ -143,6 +168,7 @@ if [ -n "$SESSION_ID" ]; then
|
|
|
143
168
|
if [ -n "$DOFLOW_RUN" ]; then
|
|
144
169
|
leak_out="$(run_with_timeout 2 -- "$DOFLOW_RUN" leak-scan "${leak_args[@]}" 2>/dev/null || true)"
|
|
145
170
|
if [ -n "$leak_out" ] && printf '%s' "$leak_out" | grep -q ':[0-9]'; then
|
|
171
|
+
doflow_decide advisory internal-identifiers-found
|
|
146
172
|
printf 'doflow: internal identifiers found in shipped files (warning, not a block):\n%s\n' \
|
|
147
173
|
"$leak_out" >&2
|
|
148
174
|
fi
|
|
@@ -258,6 +284,7 @@ if grep -qiE -- "$STUB_PATTERN" <<<"$STUB_TEXT" 2>/dev/null; then
|
|
|
258
284
|
fi
|
|
259
285
|
[ -n "$MESSAGE_SUM" ] && [ -n "$GUARD_FILE" ] && { printf '%s\n' "$MESSAGE_SUM" > "$GUARD_FILE"; } 2>/dev/null || true
|
|
260
286
|
echo "[stop-check] The last response contains an unfinished-work marker (TODO/FIXME/stub comment or a not-implemented raise/throw)." >&2
|
|
287
|
+
doflow_decide deny unfinished-work
|
|
261
288
|
exit 2
|
|
262
289
|
fi
|
|
263
290
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
+
case $0 in */*) _DOFLOW_POLDIR=${0%/*} ;; *) _DOFLOW_POLDIR=. ;; esac
|
|
2
3
|
# subagent-audit.sh — Canonical Policy Library: SubagentStart + SubagentStop hook
|
|
3
4
|
# (pure observability, no deny path)
|
|
4
5
|
#
|
|
@@ -18,11 +19,40 @@
|
|
|
18
19
|
|
|
19
20
|
set -euo pipefail
|
|
20
21
|
# shellcheck source=lib.sh
|
|
21
|
-
source "$
|
|
22
|
-
require_jq
|
|
23
|
-
|
|
22
|
+
source "$_DOFLOW_POLDIR/lib.sh"
|
|
24
23
|
INPUT=$(cat)
|
|
25
|
-
|
|
24
|
+
POLICY_DIR=$_DOFLOW_POLDIR
|
|
25
|
+
if [ -f "$POLICY_DIR/contract.sh" ]; then
|
|
26
|
+
# shellcheck source=contract.sh
|
|
27
|
+
source "$POLICY_DIR/contract.sh"
|
|
28
|
+
doflow_decision_init subagent-audit 1
|
|
29
|
+
else
|
|
30
|
+
doflow_decision_init() { :; }
|
|
31
|
+
doflow_decide() { :; }
|
|
32
|
+
( . "$POLICY_DIR/capture-failure.sh"; doflow_capture_failure subagent-audit contract-missing ) >/dev/null 2>&1 || true
|
|
33
|
+
exit 0
|
|
34
|
+
fi
|
|
35
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
36
|
+
doflow_decide error dependency-missing:jq
|
|
37
|
+
( . "$POLICY_DIR/capture-failure.sh"; doflow_capture_failure subagent-audit dependency-missing:jq ) >/dev/null 2>&1 || true
|
|
38
|
+
fi
|
|
39
|
+
require_jq
|
|
40
|
+
SESSION_ID="" ENVELOPE_CLASS=missing
|
|
41
|
+
{ IFS= read -r -d '' ENVELOPE_CLASS; IFS= read -r -d '' SESSION_ID; } < <(
|
|
42
|
+
printf '%s' "$INPUT" | jq -j 'if ((. | type) != "object") or ((.doflow_event | type) != "object") or ((.doflow_event | has("version")) | not) then "missing" elif ((.doflow_event.version | type) == "number") and .doflow_event.version == 1 then "v1" else "unsupported" end, "\u0000", (.session_id // "" | if type == "string" then rtrimstr("\n") else tojson end), "\u0000"' 2>/dev/null
|
|
43
|
+
) || true
|
|
44
|
+
ENVELOPE_CLASS=${ENVELOPE_CLASS:-missing}
|
|
45
|
+
if [ ! -f "$POLICY_DIR/contract.sh" ]; then ENVELOPE_CLASS=missing; fi
|
|
46
|
+
if [ "$ENVELOPE_CLASS" = unsupported ] || [ "$ENVELOPE_CLASS" = missing ]; then
|
|
47
|
+
code=envelope-version
|
|
48
|
+
[ "$ENVELOPE_CLASS" = missing ] && code=envelope-missing
|
|
49
|
+
[ -f "$POLICY_DIR/contract.sh" ] || code=contract-missing
|
|
50
|
+
doflow_decide unsupported "$code"
|
|
51
|
+
if [ -f "$POLICY_DIR/contract.sh" ]; then
|
|
52
|
+
( . "$_DOFLOW_POLDIR/capture-failure.sh"; doflow_capture_failure subagent-audit "$code" ) >/dev/null 2>&1 || true
|
|
53
|
+
fi
|
|
54
|
+
exit 0
|
|
55
|
+
fi
|
|
26
56
|
|
|
27
57
|
# Guard: if we can't determine the session, nothing to do
|
|
28
58
|
[[ -z "$SESSION_ID" ]] && exit 0
|
|
@@ -41,6 +71,6 @@ jq -nc \
|
|
|
41
71
|
--arg agent_type "$AGENT_TYPE" \
|
|
42
72
|
--arg agent_id "$AGENT_ID" \
|
|
43
73
|
'{timestamp: $ts, session_id: $session, event: $event, agent_type: $agent_type, agent_id: $agent_id}' \
|
|
44
|
-
>> "$SESSION_PATH/subagent-audit.log" 2>/dev/null ||
|
|
74
|
+
>> "$SESSION_PATH/subagent-audit.log" 2>/dev/null || doflow_decide error audit-write-failed
|
|
45
75
|
|
|
46
76
|
exit 0
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
+
case $0 in */*) _DOFLOW_POLDIR=${0%/*} ;; *) _DOFLOW_POLDIR=. ;; esac
|
|
2
3
|
# user-prompt-submit.sh — Canonical Policy Library: UserPromptSubmit hook
|
|
3
4
|
#
|
|
4
5
|
# 022-hooks-remaining-duplication: claude and codex's UserPromptSubmit policies share the same
|
|
@@ -26,22 +27,52 @@
|
|
|
26
27
|
|
|
27
28
|
set -euo pipefail
|
|
28
29
|
# shellcheck source=lib.sh
|
|
29
|
-
source "$
|
|
30
|
-
require_jq
|
|
31
|
-
|
|
30
|
+
source "$_DOFLOW_POLDIR/lib.sh"
|
|
32
31
|
INPUT=$(cat)
|
|
32
|
+
POLICY_DIR=$_DOFLOW_POLDIR
|
|
33
|
+
if [ -f "$POLICY_DIR/contract.sh" ]; then
|
|
34
|
+
# shellcheck source=contract.sh
|
|
35
|
+
source "$POLICY_DIR/contract.sh"
|
|
36
|
+
doflow_decision_init user-prompt-submit 1
|
|
37
|
+
else
|
|
38
|
+
doflow_decision_init() { :; }
|
|
39
|
+
doflow_decide() { :; }
|
|
40
|
+
( . "$POLICY_DIR/capture-failure.sh"; doflow_capture_failure user-prompt-submit contract-missing ) >/dev/null 2>&1 || true
|
|
41
|
+
printf '{}'
|
|
42
|
+
exit 0
|
|
43
|
+
fi
|
|
44
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
45
|
+
doflow_decide error dependency-missing:jq
|
|
46
|
+
( . "$POLICY_DIR/capture-failure.sh"; doflow_capture_failure user-prompt-submit dependency-missing:jq ) >/dev/null 2>&1 || true
|
|
47
|
+
fi
|
|
48
|
+
require_jq
|
|
33
49
|
# One jq pass over the payload for every field this hook reads (each pass costs about 8 ms on a
|
|
34
50
|
# 20 KB prompt, and the budget is 100 ms), NUL-separated: session_id, cwd and session_title, null
|
|
35
51
|
# and false read as empty as json_field does, with any NUL inside a value dropped so no field can
|
|
36
52
|
# shift into the next; then `subagent` when agent_id or agent_type holds anything but null or "".
|
|
37
|
-
SESSION_ID="" CWD="" PAYLOAD_TITLE="" SUBAGENT=""
|
|
38
|
-
{ IFS= read -r -d '' SESSION_ID; IFS= read -r -d '' CWD; IFS= read -r -d '' PAYLOAD_TITLE; IFS= read -r -d '' SUBAGENT; } < <(
|
|
53
|
+
SESSION_ID="" CWD="" PAYLOAD_TITLE="" SUBAGENT="" ENVELOPE_CLASS=missing
|
|
54
|
+
{ IFS= read -r -d '' SESSION_ID; IFS= read -r -d '' CWD; IFS= read -r -d '' PAYLOAD_TITLE; IFS= read -r -d '' SUBAGENT; IFS= read -r -d '' ENVELOPE_CLASS; } < <(
|
|
39
55
|
printf '%s' "$INPUT" | jq -j '. as $p
|
|
40
|
-
| [$p.session_id, $p.cwd, $p.session_title]
|
|
56
|
+
| [if ($p.session_id | type) == "string" then ($p.session_id | rtrimstr("\n")) else $p.session_id end, $p.cwd, $p.session_title]
|
|
41
57
|
| map(if . == null or . == false then "" elif type == "string" then . else tojson end | split("\u0000") | join(""))
|
|
42
58
|
| . + [if any($p.agent_id, $p.agent_type; . != null and . != "") then "subagent" else "" end]
|
|
59
|
+
| . + [if (($p | type) != "object") or (($p.doflow_event | type) != "object") or (($p.doflow_event | has("version")) | not) then "missing" elif (($p.doflow_event.version | type) == "number") and $p.doflow_event.version == 1 then "v1" else "unsupported" end]
|
|
43
60
|
| join("\u0000") + "\u0000"' 2>/dev/null
|
|
44
61
|
) || true
|
|
62
|
+
ENVELOPE_CLASS=${ENVELOPE_CLASS:-missing}
|
|
63
|
+
if [ ! -f "$POLICY_DIR/contract.sh" ]; then ENVELOPE_CLASS=missing; fi
|
|
64
|
+
|
|
65
|
+
if [[ "$ENVELOPE_CLASS" == unsupported || "$ENVELOPE_CLASS" == missing ]]; then
|
|
66
|
+
code=envelope-version
|
|
67
|
+
[[ "$ENVELOPE_CLASS" == missing ]] && code=envelope-missing
|
|
68
|
+
[[ -f "$POLICY_DIR/contract.sh" ]] || code=contract-missing
|
|
69
|
+
doflow_decide unsupported "$code"
|
|
70
|
+
if [[ -f "$POLICY_DIR/contract.sh" ]]; then
|
|
71
|
+
( . "$_DOFLOW_POLDIR/capture-failure.sh"; doflow_capture_failure user-prompt-submit "$code" ) >/dev/null 2>&1 || true
|
|
72
|
+
fi
|
|
73
|
+
printf '{}'
|
|
74
|
+
exit 0
|
|
75
|
+
fi
|
|
45
76
|
|
|
46
77
|
[[ -z "$SESSION_ID" ]] && exit 0
|
|
47
78
|
|
|
@@ -65,7 +96,7 @@ nudge_message() {
|
|
|
65
96
|
# N5: a subagent prompt carries agent_id or agent_type (read with the other fields above).
|
|
66
97
|
[[ -z "$SUBAGENT" ]] || return 0
|
|
67
98
|
# N6: the policy script's physical directory, found with built-ins (a subshell costs about 4 ms).
|
|
68
|
-
|
|
99
|
+
policy_dir=$_DOFLOW_POLDIR
|
|
69
100
|
here="$PWD"
|
|
70
101
|
cd -P -- "$policy_dir" 2>/dev/null || return 0
|
|
71
102
|
policy_dir="$PWD"
|
|
@@ -103,6 +134,7 @@ emit_envelope() {
|
|
|
103
134
|
if [[ -f "$INJECTED_FLAG" ]]; then
|
|
104
135
|
NUDGE=$(nudge_message 2>/dev/null) || NUDGE=""
|
|
105
136
|
if [[ -n "$NUDGE" ]]; then
|
|
137
|
+
doflow_decide advisory prompt-nudge
|
|
106
138
|
emit_envelope "$NUDGE" ""
|
|
107
139
|
else
|
|
108
140
|
echo "{}"
|
|
@@ -221,6 +253,7 @@ touch "$INJECTED_FLAG"
|
|
|
221
253
|
# The nudge, when earned, follows the context after a blank line.
|
|
222
254
|
NUDGE=$(nudge_message 2>/dev/null) || NUDGE=""
|
|
223
255
|
if [[ -n "$NUDGE" ]]; then
|
|
256
|
+
doflow_decide advisory prompt-nudge
|
|
224
257
|
CONTEXT+=$'\n\n'"$NUDGE"
|
|
225
258
|
fi
|
|
226
259
|
|