Skip to content

Stack 1/5: Foundations — repair, Zeitwerk, durable journal, orchestrator composition (rounds 1-4)#1

Merged
codenamev merged 46 commits into
mainfrom
claude/stack-1-foundations-y0k02n
Jul 7, 2026
Merged

Stack 1/5: Foundations — repair, Zeitwerk, durable journal, orchestrator composition (rounds 1-4)#1
codenamev merged 46 commits into
mainfrom
claude/stack-1-foundations-y0k02n

Conversation

@codenamev

@codenamev codenamev commented Jul 7, 2026

Copy link
Copy Markdown
Owner

First slice of the persona-driven development series (rounds 1-4). Each cut point in this stack was a green-suite state.

Framework

  • Repaired the truncated test suite (a Thor exit(1) was killing rspec at 65/474) and the 12 latent failures it hid
  • Zeitwerk as the single code loader (removed the dual require_relative scheme; 19x faster require)
  • Fail-fast LLM credential validation; thread-safe assembly init; real error hierarchy
  • PlanOrchestrator composes with running reactors; scheduler deadlock at tight concurrency fixed (barrier + acquire inside the fiber); canceled plans no longer report :completed
  • ExecutionJournal: fsynced JSONL crash recovery, descriptions as idempotency keys across runs
  • Task payloads, direct callable agents, dependency_outputs, retry policy honoring backoff; errors testify about their own retryability
  • Pluggable web_search backend replacing hardcoded fake results

Examples & docs

  • First rounds of persona examples (haiku agent, Agentic.run, renga circle, ticket screener, latency lab, journal drills…) and field notes under docs/perspectives/

Stacked under #2 (rounds 5-7). Review here focuses on lib/ + spec/; examples are offline-runnable demos.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF

claude added 30 commits July 5, 2026 20:54
The CLI spec invoked 'agent create' without the required --purpose
option, causing Thor to exit(1) mid-suite and silently truncate the
run at 65 of 474 examples. Repairing that revealed 12 latent failures:

- CLI 'agent create' used Agent.new with a block that initialize never
  yields, silently discarding role/purpose; now uses Agent.build
- PersistentAgentStore#store never assigned generated IDs back to the
  agent, so re-storing an agent duplicated it instead of versioning it
- PersistentAgentStore#list_all now accepts both the documented
  all(filter: {...}) form (ADR-015) and the bare all(capability: ...)
- Agentic.register_capability/.assemble_agent now use the public
  readers instead of module ivars
- AgentAssemblyEngine keyword inference now stem-matches capability
  names ('Analyze the data' matches data_analysis), compounds
  importance across sources, and lets explicit input capabilities
  raise importance over weaker keyword matches
- ExecutionObserver#display_progress prints the newline that ends the
  carriage-return progress line when all tasks complete
- Align LLM strategy specs with the implemented fallback design and
  replace unsupported expect_any_instance_of + have_received

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Documents the multi-perspective review of the codebase (Matz, DHH,
tenderlove, fxn, ioquatix, Jeremy Evans, solnic, Mike Perham, Sandi
Metz, ankane), the synthesis of what they agree on, and the prologue
field notes on the truncated test suite discovered before the builds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Remove all require_relative calls for Zeitwerk-managed constants (nine
in lib/agentic.rb plus the rest scattered through lib/), add the missing
'ui' => 'UI' inflection that the manual requires had been masking, and
require thor from each CLI file that reopens class CLI < Thor so the
directory is loadable file-by-file.

The CLI (and its Thor/tty-* dependency stack) is no longer eagerly
loaded by the library entrypoint; exe/agentic reaches Agentic::CLI
through a normal autoload. Measured: require 'agentic' drops from
~272ms to ~14ms and from 612 to 186 loaded features.

Part of the Rubyist-perspectives build series (Xavier Noria persona);
field notes in docs/perspectives/field-notes/04-fxn.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
A no-network, no-API-key example showing an agent, a lambda-backed
capability, and a result on one screen. Field notes in
docs/perspectives/field-notes/01-matz.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Agentic.run(goal, model:, concurrency:) compresses the planner ->
task definitions -> orchestrator -> provider pipeline into the single
method most library users actually want, mirroring what the CLI's
execute_plan_immediately already did internally.

Part of the Rubyist-perspectives build series (DHH persona); field
notes in docs/perspectives/field-notes/02-dhh.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
benchmark/boot.rb measures wall time, allocations, and loaded-file
count for requiring the library, autoloading the CLI, and initializing
agent assembly - each in a fresh subprocess.

Agentic.initialize_agent_assembly previously memoized global state with
a bare nil check; concurrent callers could each build their own
PersistentAgentStore against the same index file. It now uses a mutex
with a double-checked re-entry, and assigns the flag ivar last so
lock-skipping readers never observe a half-built system.

Part of the Rubyist-perspectives build series (Aaron Patterson
persona); field notes in docs/perspectives/field-notes/03-tenderlove.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
execute_plan now runs under Sync instead of a root Async block, so it
joins an existing reactor (Falcon, nested tasks) instead of spawning a
detached child and reading completion timestamps that were never set.
Standalone callers are unchanged.

apply_retry_backoff previously spawned a detached Async task to sleep
and returned immediately, so retries never observed their configured
backoff; it now sleeps in the current task, which the fiber scheduler
keeps non-blocking for sibling tasks. Backoff specs now assert the
orchestrator actually waits instead of stubbing Async::Task.current.

Part of the Rubyist-perspectives build series (Samuel Williams
persona); field notes in docs/perspectives/field-notes/05-ioquatix.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Remove the silent 'ollama' default access token that made the CLI's
check_api_token! guard unreachable and turned misconfiguration into a
request-time 401. Configuration#validate! now raises
Agentic::Errors::ConfigurationError at LlmClient construction when
neither a token nor an api_base_url is set. Base-URL-only setups
(Ollama, etc.) remain supported via an explicit placeholder token.

The library logger now defaults to :warn - the CLI raises verbosity
for interactive use instead of the library narrating INFO lines into
every host's stdout.

Part of the Rubyist-perspectives build series (Jeremy Evans persona);
field notes in docs/perspectives/field-notes/06-jeremyevans.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
CapabilityValidator compiles each CapabilitySpecification's declared
inputs/outputs into memoized Dry::Schema definitions - putting the
gem's existing (and previously unused) dry-schema dependency to work.
Declared types are enforced, required keys required, undeclared keys
still permitted.

Violations raise Agentic::Errors::ValidationError carrying the
capability name, the side of the contract that failed, and every
violation at once, replacing the duplicated first-failure RuntimeError
type checks in CapabilityProvider. Adds the previously missing
CapabilityProvider spec coverage.

Part of the Rubyist-perspectives build series (Piotr Solnica persona);
field notes in docs/perspectives/field-notes/07-solnic.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
An append-only JSONL journal that wires into PlanOrchestrator's
lifecycle hooks: one locked, flushed, fsynced line per task/plan event,
with optional chaining through existing hooks so CLI observers keep
working. ExecutionJournal.replay reconstructs completed work, outputs,
and failures so a resumed process can skip tasks it already paid an
LLM for; retry-then-succeed collapses to completed.

Part of the Rubyist-perspectives build series (Mike Perham persona);
field notes in docs/perspectives/field-notes/08-mperham.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Agent#execute_with_schema previously preferred the text_generation
capability and silently discarded the schema argument, so tasks with
output schemas lost their structure guarantees without a trace. It now
prefers the LLM client (which enforces the schema) and raises
SchemaNotSupportedError when only capability execution is available.

FactoryMethods gains an inherited hook so subclasses receive their
parent's configurable attributes and assembly instructions instead of
silently starting empty. Agent.from_h no longer uses the
ignored-block Agent.new constructor.

String raises in Agent become typed errors (CapabilityNotFoundError,
AgentNotConfiguredError, Errors::LlmError). All error classes are
consolidated into lib/agentic/errors.rb so referencing the Errors
namespace loads every class - sibling error constants split across
files were only autoloadable after their file's namesake happened to
load first.

Part of the Rubyist-perspectives build series (Sandi Metz persona);
field notes in docs/perspectives/field-notes/09-sandimetz.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Replace the hardcoded mock web_search implementation with
Agentic::Capabilities::WebSearch: a zero-configuration DuckDuckGo
Instant Answer backend (no API key, no new dependencies) behind a
one-lambda seam for swapping in SerpAPI/Brave/Tavily/internal search.
Non-JSON responses (blocked networks, proxy error pages) raise a
descriptive Agentic::Error instead of a bare JSON::ParserError.

Part of the Rubyist-perspectives build series (Andrew Kane persona);
field notes in docs/perspectives/field-notes/10-ankane.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Logger#initialize(*args) folded keyword arguments into a positional
hash that ::Logger reads as shift_age, so level: was ignored and the
logger always ran at DEBUG. Forward with (...) so the :warn default
(and any caller-supplied level) takes effect.

Addendum to the Jeremy Evans persona build; field notes updated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
…ound 2)

Three poet agents compose linked verse under PlanOrchestrator
dependencies; field notes record the output-piping and provider
frictions hit while building with the gem.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Five tickets screened, categorized, and draft-replied in parallel via
capability pipelines; field notes call out the agent-provider ceremony
and task-payload workaround as the main consumer friction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
One orchestrator task per lib/ file, each dissected with Prism; the
gem audits its own method lengths. Field notes cover the
regex-vs-parser lesson and why fiber concurrency doesn't speed up
CPU-bound tasks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Maps a gem's constant tree via per-file survey tasks and Prism,
auditing each file against the constant Zeitwerk expects - including
the GemInflector version.rb special case the map itself initially got
wrong. Field notes on verifying the verifier.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Measures orchestrator fan-out at concurrency 1/4/20 against ideal
(within 10ms at every limit) and proves a plan shares a host reactor
with sibling tasks instead of monopolizing it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Four deterministic DBA rules as capabilities, one review task per
table; advisories with exact remediation. Field notes argue facts
belong to rules and prose to models.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Extract/transform/load capabilities composed via registry.compose;
malformed records are rejected at the first contract boundary with
every violation named. Field notes on parse-optimistically,
validate-at-the-boundary.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
A forked child dies with exit! mid-batch; the parent replays the
ExecutionJournal and finishes without re-paying for journaled work.
Field notes on crash-testing durability and the idempotency-key gap
it exposed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Three deterministic critic agents (rules, squint test, naming) review
a method in parallel; the sensei prescribes one smallest next step.
Reviews the gem's own 90-line schedule_task. Field notes on
multi-perspective review being buildable today from existing
primitives.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Gem Scout rides the pluggable web_search backend with an offline
index and a separate scoring capability. The perspectives README
gains the round-2 table plus the consumer-side consensus: tasks need
payloads, orchestrators should accept agents directly, dependents
need output piping.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
The consensus from ten personas building with the gem, implemented:

- Task#payload carries arbitrary domain objects, opaque to the framework
- add_task(task, deps, agent:) attaches an agent or bare callable
  directly; callables receive the Task and their return value becomes
  the output (CallableAgent adapter)
- Dependency output piping: the orchestrator feeds completed
  dependency outputs into dependents (Task#dependency_outputs,
  Task#output_of); dependencies may be Task objects or ids
- execute_plan's provider is now optional (fails fast when unresolvable)
  and accepts a block as a plan-wide agent factory
- TaskDefinition#to_task bridges planner output to orchestrator input
- registry.compose accepts inputs:/outputs: so composed capabilities
  get boundary validation like primitive ones
- ExecutionJournal events carry task descriptions as cross-run
  idempotency keys; ReplayedState#completed? answers by id or name
- README documents the concurrency contract (IO scales, CPU doesn't),
  direct-agent usage, and which layer to start with

All six round-2 examples that carried the provider-adapter workaround
are rewritten on the new API - each got shorter and lost its shared
mutable state.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
A rumor garbles its way through five villagers via dependency output
piping alone - the feature round 2 asked for, exercised with zero
scaffolding. Field notes compare the two rounds line for line.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Three parallel repo collectors fan into one writer task through
dependency output piping - real git/TODO/spec data, no adapters.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
…xample

schedule_dependent_tasks ran inside the completing task's semaphore
slot, and spawning a dependent via semaphore.async blocks when the
semaphore is full - so two slot-holders finishing together at a tight
limit deadlocked waiting for each other's slots. Tasks now spawn
through the barrier and acquire the semaphore inside the spawned
fiber; the slot body is extracted into execute_task_in_slot.
Regression spec covers the diamond-at-limit-2 case.

Found by examples/plan_gantt.rb (tenderlove round 3), which renders a
plan's execution as an ASCII timeline from lifecycle hooks - including
queue-wait time, which is how the deadlock surfaced.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Per-file YARD coverage surveys fan into a single report task via
dependency piping; 90.2% coverage measured, with the Thor-desc blind
spot documented. Field notes on order-sensitive Prism traversal.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Lifecycle hooks feed an Async::Queue consumed by a renderer task in
the same reactor - the promised streaming observability built from
existing parts. Field notes document the hooks-run-inline contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Seed-deterministic bidirectional fuzz of every registered capability
contract: conforming inputs must pass, dropped-required and
type-corrupted inputs must fail. 34 trials, boundary holds, CI-ready
exit code.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
claude added 16 commits July 6, 2026 14:25
Commands as composed capabilities with their own contracts (the
round-3 compose inputs:/outputs: feature); dispatch is lookup +
validation + rescue-to-rejection. Contract rejections and domain
rejections stay distinguishable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Scripted-flaky task under exponential backoff with journaled retries;
the timeline proves the backoff waits and the journal answers
completed? by description across runs. Field notes flag the
retryable-errors string matching and jitter default.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Lifecycle hooks record every orchestrator message and reply; the run
renders as an ASCII sequence diagram, making the mediator pattern -
and the newly framework-owned output hand-off - visible and teachable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Release notes drafted from real git history via a contract-checked
classifier fanned across 40 commits into one writer. The perspectives
README gains the round-3 table and findings: adapter tax eliminated,
the Gantt-discovered deadlock fixed, and the next prioritized asks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
The prioritized asks from the round-3 field notes, implemented:

- Named dependencies: add_task(task, needs: {shipped: commits}) pipes
  outputs addressable as task.needs.shipped / task.needs[:shipped]
  (NamedOutputs), composing with positional dependencies
- Task#previous_output shorthand for single-dependency chains
- task_slot_acquired lifecycle hook with waited: duration, separating
  queue wait from run time; the Gantt example now renders '.' for
  queued and '#' for running
- Retry policies consult the error's own retryable? verdict (captured
  through TaskFailure.from_exception) before the retryable_errors type
  list; Task#perform now preserves it too
- Contract value predicates: enum:, min:/max:, and non_empty: on
  declared capability inputs/outputs, enforced via dry-schema
- Hooks documented as running inline on the task's fiber

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Three artists draw creature parts in parallel; the assembler reads
them by name via needs: - round 3's named-dependency ask as a parlor
game. Seeded, reproducible creatures.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Parallel environment checks with a named-dependency diagnosis and a
CI-enforceable exit code - the onboarding wiki page, deleted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Measures wall time and queue-wait (via the new task_slot_acquired
hook) across concurrency limits and recommends where adding lanes
stops paying - the long-pole flatline made visible.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Constant-reference graph between files via parallel Prism surveys and
a fan-in atlas: load-bearing walls, heaviest leaners, and mutual
dependencies. Field notes on namespace-relative constant resolution
and the default-proc trap.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Two concurrent plans in one reactor bounded by a single
credential-scoped Async::Semaphore; the high-water mark proves the
ceiling holds across plan boundaries while calls interleave freely.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
…el example

overall_status never consulted the :canceled state, so a canceled plan
with no failures reported itself complete. Canceled tasks now yield
:canceled. Found by examples/invariant_sentinel.rb (Jeremy Evans round
4), which checks domain invariants after every task via hooks and
arrests the plan at the first broken law.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Order lifecycle where transition guards are enum predicates on
capability contracts - no runtime transition table, illegal moves are
type errors naming the legal alternatives, and output enums keep
transitions honest about their destination.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Three failure modes under one retry policy: the rate limit's own
retryable? verdict earns retries, the auth error's verdict overrules a
deliberately-misconfigured type list, and opinion-less errors fall
back to the list.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Pre-execution design review of dependency graphs: god tasks, deep
chains, orphans - with one prescribed first move. Its best finding is
the missing read-only graph accessor three tools have now had to
crowbar around.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
…ppet

Parses every ruby fence with Prism and resolves every Agentic
constant named against the loaded gem. First run caught the
capability-composition snippet's literal '{ data: { ... } }' - the
exact snippet round 1 flagged as docs-ahead-of-code - now fixed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF
Adds the round-4 table to the perspectives README: ten new
experiments on the round-4 release, two more defects found by
examples (canceled-status lie, four-round-old broken README snippet),
and the next prioritized asks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeZLdZ3M4q4cho4DZPfSkF

@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: Aaron Patterson, Mike Perham, Sandi Metz. Verified locally: 540 examples, 0 failures at this cut.


Aaron Patterson (tenderlove) — The two fixes that matter here are correct in the way that counts: the suite-truncation repair (a CLI exit(1) killing rspec mid-run is the kind of bug that silently rots a whole project — 12 latent failures were hiding behind it), and the scheduler deadlock fix. Spawning through the barrier and acquiring the semaphore inside the fiber is the right shape — two slot-holders scheduling dependents at a tight limit can't wait on each other anymore, and the inline comment explains why, which future-me appreciates. Zeitwerk-as-single-loader with the boot benchmark receipt: good.

One nit to fix at the top of the stack (don't churn this slice): task durations are measured with Time.now deltas. Wall clocks step under NTP; everything downstream (journal baselines, percentiles in later slices) eats that noise. Use Process.clock_gettime(Process::CLOCK_MONOTONIC) for durations. Approving with that noted.

Mike Perham (mperham) — The journal is the piece I came to inspect. Locked, flushed, fsynced per event, one JSON line each: boring, correct, exactly what crash recovery needs. Descriptions as idempotency keys across runs is the design decision that makes resume actually work (ids are per-run; descriptions are stable). Errors testifying about their own retryability (retryable? on the error, consulted by policy) beats every string-matching retry table I've ever deleted. Canceled plans no longer lying :completed — good; the deeper cancel semantics get fixed properly in slice 4, which is the right place. Approve.

Sandi Metz — The seams are honest: add_task(task, deps, agent:) accepts a bare callable, results are objects (TaskResult/TaskFailure) instead of control flow, and execute_with_schema now does what its name promises. The factory subclassing fix pays down real inheritance debt. One note-only smell: ReplayedState#durations_by_description is a pure alias of durations — an extra name for the same message. Not worth churn now; don't add a third. Approve.


Panel verdict: approve (posted as comment; GitHub disallows self-approval). One action item carried to the top of the stack: monotonic-clock durations.


Generated by Claude Code

@codenamev codenamev merged commit 518f681 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