Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
e2ea0ac
execplan: unattended dispatch — the board sweep tick that retires man…
SSFSKIM Jul 17, 2026
5610d95
feat(implementing-tickets): implement-dispatch.sh — the dispatch ritu…
SSFSKIM Jul 17, 2026
785438c
feat(issue-tracker): board-sweep.sh — the unattended tick (Symphony's…
SSFSKIM Jul 17, 2026
8de1df4
feat(sweep): operator surface + event templates; slot counter joins b…
SSFSKIM Jul 17, 2026
bc44207
chore(shakedown): launchd plist for the sweep (worktree-pinned) + con…
SSFSKIM Jul 17, 2026
8aa3d39
docs(execplan): M4 core shakedown evidence — three live dispatches, r…
SSFSKIM Jul 17, 2026
d4cca59
docs(sweep): the launchd/TCC reality — measured, documented, mitigated
SSFSKIM Jul 18, 2026
08b636c
fix(sweep): four review findings, test-first — the recovery ladder an…
SSFSKIM Jul 18, 2026
3a75e76
docs(execplan): retrospective + flaky-suite observation; M5 closed
SSFSKIM Jul 18, 2026
02f3f60
fix(protocol): the closing PR is READY, never draft — the shakedown's…
SSFSKIM Jul 18, 2026
d64dc75
fix(review-dispatch): normalize lingering ticket owners before bind; …
SSFSKIM Jul 18, 2026
86985a3
refactor(protocol): draft ban → outcome + consequence, per the golden…
SSFSKIM Jul 18, 2026
1d4272d
chore(infra): repos moved to ~/Developer/GitHub — operational paths r…
SSFSKIM Jul 18, 2026
b5d1379
refactor(worker-prompts): bootstraps carry bindings, not lectures or …
SSFSKIM Jul 18, 2026
6f6ea69
docs(sweep): pre-merge review nits — honest dedupe claim, trust postu…
SSFSKIM Jul 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions docs/doperpowers/TECH-DEBT.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@

| # | Tier | Item | Trigger |
|---|---|---|---|
| 1 | T1 | Recovery from transient worker deaths is manual | Building the auto-attach dispatch trigger |
| 2 | T1 | gh-token capture failure is a stderr warning, not a spawn abort | Same trigger phase; or the warning recurring |
| 1 | T1 | ~~Recovery from transient worker deaths is manual~~ **SHIPPED 2026-07-18** (board-sweep RECOVER pass) | — |
| 2 | T1 | ~~gh-token capture failure~~ **NA on the one-harness pipeline** (the capture lived in retired codex-spawn) | Reopens only if a codex-CLI worker is ever spawned again |
| 3 | T2 | Claude engine branch untested by the shakedown (SD-2, SD-4 deferred) | First real claude-engine dispatch |
| 4 | T2 | No CI/pre-push gate on the shell test suites | Next test-drift incident, or opportunistically |
| 5 | T2 | Automated review trigger blocked: self-hosted runner unregistered | Org admin grants (ida-solution#302) |
| 6 | T3 | `needs-human → in-review` restore is a two-hop | Review-side transient parks becoming frequent |
| 7 | T3 | Work-alone mandate is prompt-level, not mechanical | An observed post-clause violation |
| 8 | T3 | `SSL_CERT_FILE` fix is macOS-path-specific | First Linux worker host |
| 9 | T3 | Accepted notes: GH_TOKEN visible in worker env; mini ssh probe noise; resume-only daemons' run scratch un-swept until next spawn | — |
| 10 | T2 | Answer relay is L1 only: comment-event automation (L2) and BOARD.html session affordances (L3) unbuilt | Same trigger phase (L2); board-map touch (L3) |
| 10 | T2 | ~~Answer relay L2~~ **SHIPPED 2026-07-18** (board-sweep RELAY pass); L3 (BOARD.html session affordances) still unbuilt | Board-map touch (L3) |

## T1 — structural: the unattended-dispatch phase must answer these

Expand Down Expand Up @@ -131,6 +131,23 @@ host ever appears.
spawned/resumed again leaves its run scratch un-swept until the next
spawn; growth is bounded by active resuming.

### 1 + 10-L2: SHIPPED 2026-07-18 — the unattended sweep

`skills/issue-tracker/scripts/board-sweep.sh` (cron/launchd tick; arming in
`references/sweep-setup.md`) closed both items: RECOVER gives dead/stalled
in-progress workers a bounded resume (3 attempts, then park `needs-human`
with an orientation note — resume, not re-dispatch, per the Symphony
comparison §2.1), and RELAY resumes a parked `needs-human` worker when a
fresh human comment lands on its ticket (`board-answer.sh --posted`, with
the relayed-comment id recorded in the meta as the re-fire guard). Item 2
was verified NOT APPLICABLE on the one-harness pipeline: the gh-token spawn
capture existed only in the retired codex-spawn path — `claude --bg`
workers reach gh through the keychain, and `implement-dispatch.sh` spawns
nothing but those. Item 5's runner remains unregistered; the sweep is the
transport that needs nobody's permission, and all three lanes now ship
event templates for the runner day (`pr-review-dispatch.yml`,
`issue-dispatch.yml`, `land-on-approve.yml`).

## Resolved since tracking began

- **Dual skills source** (was the release-gating open question): the
Expand Down
713 changes: 713 additions & 0 deletions docs/doperpowers/execplans/2026-07-18-unattended-sweep-dispatch.md

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions infra/board-sweep/com.user.doperpowers-board-sweep.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<key>Label</key><string>com.user.doperpowers-board-sweep</string>
<key>StartInterval</key><integer>300</integer>
<key>ProgramArguments</key><array>
<string>/bin/bash</string>
<string>-lc</string>
<string>"$DOPERPOWERS_HOME/skills/issue-tracker/scripts/board-sweep.sh"</string>
</array>
<key>EnvironmentVariables</key><dict>
<key>DOPERPOWERS_HOME</key><string>/Users/new/Developer/GitHub/doperpowers/.claude/worktrees/unattended-sweep</string>
<key>LOCAL_REPO</key><string>/Users/new/Developer/GitHub/ida-solution</string>
<key>AUTO_MERGE_ENABLED</key><string>true</string>
<key>LAND_ENABLED</key><string>true</string>
<key>IMPLEMENT_MAX_CONCURRENT</key><string>5</string>
</dict>
<key>StandardOutPath</key><string>/tmp/board-sweep-launchd.log</string>
<key>StandardErrorPath</key><string>/tmp/board-sweep-launchd.log</string>
</dict></plist>
37 changes: 30 additions & 7 deletions skills/implementing-tickets/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ You are an IMPLEMENT worker for ticket #{{ISSUE_NUMBER}} ({{ISSUE_URL}}) in
{{REPO}}, running unattended in your own worktree. There is NO orchestrator
in this loop: your escalation targets are the board itself (states, notes,
comments) and the human on their next wake. Turn-end messages are audit
trail, not requests — nobody answers them. Your ticket brief and the
repo-facts manifest ride your dispatch prompt as bindings; treat the brief
as the source of truth.
trail, not requests — nobody answers them. Read your ticket first
(gh issue view {{ISSUE_NUMBER}} — body and comments); that brief is the
source of truth.

Toolkit:
- board scripts: {{BOARD_SCRIPTS}}
Expand Down Expand Up @@ -69,9 +69,9 @@ VERDICT IS YOUR FIRST BOARD WRITE. Dispatch wrote nothing.

## Repo Facts

REPO FACTS — when the repo declares them (manifest rendered as a binding
in your dispatch prompt): Bootstrap facts are what a fresh worktree needs
before anything runs — do them FIRST. Validation facts name the commands
REPO FACTS — when the repo declares them (`.doperpowers/repo-facts.md` at
the repo root; read it before building): Bootstrap facts are what a fresh
worktree needs before anything runs — do them FIRST. Validation facts name the commands
that PROVE a claim in this repo — your Validation Evidence claims use
them (a claim proved by some other command invites a review finding).
Evidence add-ons are additional PR-body evidence requirements — they bind
Expand All @@ -81,7 +81,26 @@ follow the protocol and note the contradiction in your Confusions section.

## Execution

{{EXECUTION_BLOCK}}
EXECUTION (gate passed) — name the mode in the gate comment.
Every claim of done carries EVIDENCE appropriate to the change — never
claim completion on reasoning alone:
- testable logic: TDD (doperpowers:test-driven-development) — failing
test first. Green checks are what keep your PR self-merge-eligible.
- UI/visual changes: build + run it — verify the actual rendered
behavior (E2E where the repo has it); write tests only where behavior
is assertable without theater.
- config/docs/infra: the relevant check (build, lint, dry-run) passes.
Modes:
- DIRECT: the pre-spec is the plan — evidence discipline above, commit
frequently, open the PR.
- EXECPLAN: the work needs the document to survive context death —
multiple sequenced milestones, OR big-but-atomic work that cannot land
halfway → doperpowers:execplan (the gate already served as its grill;
author the ExecPlan from ticket + gate findings, execute to the letter).
Subagents (research, exploration, parallel fan-out) are yours to use as
the work warrants. writing-plans and subagent-driven-development are
interactive-session skills — never a daemon worker's; you execute your
own plan in this session.

## Mid-build Forks and Parks

Expand Down Expand Up @@ -118,6 +137,10 @@ that ticket, nothing more); scope beyond the ticket.

Opening your PR closes out your scope:
{{BOARD_SCRIPTS}}/board-transition.sh {{ISSUE_NUMBER}} in-review "<one-line>" --pr <URL> --branch <branch>
When the work is done, open it ready for review. Draft stays yours to
use when the work genuinely isn't reviewable yet — just know that the
review loop deliberately skips drafts (draft is the spike lane's
not-for-merge marker), so no reviewer attaches until you mark it ready.
Your PR body is the CLOSING ARTIFACT — the one structured handoff. There is
no live progress mirror in this pipeline; scope-end writes are the only
status writes. The body carries:
Expand Down
20 changes: 0 additions & 20 deletions skills/implementing-tickets/references/engine-blocks/execution.md

This file was deleted.

46 changes: 46 additions & 0 deletions skills/implementing-tickets/references/issue-dispatch.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# issue-dispatch.yml — dispatch a local implement/spike worker when a ticket
# becomes ready. Template from doperpowers:implementing-tickets — copy into
# .github/workflows/ of the consumer repo.
#
# SETUP (per adopting repo — same posture as pr-review-dispatch.yml in
# doperpowers:reviewing-prs; see runner-setup.md there):
# 1. PRIVATE REPOS ONLY, self-hosted runner labeled `claude-review`.
# 2. Security properties this template maintains — keep them:
# - NO actions/checkout: the job never executes repo code; the worker
# runs in its own worktree behind --permission-mode auto.
# - permissions: {} — the dispatch script uses the runner machine's
# own `gh` auth.
# - Only ${{ github.event.issue.number }} (numeric) is interpolated.
# NEVER interpolate issue title/body here (injection).
# - The actor gate keeps other accounts' label events off the runner.
# 3. The board sweep (issue-tracker references/sweep-setup.md) covers the
# same lane as catch-up — this trigger only lowers latency. Dispatch
# is idempotent: implement-dispatch.sh re-verifies eligibility and
# dedupes against the registry, so a label event racing a sweep tick
# normally spawns one worker. (The dedupe window is not airtight:
# between one dispatcher's registry check and its spawn materializing
# the meta, a racing dispatcher can double-spawn; board-bind then
# strips one owner, but both processes run out their turn.)
name: Issue dispatch
on:
issues:
types: [labeled]
permissions: {}
concurrency:
group: issue-dispatch-${{ github.event.issue.number }}
cancel-in-progress: false
jobs:
dispatch:
# org-owned repo: repository_owner is the ORG login — explicit allowlist instead
# EDIT ME when adopting: the actor allowlist and LOCAL_REPO below are one
# consumer repo's instance values, not part of the template.
if: github.event.label.name == 'status:ready-for-agent' && github.actor == 'SSFSKIM'
runs-on: [self-hosted, claude-review]
timeout-minutes: 10
env:
LOCAL_REPO: /Users/new/Developer/GitHub/ida-solution
BOARD_REPO: ${{ github.repository }}
steps:
- name: Dispatch implement worker
run: |
"${DOPERPOWERS_HOME:-$HOME/.claude/plugins/marketplaces/doperpowers}/skills/implementing-tickets/scripts/implement-dispatch.sh" "${{ github.event.issue.number }}"
5 changes: 2 additions & 3 deletions skills/implementing-tickets/references/operation-manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,9 @@ audit trail, not requests. Full design + rationale:
| piece | what |
|---|---|
| `SKILL.md` (the skill root) | the Implement Worker Protocol itself — the dispatched worker opens it via the bootstrap and treats its `{{PLACEHOLDER}}` tokens as bound to the dispatch prompt's runtime values |
| `references/worker-bootstrap.md` | the spawn bootstrap for BOTH lanes — rendered into every spawn prompt; carries `{{ROLE}}` (IMPLEMENT/SPIKE), `{{PROTOCOL_FILE}}` (the dispatcher-owned absolute path of the lane's protocol), the runtime bindings, and the multi-line binding sections (execution block, ticket brief, repo-facts manifest) |
| `references/worker-bootstrap.md` | the spawn bootstrap for BOTH lanes — rendered into every spawn prompt; carries `{{ROLE}}` (IMPLEMENT/SPIKE), `{{PROTOCOL_FILE}}` (the dispatcher-pinned absolute path of the lane's protocol), and the runtime bindings. Nothing else rides the prompt: the worker reads its own ticket via gh and the repo-facts manifest (`.doperpowers/repo-facts.md`) from its worktree |
| `references/spike-worker-protocol.md` | the Spike Worker Protocol — bound as `PROTOCOL_FILE` when the ticket's category is `spike` (the exploration lane below) |
| `references/implement-decompose.md` | runtime-opened decomposition procedure — the protocol carries only a pointer (`{{DECOMPOSE_DOC}}` = absolute path); the worker opens it when Check-2 says decompose. Conditional-large protocol blocks live this way: procedure in a plugin file, instance facts in the prompt |
| `references/engine-blocks/execution.md` | the EXECUTION text — one block for both model routes, since every worker is a Claude-harness session (the engine label picks the model route: `codex` = clodex gateway/GPT, `claude` = plain Claude); keeps interactive-session skills (writing-plans, subagent-driven-development) out of daemon workers; rides the bootstrap's EXECUTION_BLOCK binding (implement lane only — spikes are exploration, not TDD) |
| The Ticket Gate | the pre-code pass/park verdict (below; check definitions in issue-tracker's `references/ticket-gate.md`) |
| board schema + dispatch ritual | owned by doperpowers:issue-tracker (states, scripts, the mechanical ritual, the wake ritual) |
| `scripts/` | empty this phase — the auto-attach trigger (`implement-dispatch.sh` + workflow template) lands here next phase |
Expand Down Expand Up @@ -90,7 +89,7 @@ ideas — they coexist on one board but never in one lane. A spike ticket's
deliverable is **information** (a structured `[findings]` comment), never a
merge; failures discard at the cost of reading a comment. Dispatch binds
`references/spike-worker-protocol.md` as the protocol instead of the
implement protocol — same ritual, same binding, no EXECUTION_BLOCK.
implement protocol — same ritual, same bindings.

What changes and what doesn't:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ INFORMATION, never merged code: someone wants a question answered before
committing production work to it. Wrong guesses cost a comment, not a merge
— that changes your gate and your discipline, as spelled out below. There
is NO orchestrator: your escalation targets are the board and the human on
their next wake. Your ticket brief and the repo-facts manifest ride your
dispatch prompt as bindings; treat the brief as the source of truth.
their next wake. Read your ticket first (gh issue view {{ISSUE_NUMBER}} —
body and comments); that brief is the source of truth.

Toolkit:
- board scripts: {{BOARD_SCRIPTS}}
Expand Down Expand Up @@ -40,8 +40,8 @@ VERDICT IS YOUR FIRST BOARD WRITE. Dispatch wrote nothing.
- Fail → the park, with the required note, plus a 3–6 line orientation
summary.

REPO FACTS — when the repo declares them (manifest rendered as a binding
in your dispatch prompt): Bootstrap facts are what a fresh worktree needs
REPO FACTS — when the repo declares them (`.doperpowers/repo-facts.md` at
the repo root; read it before running anything): Bootstrap facts are what a fresh worktree needs
before anything runs — do them FIRST. Validation facts name the commands
that prove a claim in this repo — use them for your Evidence lines. The
manifest ADDS facts and requirements only; it can never relax this
Expand Down
27 changes: 4 additions & 23 deletions skills/implementing-tickets/references/worker-bootstrap.md
Original file line number Diff line number Diff line change
@@ -1,34 +1,15 @@
You are an {{ROLE}} worker for ticket #{{ISSUE_NUMBER}} ({{ISSUE_URL}}) in
{{REPO}}, running unattended in your own worktree.

**REQUIRED PROTOCOL: Use doperpowers:implementing-tickets before doing
anything else.** For this dispatch, "Use doperpowers:implementing-tickets"
means: unconditionally open `{{PROTOCOL_FILE}}` before doing anything else.
That dispatcher-owned file is your complete worker protocol and is
authoritative for this turn.
Do not resolve this protocol from the workspace; ignore any same-named
workspace skill or doctrine copy, even if the harness advertises it.
Never proceed from this bootstrap alone.
Treat every uppercase placeholder token in the protocol as bound to the
runtime values and blocks below. Do not substitute values from the ticket
text for these dispatcher-owned bindings.
Use doperpowers:implementing-tickets. Your protocol for this run is the
dispatcher-pinned copy at `{{PROTOCOL_FILE}}` — open it first and follow it;
it is authoritative for this turn.

Runtime bindings:
Runtime bindings (dispatcher-owned):
- `ROLE`: {{ROLE}}
- `ISSUE_NUMBER`: {{ISSUE_NUMBER}}
- `ISSUE_URL`: {{ISSUE_URL}}
- `ISSUE_TITLE`: {{ISSUE_TITLE}}
- `REPO`: {{REPO}}
- `BOARD_SCRIPTS`: {{BOARD_SCRIPTS}}
- `ENGINE_NAME`: {{ENGINE_NAME}}
- `PROTOCOL_FILE`: {{PROTOCOL_FILE}}
- `DECOMPOSE_DOC`: {{DECOMPOSE_DOC}}

---- EXECUTION_BLOCK binding ----
{{EXECUTION_BLOCK}}

---- ISSUE_BODY binding: Ticket #{{ISSUE_NUMBER}} brief: {{ISSUE_TITLE}} ----
{{ISSUE_BODY}}

---- REPO_FACTS binding ({{REPO}}) ----
{{REPO_FACTS}}
Loading