@miller-tech/uap 1.108.0 → 1.109.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/docs/INDEX.md CHANGED
@@ -41,6 +41,7 @@ Full map: **[The UAP Delivery Pipeline](guides/DELIVERY_PIPELINE.md)**.
41
41
  | [**The Delivery Pipeline**](guides/DELIVERY_PIPELINE.md) | The station-by-station tour — the big-picture map of the whole floor ⭐ |
42
42
  | [**What UAP Does Automatically**](guides/AUTOMATIC_FEATURES.md) | Every feature in benefit / when-it-kicks-in terms — install once, it all self-applies ⭐ |
43
43
  | [**`uap deliver`**](guides/DELIVER.md) | The Build+QC harness — convergence loop to verified completion, tiered gates, CI/deploy feedback loop ⭐ |
44
+ | [**Orchestrator & Hands-Free**](guides/ORCHESTRATOR.md) | The long-task autonomy layer — blackboard orchestrator, epic controller, completion ledger, auto-seed/resume; any model runs a huge build to 100% hands-free ⭐ |
44
45
  | [Memory](guides/MEMORY.md) | The 4-tier memory system, write-gates, semantic recall |
45
46
  | [MCP Router](guides/MCP_ROUTER.md) | Token-optimizing tool proxy + FTS5 output compression |
46
47
  | [Worktree Workflow](guides/WORKTREE_WORKFLOW.md) | Branch-per-feature isolation, auto-PR, enforcement |
@@ -60,9 +60,10 @@ mine→propose→validate→accept and persists results. Humans do both today.
60
60
  key=(model_family, failure_sig) (env + scaffold snapshot)
61
61
  ```
62
62
 
63
- New code lives under `src/self-harness/`: `orchestrator.ts`, `mine.ts`, `propose.ts`, `mods.ts` (the
64
- DSL), `apply.ts`, `transfer.ts`, plus `uap self-harness` CLI. It **reuses** `benchmarks/paired/` for
65
- validation and the HALO exporter for mining no reimplementation.
63
+ New code lives under `src/self-harness/`: `orchestrator.ts` (one iteration), `validate.ts` (the real
64
+ paired-bench validator), `run.ts` (the committing loop + versioned snapshot), `mine.ts`, `propose.ts`,
65
+ `mods.ts` (the DSL), `transfer.ts`, plus the `uap self-harness` CLI. It **reuses** `benchmarks/paired/`
66
+ for validation and the HALO exporter for mining — no reimplementation.
66
67
 
67
68
  ## 4. The Modification DSL — bounded, typed, reversible
68
69
 
@@ -171,9 +172,15 @@ Controls:
171
172
  - **P0 — Plumbing (small).** Promote the failure-mode logs to structured HALO spans; add the held-out
172
173
  regression suite; pin a stable `signature` hashing. *Exit:* `uap harness analyze` emits a typed
173
174
  `WeaknessReport`.
174
- - **P1 — Closed loop, env+scaffold DSL (Option A).** `uap self-harness run` orchestrating
175
- mine→propose→validate→accept over `env`+`scaffold` Mods, reusing `benchmarks/paired/`. *Exit:* one
176
- autonomous iteration that accepts a real Mod (e.g. re-discovers `LLAMA_N_PREDICT=4096`) with stats.
175
+ - **P1 — Closed loop, env DSL (Option A). [BUILT]** `uap self-harness run` orchestrates
176
+ mine→propose→validate→decide over `env` Mods, reusing `benchmarks/paired/`. The real validator
177
+ (`validate.ts`) runs a baseline arm, physically toggles the env knob + restarts the server, runs a
178
+ candidate arm, reverts, and pairs the two into a `Comparison`; held-out runs the same way. `--apply`
179
+ commits accepted Mods to the env file, restarts once, and writes a **versioned profile snapshot**
180
+ (`run.ts`, `profile.ts`) + append-only history; without it the run is a pure dry-run that touches
181
+ nothing. *Exit met:* one autonomous iteration accepts a real env Mod (e.g. re-discovers
182
+ `LLAMA_N_PREDICT=4096`) with paired stats. `scaffold`/`middleware` Mods are not auto-validated here —
183
+ they route through the human-gated pending queue (§9).
177
184
  - **P2 — Middleware Mods (Option B).** Add the `middleware` Mod class + the `toolcall-path-normalizer`.
178
185
  *Exit:* the loop proposes+validates the normalizer and measurably cuts path-garbling on the medium
179
186
  suite (the ceiling manual fixes couldn't crack).
@@ -126,6 +126,65 @@ Install UAP (`npm i -g universal-agent-protocol`) and every feature below activa
126
126
 
127
127
  ---
128
128
 
129
+ ## Long-Task Autonomy (Hands-Free) ✨
130
+
131
+ *The Fable-parity layer: any model runs a huge build to 100% without a human in
132
+ the loop. Full guide: [The Orchestrator & Hands-Free Persistence](ORCHESTRATOR.md).*
133
+
134
+ ### Orchestrator (blackboard, minimal context)
135
+ **What it does:** Decomposes a complex mission into a task DAG and runs each task
136
+ in a **fresh, minimal context** — its own goal + the verified interface
137
+ contracts of its direct dependencies + a small memory pull — instead of one
138
+ giant prompt. Publishes each task's contract back for dependents and future
139
+ sessions; can adaptively add discovered tasks (`NEW_TASKS`).
140
+
141
+ **When it kicks in:** Automatically for any decomposed mission (auto-on). Turn
142
+ off with `uap orchestrator off` / `--no-orchestrate`.
143
+
144
+ **Why it matters:** A small-context local model (Qwen3.6) can multi-step through
145
+ a build far larger than its window, because it never has to hold the whole thing.
146
+
147
+ ### Epic Controller (loop epics as fresh sessions)
148
+ **What it does:** For a *massive* mission (design → build → operational
149
+ readiness), runs it as a **sequence of epics**, each a fresh mission that sees
150
+ only prior epics' summaries. An epic that fails its gates is retried with a
151
+ fresh session fed the last failure — looped until accepted.
152
+
153
+ **When it kicks in:** Auto for genuinely epic-scale missions (complex *and*
154
+ ≥ 1200 chars); force with `--epics`.
155
+
156
+ **Why it matters:** "Start a new session per task, inject only what's needed,
157
+ loop until complete" — without you orchestrating it by hand.
158
+
159
+ ### Hands-Free Persistence (can't stop until done)
160
+ **What it does:** A **completion ledger** (`.uap/completion-ledger.json`) is the
161
+ objective definition of done for the whole build. The Stop hook **blocks
162
+ session-end** while any item remains, handing the model a *"NOT DONE —
163
+ REMAINING: …"* message; the reactor injects a *"keep going"* directive each
164
+ turn. Intensity is model-aware (trust Fable, nudge Opus/GPT, firmly drive
165
+ local models), and it's bounded so it can never wedge.
166
+
167
+ **When it kicks in:** Auto-on whenever an active ledger has remaining items.
168
+ Casual sessions with no build in progress are untouched. Disable with
169
+ `uap handsfree off` / `UAP_HANDSFREE=0`.
170
+
171
+ **Why it matters:** Every model behaves like Fable on long tasks — persisting to
172
+ objective completion instead of stopping at the first plausible finish.
173
+
174
+ ### Plan Auto-Seed & Auto-Resume
175
+ **What it does:** When the model writes a plan (Claude Code `TodoWrite`), a hook
176
+ **mirrors it into the ledger automatically** — no manual `init`. On session
177
+ start, an unfinished build is **auto-resumed** (*"Resuming a build in progress —
178
+ N/M done, REMAINING: …"*).
179
+
180
+ **When it kicks in:** Any interactive session with a real multi-step plan
181
+ (≥ 3 todos). `uap deliver` epic mode seeds the ledger directly.
182
+
183
+ **Why it matters:** The whole loop is zero-touch — the model's own plan becomes
184
+ the definition of done, and work resumes across sessions without being asked.
185
+
186
+ ---
187
+
129
188
  ## Developer Experience
130
189
 
131
190
  ### RTK (Rust Token Killer)
@@ -135,6 +194,18 @@ Install UAP (`npm i -g universal-agent-protocol`) and every feature below activa
135
194
 
136
195
  **Why it matters:** Token costs add up fast. RTK transparently optimizes every command without you thinking about it.
137
196
 
197
+ ### Dashboard — Savings & Orchestration Visibility
198
+ **What it does:** `uap dash serve` shows **Token Savings by Influence** (real
199
+ tokens/cost saved per mechanism — RTK, model routing, context compression — with
200
+ honest *measured/estimated* labels) and an **Orchestrations & Hierarchy** tree
201
+ (the live mission → epic → task tree with the agents on each node and the active
202
+ build ledger's progress).
203
+
204
+ **When it kicks in:** Whenever you run `uap dash serve` (open `http://localhost:3847`).
205
+
206
+ **Why it matters:** See exactly where UAP is saving you tokens and how a
207
+ long-running build is progressing — no guesswork.
208
+
138
209
  ### HALO (High-Level Agent Orchestrator)
139
210
  **What it does:** Provides a high-level orchestration layer for complex multi-step tasks, automatically decomposing them into subtasks and managing dependencies.
140
211
 
@@ -42,6 +42,13 @@ uap deliver "migrate the auth module to JWT" --guidance-file ./guidance.txt
42
42
  echo "prefer RS256 and keep the existing /login route" >> ./guidance.txt
43
43
  ```
44
44
 
45
+ For **huge, multi-epic builds**, `deliver` goes further: it decomposes the
46
+ mission into a task DAG (the **blackboard orchestrator**, minimal fresh context
47
+ per task) and, for epic-scale work, loops whole **epics as fresh sessions**
48
+ until each is accepted. Combined with **hands-free persistence**, any model —
49
+ local or frontier — runs the build to 100% without stopping. See
50
+ **[The Orchestrator & Hands-Free Persistence](ORCHESTRATOR.md)**.
51
+
45
52
  ---
46
53
 
47
54
  ## Auto-optimization
@@ -0,0 +1,273 @@
1
+ # The Orchestrator & Hands-Free Persistence
2
+
3
+ > **🏭 Where this fits:** BUILD — the station that turns *"build this whole
4
+ > thing"* into finished, verified work without a human babysitting the loop.
5
+ > **What it delivers:** any model — local or frontier — runs a huge multi-epic
6
+ > build hands-free, in fresh minimal context per task, and **cannot stop until
7
+ > the whole thing is 100% done**. Install once; it self-applies. Automagic. ✨
8
+
9
+ This is UAP's answer to the question *"why can Fable grind on a huge task for
10
+ hours while other models stop after ten minutes?"* Fable's persistence is
11
+ trained into the weights. UAP puts the same persistence in the **harness** —
12
+ so **every** model gets it.
13
+
14
+ - **[The orchestrator](#the-orchestrator)** runs a decomposed build as a task
15
+ DAG, each task in a fresh, minimal context.
16
+ - **[The epic controller](#the-epic-controller)** loops whole epics as fresh
17
+ sessions, retrying until each is accepted.
18
+ - **[Hands-free persistence](#hands-free-persistence)** is the outer loop: a
19
+ completion ledger defines "done", and the Stop hook + reactor refuse to let
20
+ the model stop until the ledger reads 100%.
21
+
22
+ All of it is **auto-on**. You don't pass a flag. You describe a big build, and
23
+ the machinery engages.
24
+
25
+ ---
26
+
27
+ ## TL;DR — install & go
28
+
29
+ ```bash
30
+ npm install -g @miller-tech/uap
31
+ uap setup # wires the hooks into your harness
32
+ ```
33
+
34
+ That's it. From then on:
35
+
36
+ - Ask for a big, multi-part build (in Claude Code, `uap deliver`, opencode, …).
37
+ - The model writes a plan (a todo list, or `uap deliver` decomposes it).
38
+ - UAP captures that plan as a **completion ledger** and drives the model
39
+ through it — fresh context per task, keep-going injected each turn, and the
40
+ session **blocked from ending** until every item is done.
41
+ - A new session **auto-resumes** an unfinished build.
42
+
43
+ Nothing to configure. To *turn things off* or tune them, see
44
+ [Controls](#controls-toggles--env).
45
+
46
+ ---
47
+
48
+ ## The orchestrator
49
+
50
+ When `uap deliver` (or an interactive session routed through it) gets a
51
+ genuinely complex, multi-part mission, it **decomposes** the work into a task
52
+ DAG and runs it on a **blackboard orchestrator** instead of one giant
53
+ convergence loop.
54
+
55
+ The key idea is **minimal fresh context per task**. Each leaf task runs in its
56
+ own convergence loop that sees only:
57
+
58
+ - a one-line orientation snippet of the overall mission,
59
+ - its own goal, acceptance criteria, and the files it owns,
60
+ - the **verified interface contracts** of its *direct* dependencies (a few
61
+ hundred tokens — "what already exists"), and
62
+ - a small, relevant pull from memory.
63
+
64
+ Not the full spec. Not every prior summary. This is what lets a small-context
65
+ local model (e.g. Qwen3.6-35B-A3B) multi-step through a build far larger than
66
+ its window.
67
+
68
+ **What it does automatically:**
69
+
70
+ | Capability | Behavior |
71
+ |---|---|
72
+ | **Auto-decompose** | Complex, long missions split into 2–8 phases along the `plan → design → implement → review → release` lifecycle. |
73
+ | **Minimal context** | Each task's prompt is hard-capped by a context governor (default 6000 chars); furthest dependencies are elided first. |
74
+ | **Memory in/out** | Each task pulls relevant design decisions from memory (`retrieveDesign`) and publishes its verified contract back (`publish`) for dependents and future sessions. |
75
+ | **Adaptive re-planning** | A task that discovers missing work can emit `NEW_TASKS:[…]`; they're validated, de-duped, and folded into the DAG. |
76
+ | **Dependency-correct** | A task runs only after every dependency has *succeeded*; a failed dependency blocks (never builds on a broken base). |
77
+
78
+ It is **auto-on** for any decomposed mission — minimal-context orchestration is
79
+ strictly better than a full-mission prompt for small models.
80
+
81
+ ---
82
+
83
+ ## The epic controller
84
+
85
+ One `deliver` run drives one task DAG. A **massive** mission — *design → build
86
+ → operational readiness* — is more than one DAG: it's a **sequence of epics**,
87
+ each itself a full mission. The epic controller is the loop above the
88
+ orchestrator:
89
+
90
+ ```
91
+ for each epic (dependency-ordered):
92
+ run it as a FRESH mission ← fresh context; only prior epics'
93
+ compact summaries are injected
94
+ check acceptance (gates + judge)
95
+ if not accepted:
96
+ RETRY with a fresh session ← fed the previous attempt's failure
97
+ …until accepted or the attempt budget is spent
98
+ ```
99
+
100
+ - **Fresh session per epic/attempt** — exactly the "start new sessions per
101
+ task, inject only what's needed, loop until complete" pattern.
102
+ - **Generator ≠ Evaluator** — an epic that "delivered" but fails the acceptance
103
+ judge is retried; a delivered-and-accepted epic is marked done.
104
+ - **Never spins forever** — bounded per-epic attempts; stagnation gives up
105
+ gracefully.
106
+
107
+ It **auto-engages** for genuinely epic-scale missions (complex *and* ≥ 1200
108
+ characters). Smaller work uses the orchestrator or the plain loop.
109
+
110
+ ---
111
+
112
+ ## Hands-free persistence
113
+
114
+ The orchestrator and epic controller run inside `uap deliver`. But most huge
115
+ builds are driven **interactively** (Claude Code, opencode) where the model
116
+ decides when it's "done". Hands-free persistence makes *that* Fable-like too —
117
+ for any model — using four pieces that all ship **auto-on**.
118
+
119
+ ### B · The completion ledger — the objective definition of done
120
+
121
+ `.uap/completion-ledger.json` is the persistent, whole-build checklist: the
122
+ epic/task DAG with a status per item. Completion is judged against **this**,
123
+ not the model's self-assessment, and it survives across turns *and* sessions.
124
+
125
+ The ledger is populated automatically:
126
+
127
+ - `uap deliver` epic mode writes it from the planned epics and marks items done
128
+ as epics are accepted.
129
+ - **Interactive sessions**: whenever the model writes a plan with the built-in
130
+ todo tool (Claude Code `TodoWrite`), a PostToolUse hook **mirrors that plan
131
+ into the ledger** — statuses follow (`completed → done`, `in_progress`,
132
+ `pending`). No manual step. (Only a real multi-step plan seeds it — ≥ 3 todos
133
+ by default.)
134
+
135
+ ### A · The Stop-hook block — you can't stop early
136
+
137
+ When the model tries to end its turn, the Stop hook checks the ledger. If the
138
+ build is incomplete it **blocks the stop** and hands the model a
139
+ *"NOT DONE — REMAINING: …"* message, so it keeps working. Fully guarded so it
140
+ can never wedge:
141
+
142
+ - honors the harness `stop_hook_active` flag (never re-blocks in a loop),
143
+ - only blocks when there's an **active ledger with remaining items**,
144
+ - bounded by per-build **block + stagnation counters** — progress resets the
145
+ counter; a genuine stall gives up. *"Never stop early"* and *"never spin
146
+ forever"* are the same feature.
147
+
148
+ ### C · Model-aware intensity — trust Fable, drive the local model
149
+
150
+ The forcing scales **inversely** with a model's intrinsic persistence:
151
+
152
+ | Family | Intensity | Behavior |
153
+ |---|---|---|
154
+ | **Fable** | light | Trust it; minimal safety net. |
155
+ | **Opus / Sonnet / GPT** | moderate | Nudge past premature "done". |
156
+ | **Qwen / local** | aggressive | Firmly drive; more blocks tolerated. |
157
+
158
+ ### D · Reactor injection — the standing "keep going" directive
159
+
160
+ While a build is in progress, every prompt gets a
161
+ *"keep going until 100% — REMAINING: …"* directive injected (via the reactor
162
+ hook you already have). Casual sessions with no active build see nothing.
163
+
164
+ ### Auto-resume — pick up where you left off
165
+
166
+ On session start, if a build was left unfinished, a resume banner surfaces it
167
+ (*"Resuming a build in progress — N/M done, REMAINING: …"*) so a fresh session
168
+ continues it **without being asked**.
169
+
170
+ **The full loop, all automatic:**
171
+
172
+ ```
173
+ model plans (todos) → ledger auto-seeds → reactor injects "keep going" each turn
174
+ → Stop hook blocks until 100% → new session auto-resumes the remainder
175
+ → done when every ledger item is complete
176
+ ```
177
+
178
+ ---
179
+
180
+ ## Seeing it: the dashboard
181
+
182
+ `uap dash serve` (open `http://localhost:3847`) now includes two panels for
183
+ this machinery:
184
+
185
+ - **Orchestrations & Hierarchy** — the live `mission → epic → task` tree with
186
+ per-node status and the agents working each node, plus the active build
187
+ ledger's progress.
188
+ - **Token Savings by Influence** — real tokens/cost saved, attributed per
189
+ mechanism (RTK, model routing, context compression), with honest
190
+ *measured / estimated* labels.
191
+
192
+ ---
193
+
194
+ ## Controls (toggles & env)
195
+
196
+ Everything is on by default. These are for turning it **off** or tuning it.
197
+
198
+ ### Orchestrator
199
+
200
+ ```bash
201
+ uap orchestrator status # show effective setting
202
+ uap orchestrator off # sequential phase runner instead of the blackboard
203
+ uap orchestrator on | auto # re-enable (auto = default)
204
+ uap deliver "…" --no-orchestrate # disable for one run
205
+ ```
206
+
207
+ | Knob | Effect |
208
+ |---|---|
209
+ | `UAP_DELIVER_ORCHESTRATE=0` | Disable the orchestrator. |
210
+ | `.uap.json` `deliver.orchestrate: "off"` | Persistent disable. |
211
+ | `UAP_DELIVER_CONTEXT_BUDGET` | Per-task context char cap (default 6000). |
212
+ | `UAP_DELIVER_MAX_PHASES` | Phase ceiling (default 8, hard max 20). |
213
+ | `UAP_DELIVER_MAX_TASKS` | Re-planning task cap (default 40). |
214
+
215
+ ### Epic controller
216
+
217
+ ```bash
218
+ uap deliver "…" --epics # force the epic controller
219
+ uap deliver "…" --no-epics # disable it for this run
220
+ ```
221
+
222
+ | Knob | Effect |
223
+ |---|---|
224
+ | `UAP_DELIVER_EPICS=1` | Force epic mode. |
225
+ | `UAP_DELIVER_EPIC_ATTEMPTS` | Fresh-session attempts per epic (default 2). |
226
+
227
+ ### Hands-free persistence
228
+
229
+ ```bash
230
+ uap handsfree status # master switch, model profile, ledger progress
231
+ uap handsfree on | off # toggle (persisted to .uap.json)
232
+ uap handsfree remaining # what's left in the current build
233
+ uap handsfree init --mission "…" --items '[{"id":"e1","title":"…"}]' # seed manually
234
+ uap handsfree complete <id> | fail <id> # update an item
235
+ ```
236
+
237
+ *(alias: `uap hf …`)*
238
+
239
+ | Knob | Effect |
240
+ |---|---|
241
+ | `UAP_HANDSFREE=0` | Disable hands-free entirely. |
242
+ | `.uap.json` `handsfree.enabled: false` | Persistent disable. |
243
+ | `UAP_HANDSFREE_INTENSITY` | Force `light` / `moderate` / `aggressive`. |
244
+ | `UAP_HANDSFREE_MIN_TODOS` | Min todos before a plan auto-seeds a ledger (default 3). |
245
+
246
+ ### Model routing
247
+
248
+ ```bash
249
+ uap model routing status # is routing on? which table + complexity tiers?
250
+ uap model routing on | off # toggle the whole multi-model table
251
+ uap model routing use <preset> # apply a preset (e.g. fable-local-opus)
252
+ ```
253
+
254
+ ---
255
+
256
+ ## How it maps to the code
257
+
258
+ | Piece | Source |
259
+ |---|---|
260
+ | Blackboard orchestrator | `src/delivery/task-orchestrator.ts` |
261
+ | Epic controller | `src/delivery/epic-controller.ts` |
262
+ | Deliver wiring | `src/cli/deliver.ts` (`runOrchestratedMission`, `runEpicMission`) |
263
+ | Completion ledger | `src/delivery/completion-ledger.ts` → `.uap/completion-ledger.json` |
264
+ | Persistence profile | `src/delivery/persistence-profile.ts` |
265
+ | Stop-hook block | `.factory/hooks/stop.sh` → `uap handsfree stop-check` |
266
+ | Auto-seed / auto-resume | `templates/hooks/uap-todo-ledger.sh`, `session-start.sh` |
267
+ | Reactor injection | `src/coordination/persistence-inject.ts` |
268
+
269
+ ---
270
+
271
+ **See also:** [`uap deliver`](DELIVER.md) · [Multi-Model Routing](MULTI_MODEL.md)
272
+ · [What UAP Does Automatically](AUTOMATIC_FEATURES.md) ·
273
+ [Local Models](LOCAL_MODELS.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miller-tech/uap",
3
- "version": "1.108.0",
3
+ "version": "1.109.0",
4
4
  "description": "Autonomous AI agent memory system with CLAUDE.md protocol enforcement",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -6,7 +6,7 @@ import sys
6
6
  from pathlib import Path
7
7
 
8
8
  sys.path.insert(0, str(Path(__file__).parent))
9
- from _common import arg_str, emit, parse_cli, run # noqa: E402
9
+ from _common import emit, parse_cli, run # noqa: E402
10
10
 
11
11
  DOMAINS = {
12
12
  "openobserve": re.compile(
@@ -31,7 +31,16 @@ def pick_domain(blob: str) -> str:
31
31
 
32
32
  def main() -> None:
33
33
  op, args = parse_cli()
34
- blob = f"{op} {arg_str(args)}"
34
+ # Scope: only Bash invocations dispatch cluster commands. Other tools
35
+ # (Write/Edit/Read/etc.) may legitimately mention cluster product names
36
+ # in file content without that being a kubectl/helm call.
37
+ if op != "Bash":
38
+ emit(True, "non-Bash tool call")
39
+ # Strip heredoc bodies so file content written via cat <<MARK ... MARK
40
+ # does not pattern-match the trigger regex below.
41
+ cmd = args.get("command", "") if isinstance(args, dict) else ""
42
+ cmd = re.sub(r'<<\s*[\x27\x22]?(\w+)[\x27\x22]?[\s\S]*?\n\1\b', "", cmd)
43
+ blob = f"{op} {cmd}"
35
44
 
36
45
  if not re.search(r"\b(kubectl|helm)\b", blob):
37
46
  emit(True, "not a kubectl/helm call")