Audience: experienced software engineers who want a precise mental model of how Recurgent works today and how to contribute without violating project philosophy.
This is a guided walk from first principle to current runtime behavior.
- Start with mission and one concrete example.
- Learn the smallest core mechanism (
method_missing+ generated code). - Add one architectural layer at a time.
- Tie each layer to why it exists (ADR intent) and how it is implemented (runtime components).
- End with contribution patterns that match Recurgent tenets.
If you only have 20 minutes: read Chapters 0, 1, 5, 8, 10, 11.
Recurgent is an agent runtime where capability is discovered and hardened through use, not fully predeclared.
assistant = Agent.for("personal assistant that remembers conversation history")
assistant.ask("What's the top news from Google News, Yahoo, and NYT?")
assistant.ask("What are action-adventure movies in theaters?")
assistant.ask("What's a good recipe for Jaffna Kool?")What matters is not "did it answer once?" but:
- Did it delegate correctly?
- Did it return typed outcomes?
- Did it preserve evidence/provenance when required?
- Did stable behavior get promoted and brittle behavior get repaired?
That is the runtime's core mission: reliable emergence.
Make unknown domain methods callable without pre-registering every method.
Conventional frameworks require predefined tool schemas and static interfaces.
ADR 0001 establishes method_missing as the dynamic boundary.
Before (static interface world):
class Calculator
def add(x, y)
x + y
end
endAfter (Recurgent runtime surface):
calc = Agent.for("calculator")
calc.add(3)
calc.sqrt(144)
calc.solve("2x + 5 = 17")The runtime intercepts unknown calls, prompts a model, validates generated code, executes it, then returns Agent::Outcome.
Keep Agent host methods narrow so domain namespace stays dynamic.
Reserved host/runtime methods include tool, delegate, remember, runtime_context, to_s, inspect.
Keep dispatch logic provider-agnostic and failure semantics predictable.
Without a provider abstraction and typed errors, runtime behavior becomes vendor-coupled and brittle.
- ADR 0002: provider abstraction + model routing.
- ADR 0003: typed
Outcomeenvelope (ok/error,error_type,error_message, retriable flag).
Before (opaque failures):
result = agent.ask("...")
# could be raw string or exceptionAfter (typed boundary):
outcome = agent.ask("...")
if outcome.ok?
puts outcome.value
else
warn "#{outcome.error_type}: #{outcome.error_message}"
endEvery higher-level layer (guardrails, contracts, retries, promotion) assumes this typed boundary.
Align runtime semantics with how agents actually solve problems.
"Orchestrator/worker" language underspecifies intent and encourages process-centric designs.
ADR 0008 defines canonical UL:
Tool Builder: top-level problem owner.Tool: delegated sub-capability agent.Delegate: one Tool Builder -> Tool action.Outcome: normalized result envelope.Synthesis: Tool Builder reconciliation step.
This language appears across prompts, logs, docs, and plans.
Support multiple runtimes without semantic drift.
As Ruby and Lua evolve, implementation details can diverge from behavior contract.
- ADR 0006: monorepo runtime boundaries (
runtimes/ruby,runtimes/lua). - ADR 0007: runtime-agnostic contract package under
specs/contract/v1.
Important split:
specs/contract/: normative behavior contract and scenarios.docs/product-specs/: product/design specifications.
This separation is critical: implementation parity belongs to specs/; product intent belongs to docs/.
Allow generated tools to use dependencies safely and deterministically.
Stdlib-only generation is too limiting; in-process gem mutation is unsafe and non-deterministic.
- ADR 0010: generated program contract includes
code+dependencies. - ADR 0011: deterministic
env_idand effective-manifest execution rules. - Implementation plan: worker isolation + JSON boundary.
Before:
# generated code assumed stdlib onlyAfter:
{
"code": "...",
"dependencies": [
{ "name": "nokogiri", "version": "~> 1.18" }
]
}Execution path:
- no deps -> sandbox execution.
- deps -> worker execution in environment keyed by manifest/policy.
Make capabilities survive sessions and evolve based on evidence.
Without persistence, each restart relearns the same tools.
- ADR 0012: cross-session tool registry + artifact store.
- ADR 0013: cacheability gating + pattern memory.
Key idea:
- Persistence identity remains stable (
role + method). - Reuse is separately gated by cacheability/policy.
This prevents semantic cache poisoning (e.g., reusing query-specific logic for unrelated tasks).
Guarantee that delegation boundaries are structurally honest and evolution-friendly.
"Looks successful" can still be invalid at integration boundaries.
- ADR 0014: delegated outcome contract validation + tolerant canonicalization.
- ADR 0015: boundary referrals (
wrong_tool_boundary,low_utility) + evolution signals. - ADR 0017: runtime remains observational for utility semantics (no heuristic success->error rewriting).
- ADR 0021: external-data successes must include provenance.
- ADR 0022: normalize exhausted guardrail message at user boundary while preserving internal diagnostics.
Before (shape drift leaks):
# caller assumes a shape that may drift silently
headlines = tool.get_headlines.value[:items]After (contract boundary):
outcome = tool.get_headlines(...)
# contract validator may map mismatch to contract_violationMake regeneration safe: no leaked partial state, bounded retries, diagnosable failures.
Pre-ADR behavior could fail fast on recoverable guardrails and leak failed-attempt mutations.
ADR 0016 + follow-on telemetry plan:
- Generate -> validate -> execute lifecycle.
- Attempt isolation with commit-on-success.
- Bounded guardrail/execution/outcome repair lanes.
- Persist failed-attempt diagnostics even when later retries succeed.
Sequence (simplified):
flowchart TD
G[Generate] --> V[Validate]
V -->|recoverable violation| R[Regenerate with feedback]
R --> G
V --> X[Execute]
X -->|runtime error| RE[Execution repair]
RE --> G
X --> O[Outcome policy]
O -->|retriable non-ok| OR[Outcome repair]
OR --> G
O --> C[Contract validation]
C --> S[Success and persist]
Improve long-context behavior without premature recursion primitives.
Need stronger context access, but recursive APIs were premature.
- ADR 0018 proposed recursive primitives (
ContextView,recurse). - ADR 0019 deliberately deferred recursion and shipped structured conversation history first.
Pragmatic consequence: history is first-class runtime data (context[:conversation_history]) and observable in traces.
Turn "tool seems good" into explicit, auditable lifecycle policy.
Reliability evidence existed but was diffuse and not connected to one canonical promotion contract.
Solution (ADR 0023)
- Capture
solver_shapeas first-class telemetry (stance,capability_summary,reuse_basis,contract_intent,promotion_intent). - Maintain version-scoped scorecards.
- Lifecycle states:
candidate -> probation -> durable -> degraded. - Promote only when reliability gates pass.
Important principle:
Prompt policy remains open-ended; typed solver shape captures decisions as data.
# conceptually
prompt_policy -> decides
solver_shape -> records
promotion_policy -> gates lifecyclePrevent sibling-method drift for role-style agents.
Reliability can be high even when methods disagree on shared state conventions (:memory vs :value).
Solution (ADR 0024)
- Opt-in
RoleProfilecontracts. - Default coordination constraints (agreement required, value not pre-pinned).
- Optional prescriptive constraints for deterministic pinning.
- Scope-first model (
all_methodsdefault) so newly forged methods inherit constraints. - Violations route through existing recoverable lanes as
role_profile_continuity_violation.
Before:
# add uses :value, memory setter uses :memory
# both can appear successfulAfter:
CALCULATOR_ROLE_PROFILE = {
role: "calculator",
version: 1,
constraints: {
accumulator_slot: {
kind: :shared_state_slot,
mode: :coordination
# scope defaults to :all_methods
}
}
}Profiles define semantic correctness; scorecards define reliability. You need both.
Expose self-awareness without permitting hidden self-mutation.
Without an explicit authority boundary, reflective behavior can drift into uncontrolled policy mutation.
Solution (ADR 0025)
Design rule: separate awareness from authority.
- Awareness levels:
L1observational,L2contract-aware,L3evolution-aware. L4autonomous policy mutation explicitly excluded.- Authority tuple:
observe,propose,enact(defaultenact: false). - Proposal artifacts + maintainer-approved apply/rollback for governance changes.
self_model = agent.self_model
# { awareness_level: :l2, authority: { observe: true, propose: true, enact: false }, ... }flowchart LR
A[Caller Method] --> B[Dynamic Dispatch]
B --> C[Prompt/Provider]
C --> D[Generated Program]
D --> E[Validation + Guardrails]
E --> F[Sandbox or Worker Execution]
F --> G[Outcome Contract Boundary]
G --> H[Persistence + Scorecards]
H --> I[Observability + History]
I --> J[Promotion + Profile/Governance Signals]
classDef core fill:#d9f2ff,stroke:#1b6ca8,stroke-width:1px;
classDef safety fill:#ffe8cc,stroke:#b35c00,stroke-width:1px;
classDef evolution fill:#e6ffe6,stroke:#2d7a2d,stroke-width:1px;
class A,B,C,D core;
class E,F,G safety;
class H,I,J evolution;
Layer read order:
- Core call machinery (
method_missing, provider, execution). - Safety boundaries (guardrails, retries, contract validation, normalization).
- Evolution loop (persistence, scorecards, promotion, role profiles, authority governance).
The runtime has multiple infrastructure primitives. They are orthogonal by design: each has a narrow job and composes with others.
| Primitive | Purpose | How it works | Primary code |
|---|---|---|---|
| Dynamic Dispatch | Make unknown domain methods executable without predeclared interfaces. | method_missing routes unresolved calls into generate -> validate -> execute lifecycle. |
runtimes/ruby/lib/recurgent.rb, runtimes/ruby/lib/recurgent/call_execution.rb |
| Outcome Boundary | Keep success/failure semantics typed and composable across delegation. | Every return is coerced into Agent::Outcome; callers consume ok?/error?, value, error_type, retriable. |
runtimes/ruby/lib/recurgent/outcome.rb |
| Contract Validation | Prevent silent shape drift at delegation boundaries. | Delegated outputs are checked against declared contracts with tolerant canonicalization before acceptance. | runtimes/ruby/lib/recurgent/outcome_contract_validator.rb |
| Guardrails + Repair Lanes | Recover from common generation/runtime failures without leaking partial state. | Validation-first retries, bounded repair budgets, and retry feedback injection across code/outcome lanes. | runtimes/ruby/lib/recurgent/fresh_generation.rb, runtimes/ruby/lib/recurgent/fresh_outcome_repair.rb, runtimes/ruby/lib/recurgent/guardrail_code_checks.rb |
| Attempt Isolation | Ensure failed attempts do not pollute committed runtime state. | Snapshot-and-rollback around attempts; commit only on successful terminal attempt. | runtimes/ruby/lib/recurgent/attempt_isolation.rb |
| Execution Isolation | Prevent generated methods from leaking onto host agent surface. | Generated code executes inside disposable ExecutionSandbox receivers. |
runtimes/ruby/lib/recurgent/execution_sandbox.rb, runtimes/ruby/lib/recurgent.rb |
| Dependency Environment | Run dependency-bearing generated code deterministically and safely. | Dependency manifests map to environment IDs; worker execution for dependency paths. | runtimes/ruby/lib/recurgent/dependencies.rb, runtimes/ruby/lib/recurgent/worker_execution.rb |
| Continuity Infrastructure | Preserve coherence across turns/sessions in multiple dimensions. | Four layers: state (context), event (conversation_history), executable (artifacts/tools), response-content (content_ref + content store). |
runtimes/ruby/lib/recurgent/conversation_history.rb, runtimes/ruby/lib/recurgent/content_store.rb, runtimes/ruby/lib/recurgent/artifact_store.rb, runtimes/ruby/lib/recurgent/tool_store.rb |
| Persistence Infrastructure | Retain tool/artifact identity and evidence across sessions. | Tool registry + versioned artifact store + metadata/scorecards persisted under toolstore root. | runtimes/ruby/lib/recurgent/tool_store.rb, runtimes/ruby/lib/recurgent/artifact_store.rb |
| Promotion Infrastructure | Convert observed reliability into explicit lifecycle transitions. | Scorecards + policy gating over candidate -> probation -> durable -> degraded. |
runtimes/ruby/lib/recurgent/artifact_metrics.rb, runtimes/ruby/lib/recurgent/artifact_selector.rb |
| Role Coherence Infrastructure | Enforce sibling-method semantic coherence for role-like agents. | Opt-in role profiles with coordination/prescriptive constraints evaluated through recoverable guardrail lanes. | runtimes/ruby/lib/recurgent/role_profile_guard.rb, runtimes/ruby/lib/recurgent/role_profile_registry.rb |
| Awareness Infrastructure | Expose runtime self-model as data for inspection and proposal quality. | Call state captures awareness level, authority tuple, active contract/profile versions, and snapshot refs. | runtimes/ruby/lib/recurgent/call_state.rb |
| Authority + Governance Infrastructure | Separate propose from enact; keep mutations auditable and approved. | Proposal artifacts, approval/apply workflow, maintainer-gated mutation paths. | runtimes/ruby/lib/recurgent/proposal_store.rb, runtimes/ruby/lib/recurgent/authority.rb, runtimes/ruby/lib/recurgent.rb |
| Observability Infrastructure | Make runtime behavior mechanically inspectable and testable from traces. | JSONL logs capture prompts, outcomes, attempts, contracts, continuity, and lifecycle fields. | runtimes/ruby/lib/recurgent/observability.rb, docs/observability.md |
Reading rule of thumb:
- If you are changing execution mechanics, touch dispatch + guardrail + observability together.
- If you are changing semantic correctness, touch role profiles/contracts before promotion policy.
- If you are changing policy, route mutation through authority-governed proposal paths.
The architecture is cumulative, not monolithic.
0001-0003: dispatch, provider abstraction, typed outcomes.0004-0009: coordination language, naming, monorepo boundaries, contract packaging, contribution governance.0010-0013: dependency/runtime environment + persistence + cacheability/pattern memory.0014-0017: boundary validation + utility/semantics pressure model + validation-first recovery.0018-0022: context-history prioritization, sandbox isolation, provenance invariant, boundary normalization.0023-0025: solver-shape evidence, reliability-gated promotion, role continuity contracts, awareness/authority substrate.
Implementation plans operationalize each ADR with phased deltas, validation signals, and rollback triggers.
When you contribute, match the project's development philosophy:
- Agent-first mental model.
- Tolerant interfaces by default.
- Introspection/prescription/evolution ergonomics over process ceremony.
- Separate awareness from authority.
- Identify architectural layer touched (dispatch, boundary, persistence, evolution, governance).
- Update ADR/plan if behavior contract changes.
- Implement with typed outcomes and explicit observability fields.
- Validate with tests plus trace-level evidence.
- Keep change sets atomic and narratively coherent.
- Does this shrink dynamic namespace unnecessarily?
- Does this preserve typed
Outcomesemantics? - Does this keep non-profile flows stable unless explicitly changed?
- Are retries/guardrails bounded and diagnosable?
- Is user boundary normalized while internal diagnostics remain rich?
- If mutation/governance is involved, is authority gating explicit?
Engineer: "Can we just hardcode this one method?"
Recurgent: "Or we can define the boundary, emit evidence, and evolve the capability class."
Which pair is correct?
- scorecard defines semantics, role profile defines reliability
- scorecard defines reliability, role profile defines semantics
- both are interchangeable
Why is role_profile_continuity_violation routed through recoverable lanes instead of a brand new subsystem?
- to reuse existing retry/repair policy and telemetry semantics
- because profile checks are optional and should never be visible
- because guardrails cannot carry typed violations
What does "separate awareness from authority" enforce?
- agent cannot observe runtime state
- agent can propose, but enact requires explicit approval
- agent can always self-mutate if it passes scorecard gates
Answers:
- Quiz 1 -> 2
- Quiz 2 -> 1
- Quiz 3 -> 2
runtimes/ruby/lib/recurgent.rb(dynamic surface and primitives)runtimes/ruby/lib/recurgent/call_execution.rb+fresh_generation.rbruntimes/ruby/lib/recurgent/outcome.rb+outcome_contract_validator.rbruntimes/ruby/lib/recurgent/artifact_store.rb+artifact_selector.rb+tool_store.rbruntimes/ruby/lib/recurgent/role_profile.rb+role_profile_guard.rb+role_profile_registry.rbruntimes/ruby/lib/recurgent/authority.rb+proposal_store.rb+call_state.rbruntimes/ruby/lib/recurgent/observability.rb+observability_attempt_fields.rb
Then cross-check with:
docs/architecture.mddocs/ubiquitous-language.mddocs/adrs/0023-solver-shape-and-reliability-gated-tool-evolution.mddocs/adrs/0024-contract-first-role-profiles-and-state-continuity-guard.mddocs/adrs/0025-awareness-substrate-and-authority-boundary.mddocs/plans/contract-first-role-profiles-state-continuity-implementation-plan.mddocs/plans/awareness-substrate-authority-boundary-implementation-plan.md
Use this as the architecture timeline index.
| ADR | Core decision |
|---|---|
| 0001 | Dynamic dispatch via method_missing is the runtime core. |
| 0002 | Provider abstraction and model routing are decoupled from runtime logic. |
| 0003 | Failures are typed through Outcome instead of opaque exceptions/strings. |
| 0004 | Coordination API and language become LLM-native (for, delegate, remember). |
| 0005 | Naming hard cut to Recurgent. |
| 0006 | Monorepo runtime partitioning (runtimes/ruby, runtimes/lua). |
| 0007 | Runtime-agnostic normative contract package under specs/contract/v1. |
| 0008 | Tool Builder/Tool UL and tolerant delegation interface become canonical. |
| 0009 | Issue-first PR compliance is enforced as repository governance. |
| 0010 | Generated programs can declare dependencies; environment contract introduced. |
| 0011 | Deterministic environment identity and effective-manifest execution policy. |
| 0012 | Cross-session tool/artifact persistence and evolutionary selection policy. |
| 0013 | Cacheability gating and pattern memory for safe reuse/promotion pressure. |
| 0014 | Delegated outcome contract validation and tolerant boundary canonicalization. |
| 0015 | Boundary referral signals (wrong_tool_boundary, low_utility) + evolution lanes. |
| 0016 | Validation-first fresh generation with transactional retries and rollback. |
| 0017 | Runtime remains observational for utility semantics; no heuristic coercion. |
| 0018 | Recursive context primitives proposed (ContextView, recurse). |
| 0019 | Structured conversation history shipped first; recursion deferred. |
| 0020 | Per-attempt execution sandbox isolates generated code from host method surface. |
| 0021 | External-data success requires provenance evidence. |
| 0022 | Guardrail retry exhaustion is normalized at user boundary. |
| 0023 | Solver shape and reliability-gated promotion lifecycle become explicit. |
| 0024 | Opt-in role profiles and state continuity guard for role coherence. |
| 0025 | Awareness substrate + authority boundary (observe/propose/enact separation). |
Note: ADR status markers may lag implementation. For "what is live," use runtime code + validation reports as source of truth.
docs/plans/recurgent-implementation-plan.mddocs/plans/dependency-environment-implementation-plan.mddocs/plans/cross-session-tool-persistence-implementation-plan.mddocs/plans/cacheability-pattern-memory-implementation-plan.mddocs/plans/solver-shape-reliability-gated-tool-evolution-implementation-plan.md
docs/plans/outcome-boundary-contract-validation-implementation-plan.mddocs/plans/tool-self-awareness-boundary-referral-implementation-plan.mddocs/plans/validation-first-fresh-generation-implementation-plan.mddocs/plans/contract-driven-utility-failures-implementation-plan.mddocs/plans/guardrail-exhaustion-boundary-normalization-implementation-plan.mddocs/plans/contract-first-role-profiles-state-continuity-implementation-plan.md
docs/plans/structured-conversation-history-implementation-plan.mddocs/plans/generated-code-execution-sandbox-isolation-implementation-plan.mddocs/plans/external-data-provenance-implementation-plan.mddocs/plans/failed-attempt-exception-telemetry-implementation-plan.mddocs/plans/awareness-substrate-authority-boundary-implementation-plan.md
Planning discipline across this repo is not generic PM ceremony: each plan is expected to define measurable deltas, non-improvement expectations, phase-level improvement contracts, and rollback triggers.
Recurgent is not "LLM writes code". It is a layered runtime where:
- dynamic dispatch enables emergence,
- typed boundaries keep behavior honest,
- persistence and scorecards make behavior durable,
- contracts enforce role coherence where reliability alone is insufficient,
- governance keeps reflective evolution auditable and controlled.
If your change strengthens one layer without violating the others, it is probably aligned with project philosophy.