feat: nudge resolved offsite claims #71
No reviewers
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#71
Loading…
Reference in a new issue
No description provided.
Delete branch "build/69-offsite-stale-flag-nudge"
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?
Closes #69
Summary
Add a comment-only, idempotent nudge when every visible cross-referenced PR for an
offsiteclaim has closed. Unreadable or incomplete evidence stays quiet, and the reconciler never changes theoffsiteflag or claim.Worklog
Round 1: evaluate all panel feedback; no blocking fixes required
Add the pure
offsite_resolved_decisionand cross-reference parserAdd guarded timeline/state API reads and comment-only wiring
Document the nudge and add the Unreleased changelog entry
Cover idempotency, unreadable timelines, and non-
offsitefetch guarding in integration fixturesRun
test/run.sh, shellcheck, and actionlintAcceptance criteria
offsiteissue whose only cross-referenced PR is merged gets exactly one comment, naming that PR — and gets no second comment on the next sweep.offsiteissue with one merged and one open cross-referenced PR gets no comment.offsiteissue with no visible cross-referenced PRs gets no comment and logs nothing alarming.offsite, and none reclaims anoffsiteissue.offsiteclaimedissue triggers no extra timeline read.test/run.shgreen; shellcheck- and actionlint-clean.Verification
bash test/run.sh— 15 test files passedbash .github/scripts/shellcheck-all.shbash .github/scripts/actionlint-all.shbash .github/scripts/self-ref-check.sh🔎 reviewing head
5216369e7e🔎 reviewing head
5216369e7eVerdict: approve
Reviewed head
5216369e7ed71eb4ce8e24f16b20161ebe4d4ea3against #69.Acceptance criteria
offsite+ only closed cross-ref PR → exactly one comment naming that PR; no second on next sweepensure_comment+offsite-resolvedmarker; integration probe 29CLOSED\nOPEN→QUIET; probe 30QUIET; probe 31offsite_timelinefails closed; probe 32offsite; no reclaim ofoffsiteoffsiteclaimed → no extra timeline readtest/run.shgreen; shellcheck-cleanSpec (D1–D6)
offsite_resolved_decisionrequires non-empty states and noOPEN/UNKNOWNline (same shape asepic_decision). Empty and unreadable stay quiet.ensure_comment/<!-- issueflow:offsite-resolved -->.offsite_pr_states/offsite_timelinebelow.Verify
Ran in a detached worktree at this head:
bash test/run.sh— 15 files passed (issueflow-reconcile: 93 checks)bash .github/scripts/shellcheck-all.sh— cleantest,self-guards, release/docs exercises greenNon-blocking
a b is closed). Cosmetic only.Panel approval of this head.
🔎 reviewing head
5216369e7eApprove at head
5216369e7ed71eb4ce8e24f16b20161ebe4d4ea3.What I verified
bash test/run.sh— 15 files, all green (93/93 inissueflow-reconcile.test.sh).shellcheck-all.sh,actionlint-all.sh,self-ref-check.sh— all clean.one open offsite PR keeps quiet, the unreadable unit row, and the integrationone open cross-referenced PR suppresses the nudge); treatingUNKNOWNas resolved failsan unreadable offsite PR keeps quiet. The floor holds at both the unit and integration level.Against the spec (#69)
ensure_comment; no label edit, no reclaim, no unassign. The pre-existingno reconciler mutation names offsitegrep-check still stands, and the nudge sits after the reclaimcase, guarded byhas_issue_label offsite, so an offsite claim can never reachRECLAIManyway (claim_clock_exemptexempts it).offsite_resolved_decisionrequires non-empty states with noOPEN/UNKNOWNline: all-closed, not any-closed. Verified by mutation..error-fixture'd unreadable timeline all resolve to silence; the unreadable case additionally asserts no alarming log and integration test 32 shows the sweep's other work proceeds.<!-- issueflow:offsite-resolved -->marker via the existingensure_comment; idempotency asserted across two sweeps (comment count stays 1).offsite_cross_referenced_prsandoffsite_resolved_decisionsit above the API-edge divider, pure and fixture-driven;offsite_timeline/offsite_pr_statesbelow it, mirroringreference_states..ceremony/re-sync — correct, this repo is the source.Non-blocking nits (builder's discretion)
OPENalone → QUIET) has no exact unit row. Present rows cover 1, 2, 3, 5, 6; row 4 is only reached in practice via row 3'sCLOSED OPEN. Practically subsumed — any plausible regression that nudges on a loneOPENtrips row 3 or 5 — but the table lists it, so a one-linecheckwould close the gap.a bis closed" (singular verb). The single-PR case — the one day-one path — reads fine, and the marker makes it a one-time comment, so this is cosmetic.@null. That sweep also firesFLAG_UNASSIGNED, so the state is already flagged for repair; guarding the mention (or reusing the assignee list) would just tidy the corner.None of these gate approval: the acceptance criteria are all met at this head, and the invariant that matters — verification nudges but never grants, withholds, or clears — is both implemented and pinned by tests.
Approve at head
5216369e7e.Reviewed against #69's spec (D1–D6), acceptance criteria, and test plan, in a detached worktree.
Spec conformance
ensure_commentcall; no label, assignee, or claim mutation anywhere in the new path. The pre-existing#68guard test (no reconciler mutation names offsite) still passes.offsite_resolved_decisionnudges only when the state list is non-empty and contains noOPEN/UNKNOWN— all-closed, never any-closed. Verified by mutation: rewriting the guard togrep -qxF CLOSEDturns exactly the expected rows red (one open offsite PR keeps quiet,an unreadable offsite PR keeps quiet, plus the integration row), then the tree was restored clean.offsite_timelinereturns 1, guarded byif) and unreadable PR state (UNKNOWN) both resolve to silence; the errored-timeline integration test shows the sweep continuing with no alarming log.offsite-resolvedmarker ridesensure_comment; the two-sweep integration fixture confirms exactly one comment.offsite_pr_states/offsite_timelinesit below it.Acceptance criteria — each has a test row: merged-only nudges once and names the PR; merged+open stays quiet; zero visible refs stays quiet; errored timeline stays quiet and disturbs nothing else; no
offsitemutation or reclaim path exists; non-offsiteclaims make exactly one timeline read whileoffsiteclaims make two (asserted on the stub's recorded API calls, i.e. on the fetch guard itself, not just the outcome). Changelog line sits under## Unreleasedabove the next heading; the LABELS.md sentence matches the "only ever tells" doctrine.Verification I ran (worktree at the head; the sandbox lacked jq/shellcheck/actionlint, so I fetched static binaries — the node-dependent
version.test.shpackage-json rows fail identically onmain, pre-existing and unrelated):test/issueflow-reconcile.test.sh— 93 checks, all green.test/run.sh— same failure set asmain(md5-identical), all environmental.shellcheck-all.sh,actionlint-all.sh,self-ref-check.sh— clean.Non-blocking nit: when an
offsiteclaim has no assignee, the nudge's address renders as@null(jq -r '.assignees[0].login'). Harmless — the unassigned-claim flag comment already names the real problem — but a.assignees[0].login // "claimant"fallback would read better. Builder's discretion.🔧 addressing round on head
5216369e7eAll three panel reviewers approved this head. There are no blocking points. Round plan of record:
OPEN-only unit row — agree (non-blocking). The existing mixedCLOSED/OPENunit row and integration case already pin the all-closed rule, so I will not invalidate three current-head approvals for redundant coverage. I will record this as evaluated and deferred.a b is closed) — agree (non-blocking). This is cosmetic and outside the acceptance criteria. I will leave the approved behavior unchanged and record it as deferred rather than start a new round for prose only.@null— agree (non-blocking). The same sweep already flags an unassigned claim for repair, and the issue contract does not require this corner-case wording. I will leave the approved head unchanged and record it as deferred; any product change belongs through triage.Concrete disposition: no code changes are required. I will add and check a round-completion Worklog item, post the round summary, request the human reviewer, and set
state:needs-humanonce the body update is complete.Round summary
5216369e7ed71eb4ce8e24f16b20161ebe4d4ea3received current-head approvals fromclaude-bot-andresmgsl,grok-bot-andresmgsl, andkimi-bot-andresmgsl.test/run.sh, shellcheck, actionlint, self-reference guards, action/docs/release exercises, and label reconciliation are green.blocker:*labels, and is ready for human review.