Aster is a self-hostable agent harness for software work. It supplies the runtime primitives—model access, policy, local context, memory, skills, agents, and tool orchestration—that task capabilities share. Code review is the first deep, verification-first capability built on those primitives.
Design thesis
An agent harness should not be a long system prompt wrapped around a model. It should supply controlled execution, durable context, and narrow task-specific working sets. Aster follows two rules:
- Context is selected, not dumped. Local indexing, memory, and skills load only the material relevant to the current turn. This keeps the model grounded and preserves room for the work itself.
- Verification is a capability, not a universal tax. Some tasks need a quick, direct agent response; high-impact outputs such as review findings benefit from an independent challenge step.
The review capability
Code review is an adversarial verification task, not a generation task. Its pipeline separates hypothesis from verification and retrieves narrowly. A reviewer that trusts its own first-pass output is the implementer certifying its own work; Aster's review capability refutes candidates before emitting findings.
Crate layout
crates/
aster-models/ domain types: findings, symbols, PR shapes, Candidate/Verdict
aster-ai/ provider-agnostic OpenAI-compatible chat client (BYO model)
aster-mom/ MoM engine: parse `mom.yaml`, resolve entries against the
catalog, run the per-turn model switch
aster-index/ zero-dep code index: SQLite + FTS5 + embedded ripgrep
aster-analyzers/ runtime-selectable static backends: semgrep (CLI), ast-grep (in-process)
symbol-extractor/ tree-sitter-tags symbol extraction (14 languages)
aster-harness/ verification-first review capability
aster-persist/ filesystem-first chat transcripts + memory (see MEMORY.md)
aster-memory/ cross-session learning: distils a finished session into
durable memory blocks on the episodic/semantic split
aster-skills/ filesystem-based agent skills: SKILL.md discovery + on-demand load
aster-mcp/ progressive MCP tool injection: one bridge + scoped catalogue
aster-webmcp/ WebMCP bridge: tools a page registers through
`document.modelContext`, reached over CDP in the user's tab
aster-plugins/ Agent Plugins packages: plugin.json, skills/, mcp.json
aster-policy/ policy engine: permissions, approvals, grants, modes
aster-agents/ agent definitions: AGENT.md parsing, discovery, registry
aster-tools/ tiered search/list/find/suggest (rg/fd native, hand-rolled fallback)
aster-lsp/ minimal LSP client over stdio: diagnostics, references,
definitions
aster-sandbox/ OS-native command sandbox (macOS Seatbelt, Linux bubblewrap)
aster-web/ the model-visible web tools: search/extract/crawl over
pluggable providers, keyless by default (DuckDuckGo, Jina)
aster-serve/ `aster serve`: the browser UI, embedded and hosted
aster-acp/ Agent Client Protocol server, so ACP editors (Zed) drive
the agent natively
aster-shortcuts/ Apple Shortcuts tools (list and run, via /usr/bin/shortcuts)
aster-remote/ driving the agent from a messaging app (Telegram)
aster-cron/ schedules from `aster.yaml` installed into the OS scheduler
(launchd, cron) plus native reminders; no daemon
aster-telemetry/ optional OpenTelemetry export
aster-cli/ CLI and TUI entry points, chat loop, tool dispatch, the
context budget, previews, and the serve host
aster-eval/ grades recorded sessions and sweeps models through fixed cases
Chat sessions and durable memory are documented separately in
MEMORY.md.
How the two evaluation instruments work, what their metrics mean, and the limits
on what they can claim are documented in EVAL.md.
The Agent Plugins package format, and how a plugin's skills and MCP servers
reach a session, are documented in PLUGINS.md.
MCP integration is documented in MCP.md. aster-mcp is a
transport-agnostic boundary: it builds the model-visible injection and routes a
resolved real tool to the host. The CLI remains responsible for connecting to
an MCP server, applying approvals, and recording the invocation.
Dependency graph
graph TD
CLI[aster-cli] --> H[aster-harness]
CLI --> PS[aster-persist]
CLI --> SK[aster-skills]
CLI --> AG[aster-agents]
CLI --> MC[aster-mcp]
CLI --> PG[aster-plugins]
CLI --> TL[aster-tools]
CLI --> SB[aster-sandbox]
CLI --> WB[aster-web]
CLI --> PL[aster-policy]
CLI --> SV[aster-serve]
CLI --> SC[aster-shortcuts]
CLI --> RM[aster-remote]
CLI --> TE[aster-telemetry]
CLI --> MEM[aster-memory]
CLI --> CR[aster-cron]
CLI --> ACP[aster-acp]
CLI --> MOM[aster-mom]
CLI --> LSP[aster-lsp]
MC --> WMC[aster-webmcp]
CLI --> EV[aster-eval]
H --> AI[aster-ai]
H --> IDX[aster-index]
H --> AN[aster-analyzers]
H --> M[aster-models]
IDX --> M
SE[symbol-extractor] --> M
IDX -.builds index from.-> SE
AI -.HTTP.-> P[(any OpenAI-compatible provider)]
PS --> M
MC --> PS
CLI -.chat loop tool dispatch.-> M
The review capability depends on nothing SaaS. The only outbound network call
in its core path is through aster-ai to a model endpoint you control via env.
Runtime shape
graph LR
subgraph Host["Host (CLI / webhook / CI)"]
I[User request, diff, or automation event]
end
I --> RUNTIME
subgraph RUNTIME["Aster harness"]
direction TB
PROF[Repo profile, built once at session start]
CHAT[Chat and tool loop]
POLICY[Policy-controlled tools]
STATE[Sessions, memory, skills, agents]
PROF --> CHAT
CHAT --> POLICY
CHAT --> STATE
end
subgraph H["Review capability: aster-harness::review()"]
direction TB
HY["1. HYPOTHESIZE<br/>cheap model, high recall"]
RE["2. RETRIEVE<br/>working set from index"]
VE["3. VERIFY<br/>independent adversarial refute"]
SH["4. SHAPE<br/>canonical findings"]
HY --> RE --> VE --> SH
end
RUNTIME --> H
IDX[(aster-index<br/>SQLite)] -.evidence.-> RE
AI[aster-ai] -.model calls.-> HY
AI -.model calls.-> VE
SH --> OUT[Verified findings]
OUT --> SINK["inline comments / Linear ticket / fix-brief"]
Why a local index instead of a vector DB
Aster retrieves evidence from a zero-external-dependency SQLite index
(tree-sitter symbols + FTS5 + an embedded ripgrep) rather than a hosted vector
store. This keeps the harness self-hostable (no Docker, one binary) and makes
retrieval precise and grep-native: the engine asks for exactly the symbol,
caller, or type a finding's failure scenario needs, and nothing more.
- ripgrep (text) — raw speed for literal/regex lookups across the tree.
- ast-grep / tree-sitter (structural) — syntax-aware matches when a finding needs to reason over the syntax tree, not characters.
The index is optional in ReviewDeps: the harness runs on a bare diff and
gets sharper as more evidence sources are wired in.
The Finding object
Every stage converges on one canonical object (aster_models::code_review):
ReviewFinding— location, defect class, severity,description(the concrete failure scenario),suggestion,confidence.- Internally, a
Candidatecarries a mandatoryfailure_scenarioat birth, and aVerdictcarries the adversarial verifier'sreal/confidence/reason. See ALGORITHM.md.
This object is designed so downstream consumers — inline PR comments, Linear issue creation, and a future fix-agent — all read the same shape.