Thilke System Handbook
Thilke is an agentic development platform for turning issues, repositories, runtime context, and human approvals into durable, recoverable software changes.
The system splits source of truth across Gitea for code and issues, Thilke Agent for durable execution, Jujutsu for recoverable VCS, GraphRAG and memory for context, and Multica for UI and ingress.
This handbook is built with mdBook and published at https://docs.thilke.com.
Principles
Agentic first
Thilke favors structured context, durable artifacts, reversible operations, and approval-gated writes over opaque chat transcripts.
FOSS-first
Core pieces should be FOSS where practical: Rust, ADK-Rust, Ratatui, Jujutsu, SQLite, Gitea, mdBook, Traefik, Caddy, and nginx. Provider-specific model access is isolated behind adapters.
Durable by default
Every run should leave enough state to resume or audit it later: prompt, issue context, tool proposals, approvals, events, branch metadata, validation output, and PR links.
System Architecture
User or schedule -> Gitea issue / Multica event -> thilke-agentd plan-issue -> GraphRAG + memory -> ADK-Rust planner -> proposed-actions.json -> TUI/API approval -> execute-approved -> jj branch, validation, commit, push, PR, issue comment
git.thilke.com is the canonical source forge, control.thilke.com exposes Multica, and docs.thilke.com publishes this handbook. Core services currently run on 5.78.191.210.
Services and Endpoints
| Service | Purpose | Bind | Public route |
|---|---|---|---|
thilke-agentd | Durable ADK-Rust agent runtime | 127.0.0.1:7817 | internal |
thilke-codex-gateway | OpenAI-compatible gateway to Codex auth | 127.0.0.1:8092 | internal |
thilke-pathway-context | GraphRAG and memory API | 127.0.0.1:8090 | internal |
thilke-multica-bridge | Multica sync and ingress adapter | 127.0.0.1:8093 | internal |
thilke-docs | Static mdBook site | container port 80 | https://docs.thilke.com |
| Gitea | Forge, issues, PRs, CI | local compose | https://git.thilke.com |
| Multica | UI and task dashboard | local compose | https://control.thilke.com |
Agent Runtime
The native runtime lives in thilke/agent and replaces the earlier Pi-derived approach with a Rust-first daemon and TUI.
crates/thilke-agentd: CLI, HTTP API, run persistence, ADK-Rust integration, issue planning, action execution, Gitea client,jjadapter, GraphRAG lookup, and model config.crates/thilke-tui: Ratatui interface for viewing runs, inspecting proposed actions, and approving or rejecting actions.
Run mirrors persist request, session, events, artifacts, proposed actions, branch recovery, eval, summary, trace, and validation output.
Agent Roles
| Role | Purpose | Multica mapping |
|---|---|---|
| Purveyour | Software architect and option refinement | architect |
| Depute | General implementation | default |
| Assayour | Review validation and acceptance | review |
| Avisour | Issue triage and clarification | triage |
| Steward | Operations and reliability | ops |
| Baillif | Approved execution | executor |
| Factour | Release and packaging | release |
Issue Execution Loop
plan-issue fetches a Gitea issue, gathers repository and graph context, and writes proposed actions without repository writes. Approvals happen through the TUI or HTTP API. execute-approved refuses required, rejected, or unapproved actions and enforces branch, validation, and protected-branch safeguards.
thilke-agentd plan-issue --owner thilke --repo agent --number 1
thilke-agentd execute-approved --repo-dir /home/thilke/src/agent --run-id $RUN_ID
Jujutsu VCS Workflow
Agents use jj as the primary local VCS interface while preserving Git compatibility for Gitea. Initialize colocated jj, use jj new and jj bookmark set for branches, jj describe for commits, jj git export before Git/Gitea operations, and jj git push --bookmark for pushes. Persist jj_change_id and jj_operation_id in recovery manifests.
GraphRAG and Memory
The context service is currently a persistent SQLite-backed graph and memory API with a Pathway-compatible service boundary. It stores repository entities, services, agents, issues, run facts, semantic memory, episodic memory, and procedural playbooks.
Endpoints: POST /v1/admin/refresh, POST /v1/graphrag/search, POST /v1/memory/lookup, and POST /v1/memory/record.
Memgraph GraphRAG, AI Memory, and Agentic AI
Thilke uses Memgraph as the real-time graph backend for three related workloads:
- GraphRAG: retrieval that combines text relevance with graph expansion and Cypher traversal.
- AI Memory: semantic, episodic, and procedural memory in one connected graph.
- Agentic AI: an execution and reasoning graph for tasks, runs, actions, outcomes, and policies.
Pathway remains the ingestion and API service. Memgraph is the graph execution and memory backend. SQLite FTS remains a local fallback/search index.
Services
| Service | Endpoint | Purpose |
|---|---|---|
thilke-memgraph.service | bolt://127.0.0.1:7687 | Memgraph database for graph memory, GraphRAG, and reasoning paths |
thilke-pathway-context.service | http://127.0.0.1:8090 | Ingestion, HTTP API, dual-write adapter |
thilke-iii-worker.service | ws://127.0.0.1:49134 | Exposes graph functions to iii |
Memgraph data is persisted under /var/lib/thilke-agent/memgraph and mounted into the container.
Memory model
The adapter writes these node families:
(:ThilkeEntity)for repositories, issues, runs, artifacts, and procedures.(:Memory)for all memory records.(:Memory {layer: "semantic"})for facts, entities, and issue knowledge.(:Memory {layer: "episodic"})for run history, traces, validations, and outcomes.(:Memory {layer: "procedural"})for playbooks, policies, and repeatable workflows.
Important relationships:
(repo)-[:THILKE_RELATION {kind: "has_issue"}]->(issue)(run)-[:THILKE_RELATION {kind: "has_artifact"}]->(artifact)(memory)-[:ABOUT]->(entity)
API surface
Pathway context endpoints:
curl -fsS http://127.0.0.1:8090/healthz
curl -fsS -X POST http://127.0.0.1:8090/v1/admin/refresh
GraphRAG search:
curl -fsS -H 'content-type: application/json' \
-d '{"query":"agent memory GraphRAG","limit":8}' \
http://127.0.0.1:8090/v1/graphrag/search
Memory lookup:
curl -fsS -H 'content-type: application/json' \
-d '{"query":"jj recovery","layer":"procedural","limit":8}' \
http://127.0.0.1:8090/v1/memory/lookup
Read-oriented Cypher:
curl -fsS -H 'content-type: application/json' \
-d '{"query":"MATCH (m:Memory) RETURN count(m) AS memories","limit":5}' \
http://127.0.0.1:8090/v1/memgraph/cypher
The Cypher endpoint also accepts params for safer automation:
{
"query": "MATCH (r:ReasoningRun {run_id: $run_id})-[:PROPOSES]->(a:Action) RETURN r.run_id AS run_id, count(a) AS actions",
"params": {"run_id": "019e53d7-cb42-7272-a077-a4fa901bb73f"},
"limit": 5
}
Agentic AI reasoning graph
plan-issue mirrors its proposed execution path into Memgraph. This makes the agent plan queryable before any write action is approved, which is the first concrete slice of the Memgraph Agentic AI model.
For each issue planning run, Thilke writes:
(:ReasoningRun:AgentRun)withrun_id,agent,status,summary,model_provider,model_tier, and recovery branch.(:Task:GiteaIssue)for the source issue.(:Action:ProposedAction)for each gated action, withaction_id,kind,risk,risk_score,approval,dry_run, andordinal.(ReasoningRun)-[:PLANS_FOR]->(Task).(ReasoningRun)-[:PROPOSES {ordinal}]->(Action).(Action)-[:NEXT_ACTION]->(Action)for ordered execution-path traversal.
This lets agents and reviewers ask graph-native questions before writes occur:
curl -fsS -H 'content-type: application/json' \
-d '{"query":"MATCH (r:ReasoningRun {run_id: $run_id})-[:PROPOSES]->(a:Action) RETURN r.run_id AS run_id, count(a) AS actions","params":{"run_id":"019e53d7-cb42-7272-a077-a4fa901bb73f"},"limit":5}' \
http://127.0.0.1:8090/v1/memgraph/cypher
The current deployment was dogfooded with issue thilke/agent#1; the resulting graph contained one ReasoningRun, one GiteaIssue task, and five proposed actions connected by PROPOSES and NEXT_ACTION edges.
iii functions
The Thilke iii worker exposes:
thilke.graph.searchthilke.graph.memory_lookupthilke.graph.cypher
This gives agents a stable capability-bus interface for GraphRAG, AI memory, and reasoning-graph queries without coupling agent code directly to Memgraph.
ADK integration plan
ADK-Rust should talk to memory through a ThilkeMemoryStore adapter:
- before planning: call GraphRAG/memory lookup for issue, repo, and agent-role context;
- during execution: append episodic events and observations;
- after execution: store summary, validation result, PR outcome, cost, model tier, and errors;
- during future planning: use graph edges and prior outcomes to score action paths.
The current implementation establishes the backend, APIs, and pre-write reasoning-plan mirror. The next step is to persist post-write outcomes from execute-approved and feed success/failure edges back into future planning.
Validation
systemctl is-active thilke-memgraph thilke-pathway-context
curl -fsS http://127.0.0.1:8090/healthz
curl -fsS -H 'content-type: application/json' \
-d '{"query":"MATCH (m:Memory) RETURN count(m) AS memories","limit":5}' \
http://127.0.0.1:8090/v1/memgraph/cypher
Multica Bridge
Multica is a human-facing UI and task/event surface, not the durable source of truth. Gitea owns code, issues, PRs, and reviews. thilke-agentd owns run mirrors. jj plus branch-recovery.json owns VCS recovery. GraphRAG owns context.
The bridge maps Multica events to Thilke roles, triggers safe plan-issue runs from Gitea-linked tasks, syncs summaries back, and skips events without canonical Gitea targets.
iii Capability Bus
Thilke runs iii as the live capability bus for agent-adjacent services. It provides a discoverable worker/function/trigger surface for agents, dashboards, and bridge services while preserving durable sources of truth elsewhere.
Source of truth boundaries
thilke-agentdis the durable ADK-Rust run authority.- Gitea is authoritative for issues, branches, pull requests, and review status.
- Jujutsu/Git manifests are authoritative for recoverable code changes.
- Pathway/SQLite graph memory is authoritative for graph and memory state.
- Multica is a display and ingress adapter, not the source of truth.
- iii composes capabilities through named functions and traceable invocations.
Deployed services
| Service | Endpoint | Purpose |
|---|---|---|
thilke-iii.service | 127.0.0.1:3111, 127.0.0.1:49134 | iii engine, HTTP surface, worker websocket |
thilke-iii-worker.service | ws://127.0.0.1:49134 | Registers Thilke functions |
thilke-multica-bridge.service | 127.0.0.1:8093 | Routes eligible Multica events through iii |
The engine is installed from thilke/agent and configured at /etc/thilke-agentd/iii-config.yaml.
Durability posture
The engine no longer runs with --use-default-config. It uses source-controlled config with file-backed adapters mounted at /var/lib/thilke-agent/iii.
Current adapters:
iii-state:kv,file_based,/data/state_storeiii-stream:kv,file_based,/data/stream_storeiii-queue:builtin,file_based,/data/queue_storeiii-cron:kv,file_based,/data/cron_storeiii-observability: memory exporter for local traces, metrics, and logs
Anonymous iii telemetry is disabled with III_TELEMETRY_ENABLED=false. The engine runs with III_ENV=production.
If iii becomes multi-host, replace file-backed adapters with Redis/RabbitMQ per upstream iii guidance.
Registered Thilke functions
thilke.agent.health: checksthilke-agentdhealth.thilke.graph.search: calls Pathway GraphRAG search.thilke.multica.agent_map: exposes Multica-to-Thilke agent role mapping.thilke.agent.plan_issue: runsthilke-agentd plan-issueinto durable run mirrors.
Multica event path
sequenceDiagram
participant M as Multica UI/API
participant B as thilke-multica-bridge
participant I as iii engine
participant W as thilke-iii-worker
participant A as thilke-agentd
participant G as Gitea
M->>B: issue status/update event
B->>B: resolve role and Gitea target
B->>I: trigger thilke.agent.plan_issue
I->>W: invoke function
W->>A: thilke-agentd plan-issue
A->>G: read issue context
A->>A: write durable run mirror and proposed actions
W-->>I: run result
I-->>B: processed via iii
The path remains approval-gated. plan-issue creates context and proposed actions but performs no repository writes. Writes require explicit action approval and execute-approved.
Smoke validation
From /home/thilke/src/agent on the host:
scripts/smoke/iii-multica-process-next.sh
Expected output:
bridge_iii_enabled ws://127.0.0.1:49134
III_MULTICA_PROCESS_NEXT_OK <run-id>
Operations
Check services:
systemctl is-active thilke-iii thilke-iii-worker thilke-multica-bridge thilke-agentd
Inspect logs:
docker logs --tail 120 thilke-iii
journalctl -u thilke-iii-worker -n 120 --no-pager
Reinstall from source:
cd /home/thilke/src/agent
scripts/install-iii-services.sh
Deployment
The deployment target is 5.78.191.210. Agent services are systemd units. Public routes use Traefik file-provider and selected Docker services.
Docs build with mdbook build /home/thilke/src/docs, deploy to /var/www/thilke-docs, and are served by thilke-docs nginx on the edge-router network. The route file is /mnt/HC_Volume_105485887/projects/coolify/proxy-external/dynamic/docs-thilke.yml.
Operations
systemctl is-active thilke-agentd thilke-codex-gateway thilke-pathway-context thilke-multica-bridge
curl -fsS http://127.0.0.1:7817/healthz
curl -fsS http://127.0.0.1:8090/healthz
curl -fsS http://127.0.0.1:8093/healthz
curl -fsS https://docs.thilke.com/ | head
Use journalctl -u <service> for systemd logs and docker logs thilke-docs for docs. Use jj op log and jj undo for local operation recovery.
Monitoring and Status
Thilke agent operations use a layered status model:
- systemd service health for each local service;
- HTTP health endpoints for the daemon, Codex gateway, graph context, and Multica bridge;
- iii runtime checks for source-controlled config, telemetry opt-out, and absence of in-memory production warnings;
- durable run mirror inspection under
/var/lib/thilke-agent/runs; - optional smoke execution through the Multica-to-iii-to-agentd path.
Canonical status command
Run from the agent repository on the host:
cd /home/thilke/src/agent
scripts/thilke-agent-status.py --pretty
The script emits JSON and exits non-zero if any required service or endpoint is unhealthy.
Important fields:
ok: aggregate pass/fail.services: systemdis-activeresults.http: service health endpoint responses.iii.telemetry_disabled: verifies iii anonymous telemetry is disabled.iii.in_memory_warning_present: must befalsefor the production-like deployment.iii.config_file_active: verifies iii loaded/etc/iii/iii-config.yaml.storage.runs_count: count of durable run mirrors.latest_runs: recent run mirror metadata.
Smoke mode
Use smoke mode when validating a deploy or debugging the Multica event path:
scripts/thilke-agent-status.py --smoke --pretty
Smoke mode calls scripts/smoke/iii-multica-process-next.sh. It creates a durable plan-issue run through:
Multica bridge -> iii -> thilke-iii-worker -> thilke-agentd plan-issue
It remains write-safe. It performs planning only and does not mutate repositories.
Expected smoke marker:
III_MULTICA_PROCESS_NEXT_OK <run-id>
Direct service checks
systemctl is-active \
thilke-agentd \
thilke-codex-gateway \
thilke-pathway-context \
thilke-multica-bridge \
thilke-iii \
thilke-iii-worker
curl -fsS http://127.0.0.1:7817/healthz
curl -fsS http://127.0.0.1:8092/healthz
curl -fsS http://127.0.0.1:8090/healthz
curl -fsS http://127.0.0.1:8093/healthz
Log inspection
docker logs --tail 120 thilke-iii
journalctl -u thilke-iii-worker -n 120 --no-pager
journalctl -u thilke-agentd -n 120 --no-pager
journalctl -u thilke-multica-bridge -n 120 --no-pager
Next monitoring work
- Export the JSON status through a local authenticated endpoint.
- Add a scheduled status mirror into Multica for display-only dashboards.
- Add Prometheus counters for run duration, status, model tier, approval latency, smoke results, and path (
iiiversus direct fallback). - Add iii trace and metric retrieval to the status script once the deployed engine API shape is stable.
Security
Agent APIs bind to loopback by default. Write operations are approval-gated. Tokens live in /etc/thilke-agentd or server-local secret files and must not be committed. Public docs must not contain secrets.
Guardrails: do not print secrets, never push directly to protected branches from an agent, refuse PR creation unless validation artifacts exist, gather read-only context before writes, and treat Multica events as requests rather than trusted commands.
APIs
Agent daemon: GET /healthz, GET /v1/capabilities, GET /v1/runs, GET /v1/tools, GET /v1/runs/{run_id}/actions, POST /v1/runs/{run_id}/actions/{action_id}/approval, GET /v1/runs/{run_id}/branch-recovery.
Context service: GET /healthz, POST /v1/admin/refresh, POST /v1/graphrag/search, POST /v1/memory/lookup, POST /v1/memory/record.
Codex gateway: GET /v1/models, POST /v1/chat/completions. Multica bridge: GET /healthz, GET /v1/agents/map, POST /v1/sync/agent-to-multica, POST /v1/ingress/multica-issue, GET /v1/events/pending, POST /v1/events/process-next.
Roadmap
Near term: expand execute-approved, surface GraphRAG citations in the TUI, add remote authenticated daemon access, make Multica sync more useful without making it authoritative, and add docs/route CI.
Medium term: use real Pathway streaming where useful, add Langfuse or equivalent traces, add agent evals for laziness and validation quality, and add a GPUI dashboard.
Long term: build the self-editing game/engine loop where code, logic, and low-poly asset agents coordinate through the same durable execution graph.
Appendix: Current State
Current host paths: /home/thilke/src/agent, /home/thilke/src/docs, /home/thilke/src/platform, /etc/thilke-agentd, /var/lib/thilke-agent, /var/www/thilke-docs, and /mnt/HC_Volume_105485887/projects/coolify/proxy-external/dynamic. Installed services include thilke-agentd, thilke-codex-gateway, thilke-pathway-context, and thilke-multica-bridge. jj 0.41.0 is installed and expected for agent VCS.