@miller-tech/uap 1.40.1 → 1.41.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.
Files changed (59) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/cli/deliver-defaults.d.ts +23 -0
  3. package/dist/cli/deliver-defaults.d.ts.map +1 -0
  4. package/dist/cli/deliver-defaults.js +121 -0
  5. package/dist/cli/deliver-defaults.js.map +1 -0
  6. package/dist/cli/init.d.ts.map +1 -1
  7. package/dist/cli/init.js +29 -0
  8. package/dist/cli/init.js.map +1 -1
  9. package/dist/cli/setup.d.ts.map +1 -1
  10. package/dist/cli/setup.js +19 -0
  11. package/dist/cli/setup.js.map +1 -1
  12. package/dist/policies/policy-tools.d.ts +7 -0
  13. package/dist/policies/policy-tools.d.ts.map +1 -1
  14. package/dist/policies/policy-tools.js +24 -2
  15. package/dist/policies/policy-tools.js.map +1 -1
  16. package/package.json +3 -1
  17. package/src/policies/enforcers/7ebbc721-7540-4e9f-879a-770e0213a09b_architecture_review.py +101 -0
  18. package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
  19. package/src/policies/enforcers/_common.py +100 -0
  20. package/src/policies/enforcers/artifact_hygiene.py +52 -0
  21. package/src/policies/enforcers/cluster_routing.py +63 -0
  22. package/src/policies/enforcers/codebase_read_before_plan.py +52 -0
  23. package/src/policies/enforcers/coord_overlap.py +81 -0
  24. package/src/policies/enforcers/delivery_enforcement.py +97 -0
  25. package/src/policies/enforcers/doc_live_over_report.py +50 -0
  26. package/src/policies/enforcers/expert_review_required.py +135 -0
  27. package/src/policies/enforcers/iac_parity.py +53 -0
  28. package/src/policies/enforcers/mcp_router_first.py +37 -0
  29. package/src/policies/enforcers/memory_before_plan.py +61 -0
  30. package/src/policies/enforcers/parallel_reads.py +50 -0
  31. package/src/policies/enforcers/rtk_wrap.py +44 -0
  32. package/src/policies/enforcers/schema_diff_gate.py +80 -0
  33. package/src/policies/enforcers/session_memory_write.py +52 -0
  34. package/src/policies/enforcers/task_required.py +131 -0
  35. package/src/policies/enforcers/test_gate.py +58 -0
  36. package/src/policies/enforcers/validate_plan_before_build.py +75 -0
  37. package/src/policies/enforcers/worktree_required.py +57 -0
  38. package/src/policies/schemas/policies/architecture-review.md +51 -0
  39. package/src/policies/schemas/policies/artifact-hygiene.md +29 -0
  40. package/src/policies/schemas/policies/cluster-routing.md +31 -0
  41. package/src/policies/schemas/policies/codebase-read-before-plan.md +30 -0
  42. package/src/policies/schemas/policies/coord-overlap.md +24 -0
  43. package/src/policies/schemas/policies/delivery-enforcement.md +45 -0
  44. package/src/policies/schemas/policies/doc-live-over-report.md +32 -0
  45. package/src/policies/schemas/policies/expert-review-required.md +60 -0
  46. package/src/policies/schemas/policies/iac-parity.md +31 -0
  47. package/src/policies/schemas/policies/mandatory-testing-deployment.md +147 -0
  48. package/src/policies/schemas/policies/mcp-router-first.md +24 -0
  49. package/src/policies/schemas/policies/memory-before-plan.md +24 -0
  50. package/src/policies/schemas/policies/merge-deploy-monitor-verify.md +145 -0
  51. package/src/policies/schemas/policies/parallel-reads.md +24 -0
  52. package/src/policies/schemas/policies/rtk-wrap.md +26 -0
  53. package/src/policies/schemas/policies/schema-diff-gate.md +30 -0
  54. package/src/policies/schemas/policies/session-memory-write.md +24 -0
  55. package/src/policies/schemas/policies/task-required.md +49 -0
  56. package/src/policies/schemas/policies/test-gate.md +24 -0
  57. package/src/policies/schemas/policies/validate-plan-before-build.md +28 -0
  58. package/src/policies/schemas/policies/worktree-required.md +28 -0
  59. package/templates/hooks/uap-policy-gate.sh +5 -0
@@ -0,0 +1,145 @@
1
+ # Policy: Merge, Deploy, Monitor, Verify
2
+
3
+ **ID**: `policy-merge-deploy-monitor-verify`
4
+ **Name**: Merge, Deploy, Monitor, Verify Before Done
5
+ **Category**: completion
6
+ **Level**: REQUIRED
7
+ **Enforcement Stage**: review
8
+ **Version**: 1.0
9
+
10
+ ## Purpose
11
+
12
+ This policy enforces that a change is NOT DONE until it has been merged, rolled out via the designated pipeline, observed healthy in the target environment for a defined monitoring window, and verified with captured evidence to behave correctly end-to-end. Local "build green + tests pass" is necessary but not sufficient — DONE requires evidence from the deployed system, not just CI.
13
+
14
+ ## Rules
15
+
16
+ ```rules
17
+ - title: "Merge Gate"
18
+ keywords: ["done", "complete", "finish", "close", "resolve", "shipped"]
19
+ antiPatterns: ["direct push to master", "no pr", "skip review", "force merge", "ci failing", "merged with red ci"]
20
+
21
+ - title: "Deploy Gate"
22
+ keywords: ["done", "complete", "deploy", "release", "ship", "rollout"]
23
+ antiPatterns: ["manual deploy", "ad-hoc cluster command", "hand-edited resource", "deploy skipped", "deployment failed", "pipeline bypassed", "out-of-band rollout"]
24
+
25
+ - title: "Monitor Gate"
26
+ keywords: ["done", "complete", "monitor", "observe", "verify health"]
27
+ antiPatterns: ["no monitoring window", "skip observation", "no dashboard checked", "alerts not reviewed", "ignored error rate", "skipped post-deploy check"]
28
+
29
+ - title: "Verify Gate"
30
+ keywords: ["done", "complete", "verify", "confirm", "validate behavior"]
31
+ antiPatterns: ["unverified", "tests passed so done", "ci is enough", "no end-to-end check", "no evidence captured", "happy path only", "negative case skipped"]
32
+
33
+ - title: "Evidence Capture"
34
+ keywords: ["close task", "mark done", "complete task", "resolve task"]
35
+ antiPatterns: ["no merge sha", "no deploy url", "no monitoring evidence", "no verification output", "missing screenshot", "missing log excerpt"]
36
+ ```
37
+
38
+ ## Enforcement Behavior
39
+
40
+ ### When Triggered
41
+
42
+ This policy is enforced during the **review stage** when:
43
+
44
+ - Task status is being changed to DONE, COMPLETE, CLOSED, or RESOLVED
45
+ - A pull request is being declared "shipped"
46
+ - An incident or change request is being closed
47
+ - Work is being declared finished in any form
48
+
49
+ ### Required Actions Before Completion
50
+
51
+ 1. **Merge Gate**
52
+ - Change merged via reviewed PR from a feature/worktree branch into the integration branch
53
+ - All CI required checks green on the merge commit (build, tests, lint, type-check)
54
+ - At least one approving review (or self-review with explicit documented justification for trivial changes)
55
+ - Merge commit SHA recorded
56
+
57
+ 2. **Deploy Gate**
58
+ - Designated automated deployment pipeline executed end-to-end without error
59
+ - Application changes: artifact published and rollout completed in target environment(s) (staging through production, as scoped)
60
+ - Infrastructure changes: IaC pipeline succeeded (composes with `definition-of-done-iac`)
61
+ - Pipeline run URL recorded
62
+ - Manual deploys, ad-hoc cluster commands, and hand-edited cloud resources are FORBIDDEN as the deploy path
63
+
64
+ 3. **Monitor Gate**
65
+ - Minimum post-deploy observation window elapsed:
66
+ - 15 minutes for low-risk changes
67
+ - 1 hour for service/infrastructure changes
68
+ - 24 hours for high-blast-radius changes (auth, payments, data migrations, schema changes, traffic routing)
69
+ - Health signals reviewed and clean during the window:
70
+ - Error rate (no new error classes, no rate increase above baseline)
71
+ - Latency (p50/p95/p99 within SLO)
72
+ - Saturation (CPU/memory/connections within healthy bounds)
73
+ - Logs (no new ERROR/FATAL lines tied to the change)
74
+ - Alerts (no new alerts firing related to the change)
75
+ - Dashboard/log links recorded
76
+ - Any degraded signal blocks DONE until rolled back or rolled forward with a fix
77
+
78
+ 4. **Verify Gate**
79
+ - The specific behavior introduced/fixed is exercised end-to-end against the deployed environment
80
+ - Verification method matches change type:
81
+ - API/backend: live request against deployed endpoint with expected response asserted
82
+ - UI/frontend: interactive walkthrough of golden path AND the specific edge case
83
+ - Infrastructure: cluster/cloud CLI query confirming the resource exists and behaves as designed
84
+ - Data/schema: query confirming migrated data is shaped correctly and reads/writes succeed
85
+ - At least one negative case explicitly checked (the failure mode the change prevents does not occur)
86
+ - Evidence captured: response body, screenshot, command output, or log excerpt
87
+ - CI green is NOT verification — verification requires evidence from the deployed system
88
+
89
+ ### Verification Checklist
90
+
91
+ Before marking work as DONE, verify and attach:
92
+
93
+ - [ ] Merge commit SHA and PR URL recorded
94
+ - [ ] Deployment pipeline run URL recorded
95
+ - [ ] Target environment(s) reached and recorded (e.g. staging, production)
96
+ - [ ] Monitoring window start/end timestamps recorded
97
+ - [ ] Dashboard/log links reviewed during the window and attached
98
+ - [ ] Health signals (error rate, latency, saturation, logs, alerts) all clean
99
+ - [ ] Verification evidence captured (command output, response body, screenshot)
100
+ - [ ] Negative case checked and the prevented failure mode confirmed absent
101
+ - [ ] No new alerts fired during or after the observation window
102
+
103
+ ### Anti-Patterns to Avoid
104
+
105
+ DO NOT mark tasks as DONE when:
106
+
107
+ - The PR has been merged but the rollout hasn't run yet ("merged != deployed")
108
+ - The rollout succeeded but no one observed the system afterwards ("deployed != working")
109
+ - CI is green but the deployed environment was never exercised ("CI != production")
110
+ - The monitoring window was skipped because "it's a small change"
111
+ - Verification consisted of "the tests cover it" — tests cover code paths, not deployed behavior
112
+ - Only the happy path was verified and the negative case was skipped
113
+ - "No alerts fired" was used as proof of health when no alerts exist for the changed surface area
114
+ - The deploy/monitor/verify gates were deferred to "next sprint" or "ops can check later"
115
+ - Evidence was claimed but not actually captured or attached
116
+
117
+ ## Implementation Notes
118
+
119
+ This policy should be enforced by:
120
+
121
+ 1. **Task management gate** — block status transitions to DONE/CLOSED until evidence fields are populated
122
+ 2. **PR merge bots** — require deployment status checks before allowing merge to be marked "shipped"
123
+ 3. **CI/CD pipelines** — emit deployment and verification webhooks that the policy gate consumes
124
+ 4. **Policy gate system (`uap-policy check`)** — validate before allowing completion commands
125
+
126
+ ## Default Status
127
+
128
+ **Default: ON**
129
+ **Level: REQUIRED**
130
+
131
+ This policy is on by default for all UAP-managed projects. Disable only with explicit project-level override and documented justification (e.g. local-only experiments, scratch projects).
132
+
133
+ ## Related Policies
134
+
135
+ - `policy-completion-gate` — Local completion gates (tests, build, lint, version bump, worktree)
136
+ - `policy-mandatory-testing-deployment` — Test creation and quality requirements
137
+ - `policy-definition-of-done-iac` — IaC-specific deploy + cluster verify requirements
138
+ - `policy-iac-pipeline-enforcement` — Pipeline-only deploy path for infrastructure
139
+
140
+ The local completion gate gets a change ready to ship. This policy ensures the change actually shipped, stayed healthy, and demonstrably works in the environment that matters.
141
+
142
+ ---
143
+
144
+ _Last Updated: 2026-05-04_
145
+ _Author: Miller Tech UAP System_
@@ -0,0 +1,24 @@
1
+ # parallel-reads
2
+
3
+ **Category**: custom
4
+ **Level**: RECOMMENDED
5
+ **Enforcement Stage**: pre-exec
6
+ **Tags**: performance, parallelism, exploration
7
+
8
+ ## Rule
9
+
10
+ Two or more independent read-only operations (`Read`, `Grep`, `Glob`, non-mutating `Bash`, `WebFetch`) with no data dependency MUST be dispatched in a single tool-call batch.
11
+
12
+ ## Why
13
+
14
+ Serial fan-out multiplies wall-clock by N on every exploration. Claude Code supports parallel tool calls in one message. Measured speed-up on codebase surveys: 2–5×.
15
+
16
+ ## Enforcement
17
+
18
+ Python enforcer `parallel_reads.py` (post-exec sampler) detects serial read patterns within a tight time window and warns on the next message.
19
+
20
+ ```rules
21
+ - title: "Batch independent reads"
22
+ keywords: [read, grep, glob, webfetch, inspect]
23
+ antiPatterns: [serial-read, one-by-one, sequential-survey]
24
+ ```
@@ -0,0 +1,26 @@
1
+ # rtk-wrap
2
+
3
+ **Category**: custom
4
+ **Level**: REQUIRED
5
+ **Enforcement Stage**: pre-exec
6
+ **Tags**: rtk, tokens, efficiency
7
+
8
+ ## Rule
9
+
10
+ These commands MUST be invoked via `rtk` wrapper, not directly: `git`, `kubectl`, `docker`, `docker-compose`, `npm`, `pnpm`, `yarn`, `helm`, `terraform`.
11
+
12
+ Exception: `rtk` meta-commands (`rtk gain`, `rtk discover`, `rtk proxy`, `rtk --version`).
13
+
14
+ ## Why
15
+
16
+ RTK delivers 60–90% token reduction on dev ops (`~/.claude/RTK.md`). Missing the wrap = proportional context waste.
17
+
18
+ ## Enforcement
19
+
20
+ Python enforcer `rtk_wrap.py` inspects the Bash command string and blocks if a wrapped binary is invoked without the `rtk ` prefix.
21
+
22
+ ```rules
23
+ - title: "Wrap heavy CLIs with rtk"
24
+ keywords: [bash, shell, git, kubectl, docker, npm, pnpm, yarn, helm, terraform]
25
+ antiPatterns: [raw-kubectl, raw-git, raw-docker, raw-npm]
26
+ ```
@@ -0,0 +1,30 @@
1
+ # schema-diff-gate
2
+
3
+ **Category**: infrastructure
4
+ **Level**: REQUIRED
5
+ **Enforcement Stage**: pre-exec
6
+ **Tags**: postgres, cnpg, pgdog, migrations, schema, spock, redis
7
+
8
+ ## Rule
9
+
10
+ Changes to DB schema, connection pooler config, or replication topology MUST pass `uap schema-diff` before commit:
11
+
12
+ - `migrations/**/*.sql`
13
+ - `infra/postgres-spock/**`
14
+ - `infra/helm_charts/**/pgdog*`
15
+ - CNPG `Cluster` spec (pool sizes, instance count, connection limits)
16
+ - Redis Sentinel / Envoy HA-write proxy configs
17
+
18
+ ## Why
19
+
20
+ Branch `fix/zitadel-pgdog-capacity-v2` exists because a prior capacity change escaped review. PgDog connection limits cascade into Zitadel auth outages. Pre-commit gating prevents "v2 hotfix" cycles.
21
+
22
+ ## Enforcement
23
+
24
+ Python enforcer `schema_diff_gate.py` runs `uap schema-diff` (or checks recent successful run in memory ≤1h) when the diff touches the listed paths.
25
+
26
+ ```rules
27
+ - title: "Schema/capacity changes must pass schema-diff"
28
+ keywords: [migration, schema, pgdog, cnpg, spock, postgres, redis, sentinel, envoy, pool]
29
+ antiPatterns: [ALTER TABLE, max_connections, pool_size, instances:, replicas:]
30
+ ```
@@ -0,0 +1,24 @@
1
+ # session-memory-write
2
+
3
+ **Category**: workflow
4
+ **Level**: REQUIRED
5
+ **Enforcement Stage**: post-exec
6
+ **Tags**: memory, session, uap
7
+
8
+ ## Rule
9
+
10
+ A session that changed code (Edit/Write/MultiEdit occurred) MUST insert at least one `session_memories` row with `type IN ('decision','lesson','pattern')` before terminating.
11
+
12
+ ## Why
13
+
14
+ Session-end logs show most sessions end with no memory write even when code changed. Lessons evaporate. UAP's memory system only works if write-back happens.
15
+
16
+ ## Enforcement
17
+
18
+ Python enforcer `session_memory_write.py` runs on session-end hook: if code_changed=true, verify a matching row exists in `agents/data/memory/short_term.db`.
19
+
20
+ ```rules
21
+ - title: "Close the learning loop on code sessions"
22
+ keywords: [session-end, stop, terminate, finish]
23
+ antiPatterns: [no-memory-write, skip-lesson]
24
+ ```
@@ -0,0 +1,49 @@
1
+ # task-required
2
+
3
+ **Category**: workflow
4
+ **Level**: REQUIRED
5
+ **Enforcement Stage**: pre-exec
6
+ **Tags**: uap, task, workflow, enforcement
7
+
8
+ ## Rule
9
+
10
+ A UAP task MUST be `in_progress` before any mutating work. When no row in
11
+ `.uap/tasks/tasks.db` has `status='in_progress'`, the enforcer blocks:
12
+
13
+ - `Edit` / `Write` / `MultiEdit` on non-exempt paths
14
+ - Bash ship actions: `git commit`, `git push`, `gh pr create`
15
+
16
+ Exempt path prefixes (no task required): `.claude/`, `.cursor/`, `.opencode/`,
17
+ `.codex/`, `.forge/`, `.uap/`, `.policy-tools/`, `src/policies/`, `scripts/`,
18
+ `docs/`.
19
+
20
+ To proceed: `uap task create --type <task|bug|feature> --title "<desc>"` then
21
+ `uap task update <id> --status in_progress` (or `uap task claim <id>`).
22
+
23
+ ## Why
24
+
25
+ The UAP compliance protocol's "create a task before work" step has historically
26
+ been delivered as SessionStart text injection — advisory guidance the agent can
27
+ silently skip. Observed in practice: a full multi-PR session completed with zero
28
+ `uap task create` calls because nothing enforced it.
29
+
30
+ A `pre-exec` policy enforcer makes the task requirement a hard gate rather than a
31
+ suggestion, so UAP task tracking is guaranteed rather than best-effort. This is
32
+ the task-tracking analogue of `worktree-required`.
33
+
34
+ ## Enforcement
35
+
36
+ Python enforcer `task_required.py` resolves the primary worktree root via
37
+ `git rev-parse --git-common-dir` (so it works from linked worktrees), reads
38
+ `.uap/tasks/tasks.db`, and blocks when `COUNT(*) WHERE status='in_progress'` is
39
+ zero.
40
+
41
+ Fail-open: if UAP task tracking is not initialised (no `tasks.db`) or the DB is
42
+ unreadable, the operation is allowed — non-UAP repositories are unaffected.
43
+ Override for one-off meta-work: `UAP_NO_TASK=1`.
44
+
45
+ ```rules
46
+ - title: "A UAP task must be in_progress before mutating work"
47
+ keywords: [edit, write, multiedit, bash, git commit, git push, gh pr create]
48
+ antiPatterns: [no-task, untracked-work, skip-task-create]
49
+ ```
@@ -0,0 +1,24 @@
1
+ # test-gate
2
+
3
+ **Category**: quality
4
+ **Level**: REQUIRED
5
+ **Enforcement Stage**: review
6
+ **Tags**: testing, pr, quality
7
+
8
+ ## Rule
9
+
10
+ At PR-ready time, every changed service under `services/**` or `apps/**` MUST have a corresponding test delta (`tests/**` or `<service>/**/*.test.*`, `*_test.py`, `*.spec.ts`).
11
+
12
+ ## Why
13
+
14
+ Session-end logs show `Tests: false` far more often than `true`. Review-stage gating ensures shipping code without tests is an explicit override, not the default.
15
+
16
+ ## Enforcement
17
+
18
+ Python enforcer `test_gate.py` diffs `git diff --name-only origin/main...HEAD` against test-path regexes; blocks PR signoff if any changed service lacks a test file in the same PR.
19
+
20
+ ```rules
21
+ - title: "Changed services require test deltas"
22
+ keywords: [pr, commit, merge, review, signoff]
23
+ antiPatterns: [no-tests, skip-tests, tests-later]
24
+ ```
@@ -0,0 +1,28 @@
1
+ # validate-plan-before-build
2
+
3
+ **Category**: workflow
4
+ **Level**: REQUIRED
5
+ **Enforcement Stage**: pre-exec
6
+ **Tags**: planning, validation, accuracy
7
+
8
+ ## Rule
9
+
10
+ When a plan is marked ready and the agent is about to begin implementation (first mutating tool call after plan emission: `Edit`, `Write`, `MultiEdit`, `Bash` that modifies state), the agent MUST first execute the prompt `validate the plan` and receive an explicit pass before proceeding.
11
+
12
+ A plan is "ready" when:
13
+ - User approves with "go", "build", "implement", "proceed", "ship it", "complete all", or similar
14
+ - OR the agent emits ExitPlanMode / transitions out of a Plan phase
15
+
16
+ ## Why
17
+
18
+ User directive: "when a plan is ready to build, execute prompt 'validate the plan'". Prevents shipping on stale/unvalidated plans — catches last-mile gaps before code changes begin.
19
+
20
+ ## Enforcement
21
+
22
+ Python enforcer `validate_plan_before_build.py` tracks plan-ready state in session memory; on first mutating tool call post-ready, blocks and injects the `validate the plan` prompt. Unblocks only after a validation result is recorded.
23
+
24
+ ```rules
25
+ - title: "Ready plans require explicit validation"
26
+ keywords: [edit, write, multiedit, implement, build, ship, commit]
27
+ antiPatterns: [unvalidated-plan, skip-validation, plan-stale]
28
+ ```
@@ -0,0 +1,28 @@
1
+ # worktree-required
2
+
3
+ **Category**: workflow
4
+ **Level**: REQUIRED
5
+ **Enforcement Stage**: pre-exec
6
+ **Tags**: git, worktree, isolation, uap
7
+
8
+ ## Rule
9
+
10
+ All `Edit`, `Write`, `MultiEdit` calls on tracked files MUST occur inside a UAP worktree at `.worktrees/NNN-<slug>/`. Exemptions:
11
+
12
+ - Harness config under `.claude/`, `.cursor/`, `.opencode/`, `.codex/`, `.uap/`
13
+ - New files under `src/policies/`, `scripts/`, `docs/`
14
+ - Explicit override: user says "work directly" or `--no-worktree`
15
+
16
+ ## Why
17
+
18
+ CLAUDE.md v2.3.0 mandates worktrees; the existing hook warns but doesn't block. Formalizing closes the gap — protects in-flight user edits from agent collisions.
19
+
20
+ ## Enforcement
21
+
22
+ Python enforcer `worktree_required.py` checks whether the target file path is under `.worktrees/` and whether the session has an active worktree slug.
23
+
24
+ ```rules
25
+ - title: "File edits must occur in a worktree"
26
+ keywords: [edit, write, multiedit, create-file, modify-file]
27
+ antiPatterns: [primary-checkout, no-worktree, direct-main]
28
+ ```
@@ -30,6 +30,11 @@ export UAP_REPO_ROOT="$MAIN_ROOT"
30
30
  # actual WORKING TREE, not the (possibly bare) MAIN_ROOT. Expose the current checkout
31
31
  # so _common.worktree_root() targets the worktree when an op runs from inside one.
32
32
  export UAP_WORKTREE_ROOT="$CHECKOUT_ROOT"
33
+ # Delivery enforcement defaults to BLOCK for UAP-managed projects: substantive
34
+ # source edits must route through `uap deliver` (verified completion against the
35
+ # gates). The `:-` preserves any explicit operator/CI override (advisory|block).
36
+ # Escape hatches still apply: UAP_DELIVER_ACTIVE=1 (inside deliver) / UAP_DELIVER_BYPASS=1.
37
+ export UAP_ENFORCE_DELIVERY="${UAP_ENFORCE_DELIVERY:-block}"
33
38
  cd "$MAIN_ROOT"
34
39
 
35
40
  TOOL="$(printf '%s' "$PAYLOAD" | python3 -c 'import json,sys; d=json.load(sys.stdin); print(d.get("tool_name") or d.get("tool") or "")' 2>/dev/null || true)"