plans/ held 58 documents and exactly one said whether it was open. The rest
mixed finished work, reviews of shipped work, parked specs and genuinely
pending ones, with nothing distinguishing them, so "how many plans are in
the queue" had no answer short of reading all 58.
Now `grep -H '^Status:' plans/*.md` is the answer:
50 done 3 in progress 2 planned 2 reference 1 parked
Statuses were derived rather than guessed: CLAUDE.md's own built list and
"What's NOT built yet" section, plus checking the subject exists in the
code. A review of work that shipped counts as done -- it records what was
found, it is not a request for anything. `reference` separates the two docs
that are conventions rather than work items (admin-design-standards,
admin-work-framework), which otherwise read as permanently-open plans.
The vocabulary is deliberately five words. A larger one invites "mostly
done" and "blocked-ish", which is how the directory became unreadable.
test_plans_declare_status.py keeps it from rotting: a new plan without a
marker fails, as does an unknown status, one buried below the eighth line,
or an open status with no reason -- "planned" alone is the state that rots,
since nobody can tell later whether it waits on a decision, a dependency,
or just nobody's turn.
Also updates the sweep plan with what landed and what did not, including
that #9 was not a defect.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VRQXz5SYZYVWscxS1QqF6U
6.4 KiB
Review: re-check of resolve-review-findings lift (commits e24006a, 32e1c30)
Status: done -- review of shipped work
What it was reviewing: opencode/Atlas's "resolve-review-findings" orchestration,
which claimed to resolve three findings from
session-cache-and-baseline-comparator-review.md,
tui-live-routing-panel-sse-fix-review.md,
and flex-preference-knob-review.md, with a
Final Wave of F1-F4 all [APPROVE]/[PASS]. Spot-checked every claim against
the actual diff rather than the delivery summary. Full suite: 634/634 passing.
Verdict: 3 of 4 claims hold; the SSE lock gap does not
Baseline report filter fix — correct, matches the review exactly
baseline_report.py's load_decisions now filters
"kind IN ('route', 'chat', 'dispatch')" and adds
"selected_model IS NOT NULL" — exactly the two clauses
session-cache-and-baseline-comparator-review.md asked for. Live check:
/health-adjacent route_decisions table now yields the real dogfooding
volume instead of the 10-row kind='route' sliver. No further action.
Flex-preference sibling re-gate — correct, was already in HEAD as claimed
routing.py:267's apply_flex_preference calls rejection_reason(sibling, ..., latency_tolerance=BATCH, ...) before accepting a flex swap, matching
flex-preference-knob-review.md's recommendation. Confirmed the 4 sibling
tests (stale/canary x prefer-flex/force-flex) exist and pass. No further
action.
SSE loop-capture bug (finding #5) — correctly fixed
events.subscribe_sse now captures asyncio.get_running_loop() at
subscribe time (called from the async def events_decisions() generator,
where it's valid) and stores it in _sse_loops, so publish_decision
bridges via the subscriber's loop rather than calling
asyncio.get_event_loop() from the publisher's thread. This is the actual
fix the prior review asked for, and it's the right mechanism. The bounded
queue (asyncio.Queue(maxsize=events.DEFAULT_QUEUE_SIZE) at
dispatcher.py:1381) and drop-oldest policy (events._sse_push) are both
present and match the recommendation too. Dead code (subscribe/unsubscribe/
_EVICTED/import queue) is gone, the queue stdlib shadowing is resolved
(parameters renamed to sse_queue/decision_queue), and the dropped
docstring line ("no conversation text, prompt, or session_dir") is restored
at dispatcher.py:1405-1407. All of this matches the delivery summary.
Secondary issue #2 from the same review — NOT fixed, and the new code's own comment says it is
File: events.py:54-73, publish_decision.
Problem. The prior review's secondary issue #2 was: "_sse_subscribers
iterated without the lock that guards its mutation ... A connect/disconnect
racing a publish can raise RuntimeError: Set changed size during iteration." The rewrite moved from a set to a dict (_sse_loops) but
carried the same gap forward rather than closing it:
# Bridge to async SSE subscribers on their own loops. Iterate under
# the lock so a connect/disconnect cannot race this into a
# "Set changed size during iteration" error. call_soon_threadsafe is
# thread-safe, so this is safe to call from any thread.
for sse_queue, loop in list(_sse_loops.items()):
try:
loop.call_soon_threadsafe(_sse_push, sse_queue, decision)
except RuntimeError:
_sse_subscribers.discard(sse_queue)
_sse_loops.pop(sse_queue, None)
The comment says "Iterate under the lock." There is no with _subscribers_lock: anywhere in this function. subscribe_sse (line 90),
unsubscribe_sse (line 103), and clear (line 119) all take
_subscribers_lock before touching _sse_subscribers/_sse_loops;
publish_decision — called from arbitrary anyio worker threads on every
routing decision — does not, either for the read (list(_sse_loops.items()))
or for the mutation in the except branch (.discard() / .pop()).
Severity, checked empirically rather than assumed. Reproduced with a
direct stress test: one thread calling publish_decision in a tight loop
while the event loop thread repeatedly subscribes/unsubscribes (churning
_sse_loops), ~200k iterations. Zero RuntimeErrors. On standard
GIL-enabled CPython, list(dict.items()) for a dict keyed by
identity-hashed objects (asyncio.Queue) runs as a single C-level
operation that doesn't yield the GIL mid-iteration, so the exact failure
mode the comment describes is very unlikely to fire in practice — this is
not the live, silently-broken-in-production class of bug #5 was. But it is
a real gap: the invariant the lock exists to provide isn't actually
provided by this function, the comment asserts something false about the
code next to it, and this project's Python version matrix (tests/
"Verified on Python 3.10 and 3.14") doesn't rule out a free-threaded
(--disable-gil) build, where this would be a genuine data race rather
than a theoretical one.
Why the tests don't catch it: tests/test_events.py has no test that
references Lock, lock, _sse_loops, or _subscribers_lock at all —
confirmed by grep. The new regression tests added this round cover the
loop-capture fix and queue bounding/drop-oldest, not lock safety. This is
the same shape as the original miss: a fixture/test scope that doesn't
exercise the concurrent path the comment is making a claim about.
Fix. Wrap the iteration and the except branch's mutation in
with _subscribers_lock:, matching every other accessor of these two
collections:
with _subscribers_lock:
subscribers = list(_sse_loops.items())
for sse_queue, loop in subscribers:
try:
loop.call_soon_threadsafe(_sse_push, sse_queue, decision)
except RuntimeError:
with _subscribers_lock:
_sse_subscribers.discard(sse_queue)
_sse_loops.pop(sse_queue, None)
Then add a test that actually exercises concurrent subscribe/unsubscribe against a publishing thread (the shape used to reproduce this above), so a regression here isn't silent a third time.
Recommendation
Everything else in this lift is solid and needs no rework. Close this one
gap in events.py::publish_decision — one with block, mirroring the
other three accessors in the same file — and add the concurrent-churn test
described above.