integration-preflight is skipped in practice — a situation-shaped mandate with only phrase-shaped triggers and no enforcement #2
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The gap
skills/integration-preflight/SKILL.mddeclares itself unambiguously mandatory:and the body reinforces it:
Despite that, it keeps not firing. The body already records two skips on 2026-07-21, each costing "roughly two hours of diagnose-and-mitigate … against a thirty-second check". This issue records a third.
The third occurrence (2026-08-06/07)
A session ran three merge→release→deploy cycles on emmett — a plugin rename landed across two repos, then
services.metamcp.servers.*.commandupdates, then a flake-input bump — and never invoked the skill. Surfaced afterwards bydecision-flow:audit-flow.It would very likely have caught a real bug. The skill's stated purpose is that it "diffs declared configuration against live reality". The session's first deploy (generation 767) succeeded and changed nothing, because metamcp's seed SQL guarded its INSERT with
WHERE NOT EXISTSand its UPDATE only wroteenv/error_status— socommandcould never reconcile on an existing row. Declared Nix config vs live DB state had silently diverged: precisely this skill's target class. That was filed and fixed separately as oleks/emmett#274, at the cost of a wasted deploy cycle and the diagnosis time to work out why a successful deploy had no effect.Why this is NOT a trigger-phrase problem
The obvious fix is to widen the
descriptiontrigger list, and I think that is wrong:cluster:ship-it-agentandcluster:nixos-agent.The mismatch is structural: the mandate is situation-shaped ("any multi-step rollout"), but the discovery mechanism is phrase-shaped (trigger words in a description). A model that never scans the skill list for a request phrased as "fix this and deploy" will miss it every time, no matter how emphatic the prose is. Three skips across three independent sessions is the evidence that prose alone does not carry this.
Suggested direction
Enforcement rather than phrasing. Rough options, in increasing cost:
PreToolUsehook that fires on rollout-shaped actions —nix run .#deploy,deploy-rs,nixos-rebuild switch,flux reconcile,helm upgrade— and blocks (or warns once per session) unless a preflight has been recorded for this session. Same shape as the guards already shipping inanxious(issuer-ladder-guard.py) andcluster(gitea-agent-tea-write-guard.py), so there is precedent in-fleet for exactly this pattern.${CLAUDE_PLUGIN_DATA}) written when the preflight runs, so the hook can distinguish "ran it" from "never ran it" and stay quiet for the rest of the session.Not doing this unilaterally
Filed rather than implemented — this is a behavioural change to a plugin I don't own, and the block-vs-warn call and the exact action list are judgment calls for the owner. Happy to implement whichever shape you prefer.
Context: found via
decision-flow:audit-flow; the sibling routing-miss it surfaced was fixed inanxious:wip-claimv0.33.4.Triage — priority raised: the description-level fix has already been tried and did not hold
Found #1 after filing this, and it changes the assessment materially.
#1 (closed 2026-07-21) was exactly the "fix the description" remedy. Per its body, it "reframed as step 0 of every multi-step rollout, non-optional", inlined the retro quote, added the dependency smoke test, and — critically —
So the prose-and-triggers approach was already applied, deliberately and well, in direct response to the first two skips. The occurrence reported here happened after that fix was in place. That is the strongest argument in this issue and it was missing from the original body: this is not "the description could be better", it is "the description was already improved for precisely this reason and a session still skipped it."
That is what moves the remaining fix from phrasing to enforcement. Three skips, one of which occurred after a targeted description rewrite, is enough evidence that a
descriptioncannot carry a situation-shaped mandate on its own.Priority: medium-high, and I'd act on it
anxious/hooks/issuer-ladder-guard.py,cluster/hooks/gitea-agent-tea-write-guard.py), so this is a copy-and-adapt, not a design from scratch.Suggested scope for a first cut
Start with warn-once-per-session, not a hard block. It would have closed all three recorded occurrences, and it avoids the obvious objection that a one-line redeploy of an already-reviewed change should not need a gate. Escalate to blocking only if a warn-level hook also gets ignored — which would itself be the useful next data point.
Labelled
kind/capability-gap+reliability. (This repo had no labels at all; created those two to match the vocabularyoleks/claude-plugin-clusteralready uses, rather than inventing a parallel taxonomy.)Further occurrence — 2026-08-07, session
ee47ac17(mempalace e5-migration grind).What the work was: deploy the maintenance-mode gate (oleks/mempalace#100) to the
mempalace-nativecanary. Concretely — tagv3.5.0-24→ Woodpecker builds and publishes the image → editapps/base/mempalace/native-canary.yamlin oleks/fleet → Flux reconcile → verify the pod.That is a merge→release→deploy cycle spanning five steps, touching the fleet repo, depending on three external endpoints (Woodpecker CI, the Gitea OCI registry, the k8s API). It matches this skill's description almost verbatim — the description names "a merge→release→deploy cycle" explicitly.
integration-preflightwas not invoked.This is an occurrence report, and it sharpens the issue's thesis rather than just incrementing a counter:
The trigger phrases were not the problem. The description already covers this situation in words. The user's request was
deploy #100 so we can cut over— no phrase-shaped trigger fires on that, but the situation is squarely in scope. That is the "situation-shaped mandate with phrase-shaped triggers" gap this issue names, observed again. Consistent with the wider finding that description widening is a dead lever, no trigger-phrase edit would have caught this one either.What makes this occurrence more interesting than a clean miss: the session hit the target class and handled it ad hoc. Two pieces of undeclared / out-of-band state showed up mid-rollout, both of which are exactly what "declared vs live" exists to surface:
3.5.0-oracle-e5-migration-arm64) could not have been produced by CI..woodpecker/arm64.yamlderives the version withsed 's/^v//; s/-[0-9]*$//', which strips only a numeric suffix, and the result must key intoversions.nix. That tag came from a manualnix run .#publish-*— out-of-band state, presented by the live cluster as if it were a normal release.v3.5.0-maintenance-modewould have failed the build withNo hash for mempalace 3.5.0-maintenance-mode.I caught both by hand — grepping
versions.nixand the tag-derivation sed before tagging — which cost a couple of minutes and avoided a failed release. But that is precisely the check this gate systematizes, done informally and by luck of curiosity rather than by checklist. No damage, and that is the point: a skipped gate that happens not to bite is still a skipped gate, and next time the ad-hoc check may not happen.Also relevant to the enforcement question: a
PreToolUsehook did fire during this session's audit when aSKILL.mdwas read rather than invoked (decision-flow'sskill-bypass-notice). It works, and it is a live demonstration that the "one layer down" remedy proposed here is buildable — but it only fires on a read, and in this occurrence the skill was never touched at all, so nothing would have fired. An enforcement hook for this gate has to key on the situation (a write to the fleet repo, or a release tag push) rather than on any interaction with the skill file.Not proposing a fix here, and not touching labels — recording the occurrence per
audit-flow's tracking rule.Full session record for the occurrence above: Session Report ee47ac17 — §4 records this as the 4th occurrence and the 2nd after the #1 description rewrite, and §5 suggestion 3 argues the count is now past the threshold to build the warn-once hook.