feat(afk): scheduler skips issues blocked by another open issue #138

Merged
dominik.polakovics merged 2 commits from afk/136 into main 2026-07-11 14:16:07 +02:00

Promotes the ## Blocked by issue-body section to a machine-read contract (ADR-0042): the AFK engine no longer schedules a ready-for-agent issue while any issue that section references is still open.

What changed

  • Pure functions (internal/afk/blocked.go): ParseBlockedBy — section-scoped local #N extraction with the tolerant-but-bounded grammar of closesDirectiveRe (case-insensitive heading at any ATX level, section runs to the next heading, #70#7, refs elsewhere in the body never block); ForeignBlockedBy — cross-repo/URL refs surfaced for logging only; PartitionBlocked — splits the ready queue against the open set (any open ref blocks; closed/dangling refs never do).
  • One choke point: the gate is applied inside FilterClaimable, and launch() / ScheduleOnce now route through it instead of duplicating the claim filter inline — the locked claim path, scheduler pre-tick, ClaimableIssuesFor, and GET /repos/{id}/ready share one definition of "claimable". Manual starts skip blocked issues identically.
  • Lazy open-set fetch: Tracker.Issues(StateOpen) fires only when a claimable issue actually carries a local blocker ref — zero added forge calls for repos not using the convention (pinned by test).
  • Failure profile: fetch failure fails closed (repo tick skipped; *TrackerError → 502 on manual start); data ambiguity fails open (dangling ref unblocks). All-blocked ≡ empty queue: no launch, no consecutive-failure strike. Skipped issues emit one structured line, e.g. afk: skipped blocked issues … #87 (blocked by #74).
  • Docs: ADR-0042; to-issues skill (blockers must be #N — prose never blocks); triage skill + triage-labels (ready-for-agent + live blockers is a valid pairing); CONTEXT.md claimable definition.

Verification

  • Table tests for the grammar and partition (blocked_test.go, 32 subtests).
  • Engine-level tests: lower-numbered blocked issue skipped and claimed on a later sweep once its blocker closes (via a real success reap); all-blocked behaves as empty; no Issues(StateOpen) call without refs; open-set fetch failure fails closed on both auto and manual paths.
  • go build ./..., go vet ./..., full go test ./... green (only pre-existing sandbox-only tmuxx integration failures, present on clean main), golangci-lint 2.12.2 run ./... → 0 issues, gofmt clean.

Closes #136

🤖 Generated with Claude Code

https://claude.ai/code/session_01D9H315aPekfjPpRiBjcsX8

Promotes the `## Blocked by` issue-body section to a machine-read contract (ADR-0042): the AFK engine no longer schedules a ready-for-agent issue while any issue that section references is still open. ## What changed - **Pure functions** (`internal/afk/blocked.go`): `ParseBlockedBy` — section-scoped local `#N` extraction with the tolerant-but-bounded grammar of `closesDirectiveRe` (case-insensitive heading at any ATX level, section runs to the next heading, `#70`≠`#7`, refs elsewhere in the body never block); `ForeignBlockedBy` — cross-repo/URL refs surfaced for logging only; `PartitionBlocked` — splits the ready queue against the open set (any open ref blocks; closed/dangling refs never do). - **One choke point**: the gate is applied inside `FilterClaimable`, and `launch()` / `ScheduleOnce` now route through it instead of duplicating the claim filter inline — the locked claim path, scheduler pre-tick, `ClaimableIssuesFor`, and `GET /repos/{id}/ready` share one definition of "claimable". Manual starts skip blocked issues identically. - **Lazy open-set fetch**: `Tracker.Issues(StateOpen)` fires only when a claimable issue actually carries a local blocker ref — zero added forge calls for repos not using the convention (pinned by test). - **Failure profile**: fetch failure fails closed (repo tick skipped; `*TrackerError` → 502 on manual start); data ambiguity fails open (dangling ref unblocks). All-blocked ≡ empty queue: no launch, no consecutive-failure strike. Skipped issues emit one structured line, e.g. `afk: skipped blocked issues … #87 (blocked by #74)`. - **Docs**: ADR-0042; to-issues skill (blockers must be `#N` — prose never blocks); triage skill + triage-labels (`ready-for-agent` + live blockers is a valid pairing); CONTEXT.md claimable definition. ## Verification - Table tests for the grammar and partition (`blocked_test.go`, 32 subtests). - Engine-level tests: lower-numbered blocked issue skipped and claimed on a later sweep once its blocker closes (via a real success reap); all-blocked behaves as empty; no `Issues(StateOpen)` call without refs; open-set fetch failure fails closed on both auto and manual paths. - `go build ./...`, `go vet ./...`, full `go test ./...` green (only pre-existing sandbox-only tmuxx integration failures, present on clean main), `golangci-lint 2.12.2 run ./...` → 0 issues, gofmt clean. Closes #136 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01D9H315aPekfjPpRiBjcsX8
feat(afk): scheduler skips issues blocked by another open issue
All checks were successful
ci / native (pull_request) Successful in 5m36s
e442ca07c4
Promote the issue body's `## Blocked by` section to a machine-read
contract (ADR-0042, issue #136): the AFK engine no longer schedules a
ready-for-agent issue while any issue that section references is still
open, so unmerged blocker work is never raced.

- New pure functions in internal/afk/blocked.go: ParseBlockedBy extracts
  section-scoped local #N refs (tolerant-but-bounded, mirroring
  tracker.closesDirectiveRe), ForeignBlockedBy surfaces unevaluable
  cross-repo/URL refs for logging, PartitionBlocked splits a ready queue
  against the open-issue set.
- The gate lives inside FilterClaimable — now the one choke point the
  locked claim path (launch), the scheduler pre-tick, ClaimableIssuesFor,
  and GET /repos/{id}/ready all share, so manual starts, the auto loop,
  and the UI can never disagree on "claimable".
- Lazy open-set fetch: Tracker.Issues(StateOpen) fires only when a
  claimable issue actually carries a local blocker ref — zero added
  forge calls for repos not using the convention.
- Fail-closed on infrastructure (fetch failure skips the repo's tick,
  *TrackerError on manual starts), fail-open on data ambiguity (a
  dangling or closed ref never blocks). All-blocked behaves exactly like
  an empty queue: no launch, no failure strike. Skipped issues emit one
  structured log line naming their open blockers.
- Docs: ADR-0042, to-issues skill (blockers must be written as #N),
  triage skill + triage-labels (ready-for-agent plus live blockers is a
  valid pairing), CONTEXT.md claimable definition.

Closes #136

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D9H315aPekfjPpRiBjcsX8
Merge remote-tracking branch 'origin/main' into afk/136
All checks were successful
ci / native (pull_request) Successful in 5m45s
d0bd9714e9
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Cloonar/coding-lab!138
No description provided.