Repository navigation
Research digest: the plan-as-data bet is uncontested in Ruby, and roast just vacated the lane #23
Description
Survey of agentic's nearest neighbors: the Ruby agent gems (langchainrb, regent, sublayer), the Ruby LLM-layer gems (ruby_llm, raix), the closest conceptual competitor (Shopify's roast), the Rails one (active_agent), and the plan-and-execute frontier outside Ruby (LangGraph, OpenAI Agents SDK). Feature-level comparison against what this gem ships today, fenced by WORLD.md. Three findings, five integration candidates, four recorded non-candidates.
Finding 1: the plan-as-data bet is uncontested, and roast just vacated it
No Ruby framework ships LLM-generated inspectable plans, dependency-graph orchestration, durable journaling, or learning from execution history. langchainrb's Assistant is a conversational tool loop; regent is ReAct; sublayer is single-call generators plus triggers. The closest thing to competition was roast v0.x, whose killer feature was exactly ours: "every workflow execution automatically saved, resume from any step."
Then roast v1.0 (2026-02) rewrote workflows from YAML into a pure Ruby DSL and durable workflow replay did not survive — code search on the current repo returns zero hits for it (releases). Their plans are now code: a human can edit them, but no runtime or LLM can inspect, mutate, or replan one mid-flight. That is the precise gap WORLD.md's Direction ("the plan is the product") targets, and the best-funded neighbor just walked away from it. The lane is open.
Finding 2: the ecosystem's failure mode is provider-parity decay, and the small-core charter is the moat
Of the three Ruby agent frameworks, two are dormant (regent: no commits ~14 months; sublayer: same) and the third can't cut a release (langchainrb: active commits, but the last gem shipped 2025-05, with an open issue literally titled "Is this repo still maintained?"). The dominant open-issue theme across all three is provider-parity gaps: an 11-provider feature matrix is a treadmill that eats maintainers.
New evidence for the already-open provider question (#22, not re-raised here): raix v2.0 deleted its own provider layer and now delegates entirely to ruby_llm (changelog). Its 2.0.2–2.0.6 patch run (dropped multimodal transcripts, unenforced tool budgets, lost schema richness) is a free checklist of what breaks at a translation boundary. The Ruby ecosystem is consolidating on ruby_llm as the provider abstraction. That strengthens #22's option 3 and argues even harder against a third homegrown multi-provider matrix.
Finding 3: the frontier invests in cheap recovery, not better initial plans
LangGraph's canonical plan-and-execute is an execute→replan loop; its checkpoints support rewind, human plan-editing, and forked branches (interrupts). OpenAI's Agents SDK makes paused runs a serializable RunState that resumes with full history (HITL docs). The durable-execution camp's critique of both (diagrid): checkpoints that die with the process aren't durability. Agentic's ExecutionJournal already survives the process; what's missing is everything downstream of that fact (#21). The bet is right. The payoff is unclaimed.
Integration candidates (patterns to borrow, not dependencies)
C1: Replan-on-failure (LangGraph pattern). On task failure, feed the plan plus journal-so-far back to the LLM and regenerate the remaining plan as a new plan version. Because agentic's plans are data, this is possible here and structurally impossible in roast — the highest-leverage differentiator found. Fence: squarely Direction ("better failure recovery"); depends on graph-capable plan serialization (#20). Medium cost: plan versioning plus one orchestrator hook.
C2: Per-task failure-tolerance flags (roast pattern). continue_on_failure, tolerated-error predicates, and a succeeded?(task) accessor downstream tasks can branch on. Roast proves the Ruby ergonomics (no_abort_on_failure!). Fence: small orchestrator/task-schema change, no new deps; pairs naturally with #10's stranded-dependents fix. Cheap.
C3: Serializable paused-run state (OpenAI RunState pattern). Make "paused awaiting human approval or plan edit" a first-class persistable orchestrator state, so edit-the-plan-then-resume becomes the workflow. Extends #21's resume from crash recovery to deliberate human-in-the-loop. Fence: builds on the journal and store we already have; the diagrid survive-process-death critique is the acceptance test. Medium cost.
C4: Span read-model over the journal (regent pattern). Group journal entries into typed spans (llm_call, tool_call, task) with per-span tokens and duration, rendered hierarchically. Regent's session tracing was its one standout feature. Fence: Direction ("better execution history"); read-model only, no journal format change required. Moderate cost.
C5: Task-execution callback (langchainrb pattern). One documented hook on the orchestrator's task boundary buys user-wired tracing, redaction, and test observation without any abstraction. raix's before_completion is the same idea at the LLM boundary. Near-free.
Non-candidates (recorded so future loops don't re-raise them)
- Multi-provider feature matrix: the documented cause of death for half this survey; Provider strategy: the researched proposal WORLD.md asks for (measured coupling, three options, one lean) #22 owns the actual decision.
- Trigger daemons / hosted gravity (sublayer): an anti-goal (not a hosted platform), and speculative until a concrete use exists.
- Rails MVC agent layer (active_agent): different category; complementary (they do request/response, we do planning/execution), not something to absorb.
- MCP client (raix): integration-store gravity; capabilities keep the core small. Revisit only if a real consumer asks.
Origin: loop:research session 2026-08-23. L0 analysis only; nothing here lands without its own proposal. Full vitals: ruby_llm 1.16.0 (~11.5M downloads), raix 2.0.6, langchainrb 0.19.5, regent 0.3.4, sublayer 0.2.9, roast-ai 1.2.0 (~300K downloads), active_agent 1.3.1.
Sources: Shopify/roast · crmne/ruby_llm · OlympiaAI/raix · patterns-ai-core/langchainrb · alchaplinsky/regent · sublayerapp/sublayer · activeagents/activeagent · LangGraph interrupts · OpenAI Agents SDK HITL · diagrid on durable execution
Activity
minerva-sky commentedon Sep 15, 2026
agentic#23 follow-up: C5 task-boundary callback + C2 failure-tolerance flags
Date: 2026-09-14. Bean task-6936e519. Read against
mainat 847a565 (#34 merged) plus the #35/#37/#38 stack.C5: task-boundary callback — already shipped, undocumented
PlanOrchestrator#initializetakeslifecycle_hooks:with seven callable no-op defaults, all at task boundaries:before_task_execution,task_slot_acquired,before_agent_build,after_agent_build,after_task_success,after_task_failure,plan_completed. Each receives keyword args (task_id, task, result/failure, duration).Taskis alsoObservableand emits:status_changeand:failure_occurred. That is strictly more than the candidate asked for (langchainrb has one callback; raix hasbefore_completion).The gap is documentation: README references
lifecycle_hooksexactly once, as an argument the learning system builds for you (line ~710). A user wiring tracing, redaction, or a test observer has to read the orchestrator source to learn the hook names and signatures.Verdict: no new code. One docs PR adding a "Lifecycle hooks" section to README (table of the seven hooks, their kwargs, the inline-on-fiber caveat already in the source comment, and a 10-line tracing example). Hooks are stable public API today, so document as such.
C2: per-task failure tolerance — not present, cheap, one PR
Today's failure path in
handle_task_failure: retry if policy says so, request intervention for auth/permission/config errors, otherwise terminal. Since #34 a terminal failure moves every transitive dependent to:skippedwith aTaskExecutionResult.skippednaming the failed dependency. There is no way to say "this task is allowed to fail; run its dependents anyway."Proposed shape, additive like #35:
TaskDefinitiongainson_failure:with values"skip_dependents"(default, today's behaviour) and"continue". Serialized into_honly when non-default, parsed infrom_hash, added to the planner's strict schema as an enum with one numbered prompt instruction ("set on_failure: continue only for tasks whose result is optional to the tasks after them"). Plan-as-data means the LLM can set it, a human can edit it, and a replan (C1) can flip it.add_task(task, deps, agent:, needs:, on_failure:)records the policy per task id.all_dependencies_met?treats a dependency as met when it is:completedOR (:failedAND its policy iscontinue). Inhandle_task_failure, the tail becomes: if terminal and policy iscontinue, callschedule_dependent_tasksinstead ofskip_dependents_of. Intervention-required failures never continue; a human is already in the loop.Task#succeeded?(task_or_id)andTask#failure_of(task_or_id), fed by a newrecord_dependency_failurenext torecord_dependency_output.output_ofkeeps returning nil for a failed dependency, so existing tasks that ignore the flag see no change.NamedOutputsgets the same pair forneeds.overall_statusstays:partial_failurewhen any task failed, tolerated or not. Hiding a tolerated failure from the plan result would be lying to the journal.tolerate: ["TimeoutError"]). The retry policy already has a type list; add a per-tasktolerated_errorsonly when a consumer asks. Also not in v1: a plan-level default. Per-task is enough and keeps the schema flat.Size: ~80 LOC across task_definition, task, plan_orchestrator, task_planner (schema + prompt line), plus ~12 specs (flag round-trips, dependent runs after tolerated failure, transitive case where a skipped-policy task sits between, intervention short-circuit, status stays partial_failure). One PR, one tick.
Filed
feature/cli-wires-dependency-graphuntil Plan format: dependency graph fields + validation (1/3 for #20) #35/Planner emits dependency edges and output wiring (#20, part 2 of 3) #37/CLI wires the plan's dependency graph into the orchestrator (#20, part 3 of 3) #38 merge, sincefrom_hashand the schema moved there).minerva-sky commentedon Oct 15, 2026
Drain-loop status, 2026-10-14. Of the accepted lean (C5 + C2 this cycle): C5 turned out to already ship as
lifecycle_hooks:, documented in #39; C2 is #40 (on_failure: continue), stacked on #38. Both await merge. The rest of the digest is sequenced on open PRs: C1 (replan-on-failure) waits on the #20 series (#35, #37, #38); C3 (serializable paused-run state) extends the resume path now in the #21 PR; C4 (span read-model) waits on #46's token threading in #52. Nothing further is drainable from this issue until those merge, so the loop is leaving it alone rather than stacking a fourth PR on unmerged bases.