offsite — the label, the doctrine, and the claim-reclaim exemption
#68
Labels
No labels
attention
blocked
blocker:ci-red
blocker:conflict
blocker:drill-pending
blocker:unrequested
bug
claimed
documentation
enhancement
epic
merge-next
needs-ruling
needs-triage
offsite
post-merge
ready
release
scope:docs
scope:guards
scope:labels
scope:release-flow
stale
state:addressing
state:bots-reviewing
state:building
state:needs-human
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/ceremony#68
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Part of #56. Blocked by #52 (both edit the same reclaim-clock call site, and #52 lands first).
Accepted from discussion #67 — "
offsite— an issue whose deliverable is a PR in another repo", filed from the sherpa session at @danmt's request, who ruled its three open questions in the thread. #57 landed the human-readable half of the cross-repo linkage rule this morning; this is the machine-readable half.All line references pinned at
cf69d8c.Context — the failure, re-verified
The discussion is right about the bug and wrong about where it lives. Checked against the tree, not taken from the report:
hygiene.shruns triage's hourly backlog sweep … and it sweepsrepos.txt"hygiene.shis the triage box's own script — operator-owned, inside the box, not in this repo (FLEET.md). The reclaim that actually runs on this board isclaim_decision()— issueflow-reconcile.sh L90-L98, wired at L224-L247blockednever reaches the reclaim branch at all;needs-rulingis exempted by #52 (ruling_stale_exempt)The rule as it stands:
claim_decisionreturnsRECLAIMwhen there is no linked open PR and no activity for 48h; theclaimedbranch then comments, unassigns the owner, and flipsclaimed→ready.OPEN_PR_ISSUESis built fromclosingIssuesReferencesover this repo's open PRs (L307-L317) — andCloses #Ndoes not cross repos, which is exactly why #57 wrotePart of <owner>/<repo>#Ninto BUILDER.md L28-L31.So an issue whose deliverable is a PR in another repo has
open_pr=falseby construction, and is shaped exactly like an abandoned claim. Two are in that shape right now:heavy-duty/rig#112(merged 11:58Z)dan-claude-bot/incubator#89(ready-for-review 11:18Z)Nothing has misfired, because both saw comment traffic today. That is luck, not a guard. And reclamation is not a cheap mistake: it unassigns the owner and flips the issue back to
ready, inviting a second builder onto work that already exists.Spec
Decisions. A child of this issue may not reopen them.
offsite. Ruled by @danmt in the thread. It names a property of the issue ("do not expect a PR here"), which is what the sweep needs to know;cross-repowould name the mechanism.ruling_stale_exempt(labels on stdin →EXEMPT|SWEEP) inlib/ruling.shand consults it at theclaim_decision_atcall site. Addclaim_clock_exempt()besideclaim_decision()inissueflow-reconcile.sh— labels on stdin →EXEMPT|SWEEP,EXEMPTwhenoffsiteis present or whenruling_stale_exemptsays so — and make it the branch's single consult.lib/ruling.shkeeps owning the ruling rule; the claim clock gets one gate instead of a growingif. If #52 merges in a different shape than its current head, follow the shape it merged in and preserve the invariant: one call site, no duplicated flag list.needs-ruling's standing (LABELS.md L84-L92). The builder sets it in the same step that comments the draft-PR link on the authorizing issue, and clears it in the handoff — when the cross-repo PR merges or closes — reporting the outcome in that same comment, never as a side effect. No reconciler code path adds or removes it.FLAG_UNASSIGNEDstill runs. An offsite claim with no assignee is still a broken claim, and #52 already established the shape (exempt runs the decision at age 0 rather than skipping it).epic_decisionfires on child issues being closed (L172-L176), and an offsite child is open. It cannot suppress a nudge that its own openness already prevents. Nothing to build; recorded so nobody builds it.stalelabel is a PR-side label — applied only inlabels-reconcile.shL521-L531, never on the issue side.offsitenever appears on a PR, solabels-reconcile.shis not touched by this issue at all.queue_decisioncountsneeds-triage,epicandQUEUE_LABELSonly (L84-L98). No change — but a test pins it, because "no change needed" is a claim that must be provable.CFD3D7, the light shade ofblocked's6A737D, in the grey "legitimately quiet" family. Same reasoningneeds-rulingused for taking the light shade ofstate:needs-human— the axis should read as one family on the board.core_label_rows(), not.github/labels.conf. It is core taxonomy shared by every governed repo;labels.confcarries onlypanel=,triage-actors=and the per-reposcope:*rows.notify.shcould watch ceremony alone if issues carried this flag. That is ~4 API calls a quarter-hour and it is not why this exists. Do not cite it in the changelog line or the doctrine.Tasks
actions/labels-reconcile/labels-reconcile.sh— add theoffsite|CFD3D7|…row tocore_label_rows()next toblocked(bootstrap is dispatch-only; this creates nothing by itself).actions/issueflow-reconcile/issueflow-reconcile.sh— addclaim_clock_exempt()besideclaim_decision(), make it theclaimedbranch's single exemption gate (D3), with the why in a comment carrying this issue number.test/issueflow-reconcile.test.sh— the fixture rows below.LABELS.md— one row in the Cross-cutting table (L63-L72) and a short paragraph afterneeds-ruling's: what it means, who sets and clears it, that it stops the reclaim clock and nothing else, and that it is issue-only.BUILDER.md— two lines, both in prose that already exists rather than a new section: in Building (L28-L35), setoffsitein the same step that comments the draft link; in Claiming (L20-L21), qualify "a claim with no PR and no activity is what the staleness sweep reclaims" so it stops being false for offsite claims, and name clearing the flag at handoff.CHANGELOG.md— one line under## Unreleased, inserted above the heading below it.offsitelabel itself is created by the labels workflow'sworkflow_dispatchbootstrap, which the triage identity cannot run (gh workflow run403s — the gap that made #56's escalation invisible for an hour). Name @danmt, and link the run once it happens.hygiene.sh: triage's own hourly sweep must skipoffsiteissues for the same reason. It is the operator's file to change; this comment is the spec, not the change.Acceptance criteria
claimed+offsiteissue with no linked open PR and no activity for well over 48h is not reclaimed: no comment, no unassign, noready.offsiteis reclaimed — the guard is shown to be the reason, not an unrelated code path.claimed+offsiteissue with no assignee still gets theclaimed-unassignedflag.claimed+offsitedoes not tripFLAG_CONFLICT;offsitealone (no queue label) still getsneeds-triage.needs-ruling's exemption still behaves exactly as #52 merged it — proven by #52's own test rows still passing unmodified.offsite(D4) — pinned the way #52 pinned "the machine never setsneeds-ruling".LABELS.mdandBUILDER.mdsay all of the above, andoffsiteappears incore_label_rows()with colourCFD3D7.test/run.shgreen; shellcheck- and actionlint-clean.Test plan
Fixture rows in
test/issueflow-reconcile.test.sh, driving the pure functions directly (the file already sources the script and callsclaim_decisionthat way):claimedRECLAIM(unchanged — the control)claimed offsiteKEEPclaimed offsiteFLAG_UNASSIGNEDclaimed offsiteKEEPclaimed needs-rulingKEEP(#52's row, re-run unmodified)claimed offsite needs-rulingKEEPclaimed offsiteKEEP— passes for the ordinary reason, so row 2 is not vacuousPlus:
claim_clock_exemptonoffsitealone →EXEMPT, onblocked/ready/empty →SWEEP;queue_decisiononclaimed offsite→KEEPand onoffsitealone →ADD_NEEDS_TRIAGE; a grep-level assertion that nogh issue editline in either reconciler mentionsoffsite.The case that must fail: delete the
offsitearm ofclaim_clock_exemptand row 2 must go red. A guard that passes with its own logic removed is testing nothing — the family's standing rule.Dependencies
Blocked by #52— its PR #64 rewrites the exact call site this issue extends (decision="$(claim_decision_at …)"→ an exemption gate) and edits the sameLABELS.mdtable. Two builders in those lines is a guaranteed conflict; #64 is already carryingblocker:conflict.Part of #56.Constraints
LABELS.mdandBUILDER.mdare vendored doctrine (the.ceremony/set). This repo is the source, so no re-sync happens here — consumers get the change at their next pin bump.hygiene.shand the duty scripts live inside the boxes and are the operator's. A PR that claims the box-side sweep is fixed is lying; the spec comment is the deliverable.Every issue named by
Blocked byis closed. The sweep is moving this issue toready.Claimed by @codex-bot-andresmgsl. Starting the build now on
build/68-offsite-claim-exemption; I will open a draft PR after the first commit.Box-side specification for the operator-owned
hygiene.sh: triage’s hourly sweep must skip any issue carryingoffsitewhen evaluating stale claimed work, because its deliverable PR lives in another repository and cannot appear as a local closing PR. The flag is trusted as-is; do not resolve or verify the remote PR. This comment specifies the operator change only—the box file is outside this repository and this PR does not claim to modify it.