fix: encapsulate render-queue state in a singleton class (#1140) #1141
No reviewers
Labels
No labels
area:agents
area:dashboard
area:database
area:design
area:design-review
area:flows
area:infra
area:meta
area:security
area:sessions
area:webhook
area:workdir
security
type:bug
type:chore
type:meta
type:user-story
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/agent-hooks!1141
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "code-lead/1140"
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 #1140.
Summary
requestedSet and thedraining/drainScheduledflags inapps/server/src/infrastructure/agent-env-sync/render-queue.tsinto a singleRenderQueueclass held in a module-level singleton.letdeclarations. The drain-loop invariants (one in-flight microtask, one drain cycle at a time, burst-dedup via the pending Set) are documented in one place on the class.setRenderFn,resetRenderFn,setListAgentsFn,resetListAgentsFn) and the public API (enqueueRender,scheduleRenderOnConfigChange,flushRenderQueue) are unchanged — they are thin delegations to the singleton, so callers and existing tests need no updates.Out of scope
Test plan
just qa— 3359 / 3359 passing, all lints / schema checks green.render-for-instance.test.tsintegration tests forscheduleRenderOnConfigChange+ burst dedup pass via the new singleton.Code Review —
fix: encapsulate render-queue state in a singleton classCI: ✅ green. Single file changed (
render-queue.ts, +152 / −91). Pure refactor — public API surface unchanged.Overview
All mutable module-level state (
let draining,let drainScheduled,let _renderFn,let _listAgentsFn,const requested) is folded into aRenderQueueclass behind a singleconst queuesingleton. The exported functions become one-line delegations. The drain logic, invariants, and test-seam contract are all preserved exactly.What's good
private readonly requested— correct use ofreadonlyon theSetfield; the reference can't be reassigned but.add()/.clear()still work, exactly mirroring the oldconst requested.drainScheduledgate, outer-while picks up late arrivals) map 1-to-1 to the implementation.enqueueMany— a clean addition that letsscheduleRenderOnConfigChangeaccumulate a batch of names and callscheduleDrain()exactly once rather than once per agent, which was the implicit intent of the old code.flush()encapsulation — the polling loop (while draining || drainScheduled || size > 0) is now behind a named method instead of inlined influshRenderQueue, which is cleaner.drain()—if (this.draining) returnis technically unreachable (becausescheduleDrain()guards ondrainingbefore queuing a microtask), but keeping it is correct and safe.Minor notes (non-blocking)
Commit prefix —
fix:is slightly inaccurate for a pure refactor;refactor:would better match the conventional-commits convention in this repo. Low stakes since there's no semantic-release here.enqueueMany([])still callsscheduleDrain()— if the agent-type filter matches zero agents,enqueueManyis called with an empty array, which schedules a no-op microtask drain. The old code had identical behaviour (scheduleDrain()after the loop regardless of whether anything was added), so this is a pre-existing non-issue carried forward cleanly.No test file changes — acceptable given the public API is byte-for-byte identical and the refactor is mechanical. A unit test for
RenderQueuein isolation would be a good follow-up (especially forenqueueManyand theflush()idle-immediately case).Verdict
LGTM. The encapsulation is correct, the invariants are preserved, and the class doc makes the concurrency model explicit. The notes above are cosmetic or pre-existing; nothing warrants a change request.