From 298e3e1fe8d27609f8b715bde4231af61c3e1b95 Mon Sep 17 00:00:00 2001 From: Ricky Schema Cascade Date: Wed, 10 Jun 2026 13:22:42 +0200 Subject: [PATCH 1/2] chore(skills): restore autonomous-run-contract SKILL.md Accidentally removed in #213 (slack-comms persona pack) though prpm.json still references it at .agentworkforce/workforce/skills/autonomous-run-contract/SKILL.md. Restored verbatim from 74f881b^. Co-Authored-By: Claude Opus 4.8 --- .../skills/autonomous-run-contract/SKILL.md | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 .agentworkforce/workforce/skills/autonomous-run-contract/SKILL.md diff --git a/.agentworkforce/workforce/skills/autonomous-run-contract/SKILL.md b/.agentworkforce/workforce/skills/autonomous-run-contract/SKILL.md new file mode 100644 index 00000000..4a9cc055 --- /dev/null +++ b/.agentworkforce/workforce/skills/autonomous-run-contract/SKILL.md @@ -0,0 +1,129 @@ +--- +name: autonomous-run-contract +description: Use at the start of every autonomous, multi-PR, cutover-class delegated run. Authors the binding contract between operator and agent — the gates, the flip mechanism, the rollback triggers, the standing constraints, and the explicit escalate-to-human conditions — and surfaces it to the operator for explicit grant of (a) auto-merge authority, (b) flip-the-switch authority, (c) swarm-blockers authority. No autonomous work begins until the contract is acknowledged. +--- + +# Autonomous Run Contract + +A cutover-class delegated run (multi-PR, multi-day, irreversible-by-default infra change) does not begin with "start working." It begins with a **written contract** that the operator explicitly grants. The contract is the single document the autonomous agent re-reads at every decision point to determine whether it has authority for the next action. + +This skill produces that contract. Without it, the agent will eventually either (a) over-step (merging or flipping without authority), or (b) freeze (refusing to act because authority is ambiguous). Both are session-killers. + +## When to author a contract + +- Any run the operator describes as autonomous, hands-off, "drive to done," cutover, migration, or with an explicit grant of merge/flip authority. +- Any run that will produce 3+ PRs across 1+ days. +- Any run with an irreversible step (a flag flip, a data migration, a DNS change, a destructive cleanup). +- Any run where the operator is granting authority to spawn supporting agents. + +If none of those apply, this is regular work — skip the contract. + +## Structure of the contract + +The contract is a single markdown document (`/tmp/-contract.md` or a PR comment / channel-pinned message) with these named sections, in this order: + +### 1. Run identity + +- One-line statement of the deliverable (e.g. "Cut Nango webhook ingestion from SQS to Cloudflare Queue, dark-launched, single-switch flip"). +- Tracking PR / issue / channel. +- Start timestamp. + +### 2. Grants requested (operator must explicitly grant each) + +- **Auto-merge authority** — on which PRs, against which bar (see §3). +- **Flip authority** — for which feature flag / KV key / config write, with the exact command, when all pre-flip gates are GREEN. +- **Swarm-blockers authority** — authority to spawn N supporting agents to self-unblock when ground truth requires more concurrent work than one agent can carry; with budget (max-agents, max-cost, max-duration). +- **Rollback authority** — pre-authorized to execute the rollback procedure on any of the rollback triggers in §5. + +Each grant is requested with a one-line ask. The operator either says "granted" (any of: explicit "go", thumbs-up, "approved") or scopes it down ("granted for PRs A,B; not C"). No grant = no autonomous action of that type. + +### 3. Per-PR auto-merge bar + +The minimum bar that must hold for the agent to merge a PR autonomously. Anything below this bar = manual operator review required. + +- **CI status verified LIVE at merge time** — `gh pr view --json mergeable,mergeStateStatus,statusCheckRollup` against the head SHA at the moment of merge, not against a snapshot taken N minutes ago. `MERGEABLE` + `CLEAN`. Any state below `CLEAN` (`BLOCKED`, `BEHIND`, `UNSTABLE`, `DIRTY`) blocks the merge. +- **Substantive review by area applicable to the change**, not a wave-through. Cover at minimum: F0-class correctness (the headline behavioral change works); Worker-bundle safety (no Node-only imports added in Worker entry graph) where the change touches Worker code; dormant-safety (default-disabled, no caller turns on dormant new path); idempotency (queue/webhook handlers); rollback path preserved. +- **Bot reviewers audited, not waved.** Every CodeRabbit / Devin / Codex review comment gets read against the current PR head. Many are stale (the bot reviewed an earlier commit, the issue is fixed on HEAD); reply on the PR documenting the audit. Genuine blockers get addressed before merge. +- **Worktree for the PR work.** Never co-mingle PR changes with other branches' uncommitted state. +- **Never merge on red.** No `--admin` past red checks. If a check is broken because of an upstream issue (e.g. inherited-from-main failure), document the inheritance with a `git diff origin/main` and seek the operator's explicit waiver. + +If any clause fails, the merge is not authorized by the contract — escalate. + +### 4. Pre-flip gates (numbered, all GREEN, zero AMBER) + +These are the binding gates between "code merged" and "switch flipped." Each gate has: + +- A short name (e.g. `G3: Worker bundle B1 safety`). +- Acceptance criteria (specific, measurable — `tests/b1-worker-import-safety.test.ts` returns 0 errors; the synthetic regression for the failure class runs RED without the fix). +- An evidence pointer (CI run URL, log timestamp + structured-log query, file:line + revision, a `/tmp/-evidence.md` file). +- A status: RED / AMBER / GREEN. **AMBER is not flippable.** Either the gate is GREEN with evidence or it is RED. + +The contract enumerates every gate up front. The agent maintains a live scoreboard during the run (see `swarm-blockers-and-gate-scoreboard`). The agent does not flip until every gate reads GREEN with current evidence. + +### 5. Flip mechanism (the one switch) + +The single command that changes prod behavior, written out exactly. Examples: + +- `wrangler kv:key put --binding=ROUTER_CONFIG nango_ingestion_path queue --remote` +- `pnpm sst secret set FeatureFlagNangoQueue true --stage production` +- A pre-merged PR that flips a constant, ready to merge as a one-liner. + +The flip is one command. If it requires three commands in a sequence (e.g. enable per-provider, then enable globally), enumerate each as a sub-step with its own gate. + +### 6. Rollback triggers (immediate, no "wait and see") + +Specific, measurable signals that revert immediately when seen post-flip, without consulting the operator: + +- HTTP 5xx rate on the affected route ≥ X% over Y minutes (vs pre-flip baseline). +- DLQ depth growth ≥ X messages/minute sustained over Y minutes. +- A post-deploy-verify check (`tests/post-deploy-verify-*.sh`) regresses. +- A structured-log error class spikes ≥ Nx baseline. +- The synthetic regression test for the failure class starts failing. + +Each trigger names the exact metric source, the threshold, the duration, and the rollback command (the inverse of the flip in §5). Rollback authority is pre-granted in §2 so the agent does not pause to ask. + +### 7. Standing constraints (override everything) + +These override any other instruction, even an explicit operator grant, unless the operator re-affirms with awareness of the constraint: + +- **NEVER STOP.** This is the prime directive of an autonomous run. The agent always has a next action or an armed wake-up. It never idles waiting for permission it already holds, and never ends a run with open work and no scheduled continuation. "Blocked" is not a stopping state: if the current lane is blocked, the agent self-unblocks by spawning support, running diagnostics, working a parallel lane, or arming a wake-up against the specific signal it is waiting on. The run ends only when the deliverable is met or the operator says stop. Continuity lives in the next lead or worker, never in a halted session. A stalled overnight run is a contract failure, not a neutral pause. Corollary: **do not offload to the operator a check or action the agent can perform itself** via CLI, source, `gh`, or a spawned worker. +- **Cross-repo work needs no separate approval.** The §2 grants (auto-merge, flip, swarm-blockers, rollback) and the run's authority extend to every AgentWorkforce repo the deliverable touches, not just the primary repo named in §1. When the fix lives in a sibling repo (`workforce`, `relayfile`, `relayfile-adapters`, `relayauth`, `agents`, `relaycast`, etc.), the agent implements, opens, reviews, and, within the §3 bar, merges there under the same grant. It does not pause to re-ask the operator for permission to act in a different repo. The implementation leg of a cross-repo fix is never operator-gated; only standing constraints and explicit publish/deploy gates still apply. Treating a cross-repo dependency as a fresh approval boundary is a common autonomous-run stall. +- **No manual prod deploy.** All prod changes flow through CI. +- **No direct prod SQL.** Schema changes via Drizzle migrations through PRs. +- **Redact secrets in logs / channels / PRs / reports.** +- **Pairing rule for spawned agents:** codex impl + claude review. Never a codex lead with codex impl agents on the same channel. +- **Instrument-don't-guess after two failed fixes.** Ship a diagnostic before more fix code. +- **Battle-tested ≠ works-once.** A fix is proven only under sustained + concurrent load + with the failure-class regression test running RED without it. +- **Repo-specific rules** the operator references (e.g. cloud repo's SST resource registration seven-place change; the journal-update-for-drizzle-migrations check; the integration-adapter-registry invariant). Enumerate the specific rule files (`.claude/rules/.md`) the contract honors. + +### 8. Escalate-to-human conditions + +The agent stops and asks when: + +- An external dependency is unresolvable (a third-party API is down, a cloud quota requires operator-only action, a billing limit is hit). +- A product or security decision is required (a scope question, a UX trade-off, a data-classification call). +- Two consecutive deploy failures without a credible root cause hypothesis. +- Any suspected data loss, even speculative. +- Any irreversible action beyond the §5 flip (a destructive cleanup, a DNS change, a paid-tier upgrade). +- The §3 auto-merge bar cannot be honestly met (e.g. the CI failure is a real regression, not stale, and the fix is non-obvious). +- The §4 gates are at AMBER and the operator has not pre-authorized AMBER promotion criteria. + +For each, the agent posts the escalation as a single clear message: what is blocked, what was tried, what specific decision is needed, what the agent recommends. Then waits. + +## Authoring flow + +1. **Draft.** Write the contract from the operator's kickoff message. Fill every section. Where a value is unknown (e.g. the operator hasn't named the flip mechanism yet), name the gap explicitly with `[?: need operator input]`. +2. **Surface.** Post the full contract to the operator. Highlight the grants requested in §2 — each requires explicit acknowledgement. +3. **Iterate.** The operator will scope grants down, add/remove gates, refine triggers. Update the document until the operator says "granted" or equivalent on every grant. +4. **Pin.** Save the final contract somewhere the agent can re-read it at any decision point (a PR comment, a pinned channel message, `/tmp/-contract.md`). Note its location in the agent's session state. +5. **Re-read.** Before every irreversible action (merge, flip, rollback, spawn), re-read the relevant contract sections. Never act on memory of the contract. + +## What this skill does NOT cover + +- The actual implementation of the gates (covered by `swarm-blockers-and-gate-scoreboard`). +- The per-PR review protocol (covered by `auto-merge-and-composition-safety`). +- The diagnostic-first protocol when stuck (covered by `instrument-dont-guess`). +- The dark-launch + flip design (covered by `dormant-flip-and-rollback`). +- Tiered acceptance for high-volume work (covered by `tiered-acceptance`). + +This skill is upstream of all of them — they presuppose a signed contract exists. From 4d0acff758fbdb863c8df8b1bf656547e9063810 Mon Sep 17 00:00:00 2001 From: Ricky Schema Cascade Date: Wed, 10 Jun 2026 15:18:42 +0200 Subject: [PATCH 2/2] chore(skills): restore remaining autonomous-run skill pack The auto-merge-and-composition-safety, dormant-flip-and-rollback, instrument-dont-guess, swarm-blockers-and-gate-scoreboard, and tiered-acceptance skills were accidentally removed in #213 (slack-comms persona pack) but prpm.json still references all of them at .agentworkforce/workforce/skills/. Restored verbatim from 74f881b^. (autonomous-actor.json was intentionally relocated to packages/persona-autonomous-actor and is not restored here.) Co-Authored-By: Claude Opus 4.8 --- .../SKILL.md | 143 ++++++++++++++++++ .../skills/dormant-flip-and-rollback/SKILL.md | 111 ++++++++++++++ .../skills/instrument-dont-guess/SKILL.md | 79 ++++++++++ .../SKILL.md | 132 ++++++++++++++++ .../skills/tiered-acceptance/SKILL.md | 95 ++++++++++++ 5 files changed, 560 insertions(+) create mode 100644 .agentworkforce/workforce/skills/auto-merge-and-composition-safety/SKILL.md create mode 100644 .agentworkforce/workforce/skills/dormant-flip-and-rollback/SKILL.md create mode 100644 .agentworkforce/workforce/skills/instrument-dont-guess/SKILL.md create mode 100644 .agentworkforce/workforce/skills/swarm-blockers-and-gate-scoreboard/SKILL.md create mode 100644 .agentworkforce/workforce/skills/tiered-acceptance/SKILL.md diff --git a/.agentworkforce/workforce/skills/auto-merge-and-composition-safety/SKILL.md b/.agentworkforce/workforce/skills/auto-merge-and-composition-safety/SKILL.md new file mode 100644 index 00000000..9bb82698 --- /dev/null +++ b/.agentworkforce/workforce/skills/auto-merge-and-composition-safety/SKILL.md @@ -0,0 +1,143 @@ +--- +name: auto-merge-and-composition-safety +description: Use before auto-merging any PR in an autonomous run, and between consecutive merges that touch overlapping code. Covers the per-PR auto-merge bar (live CI verification, substantive review by area, bot-finding stale-vs-actionable triage) and the cross-PR composition discipline (serialize through green main, rebase + re-CI between merges, dormant-safety audit, force-reset over half-merged commits). +--- + +# Auto-merge and composition safety + +Per-PR CI does not catch cross-PR interactions. Two PRs that each pass CI in isolation can compose into a broken main — the `pg`-in-Worker break (#757 + #758), the duplicate-helper break (#769 + #779), the rename-vs-delete break (#785 + #782). All three were composition-time failures: each PR was individually green, the composition was red. The discipline that prevents them is **serialize merges through green main + rebase + re-CI before merging the next**. + +This skill covers the discipline. It is the per-PR auto-merge bar plus the cross-PR composition rules. + +## When to invoke + +- About to auto-merge a PR under an autonomous-run contract. +- About to merge a second PR within minutes/hours of a first that touched related code. +- About to ship a fix to a review comment that might break parity with the rest of the test suite. +- About to address a CodeRabbit / Devin / Codex bot finding. + +## Part 1 — The per-PR auto-merge bar + +These conditions are AND-ed. Any one failing = not authorized to merge; either fix or escalate. + +### 1. CI status verified LIVE at merge time + +`gh pr view --json mergeable,mergeStateStatus,statusCheckRollup` against the **current head SHA**, not a snapshot from earlier in the session. State snapshots go stale fast (a check can flip RED in a minute on a flaky job; a new commit can land on main and demote a `CLEAN` PR to `BEHIND`). + +- `mergeable: MERGEABLE` AND `mergeStateStatus: CLEAN`. +- Any of `BLOCKED`, `BEHIND`, `UNSTABLE`, `DIRTY`, `HAS_HOOKS` → not authorized. Resolve the underlying cause first (rebase, re-run flaky check, address blocking review). +- Required-status checks: every one in the `statusCheckRollup` is `SUCCESS` or `NEUTRAL`. A single `FAILURE`, `ERROR`, `CANCELLED`, or `PENDING` blocks. +- Re-verify within the last 60 seconds before the merge command runs. If you queried 5 minutes ago, query again. + +### 2. Substantive review by area + +A wave-through ("LGTM, merging") is not a review. The review must touch the areas applicable to the change. The full menu (apply the items that apply): + +- **F0-class correctness.** Does the headline behavioral change actually do the thing? Read the smallest unit of behavior end-to-end — input → branching → output. Trace one real value through the new code path. +- **Dormant-safety / default-disabled.** A new code path added in a feature PR must be unreachable in production by default. No caller turns it on. The cutover happens in a separate, intentional flip (see `dormant-flip-and-rollback`). If the PR enables-on-merge, that's a contract violation — escalate. +- **Worker-bundle safety.** If the PR touches code that ends up in a Cloudflare Worker bundle, run the import-safety check (e.g. cloud's `tests/b1-worker-import-safety.test.ts` locally). The fix for a B1 failure is always an import-graph split, never an `external` allowlist. +- **Idempotency.** Webhook and queue handlers must handle redelivery without duplicate side effects. Look for a dedup key (durable, content-derived, persistent across restarts — not an in-memory `Set`). +- **Rollback path preserved.** Can the change be undone by reverting this PR and (where applicable) flipping the corresponding flag back? If the migration is one-way (e.g. dropping a column the old code reads), the PR needs a separate documented backout, and the operator must explicitly accept the irreversibility. +- **Failure-class regression test runs RED without the fix.** Proof-of-non-vacuity: revert just the fix locally, run the test, confirm it fails. If it passes anyway, the test isn't testing what you think it is. +- **Standing-constraint compliance.** SST secret seven-place wiring complete; drizzle journal entry added; integration adapter registry updated; relayfile digest contract honored (terminal states preserved, not modeled as deletions); whatever applies in the repo. + +The review must produce written evidence — a PR comment, a `/tmp/-review.md`, or the rollup file (see file-based reporting below). "I checked" without artifacts is not substantive. + +### 3. Bot-finding triage: stale-vs-actionable + +CodeRabbit, Devin, and similar bots post comments tied to a specific commit SHA. By the time you review, those comments may already be moot — the PR has since added the fix, refactored away the criticized code, or rebased onto a head that no longer contains the snippet. + +For every bot finding: + +1. **Identify the commit it was posted against.** GitHub renders this; CodeRabbit shows the source. +2. **Look at the same file at the current PR head.** Use `gh pr diff ` filtered to the file, or check out the head branch. +3. **Decide: stale-resolved, stale-superseded, or actionable.** + - **Stale-resolved:** the criticized code no longer exists or already incorporates the fix the bot asked for. Reply on the PR documenting the audit: "Checked at HEAD ``; the criticized `foo()` call was replaced by `bar()` in commit `` which incorporates the dedup the comment requested. Resolving." + - **Stale-superseded:** the broader area was refactored; the specific concern no longer applies in the new structure. Same reply pattern — be specific about what replaced it. + - **Actionable:** still present at HEAD, the concern is real. Address before merge or escalate. + +The point: do not silently dismiss bot findings, and do not silently wave them through. Audit each, reply on the PR with the audit, then merge. + +### 4. Worktree hygiene + +PR work happens in a dedicated worktree. Never co-mingle uncommitted changes from another branch into a PR commit. Verify with `git status --porcelain` (not `git diff --quiet`, which misses staged changes) before committing. + +### 5. Never merge on red + +No `--admin` past failing required checks. If a check is broken because of inheritance from main (the same check failed on the merge-base), document the inheritance: + +```bash +git fetch origin main +git diff origin/main -- +gh run view --log | grep -A5 +``` + +Then seek explicit operator waiver. Inherited failures are not auto-waivable. + +### 6. Don't ship half-fixes + +If a fix to address a review comment makes a previously-passing oracle (the rest of the parity suite, the documented contract, the schema-validation suite) start failing — abort the fix. Document on the PR: "Attempted fix X for review comment Y; it breaks the broader parity oracle Z because [reason]. Scope is larger than this PR. Recommend [open follow-up issue / address in a dedicated PR / accept the original review comment as a known limitation]." + +A half-fix that papers over the reviewer's concern at the cost of another invariant is worse than no fix. + +## Part 2 — Cross-PR composition safety + +This is the discipline that prevents composition-time failures. Apply between every two merges in an autonomous run that touch overlapping code. + +### Serialize through green main + +After merging PR A: + +1. Wait for main's post-merge CI to complete and turn GREEN. +2. Rebase PR B onto the new main: `git fetch origin main && git rebase origin/main`. +3. Re-push and re-trigger PR B's CI. +4. Re-verify PR B against the §1 bar with the new CI run. +5. Only then merge B. + +This is slower than parallel merges. It is the only reliable way to catch the composition class of bugs. + +### Cross-PR dormant-safety composition audit + +When two PRs each add to the same dormant path (a new feature behind a flag, with both PRs contributing to the new code), audit the composition before merging the second: + +- Does the combined code in the dormant path compile and pass the parity test suite? +- Does any caller of the dormant code path exist anywhere in the tree? `git grep` the new symbol name across the full repo — if anything references it under a default-enabled branch, dormant-safety is broken. +- Did PR B accidentally remove a guard PR A added? Re-read the combined diff against the merge-base of both. + +### Force-reset over half-merged commits + +If a conflict resolution shipped broken — merge markers in source, a critical helper deleted, a rename-vs-delete misresolution that lost a file — **force-reset the branch to its pre-rebase head and dispatch the proper rebase to the original implementer agent**. Do not try to fix-forward a half-merge. + +```bash +git reflog show +git reset --hard +git push --force-with-lease origin +``` + +Then re-run the implementer with the full diff context. Fix-forward attempts on a corrupted rebase compound the damage — the next agent inherits a tree that diverges from history in non-obvious ways. + +## Part 3 — File-based reporting + +Agent-to-agent message history truncates around 150 characters per turn in some channels; the channel cannot reliably carry full reviews or verdicts. The convention: + +- Each workstream writes its status to `/tmp/ws--status.md` with the full review/verdict/evidence. +- The lead aggregates into `/tmp/-rollup.md`. +- The orchestrator surfaces to the operator only on action points (gate flips RED, escalation needed, flip authorized). + +The channel carries pointers ("review at /tmp/ws-foo-status.md"), not contents. This survives truncation and lets each agent re-read another's full work without re-asking. + +## Anti-patterns + +- Snapshot-and-merge: querying CI status at the start of the review and merging an hour later without re-querying. +- Wave-through reviews on substantive changes: "LGTM, all checks green, merging." +- Silently dismissing bot findings without auditing against HEAD. +- Merging A and B in parallel because each is individually green. +- Fix-forwarding a half-merged rebase. +- Papering over a review comment with a fix that breaks an invariant elsewhere. + +## What this skill does NOT cover + +- Authoring the contract that grants auto-merge authority (covered by `autonomous-run-contract`). +- The flip itself and rollback (covered by `dormant-flip-and-rollback`). +- The diagnostic-first protocol when fixes keep failing (covered by `instrument-dont-guess`). +- Scoreboard maintenance across the run (covered by `swarm-blockers-and-gate-scoreboard`). diff --git a/.agentworkforce/workforce/skills/dormant-flip-and-rollback/SKILL.md b/.agentworkforce/workforce/skills/dormant-flip-and-rollback/SKILL.md new file mode 100644 index 00000000..918069b6 --- /dev/null +++ b/.agentworkforce/workforce/skills/dormant-flip-and-rollback/SKILL.md @@ -0,0 +1,111 @@ +--- +name: dormant-flip-and-rollback +description: Use when designing or executing a cutover-class change (migration, ingestion path swap, provider relink, flag enablement). Encodes the dark-launch + single-switch flip pattern, the refusal to flip on amber, and the pre-authorized rollback procedure — the canonical way to make irreversible-feeling infra changes reversible-feeling. +--- + +# Dormant flip and rollback + +The safest cutover is one that looks routine after the fact because the actual switch was a single config write, and the rollback was the inverse of that write. This skill encodes the pattern. + +## When to invoke + +- Designing a multi-PR change that ends in flipping production behavior (queue swap, ingestion path migration, provider client cutover, feature general-availability). +- Reviewing a PR that proposes to enable a new code path "as part of the feature" — likely needs to be split into dormant-build + separate-flip. +- Authoring the flip command and rollback triggers for the autonomous-run contract. +- Executing the flip itself, post-gates-all-green. +- Observing a post-flip signal that matches a rollback trigger. + +## The three properties of a safe cutover + +### 1. Dormant by default + +Every PR that contributes to the new path leaves that path **unreachable in production by default**. The new code exists, is type-checked, is unit-tested, is even integration-tested against a non-prod fixture — but no live caller takes the new branch. + +Mechanisms (pick what fits the repo): + +- **Feature flag default-disabled.** A boolean read from KV / SST / env / config, default `false`. The new code path is gated on `if (flag) { new() } else { old() }`. The old path keeps running. +- **Per-item enable list.** A KV map of `{ providerId: "new" | "old" }`. The default for any item not in the map is `"old"`. Enabling means adding a single provider with `"new"`; rollback means removing the entry (which falls back to default). +- **Side-by-side route.** The new route lives at `/v2/foo`; the old at `/foo`. Until a caller (CDN router, client SDK, internal config) is updated to call `/v2/foo`, no production traffic hits it. Cutover = updating the caller. + +What does NOT count as dormant: + +- "It's only a small change." Composition-time interactions ignore intent. +- "We default to off in the constructor." If any caller passes `true`, it's not dormant. +- "The new code is faster so we just turned it on." That's not a dark launch; that's a feature ship masquerading as one. + +### 2. Single switch + +The cutover is **one command**, not a sequence of three operations that each must succeed. The command is written into the contract verbatim (see `autonomous-run-contract` §5). + +Examples: + +- `wrangler kv:key put --binding=ROUTER_CONFIG nango_ingestion_path queue --remote` +- `pnpm sst secret set FeatureFlagX true --stage production` +- Merging a pre-prepared one-line PR that flips a constant in committed config. + +If the switch requires two commands (e.g. enable per-provider for the canary set first, then a separate command for global), each is its own gated flip in the contract, with its own scoreboard entry, its own go/no-go decision, and its own rollback. + +### 3. Refusal to flip on amber + +A gate that is "probably green" is not green. The §4 pre-flip gate scoreboard is RED / GREEN, no AMBER promotion. If a gate is AMBER (passes some checks but not all; passes intermittently; passes in isolation but not under load), the flip is refused. Options: + +- Add a concrete acceptance criterion that resolves the AMBER (the test that would prove it GREEN; the load that would prove it GREEN); run it; either RED or GREEN comes out. +- Split the change so the AMBER scope can be deferred to a follow-up flip. +- Escalate to operator to explicitly accept the AMBER as known risk before flipping. + +Never flip "and watch closely" because a gate was almost green. The rollback triggers cost something to invoke (user-visible disruption, dual-write cleanup, on-call attention); a high probability of needing them is a reason not to flip. + +## The flip procedure + +When all gates read GREEN with evidence current within the last hour: + +1. **Re-verify each gate one final time.** Evidence current. CI live for any code-path-dependent gate. +2. **Re-read the contract §5 + §6.** Confirm the flip command and rollback triggers. +3. **Post the pre-flip statement** to the operator's channel: + ``` + Pre-flip statement, , + Gates: G1 GREEN , G2 GREEN , ... (full list) + Flip command: + Rollback triggers: + Rollback command: + Proceeding in 60s unless told otherwise. + ``` +4. **Execute the flip command exactly as written in the contract.** No improvisation. +5. **Immediately start monitoring** the rollback triggers from §6. Sub-minute granularity for the first 10 minutes; 5-minute granularity for the next hour; check-in at 1h, 6h, 24h. +6. **Post the flipped statement** with the timestamp, the command output, the first batch of post-flip metrics. + +## Rollback authority + +Rollback is **pre-authorized by the contract**. When any trigger from §6 fires, execute the rollback command without consulting the operator. Then post: + +``` +Rollback executed, , +Trigger: +Observed: +Rollback command: +Output: +Post-rollback metrics in 5m: ... +Recommended next action: +``` + +The rule: rollback first, diagnose second. "Wait and see if it recovers" is the failure mode that turned 5-minute incidents into 50-minute incidents. + +## After rollback + +- Do not re-flip without re-running the full gate scoreboard. +- File a post-mortem note (the autonomous-run-contract location) with: trigger that fired, observed values, what the gate scoreboard said pre-flip, what the gap was that the scoreboard missed. +- Add a new gate to the contract that would have caught this gap (and run it RED to prove non-vacuity). + +## Anti-patterns + +- **Enabling-on-merge.** A feature PR whose merge starts driving real production traffic to the new path. The cutover should be a separate, intentional, contract-gated event. +- **Coupled flip + ship.** The same PR that ships the new code path also flips the flag. There's no way to roll back without reverting the code. +- **Soft rollback ("we'll just disable it manually if it's bad").** If rollback isn't pre-authored and pre-authorized, in practice you will hesitate, and the metric will be 10× worse by the time you act. +- **Flip while a gate is AMBER.** The single biggest cause of cutover incidents in delegated-autonomous runs. +- **Implicit rollback ("revert the PR").** A code revert is slower than a config flip and requires another deploy. The contracted rollback should be the inverse of the §5 flip — a config write, sub-minute. + +## What this skill does NOT cover + +- The contract that authorizes the flip and rollback (covered by `autonomous-run-contract`). +- The per-PR review bar that ensures dormant-safety lands correctly (covered by `auto-merge-and-composition-safety`). +- Diagnosing post-flip behavior to decide between rollback and continue (covered partially by `instrument-dont-guess`; ultimately the rollback triggers in §6 are intentionally mechanical to remove the diagnose-first temptation). diff --git a/.agentworkforce/workforce/skills/instrument-dont-guess/SKILL.md b/.agentworkforce/workforce/skills/instrument-dont-guess/SKILL.md new file mode 100644 index 00000000..262c0054 --- /dev/null +++ b/.agentworkforce/workforce/skills/instrument-dont-guess/SKILL.md @@ -0,0 +1,79 @@ +--- +name: instrument-dont-guess +description: Use when a fix has failed two consecutive times for the same symptom. Encodes the discipline that the third action must be a temporary diagnostic (a /_diag endpoint, an enriched structured log, a runtime-captured snapshot) rather than another fix attempt. Also covers the discipline for removing those diagnostics once the real root cause is in. +--- + +# Instrument-don't-guess + +When a fix fails twice, the third attempt is not "another fix." It is a **diagnostic** — a temporary endpoint, an enriched log, a runtime-captured snapshot — that turns the next iteration from a guess into a measurement. Guessing past two failed fixes costs more time than the diagnostic does. Battle-scarred from several Nango / Worker / persona-deploy sessions where the third, fourth, fifth fix attempt all missed the real cause that was visible in 30 seconds of structured logging. + +## The trigger + +After two consecutive fix attempts for the **same symptom** have failed (deploy still 500s on the same call, sync still no-ops, webhook still doesn't fire), stop. The next thing you ship to production is a diagnostic, not a fix. + +A "fix attempt" is any code/config change you reasonably believed would resolve the symptom. Two of those, both shipped to where the symptom occurs, both failing to resolve it. + +What counts as "same symptom": + +- Same error class, same code path, same downstream signal. +- A "different" error that's clearly the next downstream step of the same code path is still the same symptom (e.g. "the 500 is now a 502" is not progress; you just changed where the error surfaces). + +## What the diagnostic must do + +The diagnostic captures **ground truth about runtime state at the failure point**, structured for easy reading: + +- **The actual values of variables the fix logic depends on.** Not "config.foo seemed wrong" — the literal value of `config.foo` at the moment of failure, the type, whether it's `null` vs `undefined` vs `""`. +- **The actual code path taken.** Not "I assume it went through branch A" — log the branch. +- **The actual upstream call results** (when the failure is downstream of an integration call): the HTTP status, the response body excerpt (redacted), the latency, the request headers (redacted). +- **The actual resource bindings at runtime** (when on a serverless platform): does `Resource.X.value` return the expected value at the line of use? `process.env.X`? Both? Neither? + +Forms of diagnostic, by environment: + +- **HTTP endpoint** (Next.js, Worker): a temporary `/api/_diag/` route that exercises the failing code path and returns the captured state as JSON. Gated by a header or query secret so it isn't open to the world. +- **Enriched structured log** at the failure point: `log.error("diag::", { actualValue, actualType, branchTaken, upstreamStatus, upstreamBodyExcerpt })`. +- **CloudWatch / Workers Logs probe** at deploy boot: confirm the resource bindings landed (this is the canonical cloud-repo `[boot] resource binding check FAILED` pattern). +- **Runtime snapshot file**: in a long-lived process, write the captured state to `/tmp/diag--.json` for later read. + +## What the diagnostic must NOT do + +- Mutate production data. +- Mask the symptom (a try/catch that just logs and returns 200 is anti-diagnostic). +- Carry secrets in cleartext. +- Stay in production after the root cause is in. + +## Procedure + +1. **Define the question.** Write a one-sentence question the diagnostic will answer. "Is `Resource.NangoSecretKey.value` non-empty at the entry of `/api/v1/sync/refresh` in the Worker runtime?" Not "what's wrong with the sync." +2. **Build the smallest diagnostic that answers the question.** No general-purpose dashboards; one question, one diagnostic. Often <30 lines. +3. **Ship it through CI.** Diagnostics are not "manual prod deploy" exceptions — they go through the same flow as fixes. +4. **Read the diagnostic output.** Read it literally. The most common failure mode at this step is reading what you expected to see rather than what's there. If `Resource.X.value === ""`, the value is the empty string, not "null-ish, probably a parsing issue, probably the secret seeding." +5. **Form the next-fix hypothesis from the diagnostic data, not from intuition.** If the diagnostic says `Resource.X.value === ""`, the next fix is "trace why the SST link did not materialize," not "let me try a different code path." +6. **Ship the next fix.** Verify the diagnostic flips from the failure state to the expected state. +7. **Revert the diagnostic in the same PR or an immediate follow-up.** Diagnostics are temporary scaffolding. Forgetting to revert leaves a `/_diag` endpoint live in production indefinitely (the cloud-repo Phase 3 follow-up tracker has this exact item open). + +## When two failed fixes are actually two-of-different-things + +Sometimes the two fixes addressed two different real bugs in series — each fix was correct for its layer, the second symptom is genuinely a new symptom. The signal: the symptom **changed in kind**, not just in surface. Different error class, different code path, different downstream signal. + +In that case the two-failed-fix counter resets — you're on attempt one of a new symptom. But be honest about whether the symptom actually changed; the common rationalization is to call the same symptom a different one to avoid the diagnostic step. + +## Anti-patterns + +- **Guess-and-deploy loops.** "Let me try X. ... still failing. Let me try Y. ... still failing. Let me try Z." Three deploys = three slots that could have been one diagnostic + one informed fix. +- **Vague log messages.** `log.error("something failed", err)` — what was `err.message` exactly? What were the inputs? Replace with structured logs that capture the question's answer. +- **Wrapping the symptom in retry.** "It's flaky, let me add a retry" — sometimes correct; usually it's masking a deterministic bug that the diagnostic would reveal. +- **Reading logs and seeing what you expected.** Re-read the diagnostic output, character by character, against the one-sentence question from step 1. +- **Leaving the diagnostic in.** The temporary endpoint stays. The enriched log spams CloudWatch. Both have happened. Revert in the same PR cycle. + +## Real-world cases this discipline came out of + +- The cloud-repo Nango sync 502: two fix attempts (timeouts, retry, request-handling) all missed that the Worker was using the core non-Hyperdrive db client. A diagnostic endpoint that logged `client.constructor.name` at the failure point would have surfaced this in one cycle. +- The persona-deploy persist-persona-version INSERT failure: the symptom was a 500, the `errorResponse` helper logged only `error.message`. Enriching the log to include `error.code`, `error.detail`, `error.column`, `error.constraint`, `error.routine` would have given the PG cause directly instead of triggering speculation. +- The Resource.NangoSecretKey.value `""` regression: visible in 30 seconds of a boot-time resource probe; was missed for >24h because the failure surfaced as a generic 500 downstream. + +## What this skill does NOT cover + +- Whether to fix at all vs roll back (covered by `dormant-flip-and-rollback`). +- The auto-merge bar for the fix PR (covered by `auto-merge-and-composition-safety`). +- Cross-PR composition risk for the fix (covered by `auto-merge-and-composition-safety`). +- The contract authority to ship the diagnostic (covered by `autonomous-run-contract`; diagnostics ship under the same merge authority as fixes). diff --git a/.agentworkforce/workforce/skills/swarm-blockers-and-gate-scoreboard/SKILL.md b/.agentworkforce/workforce/skills/swarm-blockers-and-gate-scoreboard/SKILL.md new file mode 100644 index 00000000..998fb921 --- /dev/null +++ b/.agentworkforce/workforce/skills/swarm-blockers-and-gate-scoreboard/SKILL.md @@ -0,0 +1,132 @@ +--- +name: swarm-blockers-and-gate-scoreboard +description: Use during an autonomous run to (a) dispatch supporting codex-impl + claude-review agent pairs against hard blockers when the orchestrator cannot make progress alone, and (b) maintain the live RED / GREEN gate scoreboard the orchestrator reads to authorize the flip. Encodes the file-based reporting convention that keeps the channel readable. +--- + +# Swarm blockers and gate scoreboard + +Two related disciplines for autonomous runs: + +- **Swarm blockers**: when the orchestrator encounters a hard blocker that requires more concurrent work than it can carry, dispatch supporting agent pairs (codex implementer + claude reviewer) to resolve the blocker from repo ground truth. Structural proof beats runtime verification where applicable. +- **Gate scoreboard**: maintain a live RED / GREEN board of every pre-flip gate from the contract, with evidence links and timestamps. The orchestrator reads this board to authorize the flip; the operator reads it to grant or deny the flip-authority moment. + +Both presuppose a contract (see `autonomous-run-contract`) and the file-based reporting convention. + +## Swarming a blocker + +### When to swarm + +- A blocker that requires reading + reasoning across enough of the repo that one agent will thrash (e.g. cross-PR composition audit, schema-vs-adapter parity audit across 40+ resources). +- A blocker that requires concurrent code-and-review iterations (impl writes, reviewer pushes back, impl iterates) faster than a single agent can carry both roles. +- A blocker the orchestrator has identified the shape of but cannot itself fix without losing focus on the run-level orchestration. + +When NOT to swarm: + +- A blocker the orchestrator can resolve in one focused 10-minute slot. +- A blocker that's actually an escalation (external dependency, product decision) — those go to the operator, not to spawned agents. +- A blocker for which the swarm budget granted in the contract (§2 `swarm-blockers authority`) has been exhausted. + +### Pairing rule (this is a standing constraint, not a preference) + +Every spawned workstream is a **codex-impl + claude-review pair**. Never: + +- A codex lead with codex impl agents (they fight over ownership; the lead-codex injects its own implementations into the impl-codex's channel, breaking the review loop). +- A solo agent for non-trivial work (no fresh-eyes review = same blindspot loops). +- A claude impl + codex review pair (the agent-role split in the cloud-repo workforce has codex implementing, claude reviewing — reversing it breaks the convention spawned agents are tuned for). + +The orchestrator role is yours — the orchestrator is a claude agent itself, but it does not implement; it dispatches and aggregates. + +### Dispatch contract per workstream + +Every spawned pair gets a written sub-contract: + +``` +WS- sub-contract + Parent run: + Goal: + Inputs: + Acceptance: -acceptance.sh returns 0"> + Out-of-scope: + Pairing: codex impl , claude review + Status file: /tmp/ws--status.md + Budget: max review-fix cycles, max wall time + Escalation: if blocked, the workstream writes BLOCKED to its status file with the blocker; do not pause silently +``` + +Surface the sub-contract to the operator if the swarm-budget grant in the main contract is being approached. + +### Structural proof > runtime verification + +For correctness-class blockers (does this code-graph property hold? does this invariant compose across PRs? does every provider have an adapter entry?), prefer a **structural proof** to a runtime test: + +- A failing typecheck against a generated source-of-truth registry. +- A `git grep` count assertion against a known-complete list. +- A bundler import-graph assertion (the cloud-repo `tests/b1-worker-import-safety.test.ts` is exemplary — it's not a runtime test; it's a static assertion over the bundled output). +- A compile-time exhaustiveness check (TypeScript discriminated-union switch with `never` default). + +Runtime verification has its place (`instrument-dont-guess` is built on it), but for invariants — "every provider declares a model in REPO_DECLARED_NANGO_PROVIDER_MODELS" — structural proof catches drift the moment it's committed, not the moment it ships to prod. + +The pattern in dispatch: include the structural-proof assertion as the acceptance verify gate. + +### Aggregation + +The orchestrator reads `/tmp/ws-*-status.md` for each active workstream. It does NOT poll the channel for status — agents truncate. Channel messages from workstreams carry only: + +- `WS- GREEN, see /tmp/ws--status.md` (success) +- `WS- BLOCKED, see /tmp/ws--status.md` (need orchestrator attention) +- `WS- ESCALATE, see /tmp/ws--status.md` (need operator attention) + +The orchestrator aggregates into `/tmp/-rollup.md` and updates the gate scoreboard. + +## The gate scoreboard + +The scoreboard is a single markdown file (`/tmp/-scoreboard.md`) maintained for the run's lifetime. It contains one row per pre-flip gate from the contract §4, with: + +``` +| # | Gate | State | Evidence | Last verified | Owner | +|---|---|---|---|---|---| +| G1 | Worker bundle B1 safety | GREEN | b1-worker-import-safety.test.ts pass @ | | orchestrator | +| G2 | Hyperdrive client on Worker | GREEN | /tmp/ws-hyperdrive-status.md, PR #743 merged | | WS-hyperdrive | +| G3 | Queue consumer idempotent under dup | RED | dedup test red; no Postgres backing yet | | WS-dedup | +| G4 | Rollback path proven | AMBER | rollback command tested in staging, not prod | | orchestrator | +| ... | +``` + +States: **RED** / **AMBER** / **GREEN**. The contract §4 says AMBER is not flippable; the scoreboard makes AMBERs visible until they resolve. + +### Scoreboard hygiene + +- **Update on every state change.** A workstream flipping a gate GREEN updates the row + bumps `Last verified`. A gate going stale (no verification in >2h on a fast-moving run) drops back to AMBER. +- **Evidence is a pointer.** Link the PR, the CI run, the status file, the structural-proof output. The row should not contain the evidence in-line. +- **The orchestrator re-verifies before the flip.** Final pre-flip pass: re-run each GREEN gate's acceptance check, refresh the `Last verified` to within the last hour, confirm every row reads GREEN. +- **One scoreboard per run.** Not per workstream. The scoreboard is the union of evidence the operator needs to grant the flip moment. + +### Gates as the orchestrator's working set + +The orchestrator does not run "all the work" — it owns the scoreboard. Every action it takes is in service of moving a RED gate to GREEN, or re-verifying a GREEN that's gone stale. If an action doesn't map to a gate, ask whether the gate is missing from the scoreboard or whether the action is out-of-scope for the run. + +## File-based reporting convention (repeated here because it's load-bearing) + +- Each workstream writes to `/tmp/ws--status.md`. +- The orchestrator aggregates to `/tmp/-rollup.md`. +- The gate scoreboard is `/tmp/-scoreboard.md`. +- The contract is `/tmp/-contract.md` (or wherever it's pinned per `autonomous-run-contract`). +- Surface to the operator only on action points (gate goes RED, escalation, flip-authorization moment, post-flip metrics, rollback). + +The channel carries one-line pointers + state changes; nothing substantive lives only in channel messages. This convention exists because agent-to-agent channels truncate, and because the operator needs to be able to read back the run state on returning to the channel after hours away. + +## Anti-patterns + +- **Spawning without a sub-contract.** Workstream drifts, blast radius unclear, orchestrator can't tell when it's done. +- **Codex lead with codex impl agents on one channel.** Ownership fights. Use claude orchestrator → codex impl + claude review pairs. +- **Polling channel for status.** Truncation eats the answer. Read status files. +- **Scoreboard going stale.** GREENs from yesterday are not GREENs today. Re-verify before the flip. +- **AMBER flips.** Either resolve to RED or GREEN. AMBER is a request for more evidence, not a flippable state. +- **Action that doesn't map to a gate.** Either it's out-of-scope (decline) or the scoreboard is missing a gate (add). + +## What this skill does NOT cover + +- Authoring the contract that grants swarm authority and lists the gates (covered by `autonomous-run-contract`). +- The per-PR auto-merge bar each workstream's PR must hit (covered by `auto-merge-and-composition-safety`). +- The diagnostic-first protocol when a workstream's fixes keep failing (covered by `instrument-dont-guess`). +- Decomposing a high-volume change into tier-1 + tier-2 (covered by `tiered-acceptance`). diff --git a/.agentworkforce/workforce/skills/tiered-acceptance/SKILL.md b/.agentworkforce/workforce/skills/tiered-acceptance/SKILL.md new file mode 100644 index 00000000..d97d44a9 --- /dev/null +++ b/.agentworkforce/workforce/skills/tiered-acceptance/SKILL.md @@ -0,0 +1,95 @@ +--- +name: tiered-acceptance +description: Use when a single gate would require deep proof across a large set (44 models × 10 providers; 60+ resources; every region; every plan tier). Splits the acceptance into tier-1 (deep proof on high-volume / high-fidelity slice) and tier-2 (smoke proof on low-volume tail), documents the explicit accepted trade-off, and preserves safety through dormant-default + per-item enable + rollback. +--- + +# Tiered acceptance + +When a single gate's acceptance criterion would require deep proof across a large set — every model × every provider, every resource × every adapter, every region — the gate becomes the long pole of the run. Either deliver less, ship without proof on the tail, or split. Splitting honestly is the option this skill encodes. + +## When to invoke + +- A gate whose ideal acceptance is "all N variants pass deeply" with N large (say ≥20). +- A long-pole gate that's blocking the rest of the scoreboard from going GREEN, with most of the work in the tail. +- A migration where the high-volume slice covers >90% of real traffic but the long-tail variants exist and have non-zero traffic. + +If N is small (<10) or the variants are uniform (same adapter, parameterized), don't tier — prove all of them. + +## The split + +Divide the set into two tiers by **realized criticality** — not by alphabetical convenience, not by ease. + +### Tier 1 — full proof + +The slice where: + +- Traffic / usage is concentrated (cover the top X% by volume — pick X to clear a defensible threshold, often 90%). +- Failure has highest blast radius (the integration the operator's customers actually use). +- Fidelity matters (provider parity is the explicit promise; long-tail divergence is less promised). + +For tier-1, the acceptance is the full proof from the original gate intent: deep behavioral parity test, sustained-load test, the failure-class regression test running RED without the fix, cross-PR composition audit, the works. + +### Tier 2 — smoke proof + +The long tail. Acceptance is reduced to: "the path executes without error on a representative sample." Not the deep parity proof. Not the sustained load. + +Smoke proof concretely means: + +- A single instance per tier-2 variant runs the happy path end-to-end and succeeds. +- The variant's wiring exists (registry entry, adapter, schema) and matches the structural-proof invariants from `swarm-blockers-and-gate-scoreboard`. +- Failure of a tier-2 variant in production is recoverable by the rollback procedure or by per-item disable. + +The tier-2 acceptance is honestly weaker. The trade-off is named. + +## Safety preservation for tier-2 + +Tier-2 ships under reduced proof. The safety net comes from the dormant-flip + per-item-enable pattern (`dormant-flip-and-rollback`): + +- **Dormant by default.** Tier-2 variants are not enabled on merge. +- **Per-item enable.** The flip mechanism enables variants one at a time (per provider, per model, per region). The operator chooses the rollout order: usually tier-1 first (deep-proof gives high confidence; observe in prod), tier-2 in batches (lower confidence; observe more carefully). +- **Per-item rollback.** A misbehaving tier-2 variant is disabled by removing one entry from the enable list. The rollback is variant-scoped, not run-scoped. The other variants stay enabled. + +This is the trade-off mitigated: deep proof on the high-fidelity slice, smoke proof on the tail, but in production each variant is enabled and observable independently, and any single variant's failure is recoverable without disrupting the others. + +## Documentation + +The split is **declared in the contract** before tier-2 work begins: + +- Contract §4 (pre-flip gates) lists the tiered gate as two entries: "Gate G7a — tier-1 full proof for {enumerated variants}" and "Gate G7b — tier-2 smoke proof for {enumerated variants}". +- Contract §6 (rollback triggers) names the per-item rollback command and the threshold per variant (often the same threshold as the run-level, just scoped to the variant's metrics). +- Contract §8 (escalate) lists the conditions that would force a tier-2 variant into tier-1 (e.g. "if the variant's traffic exceeds X% of total, promote to tier-1 and add deep proof before re-enabling"). + +Surface the split to the operator at contract-authoring time. Get explicit acknowledgement of the tier-2 reduced proof. Without that, the operator may have assumed full proof and the trade-off was never theirs to accept. + +## Enumeration discipline + +The variant list — both tiers — must be **enumerated from a generated source of truth**, never hand-maintained. The drift class is the same as the cloud-repo `REPO_DECLARED_NANGO_PROVIDER_MODELS` lesson: a hand-list that mirrors a generated list silently drifts, and a missing entry silently disables the variant. + +- Generate the variant list from the registry / adapter directory / config. +- Assert at compile time (or in the structural proof) that the generated list and the tier-1 + tier-2 enumerations are exhaustive and disjoint. +- Drift in either direction (a variant in the registry that's in neither tier; a variant in a tier that's not in the registry) is a CI failure. + +## Promotion criteria + +A tier-2 variant may need to be promoted to tier-1 mid-run or post-flip: + +- Its observed traffic share grows past a threshold the contract names. +- A real incident on the variant exposes a failure class smoke proof would not catch. +- The operator's product priorities change. + +Promotion is not a silent step. It re-opens the gate scoreboard: the promoted variant gets deep proof, the gate that was GREEN under tiered acceptance returns to AMBER until the new proof lands. + +## Anti-patterns + +- **Implicit tiering** — "we'll deep-test the important ones and smoke-test the rest" without writing down which is which. Operator-trade-off is never explicit; coverage becomes wishful. +- **Hand-maintained tier lists.** Drift = silent regression. +- **Smoke-proof masquerading as full proof** — a tier-2 variant's smoke test labeled "passed" in the report without the tier distinction noted. Hides the trade-off from anyone reading the report later. +- **Skipping the per-item-enable pattern.** Tiered acceptance without per-item enable means a bad tier-2 variant takes down all variants together. The trade-off is no longer mitigated. +- **Letting tier-2 enable on merge.** Bypasses the dormant-default safety. If tier-2 is enabled on merge, the smoke proof was effectively full-proof's responsibility; the trade-off was an illusion. + +## What this skill does NOT cover + +- The dormant-default + per-item-enable mechanism itself (covered by `dormant-flip-and-rollback`). +- The contract sections that document the tiering and the operator acknowledgement (covered by `autonomous-run-contract`). +- The gate scoreboard rows for the tiered gates (covered by `swarm-blockers-and-gate-scoreboard`). +- The cross-PR composition risk when tier-2 lands in multiple PRs (covered by `auto-merge-and-composition-safety`).