-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture Overview
Start with the live atlas: https://0xfauzi.github.io/kstrl/atlas/. It is generated from the code, so it cannot drift from it: every component, what it does, what flows between them and in which direction, which parts are built and which are planned, in layers you can switch between (how a spec becomes a merged pull request; who measures what; what feeds back to the agent; where you stand; how autonomy is earned and lost; what carries between runs; how you see what happened). Click any component to see what it is to its neighbours, and step through a journey to watch one spec cross the system. This page is the prose orientation over that map. The canonical deep dive is ARCHITECTURE.md; the adversarial role taxonomy with invariants and known limitations is docs/adversarial-design.md.
kstrl is easiest to read as a set of nested loops, each with its own clock, each measuring what the faster loop inside it produced and handing a target to it. The phase chain below is what happens inside one tick of the accept and integrate loops.
| Rate | Loop | What acts | What measures | What it steers toward |
|---|---|---|---|---|
| seconds | implement | the engineer agent | (a fast sensor, once evidence shows the loop iterates: R10.12) | one story's acceptance criteria |
| minutes | accept | retry with a structured context | mechanical checks, reviewer, security review | the component's PRD |
| tens of minutes | integrate | schedule and merge | contract tests, the dampener (R10.6) | the manifest satisfied |
| hours | intake | queue admission | queue state, spend, inbox, open PRs (R10.7), safe mode (R10.4) | the queue drained within bounds |
| days | trust | the autonomy level | run outcomes, calibration, health trending (R8.4) | the autonomy the evidence supports |
| weeks | learn | playbook and prompt changes (R9) | attribution, calibration | the harness's own detection rates |
| days | operate (R8.7, R8.8) | the release driver | runtime errors, health probes | the shipped service's objectives |
The rule that holds every loop together: what acts never measures its own result. The design behind this table, with what is closed today and what each cycle closes, is docs/control-loop-design.md.
Generated from the same model as the atlas; a change in the picture is a change in the system.
Only the components the Work layer touches are drawn. The atlas shows the same edges with the other six layers switchable.
A run starts from a markdown spec (or a SpecKit artifact directory, or a
hand-written prd.json for single-component runs):
-
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. - 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-Critiqueblock. - 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.
- 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.
- 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 (tracker item R8.7 on the Roadmap) will be Phase 4.
| 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 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.
.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
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.
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.
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.
The wiki orients; the repo governs. Canonical references: README - ARCHITECTURE - docs/