Skip to content

Architecture Overview

0xfauzi edited this page Jul 21, 2026 · 5 revisions

Architecture Overview

This page is the orientation layer. The canonical deep dive is ARCHITECTURE.md; the adversarial role taxonomy with invariants and known limitations is docs/adversarial-design.md.

The factory flow

A run starts from a markdown spec (or a SpecKit artifact directory, or a hand-written prd.json for single-component runs):

  1. Architect / PRD red-team (LLM). Decomposes the spec into a component DAG with per-component PRDs and EARS-style acceptance criteria. Halts on blocker-severity spec issues (SpecBlockerError) - there is no override flag; the spec gets fixed, not the gate.
  2. Per component, in DAG order (sequential or parallel, each in an isolated git worktree cut from origin/<base>):
    • Phase 0 - Feedforward (mechanical): module map, public interfaces, import graph, conventions - context assembled without token cost.
    • Knowledge prefix: durable facts from prior runs injected into the engineer's context.
    • Engineer (LLM): iterates until complete; must emit a ## Self-Critique block.
    • Phase 1 - Mechanical verify (no LLM): tests, typecheck, lint, diff-scope, bad-patterns; optional dead-code, mutation, fixtures.
    • Phase 2 - Reviewer (LLM): PRD-criterion verdicts plus concern categories (scope creep, test quality, security concern, ...).
    • Phase 2.5 - Security (LLM): OWASP/CWE-mapped threat review.
    • Knowledge distiller (LLM): writes durable facts to disk before the PR merges, preserving the true-delta invariant.
    • E6 checkpoint (human, optional): pause before PR merge.
    • PR create + merge via gh.
  3. Phase 3 - Contract testing (mechanical): merged tier branches run cross-component integration tests; a failing tier bisects to the breaking component and re-runs it.
  4. Evolution journal: every component outcome, error signature, cost, and finding summary is recorded.

Phase numbers are sticky by convention: new phases take fractional numbers so ordering semantics never shift. The planned release stage (R8.7) is Phase 4.

The eight roles

Role Kind Catches
Architect / PRD red-team LLM Spec ambiguity, missing failure modes, unstated assumptions
Engineer LLM Implements one story per iteration
Mechanical verifier No LLM Test/type/lint failures, diff-scope violations, bad patterns
Code reviewer LLM PRD violations, scope creep, test quality
Security reviewer LLM OWASP-mapped vulnerability categories
Contract tester No LLM Cross-component integration breaks
Knowledge distiller LLM Durable facts worth carrying to future runs
Human checkpoint Human Whatever the machines missed (optional, config-gated)

The events substrate

The filesystem is the event bus. Every run writes .kstrl/runs/<run_id>/events.jsonl - schema-versioned, typed events (~38 kinds: component_started, verification_result, pr_merged, circuit_breaker_tripped, ...). Decoding is total: unknown events degrade losslessly, torn tail lines are skipped, and sinks are observability, never control flow.

Every surface is a projection of that stream: the Textual dashboard (ks dash, the bare-ks home shell), ks status for scripts and CI, the optional one-way Linear mirror. Nothing is displayed that cannot be reconstructed from the run directory.

Run IDs are kind-prefixed (factory-, decompose-, feature-, understand-) and sort chronologically across kinds.

State layout

.kstrl/
  runs/<run_id>/events.jsonl     # the event stream (the record)
  runs/<run_id>/components/<id>/ # per-component logs and phase evidence
  knowledge/<component>/<run>/   # durable facts (markdown, per-fact files)
  evolution.jsonl                # component outcomes, error signatures
  experiments.tsv                # metrics for trend analysis
  worktrees/<run>/<component>/   # isolated build workspaces
  factory.lock                   # single-run flock

Agents and adapters

Four adapters: claude-code (CLI), codex (CLI), claude-sdk (optional extra, in-loop budget enforcement, opt-in only - auto-selection never picks it), and custom (any command). All subprocess execution runs through a deadline streamer with process-group kill; every subprocess in the system has a bounded timeout. An OS-level sandbox (write-scope = worktree, network deny by default) applies where the agent supports it.

Safety and budgets

Defense in depth, all config-gated: per-phase and per-component timeouts, a no-progress circuit breaker (unchanged diff + unchanged failures = halt), an adversarial-call budget shared across review/security/distill phases, a run-level token budget enforced at phase boundaries, a 5 MB output cap per agent invocation, and run/worktree flocks. Reviewer and security findings distinguish infrastructure_error ("never ran") from "ran clean" - an empty findings list is a safe success signal.

Configuration

kstrl.toml sections mirror config dataclasses ([agent], [factory], [verify], [security], [timeout], [breaker], [sandbox], [knowledge], [evolution], [notify], [linear], ...). Precedence: CLI flag > KSTRL_* env > kstrl.toml > dataclass default. See kstrl.toml.example and docs/env-vars.md.

Clone this wiki locally