Stack 4/5: Concurrency hardening — prompt cancel, thread-safe limiter, remove/rewire (rounds 11-12)#4
Conversation
- Prompt cancel_plan: transitions ALL bookkeeping first, then stops every scheduled fiber (never the calling fiber) instead of the reactor handle; a post-acquire guard keeps canceled slot-waiters from running. Ordering matters: stopping a fiber synchronously admits the next waiter, which must already read as canceled. - Fail-fast relation declarations: RelationRules.validate_declaration! runs at validator construction - unknown relations, undeclared fields (fail-open typos), and sum_lte over non-numbers refuse to boot instead of crashing mid-validation in the wrong error class. - RateLimit#try_acquire: non-blocking admission for both modes - budgets say no instead of making callers wait. - Typed-fields-only projection: relations over untyped fields stay out of draft-07 keywords (still in x-agentic-rules), closing the nil-vs-null divergence from the projection side. Modernizes the four asking examples: cancel_drill and relation_prober flip to green acceptance tests, retry_budget's wallet becomes a real windowed RateLimit, projection_agreement verifies the projection declines at its frontier. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Exact per-operation object counts via GC.stat: 37 objects per happy validation, 11x that on rejection, the graph snapshot's immutability priced as a purchase, and zero GC runs inside a 10-task plan. New persona cast for round 11. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Real threads against everything shared: the journal (1200/1200 lines, zero torn) and registry hold on real locks; the windowed limiter's check-then-act coasts on the GVL and the drill says so. Also fixes a genuine load-order bug the drill caught: the journal used Time#iso8601 without requiring 'time'. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Little's Law over the journal's duration percentiles: lanes computed per task at p50 and p95, then the plan checked against every configured limit - the provider quota binds before the concurrency limit anyone was arguing about. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
A pain score per task and per plan: extra join inputs, deep chains, anonymous edges, and orphans each priced; a pipe is free after calibration taught the metric to agree with taste on the easy cases. The monster scores 25 and the meeting ends. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
One pipeline in two postures: ten conditionals of timidity vs a contract barricade at both doors. Fed the same garbage, the timid version launders it into a plausible wrong answer; the confident one says no at the door with field names attached. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Red-green-refactor for a plan: five structural assertions written before any tasks exist, smallest-step additions, two deliberate sins caught by name, and a green rebuild. Files the round-12 ask: remove_task or a rewire seam - plans are add-only, so refactoring currently means demolition. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Seven named cops for capability specs with an offense report: autocorrect for transformations with one right answer (names, enum order), refusal with reasons for judgment calls (descriptions, types, rule messages, scope creep). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
The :telemetry pattern on Agentic's hooks: namespaced event tuples, measurements split from metadata, runtime attach/detach, and crash isolation - a ten-line bridge, a producer ignorant of its audience. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
A pure use-case speaking only to ports, delivered first by Agentic and then by a bare call to rehearse the migration, with a mechanical purity scan (exit 1 on leak) proving the dependency arrow points one way. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
One pipeline definition, three isolated shard executions: per-shard journals as N recovery stories, per-shard rate limits as noisy-neighbor containment, and a fleet-wide rerun where an ignorant control plane lets the journals decide - the crashed shard resumes exactly two steps. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Adds the Round 11 table - a new cast of ten prolific Rubyists (ko1, headius, nateberkopec, zenspider, avdi, kytrinyx, bbatsov, josevalim, jodosha, eileencodes) - and findings to the perspectives index, and regenerates the examples catalog (100 examples). Next asks recorded: thread-safe windowed limiter bookkeeping, remove_task/rewire, multiprocess journal drill. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
- Thread-safe windowed limiter: a real Mutex around the stamp bookkeeping in both acquire and try_acquire (sleeps stay outside the lock) - the answer is the same on every Ruby VM. - remove_task and rewire_task: surgical plan refactoring. Removal is pending-only and refuses to orphan dependents (naming them); rewiring replaces dependencies/needs in place with the same shapes as add_task, validating targets exist. - Process drill (examples/process_drill.rb): four forked writers, one journal, a thousand events - the flock claim certified across real processes, not just threads. Modernizes the asking examples: the plan kata's refactor step now refactors in place via rewire_task, and the threads drill asserts (rather than observes) the mutex-backed admission count. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Owner-checked public method surface of eleven core classes, cross-referenced against 102 example programs: 112 methods, 58 exercised, 54 accidental - scheduler internals wearing public visibility, priced as the semver loan they are. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
A nine-room reading tour computed from the code's who-mentions- whom, ordered so no stop assumes an unmet concept, with human one-liners for purpose. The trail starts with task_failure: the house talks about failure before work, and that's its values. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Derailed-style require costs in pristine child processes: the gem itself is nearly free (Zeitwerk defers everything) and the bill lands at first constant touch - 5.6MB/118ms as async and dry-schema arrive. Deferred is not free; it's a bill with a different due date. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
TestProf's EventProf for plans: task-seconds by tag with shares and worst offenders (llm owns 78%), plus effective parallelism (2.4x on 3 lanes) indicting stage barriers rather than capacity. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Eight hostile files against ExecutionJournal.replay: six recover with good manners, but a torn tail (the exact artifact of the crash the journal exists for) and binary garbage crash replay in the wrong error class, denying all recovery. Exit 1 by design - the probe is the round-13 acceptance test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
An owned port at the LLM boundary plus a twenty-line verifier: doubles must match the port's methods and parameter shapes at load time. The drifted double would have passed every test - unverified fakes don't fail, they vouch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
A thirty-line Sinatra-flavored DSL - step/after:/needs: with the block as the agent - built entirely over the public API so it can never drift from the engine, with define-time failure for typo'd wiring and the orchestrator exposed as the escape hatch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
The pattern that makes LLM components shippable: contract violations become the correction prompt for a bounded retry loop. The declared constraints generate better feedback than hand- crafted templates, the bound caps the bill, and terminal failure keeps the paper trail. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
A contract field rename through the three-release choreography: translate at the door, warn once per call site with the caller named, tally as the migration plan, strict mode as the CI-enforced deadline. The shim lives outside the contract so derived tools speak only the future tense. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
The journal's write path weighed layer by layer: serialization is 0.4% of the real write; the fsync is the other 99.6% and is the product, not overhead. Group commit priced as a different durability promise (9x throughput, 19 acknowledged events of crash exposure), not a faster same one. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Adds the Round 12 table - a third cast of ten prolific Rubyists (wycats, sarahmei, schneems, palkan, flavorjones, searls, rkh, obie, rafaelfranca, byroot) - and findings to the perspectives index, and regenerates the examples catalog (111 examples). Next asks recorded: tolerant journal replay, fsync_every group-commit mode, strict-shapes audit replay. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Review finding (headius, stack 4 panel): track's in_flight and high_water updates ran outside the mutex, so windowed mode under real threads could misreport the high-water mark. Counter updates now share the window mutex; the yielded work stays outside the lock so windowed work is never serialized. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
codenamev
left a comment
There was a problem hiding this comment.
Persona review panel: Charles Nutter, Katrina Owen, Bozhidar Batsov. Verified locally: 591 examples, 0 failures at this cut (including the fix below).
Charles Nutter (headius) — This is the slice where the thread-safety claims get real, so I read it with JRuby eyes. The windowed limiter's stamp Mutex is correctly scoped (check-and-stamp atomic, sleep outside the lock). One finding, now fixed in this PR (fix: bring RateLimit's in-flight counters under the window mutex): track mutated @in_flight/@high_water outside the new mutex, so under real threads the high-water mark could misreport — the exact "passes on MRI, lies on JRuby" shape. The fix keeps the yielded work outside the lock, which matters: holding a mutex across user work would serialize everything the limiter exists to parallelize. Also credit where due: the Time#iso8601-without-require "time" load-order bug this slice fixes is the classic works-on-my-boot species my users hit constantly. Approve with the fix in.
Katrina Owen (kytrinyx) — remove_task/rewire_task are the refactoring seam I asked for, and the guards encode the right pedagogy: removal refuses to orphan dependents and names them ("rewire them first" is an instruction, not a scolding), rewiring validates every target before mutating anything, and both are pending-only so a running plan can't be edited out from under itself. The plan kata in this slice refactors in place under green assertions — red, green, refactor, with all three words meaning what they say. The cancel_plan fix's ordering comment (bookkeeping first, because stopping a fiber synchronously admits the next waiter) is exactly the kind of constraint a comment exists for. Approve.
Bozhidar Batsov (bbatsov) — Style and consistency pass: fail-fast relation declarations produce errors that name the rule, the relation, and the offending field (rule :fits (sum_lte) can only sum declared numbers; :b is not) — error messages as documentation, the way I like them. try_acquire's docstring states the budget-vs-queue distinction instead of leaving it to folklore. Everything standardrb-clean. One consistency note, no action: remove_task raises ArgumentError for already-started tasks where a case could be made for a domain error class — but ArgumentError for caller mistakes is a defensible, consistent convention across this API, so keep it uniform. Approve.
Panel verdict: approve (as comment; self-approval disallowed). The headius finding was fixed in-slice; the high-water assertion is pinned in the concurrency contract spec in slice #5.
Generated by Claude Code
Fourth slice (rounds 11-12), stacked on #3. This is the slice that fixes the two defects the round-10 drills caught.
Framework
cancel_plan: transitions ALL bookkeeping first, then stops scheduled fibers (never the reactor handle, never the calling fiber); post-acquire guard keeps canceled slot-waiters from running. Before: status flipped instantly while every agent ran and billed. After: 30ms, 2/6 agentssum_lteover non-numbers refuse at validator construction instead of crashing mid-validation as rawTypeErrorRateLimit#try_acquire(non-blocking admission for budgets/cron guards); relations over untyped fields stay out of draft-07 keywords (nil-vs-null frontier closed from the projection side)remove_task/rewire_task: surgical plan refactoring (pending-only, dependents named, targets validated)Time#iso8601withoutrequire "time"Examples & docs
Review focus:
cancel_planordering, the mutex placement inrate_limit.rb,remove_task/rewire_taskguards.🤖 Generated with Claude Code
https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Generated by Claude Code