rework(afk): one priority-ordered spawn pass — drain the pipeline before filling it #186
No reviewers
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/coding-lab!186
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "afk/185"
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?
One spawn loop for the whole AFK/autoland fleet, ordered by pipeline stage, replacing the two independent loops that raced for the same instance cap — plus the decided-along-the-way counter separation. Note: PR #184 merged while this run was starting; this branch is based on top of it.
What ships
The single spawn pass (
internal/afk/spawn.go):SpawnOnce, serialized under its ownspawnMu— one repo listing, one liveness snapshot, one shared forced-auth memo per pass. The old sweeps became gather-only producers:landerCandidates(autoland.go) andnewWorkCandidates(scheduler.go) emit candidates and never launch.spawnPassstable-sorts bySpawnStage(StageLander < StageFix < StageNewWork— priority is DATA on the candidate) and launches down the list: spawned counts the slot, atCap raises the snapshot to that repo's cap (the scheduler's per-repo floor-raise, now also replacing autoland's whole-sweep stop, which was correct only under a single global cap). Both loops and the HTTP toggle-on/reset kicks invoke the one pass; the reaper tick runs it right afterReapOnce— drain-then-fill made literal.Cap ownership:
UnderCapleft both pure predicates (ShouldLaunchAuto,ShouldSpawnLander) — the pass is the cap's sole consumer, the locked per-launch guards inlaunch/LaunchLanderstay as the authoritative backstop on the same path. Producers keep a cheap at-cap veto against the pass snapshot before their forge reads (load-shedding hint, never enforcement) — for the new-work producer that veto is new and savesReadyIssues+FilterClaimableon at-cap repos.#182's extension point:
StageFixis reserved between lander and new work; the fix-forward producer plugs in by emitting candidates at that rank — an addition to the gather, not a modification of the pass. Exercised today by stub candidates in the pass-core tests.Counter separation:
reapRun's failure switch is guarded toafk_manual/afk_auto. A lander death/timeout no longer incrementsconsecutive_failures(lander flakiness must not pause unrelated AFK work), and a lander reject-success no longer resets it (a correct rejection is evidence FOR the brake, not against it — under fix-forward the old behavior would compound). Landers get no counter; the budget clock stays deliberately shared.Docs: ADR-0049 pins the pass, the no-queue stance (candidates re-derived every pass — ADR-0015 poll-only stands), the counter decision, and the rejected alternatives; CONTEXT.md gains the Spawn pass domain term and the counter-scoping edits. ADR-0048 verified untouched-correct (it pins the contract the engine reads, not the engine).
Acceptance criteria → proof
EffectiveCapfor launch decisions —spawnPass(+ its in-pass producer hints and the locked backstops); zero cap reads left in the predicates. Pinned structurally byTestShouldLaunchAuto/TestShouldSpawnLander(no cap rows) and behaviorally by the pass tests.lander > fix > new AFKpinned over a mixed candidate set —TestSpawnStageOrder,TestSpawnPass_stageOrderIsTheOnlySortKey(includes the StageFix stub).TestSpawnOnce_lastSlotGoesToLanderNotNewWork(also proves the loser is deferred, not lost, and left no claim-branch side effects).TestSpawnOnce_concurrentReaperAndSchedulerTicksCannotOversubscribeCap: the literal tick bodies race against one repo at cap 1 with both candidate kinds spawnable; green under-race -count=4.TestReap_landerDeathDoesNotStrike(seeds 1 and 0),TestReap_landerRejectDoesNotResetCounter,TestReap_landerMergeDoesNotResetCounter, reworkedTestAutolandCycleIntegration/lander_death_does_not_strike_and_a_paused_repo_stops_spawning(pause now seeded via the AFK path it belongs to);TestReap_death/TestReap_timeout/TestReap_successCycle/TestThreeStrikes_*untouched and green.ScheduleOnce→SpawnOnce); the only assertion-level test changes are the two dropped UnderCap predicate rows (coverage moved to the pass tests) and the deliberately flipped lander-strike pins.StageFixflow through the pass inspawn_test.go.Decisions worth review
Pausedpredicate term stays): pause suppression is unchanged; only the counter's feeding direction changed.Verification
gofmtclean ·go build ./...· fullgo test ./...green (real-tmux afk cycle suites included) ·golangci-lint run(2.12.2, CI's pin): 0 issues · concurrency AC additionally under-race -count=4·web/untouched.Closes #185