Skip to content

Commit fc35116

Browse files
authored
epoch
2 parents ec820b5 + 4e4415e commit fc35116

337 files changed

Lines changed: 68632 additions & 25 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.config/wt.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Koan project worktree hooks
2+
# Docs: https://worktrunk.dev/hook/
3+
4+
[post-create]
5+
deps = "uv sync --dev"
6+
7+
[post-start]
8+
copy = "wt step copy-ignored"
9+
10+
[pre-merge]
11+
check = "uv run ruff check ."
12+
test = "uv run pytest"

.github/workflows/ci.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: ["main"]
6+
pull_request:
7+
workflow_dispatch:
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- name: Checkout repository
14+
uses: actions/checkout@v4
15+
16+
- name: Set up Python
17+
uses: actions/setup-python@v5
18+
with:
19+
python-version: "3.12"
20+
21+
- name: Install uv
22+
uses: astral-sh/setup-uv@v4
23+
24+
- name: Install dependencies
25+
run: uv sync --dev
26+
27+
- name: Run tests
28+
run: uv run pytest

.gitignore

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
1-
node_modules/
2-
dist/
31
.pi/
42
.DS_Store
3+
4+
.claude/
5+
plans/
6+
.env
7+
.env.*
8+
*.log
9+
10+
# Frontend build output (committed source lives in frontend/src/)
11+
koan/web/static/app/
12+
frontend/node_modules/
13+
frontend/dist/
14+
__pycache__/

.koan/memory/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.index/
2+
summary.md
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
title: Persistent orchestrator over per-phase CLI spawning
3+
type: decision
4+
created: '2026-04-16T07:13:41Z'
5+
modified: '2026-04-16T07:13:41Z'
6+
---
7+
8+
This entry documents the orchestrator spawn architecture decision in koan's workflow engine (`koan/driver.py`). On 2026-04-02, Leon redesigned the system to replace per-phase CLI process spawning with a single long-lived orchestrator process running the entire workflow in one continuous session. Previously, each planning phase spawned a fresh `claude`, `codex`, or `gemini` CLI process; a separate `workflow-orchestrator` subagent was then spawned to present the user with a phase-selection decision after each phase completed. Leon's rationale: per-phase spawning caused compounding context loss (each new process re-derived what the previous had explored), and the workflow-orchestrator role was architecturally wasteful -- "a process-boot just to ask a question." Two alternatives were explicitly rejected: (1) API-based conversation (driver calling the LLM API directly) -- would have bypassed the runner abstraction handling model selection, MCP config, output streaming, and thinking mode; (2) context injection into fresh processes per phase -- cheaper but fails to provide a persistent reasoning chain and does not eliminate the workflow-orchestrator overhead. The redesign landed in `koan/driver.py` as a single `spawn_subagent()` call awaiting the orchestrator's exit, and added `koan_set_phase` as the new phase-transition tool replacing the two-tool `koan_propose_workflow` / `koan_set_next_phase` dance.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
title: Step-first workflow pattern -- boot prompt is exactly one sentence
3+
type: decision
4+
created: '2026-04-16T07:13:50Z'
5+
modified: '2026-04-16T07:13:50Z'
6+
---
7+
8+
The step-first workflow pattern governs how all LLM subagent CLI processes in koan receive task instructions. On 2026-02-10, Leon established this as a load-bearing architectural invariant in the koan initial design (documented in `docs/architecture.md` as Invariant 2 and enforced in `koan/web/mcp_endpoint.py`). The rule: every subagent's boot prompt is exactly one sentence -- role identity plus "Call koan_complete_step to receive your instructions." Task details, phase guidance, and tool lists arrive exclusively as the return value of the first `koan_complete_step` MCP call. The pattern was motivated by a failure mode observed with haiku-class (weaker) models: complex task instructions in the boot prompt caused these models to produce text output on the first turn and exit without ever entering the tool-calling loop. Three reinforcement mechanisms make the pattern robust across model capability levels: primacy (boot prompt is the LLM's very first message), recency (`format_step()` in `koan/phases/format_step.py` always appends "WHEN DONE: Call koan_complete_step..." last), and muscle memory (by step 2 the LLM has called the tool multiple times, locking in the pattern).
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
title: Server-authoritative projection via JSON Patch over symmetric dual fold
3+
type: decision
4+
created: '2026-04-16T07:13:57Z'
5+
modified: '2026-04-16T07:13:57Z'
6+
---
7+
8+
The koan projection system maintains frontend-visible workflow state for the browser dashboard, served via Server-Sent Events from `koan/projections.py`. On 2026-03-29, Leon decided to replace a dual fold architecture with a server-authoritative JSON Patch model. The prior design maintained two independent fold implementations -- one in Python (`koan/projections.py`) and one in TypeScript (`frontend/src/sse/connect.ts`) -- required to produce identical projections from the same event sequence. Two production bugs traced directly to these folds diverging: fragmented thinking cards in the activity feed, and scout events appearing incorrectly in the primary agent's conversation feed. Leon's decision: Python computes the fold and the RFC 6902 JSON Patch diff after each event; the browser applies patches mechanically via `fast-json-patch` with no fold logic, no event interpretation, and no business rules. Simultaneously, Leon adopted camelCase for all wire-format keys so patches apply directly to the Zustand store without a field-renaming layer. The correctness guarantee is now structural: one fold in one place.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
title: File boundary invariant -- LLMs write markdown, driver writes JSON
3+
type: decision
4+
created: '2026-04-16T07:14:03Z'
5+
modified: '2026-04-16T07:14:03Z'
6+
---
7+
8+
The file boundary invariant is a load-bearing architectural constraint in koan governing file ownership across the system's actors. On 2026-02-10, Leon established this rule in the koan initial design (documented in `docs/architecture.md` as Invariant 1). The rule: LLM subagents write markdown files only; the koan driver (`koan/driver.py`) reads and writes JSON state files exclusively; tool code in `koan/web/mcp_endpoint.py` bridges both worlds by writing JSON state (for the driver) and templated markdown status files (for LLMs) in the same operation. Leon's stated rationale: if an LLM writes a JSON file, schema drift and parse errors in the payload become runtime failures in the deterministic driver, while markdown is forgiving. The invariant is enforced structurally -- planning-role subagents have write access scoped to the run directory (`~/.koan/runs/<id>/`) but no mechanism to produce JSON state files, and the driver reads JSON state files and exit codes only, never parsing markdown.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
title: Phase trust model -- plan-review as designated adversarial verifier
3+
type: decision
4+
created: '2026-04-16T07:35:13Z'
5+
modified: '2026-04-16T07:35:13Z'
6+
related:
7+
- 0001-persistent-orchestrator-over-per-phase-cli.md
8+
---
9+
10+
The plan workflow's phase trust architecture in koan (`docs/phase-trust.md`, `koan/lib/workflows.py`) was designed around an asymmetric verification model. On 2026-02-10, Leon formalized this as part of the initial koan design: phases in the plan pipeline (intake, plan-spec, execute) were built to trust each other's outputs without re-verification; only plan-review was designated as the adversarial verifier. Leon documented the rationale in `docs/phase-trust.md`: cross-phase re-verification is the "intrinsic self-correction" anti-pattern -- research shows the same LLM re-checking its own prior work is more likely to change correct conclusions to incorrect ones than the reverse. Leon gave plan-review the CRITIC role: it uses the actual codebase as an external tool to check every file path, function name, signature, and type claim in `plan.md` against reality. Leon also decided that plan-review would be advisory only -- it reports findings with severity classification and may suggest looping back to plan-spec for critical or major issues, but it does not modify `plan.md` itself.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
title: Directory-as-contract -- task.json over CLI flags for subagent configuration
3+
type: decision
4+
created: '2026-04-16T07:35:24Z'
5+
modified: '2026-04-16T07:35:24Z'
6+
related:
7+
- 0004-file-boundary-invariant-llms-write-markdown.md
8+
---
9+
10+
The subagent configuration mechanism in koan (`koan/subagent.py`, `docs/subagents.md`) was redesigned on 2026-02-10 when Leon replaced a 9-CLI-flag approach with a task.json file convention, later documented as Invariant 6 (Directory-as-contract) in `docs/architecture.md`. The previous design passed task configuration as 9 CLI arguments; Leon replaced it after identifying four problems: (1) the flat flag namespace caused naming collisions (`--koan-role` vs `--koan-scout-role`); (2) role-specific fields mixed with common fields without structure; (3) `--koan-retry-context` needed to carry multi-paragraph summaries exceeding practical CLI limits; (4) after a crash, reconstructing what a subagent had been asked required parsing process arguments from system logs. Leon adopted the convention that the driver would write `task.json` atomically (tmp + `os.rename()`) to the subagent directory before spawn. The subagent discovers its MCP endpoint by reading `mcp_url` from that file. No structured configuration flows through CLI flags, environment variables, or other process-level channels. Leon designated `task.json` as write-once by the parent before spawn and read-once by the parent at agent registration, never modified afterward.

0 commit comments

Comments
 (0)