feat(notify): done-signal trigger — notify when an AFK run opens its PR/change request #100
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/coding-lab#100
Loading…
Add table
Add a link
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?
What to build
Push a notification when the reaper detects an AFK run's done-signal (a PR or change request whose head branch equals the run's branch) — "the agent finished; review and merge is on you now."
AFKRunEndedseam), covering both tracker bindings through the one Tracker contract: on aforge-bound repo the done-signal is a PR, on abuiltin-bound repo a change request.<repo>~<label> opened PR #<n>(or the change-request equivalent), body = the PR/CR title,tag = <run-id>, tap deep-links to/runs/:idinside the PWA — lab's chat is where review/merge context lives; never bounce out to the forge URL.Acceptance criteria
tag = run-id, and the/runs/:idroute (hermetic: fake sender + the existing fake tracker)Blocked by
Agent Brief
Category: enhancement
Summary: Fire a push notification when the reaper detects an AFK run's done-signal — a PR (forge binding) or change request (builtin binding) whose head branch equals the run's branch. The blocker (#98) has landed; this hooks the second trigger into the shipped sender.
Current behavior:
The reaper already detects the done-signal: it lists the repo's pulls through the Tracker contract, checks head-branch presence (open or merged counts, closed does not), classifies the run, and — exactly once per run, via an idempotent claim — executes the terminal reap. That reap site already carries one injected, nil-safe seam: the metrics hook that records how an AFK run ended (
AFKRunEnded). Nothing there sends notifications. The landed push sender (Broadcast(Payload{Title, Body, Tag, Route})) is fire-and-forget and never blocks or errors at the call site.Desired behavior:
When the reaper's terminal classification of an AFK run is success (done-signal present), one notification is broadcast:
<repo>~<label> opened PR #<n>on a forge-bound repo, or the change-request equivalent on a builtin-bound repo. Repo and run fields (repo name, session name, branch, run ID) are all in scope at the reap site.PR #<n>rather than blocking or skipping the notification. Also note the PR's number currently doesn't flow down to the terminal reap step — only a presence boolean does — so the matched pull ref must be threaded down or re-derived there./runs/<id>) — lab's chat is where review/merge context lives; never a forge URL, which would bounce the user out of the PWA.Semantics:
Key interfaces:
AFKRunEndedwhen the outcome is success. A nil notifier means no notifications, no other behavior change.push.Senderis a concrete struct (no interface); for hermetic tests either define a narrow local notifier interface to record payloads, or use the landed recipe (real sender + httptest fake gateway +Flush()), alongside the existing fake tracker.Acceptance criteria:
Out of scope:
Landing audit — PR #110 →
PASSVerification signal relied on: Forgejo Actions CI
ci / native— success (4m7s, run #109). Not re-run.Checks performed:
feat(notify): done-signal trigger …is Conventional Commits ✓. AFK contract: body carriesCloses #100✓.afk/100containsmaintip — no conflict.tracker.DonePullrefactor is behaviour-preserving:rank < stateRank(PullMerged)gates out closed(1)/unrecognized(0), admits merged(2)/open(3) — byte-for-byte the oldPRPresentopen||mergedsemantics.PRPresentis now the boolean projection ofDonePullfrom one sharedbestPullloop, so "is there a done-signal" and "which pull" cannot diverge.outcome == OutcomeSuccess && notify != nil.ClassifyreturnsOutcomeSuccessiffprPresent, andprPresent ⟹ DonePull ok, sodonePullis always a real pull when the push fires — no zero-value payload risk.afkdoes not importinternal/push—notify.goimports onlystore+tracker;cmd/labmapsafk.Notification→push.Payload1:1 (both are{Title, Body, Tag, Route}).pushSender.Broadcast(async), so the reaper never blocks on gateway I/O.Verdict: PASS. Awaiting free-text merge confirmation.