Repository navigation
chore(loop): let a triaged task actually reach the implementing stage - #49
Conversation
Triage could decide an issue was `ready-for-agent` and nothing would ever happen to it. The route table had no `feature`/`bugfix` entry at all: `run()` was reachable only for `triage`, `pr-review`, `deps` and the two schedules, so `STAGE_OUTCOMES.bugfix` was unreachable on Actions and the A1 acceptance item for "bugfix-feature" was never unsampled — it was structurally unreachable. The loop could classify work and then never do any. Two mechanisms, because the obvious one does not work: - **A person applies `ready-for-agent`** → `issues.labeled` runs the stage directly, and `stageForIssue` picks `bugfix` when the issue also carries `bug`, else `feature`. This doubles as the manual claim path: labelling any issue hands it to the loop. - **The loop applies it** (the ordinary triage verdict) → no run exists to route. GitHub suppresses workflow runs for events raised by `GITHUB_TOKEN`, which is the documented recursion guard, and it is why the loop has never had an `issues` event. `workflow_dispatch` is the stated exception, so `entry.mjs` asks for the next stage explicitly (`dispatchStage`). That is the whole reason for the new `actions: write` scope — the narrowest one that makes the loop able to start its own work. Only `ready-for-agent` is a claim. The other four role labels are states the loop itself writes, and reacting to them would make it chase its own output. Also in scope, because it defines this path's vocabulary and had drifted from it: `docs/agents/ops.md` now lists the claim signal in the trigger map and states both mechanisms, and `scripts/loop/README.md` carries the same for the workflow table. Verification: the router is a pure function, so the route is tested offline rather than by firing GitHub events — 17 assertions covering the claim (both stages), the four non-claim labels, a stale `ready-for-agent` that must not hijack another label event, the untouched pre-existing routes, and `stageForIssue` against both label shapes. `gh-check` now reports the dispatch scope as a needed capability rather than a forbidden one.
ea035b7 to
f2a58e5
Compare
Coverage
|
|
Loop triage — PR #49 edits the loop's own machinery and norms layer ( The gap is real. On Verified read-only. Diff vs Points for the reviewer.
Proposed action: label |
Triage could decide an issue was
ready-for-agentand nothing would ever happen to it. This fixes the last missing edge in the loop's automatic path, and it was just reproduced end to end on issue #48.The gap, demonstrated
Issue #48 was filed; the loop triaged it correctly (run
35005501011):…and stopped. The task sits in
ready, the label is on the issue, and no stage that implements anything can ever pick it up.route.mjshad nofeature/bugfixentry at all —run()was reachable only fortriage,pr-review,depsand the two schedules.STAGE_OUTCOMES.bugfixwas structurally unreachable on Actions, which is also why the A1 acceptance item for "bugfix-feature" was never unsampled: it could not run.Two mechanisms, because the obvious one does not work
A person applies
ready-for-agent→issues.labeledruns the stage directly.stageForIssuepicksbugfixwhen the issue also carriesbug, elsefeature. This doubles as the manual claim path: labelling any issue hands it to the loop.The loop applies it (the ordinary triage verdict) → there is no run to route. GitHub suppresses workflow runs for events raised by
GITHUB_TOKEN— the documented recursion guard — which is why the loop has never had anissuesevent in its history.workflow_dispatchis the stated exception, soentry.mjsasks for the next stage explicitly (dispatchStage). That is the entire reason for the newactions: writescope: the narrowest one that lets the loop start its own work.Only
ready-for-agentis a claim. The other four role labels are states the loop itself writes, and reacting to them would make it chase its own output — a staleready-for-agentsitting on an issue must not hijack a laterneeds-infoevent.Scope change
permissionsgainsactions: write. What it buys and what it does not:loop.yml(how the hand-off works)main(theMain branchruleset refuses every credential).github/workflows/**(noworkflowspermission exists forpermissions:to grant)administration)A failed dispatch warns and leaves the task claimable rather than failing the run — a stalled claim should be visible, but it is not a failure of the triage that produced it.
Verification
The router is a pure function, so it is tested offline rather than by firing GitHub events: 17 assertions covering the claim (both stages), the four non-claim labels, a stale
ready-for-agentthat must not hijack another label event, the untouched pre-existing routes, andstageForIssueagainst both label shapes (API objects and plain strings).gh-checknow reports the dispatch scope as a needed capability instead of a forbidden one.docs/agents/ops.mdgains the claim signal in its trigger map plus both mechanisms;scripts/loop/README.mdcarries the same for the workflow table and the piece-by-piece table.After this merges
Issue #48 is already sitting in
readywithbug+ready-for-agent, so it becomes the first real exercise of the path this PR adds: dispatch →bugfix→ branch → PR.