Execute Backlog
SkillDocs & knowledgeUse when ready to attack the GitHub issue backlog with discipline. Scans open issues, bundles related work into an efficient execution plan, executes the bundle with verification, then closes issues + writes a retrospective that includes self-improvement notes for this skill. Trigger phrases — "execute backlog", "work the backlog", "knock down P1 issues", "ship a bundle", "/execute-backlog".
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Execute Backlog skill
What this skill tells your AI
The instructions your AI receives, as published by tylerjrbuell/reactive-agents-ts in .agents/skills/execute-backlog/SKILL.md and read by ahel’s review.
Purpose: turn the GitHub issue backlog into shipped work using a deterministic loop: SCAN → BUNDLE → PLAN → EXECUTE → VERIFY → UPDATE → RETRO. Each pass closes a coherent bundle of issues, leaves the repo greener than it found it, and writes a retro that improves the skill itself.
Operating principle (Extreme Ownership): every issue in the bundle is "owned" by the executing agent for the duration of the pass. No half-finished issues. No "punted" verification. If a bundle can't ship clean, the bundle gets descoped — not the verification.
When to Use
- Backlog has ≥5 open issues with
priority:p1or higher - A sprint / release-gate moment: pick the next high-impact bundle
- User says "let's work the backlog", "execute audit-2026-05-21", "knock down the killswitch issues"
- Agentic team handoff: spawn one of these per worker, each owning a label-scoped bundle
Don't use for:
- One-off bug fixes (use
agent-tdddirectly) - Backlog audit / re-prioritization (use
architecture-audit) - New feature work (use
reactive-feature-dev)
Inputs (declared by caller)
The skill MUST be invoked with at least one filter — never run unfiltered.
filter:
labels: # optional: ["area:providers", "audit-2026-05-21"]
priority: # optional: "p0" | "p1" | "p2"
phase: # optional: "phase:C" | "phase:E"
max_bundle_size: 5 # default 5; cap on issues per execution pass
budget_minutes: 90 # default 90; abort if exceeded, descope cleanly
Caller examples:
/execute-backlog labels=audit-2026-05-21 priority=p1/execute-backlog labels=area:providers,phase:E/execute-backlog labels=health-sweep,priority:p1 max_bundle_size=3
Phase 1 — SCAN
Query GitHub for candidate issues matching the filter.
gh issue list \
--state open \
--label "<comma-joined labels>" \
--json number,title,labels,body,createdAt,updatedAt \
--limit 100
For each candidate, parse:
- Issue number, title
- All labels (especially
area:*,priority:*,verified) - Primary file/location (regex from body:
**Location:**line) - Verified-by present? (boolean —
**Verified-by:**block in body)
Filter rules:
- Drop any issue without
verified-by:evidence — file a comment asking for verification; do not execute on unverified claims (the 2026-05-21 inflation pattern is the reason this rule exists) - Drop
phase:mismatches ifphase:filter was provided - Drop issues with the
blockedlabel - Drop issues assigned to someone else
Drift check (added 2026-05-21): for any candidate carrying a verified-by command with file:line references, re-run the command. If the emitted line numbers differ from the issue body's claimed lines by >5 on any row, mark the candidate 🟡 drift detected and re-read the cited spans to confirm the semantic cast/pattern still matches. Counts can match while locations move 25+ lines — that means the surrounding logic refactored and the fix shape may no longer apply. Acceptable to proceed; not acceptable to skip the check.
Drift check addendum (added 2026-05-21 v3): semantic-equivalent pattern grep. When the issue body claims N sites but the primary grep returns fewer, the gap may be a syntactic variant of the same anti-pattern, not real drift. Before declaring 🟡 drift detected, additionally grep the known equivalence classes:
| Primary pattern | Semantic equivalents to also grep |
|---|---|
(x as any) | (x as unknown as {…}), (x as unknown as Record<…>) |
: any | : unknown (intentional widening), : Record<string, any>, untyped function-type Function |
as Function | (...args: any[]) => any, Callable aliases |
If the sum of equivalence-class matches reproduces the claimed count, proceed (no drift — issue author counted across both forms). If the sum still falls short, mark drift and re-read the cited spans. (Reason: 2026-05-21 #71 spawn — issue claimed 7 sites; primary grep (state as any) returned 3; the other 4 lived under as unknown as { … } narrowings. Including both forms recovered the exact 7.)
Output: candidate set, sorted by priority:p0 > p1 > p2 > p3, then by verified label (verified issues rank higher), then drift-clean before drift-detected.
Cross-package consistency probe (added 2026-05-22 v9). When the issue touches per-framework / per-platform packages providing equivalent APIs (e.g., @reactive-agents/react + @reactive-agents/svelte + @reactive-agents/vue all exporting useAgentStream-style hooks/factories), briefly diff the impl shape across siblings:
# Side-by-side compare of equivalent files
diff packages/<sibling-a>/src/<file>.ts packages/<sibling-b>/src/<file>.ts | head -40
Same name + equivalent signature + divergent behavior = latent defect. Surface in the plan's "Adjacent improvement found" section. Fix opportunistically per the test+fix combo rule (Phase 4 v9). (Reason: 2026-05-22 #82 closeout — vue's useAgentStream.StreamError branch threw + was caught by inner try/catch; svelte's equivalent branch used direct next.error = …; next.status = "error" and worked correctly. Sibling diff would have flagged the divergence before the test had to.)
Phase 2 — BUNDLE
Group candidates into one bundle (max max_bundle_size) that ships together. A bundle is coherent when ≥1 of these holds:
| Cohesion signal | Example |
|---|---|
Same area:* label across all members | All area:providers |
| Primary files overlap (same dir, same package) | All under packages/runtime/src/builder/ |
| Root-cause cluster | HS-06/07/08 all share "untyped state shape" |
| Cross-cutting fix shape | "Remove as any from N hook surfaces" |
| Untyped schema field needs structured access in callers | Local widening type + boundary helper inside the consuming package — see "default fix shape" below |
Default fix shape for typing issues (added 2026-05-21 v5). When the cited as any casts all narrow the same schema-typed-unknown field, default to:
- Create
<domain>-context.ts(or-state.ts) in the consuming dir. - Define
<Domain>Context = ExecutionContext & { <field>: <ConcreteShape> }(interface mirroring runtime usage, NOT the schema source-of-truth). - Export
as<Domain>Context(c)boundary helper — single named cast. - Migrate each cited site through the helper; delete the cast at sources where the field was already typed (dead-cast sweep — see Phase 4).
Three shipped precedents to copy from: #71 HandlerState (packages/reactive-intelligence/src/controller/handler-state.ts), #72 typed BuilderState option groups (packages/runtime/src/builder/to-config.ts), #73 ThinkContext (packages/runtime/src/engine/phases/agent-loop/think-context.ts). The pattern keeps each fix inside its consuming package (cross-package descope gate satisfied automatically).
Bundling algorithm:
- Take the highest-priority candidate as seed
- Greedily add candidates that share ≥1 cohesion signal with the bundle
- Stop at
max_bundle_sizeOR when no remaining candidate has cohesion ≥1 with the bundle - If the bundle has <2 issues after greedy growth → still proceed (singleton bundles are fine)
Hard gate (added 2026-05-21): cross-package descope. Before locking the bundle, re-grep each candidate's verified-by command and inspect the file paths it emits. If those paths span ≥2 packages (packages/<a>/… vs packages/<b>/…), descope to a per-package bundle even if the issue body's "Fix direction" suggests otherwise. The body lies; the grep doesn't. (Reason: 2026-05-21 #73 spawn — body said "type properly in the think phase" but the actual as any targets resolved to types owned by @reactive-agents/llm-service and the kernel-context shape, both other packages.)
Multi-package test-infra split (added 2026-05-22 v7). The cross-package gate isn't only for typing/refactor issues — it applies the same way when an issue cites adding tests / infra / docs to N packages. Ship N bundles (one per package), each with its own PR. Name the follow-up bundles in the seed bundle's PR description so the queue is explicit. (Reason: 2026-05-22 #82 spawn — issue cited zero tests across packages/react/, packages/svelte/, packages/vue/. Shipped bundle/react-smoke-tests first; named bundle/svelte-smoke-tests and bundle/vue-smoke-tests as follow-ups in the PR body. Disjoint scopes, independent CI, no cross-package merge conflicts.)
Output: named bundle. Pattern: <area>-<theme> (e.g., providers-untyped-hooks, runtime-builder-as-any-sweep).
Open a new GH issue or use an existing tracker as the bundle parent — link it to all members via Tracks: #N lines. Apply the tracking label.
Phase 3 — PLAN
For the bundle, write a concrete execution plan to wiki/Planning/Implementation-Plans/YYYY-MM-DD-<bundle-name>.md.
Plan must contain:
# Bundle: <name>
Date: YYYY-MM-DD
Budget: <budget_minutes> min
Issues: #N, #N, #N
## Acceptance criteria (per issue)
- #N: <one-sentence done definition tied to the verified-by claim>
## Execution units (ordered)
1. **Unit 1:** <one or two issues, ≤45 min, files touched, tests touched>
2. **Unit 2:** ...
## Risk register
- <risk> → <mitigation>
## Verification protocol (cross-cutting)
- `bun test packages/<changed>/` — full pass
- `bun run build` — green
- `bunx turbo run typecheck --filter=<changed>` — green
- Sample replay or trace test if behavior change
## Out-of-scope (explicit)
- <thing> — punt to next bundle
Plan gates:
- If total estimated effort >
budget_minutes→ descope to fit; do NOT skip verification - If any unit depends on infra not in the repo → mark
blockedon that issue, drop from bundle - If two units conflict (same file, conflicting changes) → sequence them, never parallelize
Substrate-aware test strategy (added 2026-05-22 v8). When the bundle adds tests to a new framework/package, identify the test substrate up front. Three classes:
| Substrate | Examples | Default coverage |
|---|---|---|
| Render-bound | React hooks, Vue setup(), web components | Public-surface smoke + type contracts. Behavioral via render = follow-up bundle (justify the @testing-library/X + happy-dom investment separately). |
| Framework-agnostic | Svelte stores (writable), Solid signals, Effect.Effect | Behavioral coverage with mocked I/O (e.g., globalThis.fetch = async () => new Response(...)). No DOM/render needed. |
| Pure | Plain JS factories, helpers, parsers | Behavioral coverage directly. No mocking infrastructure beyond stub inputs. |
Picking the wrong default = scope creep (render-bound bundle pulled into the test-infra rabbit hole) or coverage gap (framework-agnostic bundle capped at smoke when behavioral was cheap). (Reason: 2026-05-22 #82 spawn — react bundle (#100) capped at 6 smoke cases due to render-context requirement; svelte bundle (#101) shipped 13 cases including 9 behavioral because stores work in any runtime. Coverage gap would have been ~half if both bundles defaulted to "smoke only".)
When in doubt, write one case at the framework-agnostic tier and see if it runs under bare bun:test. If yes, proceed behavioral. If "Invalid hook call" / setup errors → drop to smoke + name the follow-up bundle.
Read the superpowers:writing-plans skill conventions (location override: wiki/Planning/Implementation-Plans/).
Fire-site reachability check (added 2026-05-21 v4): before designing integration-style tests for any unit, grep the call graph to verify the test scenario will actually exercise the code under fix. A hook/handler/wrapper can be registered without being fired if the test scenario routes through an alternate code path (e.g., withTestScenario short-circuits the reactive loop and bypasses runner.ts:683 runPhaseHooks). Quick check:
# 1. Locate where the unit under fix gets invoked
grep -rn "<wrapper-or-helper-name>\|<registered-fn-pattern>" packages/
# 2. Confirm at least one fire site is reached by the planned test config
# (provider, reasoning, strategy, test scenario, etc.)
If reachability is uncertain, default to direct-invocation tests (instantiate the helper / pull from registry / call wrapper directly) rather than full-stack agent.run() tests. Reason: 2026-05-21 #74 spawn — initial test design called agent.run() with withTestScenario + withReasoning(), expecting the harness before('think') wrapper to fire. It never did. Probes confirmed the wrapper was registered but the kernel-loop fire site was bypassed. Direct invocation via RegistrationHarness._collected pinned the unit in 6 tests, 0 flakes.
Adjacent-improvement detection at baseline (added 2026-05-25 v11). When the post-branch baseline bun test or bunx turbo run typecheck reports failures in the SAME package as the bundle, scan each failure's fix-shape against the bundle's root-cause class:
| Baseline failure type | Adjacent-improvement test |
|---|---|
| Typecheck red w/ identical anti-pattern as cited verified-by | Add to bundle. Same helper / fix shape. No scope creep. |
| Pre-existing test fail in untouched code path | File follow-up issue; don't block bundle. |
| Lint warning in adjacent file with shared fix recipe | Add to bundle iff ≤5 LOC delta and same package. |
If adjacent improvement adopted, update the plan doc's "Adjacent improvement found" section AND broaden the verified-by recheck command from per-file to workspace-wide (or per-package-wide). Generalization of the v9 "test+fix combo" rule. (Reason: 2026-05-25 #85 spawn — baseline typecheck flagged 5 errors in tests/controller/dispatcher-compose-bridge.test.ts with identical InterventionHandler<TDecision> contravariance root cause as the cited 9 sites in handlers/index.ts. Bundling them added 9 helper applications, 0 LOC of new design, silenced all 5 errors. Excluding them would have left a follow-up bundle with one-line diff each — wasteful.)
Phase 3.5 — BRANCH (mandatory)
Before any code edits land, create a dedicated feature branch off main for the bundle.
Local-main-ahead check (added 2026-08-16 v13). Some repos hold work on local main unpushed until a release/tag event (e.g. reactive-agents-ts itself — see .agents/MEMORY.md's repo-workflow note). Branching from origin/main in that case silently drops every local-only commit from the new branch's base. Check first:
git fetch origin main
git log --oneline origin/main..main | wc -l
If non-zero, branch from local main instead — those commits are real, intentional, and the bundle should build on top of them:
git checkout -B bundle/<bundle-name> main
If zero (origin and local main agree), the original form is correct:
git checkout -B bundle/<bundle-name> origin/main
Naming pattern: bundle/<area>-<theme> (e.g., bundle/runtime-builder-state-typing). The branch is the unit-of-work for the entire bundle. All commits in Phase 4 land here; the Phase 6 PR (or local merge, see Phase 6a's hold-until-tag note) ships them together.
If the working tree is dirty when this skill is invoked, stop and surface the dirt — do not stash silently. The caller decides: commit, discard, or move out of the way. (Reason: per feedback_commit_before_branch.md, exploratory state must not get mixed into bundle commits.)
Live-peer check (added 2026-08-16 v12). A clean/acknowledged dirty-tree check at branch time is a snapshot, not a lock — it says nothing about whether another agent session is actively mutating the same checkout during EXECUTE. Before the first git add in Phase 4, and again immediately before every git commit in this bundle, call ListAgents (or the harness equivalent) and check for other non-idle sessions. If one is active on the same repo path, treat it as a hard-stop: wait for it to go idle before staging/committing, don't proceed on a guess. git add <specific files> does NOT protect against this — the index is shared process-wide, not per-agent-session, so a peer's git commit (even a plain git commit -m ... with no path args) sweeps up whatever you staged a moment earlier into THEIR commit. The only real isolation from a genuinely concurrent session is a dedicated worktree (superpowers:using-git-worktrees); the ListAgents check is the cheap mitigation when a worktree wasn't set up. (Reason: 2026-08-16 #198 spawn — a second live session's git commit on the same checkout swept 8 staged bundle files into an unrelated commit; recovery via git reset --soft HEAD~1 itself raced the same peer recommitting mid-recovery. Caught only by manually running ListAgents and confirming the peer had gone idle before redoing the split.)
Branch-before-edit discipline (added 2026-08-18 v15). SCAN/BUNDLE/PLAN research (reading files, grepping, running madge/grep/find to verify claims) is allowed on main before the branch exists. The moment a fix's shape is understood well enough to write one line of it, git checkout -B bundle/<bundle-name> <base> MUST run first — do not let "I'm just confirming the fix works" turn into an Edit/Write call while still on main. If it happens anyway, don't panic-revert: git checkout -b bundle/<name> from the current commit carries uncommitted working-tree changes onto the new branch (same commit, so nothing is lost), then git add only the intended files before the first commit — leave any unrelated pre-existing dirt (build-generated timestamp files, etc.) unstaged. (Reason: 2026-08-18 #184 spawn — investigation flowed directly into a type-extraction edit on main with no branch created; caught before the first commit, recovered via post-hoc branch-from-same-commit + selective staging, zero actual harm, but the skill's own Phase 3.5 ordering should have prevented needing the recovery at all.)
Baseline capture (added 2026-05-21): immediately after branching, pin the pre-EXECUTE state:
bun run build 2>&1 | tail -3 # → record "Tasks: N/N successful"
bun test 2>&1 | tail -3 # → record pass/fail/skip counts
Stash the numbers in the plan doc under a ## Baseline heading. Phase 5 compares against these; pre-existing reds get filed as follow-up issues (see #93 pattern) rather than blocking the bundle. Without this baseline, a pre-existing failure surfaced by your edits looks like a regression and you'll burn budget chasing it.
Phase 4 — EXECUTE
For each execution unit, follow agent-tdd discipline:
RED → write/find failing test demonstrating the issue
GREEN → minimum fix that turns the test
REVIEW → run review-patterns; address findings
COMMIT → conventional commit, citing GH issue numbers
RED authority check (added 2026-05-25 v10). Before relying on a RED test to pin a missing type or field, check two harness conditions that can silently mask the RED:
- Tests excluded from typecheck. Run
grep -A2 '"exclude"' packages/<X>/tsconfig.json— if"tests/**/*"is excluded, missing-type errors in the RED test will NOT fail typecheck. The "RED" passes against pre-fix state at type level. - TaggedError / structural-type leniency. Effect's
Data.TaggedErrorstores any field passed to its constructor, even if the payload type doesn't declare it.expect(err.newField).toBeDefined()will pass against pre-fix state if the test constructs the error withnewField. Same for plain TS structural types — passing extra properties to a struct constructor is accepted at runtime.
When either holds, the RED is post-hoc regression coverage only — it doesn't prove the pre-fix state was broken. Acceptable; just note in the plan's risk register and don't claim "test failed before fix, passes after" in the retro unless you confirmed it. To strengthen RED authority for type fields: write a temporary src/.test-types.ts smoke file that imports and destructures the new field, run tsc --noEmit on src/, then delete the smoke file before commit. Overkill for most fixes; flag only when the pre-fix RED must be authoritative.
Fiber-interruption regression test construction (added 2026-08-16 v14). When a regression test forks a fiber specifically to interrupt it later (proving fiber-supervised cancellation — e.g. "does interrupting this Effect actually stop the underlying work"), keep the fork, the wait-for-condition, and the Fiber.interrupt call inside ONE Effect.gen / single Effect.runPromise call. A bare Effect.runPromise(Effect.fork(effect)) returns a Fiber handle, but the ephemeral scope created for that one runPromise call closes immediately after fork returns — which can interrupt the child fiber before the test ever gets to observe or deliberately interrupt it. This reads as "the fix doesn't work" (the condition you're polling for never becomes true) when it's actually a test-authoring bug, not a fix bug — verify with a quick throwaway debug script logging fiber state before concluding the production fix is wrong. (Reason: 2026-08-16 #35 spawn — a code-action Worker-interruption regression test's first draft forked outside the observing Effect.gen, silently self-interrupting before the sandboxed code reached its first tool call; cost several minutes chasing an unrelated red herring (effect version dual-package hazard in a /tmp debug script) before finding the real cause.)
(Reason: 2026-05-25 #75 spawn — RED test parse-error-attempts.test.ts was written expecting typecheck failure on ParseAttemptError not-yet-exported and LLMParseError.attempts not-yet-declared. Both conditions held: packages/llm-provider/tsconfig.json excludes tests; Data.TaggedError stored attempts field at runtime regardless of type declaration. The "RED" passed against pre-fix state. Fix still landed clean; lesson is to not over-claim RED→GREEN narrative in retros when these conditions hold.)
Single-area mechanical-scaffold bundle template (added 2026-05-25 v10). When N sites in one package share an identical scaffold (same imports, same control flow, same fix shape), prefer mechanical in-place edit over helper extraction. Heuristic:
| Condition | Action |
|---|---|
| N ≤ 10 sites, same package, identical scaffold | Mechanical push/edit at each site. Verified-by = grep -c '<new-pattern>' returns N. |
| N > 10 sites, OR scaffold spans packages, OR scaffold has 3+ divergent variants | Extract helper. Test helper directly with mocked inputs. |
Mechanical edit wins on review surface (one shape to verify N times) and ships faster. Helper extraction trades that for de-duplication; only worth it when the dup cost exceeds the abstraction cost. (Reason: 2026-05-25 #75 — 5 providers × ~50 LOC identical retry loop. In-place push of 2 lines per provider beat extracting parseStructuredWithRetry helper on budget AND eliminated SDK-mock test complexity. Verified-by grep -c parseAttempts.push → 10 caught all sites in one check.)
Dead-cast sweep (added 2026-05-21 v5). Before migrating each cited as any site through a new helper, check whether the underlying type already supports the access pattern (the schema may have been tightened since the cast was added; the cast was historic). Delete dead casts outright — lighter diff, no helper indirection, less maintenance. Procedure for each site:
# Read the cited line's surrounding context
# Check the field's type in the schema (e.g., `packages/runtime/src/types.ts`)
# If the type already covers the access → delete the cast
# If `unknown` / `any` / missing field → migrate via the boundary helper
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 27
- Forks
- 4
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
execute-backlog- Source
- github.com/tylerjrbuell/reactive-agents-ts