You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: skills/work/work-implement-queue/SKILL.md
+6-1Lines changed: 6 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -43,13 +43,15 @@ Before building the queue, reclaim issues an earlier implement-run crashed on: a
43
43
44
44
### 4. Announce the batch — then drain
45
45
46
-
**`ai: ready` is already the human's approval** to work an issue — the label means "scoped + approved for an AI agent to pick up". So the drain does **not** gate on a fresh confirmation: **announce** the ordered queue plus the cap, branch strategy and parallel mode — with the **concurrency** it will run at when that mode is `parallel` (call out any **dependency-forced order**, any **mutex-forced wave split**, plus issues **deferred** or **skipped**), then drain. Under `/loop` it runs unattended.
46
+
**`ai: ready` is already the human's approval** to work an issue — the label means "scoped + approved for an AI agent to pick up". So the drain does **not** gate on a fresh confirmation: **announce** the ordered queue plus the cap, branch strategy and parallel mode — with the **concurrency** it will run at when that mode is `parallel` (call out any **dependency-forced order**, any **mutex-forced wave split**, plus issues **deferred** or **skipped**), then drain. Say so too when `work.queueBranch` is on, naming the `ai/queue-<hash>` the PRs will target and whether it is being reused or cut. Under `/loop` it runs unattended.
47
47
48
48
-**Plan-only triggers** ("just show me", "dry run", "nur den Plan", "don't run") still stop after the plan.
49
49
- If the ready-gate is **widened** (`labels.ready: false`, so issues were never explicitly opted-in), confirm before working those — there is no per-issue approval to lean on.
50
50
51
51
### 5. Drain
52
52
53
+
**First, if `work.queueBranch` is on** (`worktree` only — inert under `branch:<name>`, which opens no per-issue PR to group): **reuse or cut the queue branch, and open its PR, before the first worker starts.** An `ai/queue-*` PR already **open** against `pr.base` → reuse that branch; none open → cut `ai/queue-<hash>` from `pr.base` and open the `ai/queue-<hash>` → `pr.base` PR. The hash only keeps concurrent drains apart and encodes nothing later read back. Hand the branch to every worker as its base — that is the whole of what the mode changes for them. Doing this **here** rather than at step 1 means an empty queue cuts nothing. **Cannot cut the branch or open that PR → stop and report**, having drained nothing: falling back to `pr.base`, or draining onto a branch carrying no PR, is how work is stranded where nobody looks for it. The drain **never merges or fast-forwards** that PR — the target repo's own workflow lands it. Rules: **Queue branch** in `work-implement`'s REFERENCE.
54
+
53
55
For each issue, up to `work.cap`, spawn a **fresh worker** that runs `work-implement` on exactly that issue:
54
56
55
57
-**sequential** (`parallel: false`) — one worker at a time; **re-fetch** the next eligible issue each iteration.
@@ -72,6 +74,8 @@ Release the lock. Summarise each issue and its outcome (handed to `reviewRequest
72
74
73
75
Issues now in `reviewRequested` are the drain's hand-off — the `work-review-queue` picks them up. Name the count.
74
76
77
+
**Under `work.queueBranch`, the queue PR is a hand-off artifact too** — report its url beside them, and say plainly that this drain does **not** land it: landing belongs to the target repo's fast-forward workflow, and that workflow can only act while the queue branch still contains `pr.base`'s tip. Where anything else has landed on `pr.base` since, say so too — the fast-forward is off the table until that repo puts the branch back on top, which is its call and not this drain's. A queue PR nobody knows is waiting is the mode's failure mode; naming it every run is what keeps it from becoming one.
78
+
75
79
**Then name the queue's state**, so a repeating driver (`/loop`, cron, a human) knows whether to run again, wait, or stop — instead of that rule living in whoever typed the loop prompt. **Query the tracker again first**: work that became eligible while the last issue was being implemented is already there, and waiting on input that exists wastes an interval. Then decide **in this order**:
76
80
77
81
1.**Stopped on `work.cap` with eligible issues left → `work remaining`.** Run again **immediately**, never wait: a cap-ended drain is not an empty queue.
@@ -136,6 +140,7 @@ of a file.
136
140
-**Never run a declared mutex pair concurrently** — split them across waves of the same run, under either branch strategy. A mutex **delays**; it never defers, labels, or blocks, and the skill never writes a `mutex:` label.
137
141
-**Never work an issue whose labels contradict each other** — `needsTriage` beside a lifecycle label is withheld and reported, never resolved by obeying the more permissive of the two, and never written to the tracker.
138
142
-**This loop never reviews.** It produces `reviewRequested`/`blocked` only; `done`/`changes-requested`/`needs human` are the review loop's and the human's.
143
+
-**The queue branch is opt-in, and the drain never lands it** — off unless `work.queueBranch` says otherwise, because only the repo knows whether a workflow exists to fast-forward it; the drain opens that PR and stops there, holding no credential that could write to a protected branch. Cutting the branch or opening the PR fails → stop, never fall back to `pr.base`.
Copy file name to clipboardExpand all lines: skills/work/work-implement/DESIGN.md
+3-1Lines changed: 3 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -54,9 +54,11 @@ The lifecycle is **symmetric**: each loop has a **waiting** state and an **in-fl
54
54
-**One tracker per repo**, never both. If labels mirror GitHub↔Linear, that is the integration's job; the agent writes only the configured side.
55
55
- **`REFERENCE.md` is one file, shaped by ownership rather than size.** Splitting it into per-task rule files behind an index was measured and turned down: most of what looked conditional was the **drain's** mechanics misfiled in the worker's reference, and the genuinely conditional remainder did not pay for a directory, an index and a second read per pass. Where a mechanic is _hosted_ is decided by the reader that cannot delegate — the queue declares and checks its worker, so it may assume the worker's `REFERENCE`; the worker runs standalone and declares no sibling, so the selection query, dependency ordering and the lease rules stay with it and the drain names them there. Rejected: **a `rules/` directory** (machinery against a remainder too small to pay for it); **deleting rationale to shrink the file** (the reason it is there is that an agent which knows why a rule exists does not route around it); **a `REFERENCE.md` per queue skill** (it moves prose into a skill whose siblings cannot read it). Recorded as [ADR-0025](https://github.com/TitusKirch/skills/blob/main/docs/99.adr/0025-keep-the-work-loops-reference-whole.md).
56
56
57
+
- **The queue branch groups PRs without the drain gaining the power to land them.** Under `worktree` a drain at cap opens up to ten PRs into `pr.base`, each triggering the repo's full CI for work that all lands on one branch anyway — cost scaling with queue length rather than with the result. `work.queueBranch` retargets them at one `ai/queue-<hash>`, closed by a single PR into `pr.base`. **What it deliberately does not do is merge that PR.** Landing it is the **target repo's** job, via a workflow that mints its own token in CI and **fast-forwards** the integration branch onto the queue PR's head — so the credential able to write to a protected branch never sits on the machine running the loop, and the mode needs strictly less privilege than `branch:dev`, which pushes to that branch directly. The fast-forward, rather than a merge commit, is what keeps **one CHANGELOG entry per issue**: it carries every commit across individually and unchanged, so release-please still sees them, and it leaves `allowed_merge_methods` on the base branch untouched. It is also **conditional**, and the skill states the condition rather than promising the landing: a fast-forward exists only while the queue branch still contains the integration branch's tip, so anything else landing there beside it — release-please, a human's squash, another clone's `branch:dev` run — closes the window, and reuse-before-cutting widens it by making the queue PR long-lived. Recovering from that is the **target repo's** job, because putting the branch back on top is the same protected-branch write the split just refused the drain; giving the loop that power back to rescue its own artifact would buy the recovery at the price of the whole reason the mode needs less privilege than `branch:dev`. Rejected: **squashing the queue PR** (discards the per-issue entries, the reason the merge method was ever in question); **a merge commit** (needs `merge` allowed on the integration branch, and rulesets match the **base** ref, not the head — so the permission cannot be scoped to `ai/queue-*` and would apply to every PR into `dev`); **GitHub Merge Queue**, the native fit, rejected on **availability** rather than on design — it needs Enterprise Cloud or an org-owned public repo, and neither this repo nor the private estate qualifies. **Opt-in, defaulting off**, because nothing in a drain can verify the landing workflow exists and its absence fails silently and completely: every issue PR green, reviewed, and stranded on a branch nothing merges. The saving is likewise the repo's to make and is **not promised** — it exists only where workflow triggers are scoped to the integration branches, and on a public repo, where minutes are free, it is tidiness rather than money.
58
+
57
59
## Deferred (v2)
58
60
59
-
-**Stacked branches for dependent issues (`worktree` mode)** — branch-off-parent, PR base retargeting and a revision cascade fight the stateless model. What `worktree` does instead is **decline to select** a dependent whose prerequisite has not landed (above), which needs none of that machinery: it waits for the prerequisite rather than building on it. Ordering, the other half, is implemented for `branch:<name>`, where accumulation makes it pure ordering.
61
+
-**Stacked branches for dependent issues (`worktree` mode)** — branch-off-parent, PR base retargeting and a revision cascade fight the stateless model. What `worktree` does instead is **decline to select** a dependent whose prerequisite has not landed (above), which needs none of that machinery: it waits for the prerequisite rather than building on it. Ordering, the other half, is implemented for `branch:<name>`, where accumulation makes it pure ordering. The **queue branch** above is not this and does not revive it: every issue still branches off one common base and no PR is retargeted at another issue's branch — grouping the PRs says nothing about the dependencies between them, so the prerequisite gate applies there unchanged.
60
62
-**`branch` extensions** — `dev`/named-branch grammar is designed in (`branch:<name>`); richer targets later.
61
63
-**Structured PR-feedback ingestion** (thread resolution, author trust, auto-detect) — only if conversational feedback proves insufficient.
0 commit comments