Skip to content

Core Concepts

This page defines the core abstractions in Trurlic: the decision graph, the advance loop, context briefs, and patterns. For the higher-level motivation, see the Overview. For the module layout and thread model, see Architecture.

Trurlic’s foundation is a typed decision graph stored in .trurlic/ at your project root. The graph contains four node types and five edge types.

Components — the architectural building blocks of your system. Each component is a TOML file in .trurlic/components/. Components represent logical boundaries: auth, database, rate-limiter, api-gateway. They are containers for decisions, not code modules — one component may span multiple files or packages.

Decisions — individual architectural choices. Each decision records what was chosen, why, what alternatives were considered and rejected, who made the decision (human or agent), and when. Decisions are TOML files in .trurlic/decisions/.

Patterns — cross-cutting rules synthesized from multiple related decisions. A pattern is a reusable constraint that applies across components. For example, if three components all use Redis for state, the pattern state-in-redis captures that shared approach with a single rule. Patterns are TOML files in .trurlic/patterns/.

Project — the root node. Project-wide decisions (rules) are attached to the special project component. These are inviolable constraints that apply to every component.

EdgeMeaning
BelongsToDecision → Component. Every decision belongs to exactly one component (or project).
ConnectsToComponent → Component. Directional data/control flow. Related decisions from connected components appear in context briefs.
DependsOnDecision → Decision. Logical dependency — the dependent decision assumes the parent holds.
ConstrainsDecision → Decision. This decision restricts the choices available to another.
SupersedesDecision → Decision. Replaces a prior decision. The old decision is marked superseded but preserved for history.

graph.toml is a compiled edge index — a derived file rebuilt deterministically from node files. It maps every node to its edges and stores BLAKE3 content hashes for integrity. You can hand-edit any TOML node file and run trurlic check to reconcile the index.

The advance loop is the orchestration hub for multi-step workflows. When an AI coding agent needs to work on a component, it calls advance() repeatedly until the workflow completes.

Agent calls advance(component, task_type)
┌─────────────┐
│ Workflow │ ← computes next step from graph state
│ Engine │ ← pure function, no I/O, no LLM calls
└──────┬──────┘
{ step, action, requires_user_input, ready }

advance() is a pure function of graph state plus inputs. Same inputs produce the same output, always. It never calls an LLM, never touches the filesystem, never allocates beyond the response. The state machine checks preconditions against the current graph to determine what step comes next.

Seven task types, each with a distinct step sequence:

Task typePurposeGated steps
new_componentBuild a component from scratchYes — scope, concerns, summary
featureAdd functionality to an existing componentYes — constraints, concerns, summary
fixApply a bugfix within existing constraintsConditional — constraints, impact
learnStudy existing decisions before implementingYes — explanation, code analysis, summary
reviewChallenge decisions for drift or stalenessYes — walkthrough, drift, audit, summary
hardenStrengthen coverage for uncovered concernsYes — coverage audit, concerns
bootstrapAutonomous project scan from source codeNo — fully autonomous

For the complete step sequences and gating rules, see Workflow Engine.

Some steps are gated — they require evidence of human involvement before the workflow progresses. A gated step returns requires_user_input: true. The agent must present the step’s prompt to the user, collect their response (minimum 20 bytes of meaningful content), and pass it back as step_evidence on the next advance() call.

This is how Trurlic enforces human authorship of decisions. The agent facilitates the conversation; the human makes the architectural choices.

A context brief is the structured output of get_context(component). It is the authoritative document that constrains code generation. A brief contains:

Rules — project-wide decisions marked as inviolable. Every line of generated code must respect them. Rules appear at the top of every brief regardless of which component is queried.

Component decisions — the specific architectural choices for this component. Each decision includes the choice, reason, and attribution.

Patterns — cross-cutting constraints that apply to this component.

Related decisions — relevant decisions from connected components. If auth connects to rate-limiter, the rate limiter’s brief includes auth decisions that affect its behavior.

Override policy — rules for how agents should handle conflicts. Rules are inviolable. Component decisions are strong defaults — follow them unless the user explicitly revises them via design. The agent must never silently deviate.

get_context supports two depth modes:

  • full (default) — complete context with reasoning, alternatives, and related decisions. Used before implementation starts.
  • constraints — choice text only, compact brief, no related decisions. 60–70% fewer tokens. Used for mid-implementation compliance checks.

Trurlic tracks ten architectural concern areas and matches them against decision content via keyword analysis. Concerns are priority-ordered — security gaps surface before stylistic ones.

  1. Security boundaries
  2. Error handling & failure modes
  3. Concurrency & locking
  4. Integrity & validation
  5. Performance constraints
  6. External interfaces & APIs
  7. Storage & persistence
  8. Data format & serialization
  9. Dependencies & coupling
  10. Migration & versioning

During design conversations and the advance loop, uncovered concerns are flagged so the developer can address them before implementation begins. When a task description is provided, concern coverage is filtered by keyword relevance to the task, focusing the conversation on what matters.

A pattern is a named synthesis of multiple related decisions into a reusable rule. Patterns emerge when the same approach appears across multiple components.

For example, if auth, rate-limiter, and session all decide to use Redis for state, the pattern state-in-redis captures the shared constraint: “shared pool via app state, no per-component connections.” Future components that need state get this pattern in their brief automatically.

Patterns require at least two source decisions. They are recorded via record_pattern (MCP) or detected automatically by the workflow engine during pattern_detection steps.

Every decision carries an attribution field: user (a human was present and authored the choice) or agent (the agent recorded it autonomously, e.g., during bootstrap). Agent-attributed decisions are flagged with ⚠ in context briefs and the interactive map, signaling that a human should review them.

For the module layout, thread model, and data flow, see Architecture. For the integrity guarantees on writes, see Integrity Model. For the full MCP tool surface, see MCP Tools Reference.