Skip to content

Stack 4/5: Concurrency hardening — prompt cancel, thread-safe limiter, remove/rewire (rounds 11-12)#4

Merged
codenamev merged 25 commits into
mainfrom
claude/stack-4-hardening-y0k02n
Jul 7, 2026
Merged

Stack 4/5: Concurrency hardening — prompt cancel, thread-safe limiter, remove/rewire (rounds 11-12)#4
codenamev merged 25 commits into
mainfrom
claude/stack-4-hardening-y0k02n

Conversation

@codenamev

Copy link
Copy Markdown
Owner

Fourth slice (rounds 11-12), stacked on #3. This is the slice that fixes the two defects the round-10 drills caught.

Framework

  • Prompt 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 agents
  • Fail-fast relation declarations: unknown relations, undeclared fields (fail-open typos), and sum_lte over non-numbers refuse at validator construction instead of crashing mid-validation as raw TypeError
  • RateLimit#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)
  • Thread-safe windowed limiter: real Mutex around stamp bookkeeping (was coasting on the GVL)
  • remove_task / rewire_task: surgical plan refactoring (pending-only, dependents named, targets validated)
  • Fixed a genuine load-order bug the threads drill caught: journal used Time#iso8601 without require "time"

Examples & docs

Review focus: cancel_plan ordering, the mutex placement in rate_limit.rb, remove_task/rewire_task guards.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF


Generated by Claude Code

claude added 24 commits July 7, 2026 16:31
- 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 codenamev changed the base branch from claude/stack-3-operations-y0k02n to main July 7, 2026 23:38

@codenamev codenamev left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@codenamev codenamev merged commit f632cc7 into main Jul 7, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants