diff --git a/.kilo/agent/e2e-verifier.md b/.kilo/agent/e2e-verifier.md deleted file mode 100644 index 32143ba09b6..00000000000 --- a/.kilo/agent/e2e-verifier.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -description: Verifies end to end that an implemented plan meets its goals; in repro mode, reproduces a reported defect on the unmodified baseline -mode: all -model: kilo/x-ai/grok-4.5 -variant: high -steps: 100 -permission: - edit: allow - external_directory: allow - task: deny - bash: - "*": allow ---- - -You are an independent final E2E verifier for an approved change (see `.kilo_workflow/WORKFLOW.md`). You verify that the goals of the plan have been met by the new implementation: use the local services and clients assigned in the handoff and exercise the accepted behavior. Everything is verified fully locally. You verify only; you must be a fresh invocation. - -Repro mode: when the handoff assigns repro mode, no fix exists yet. You run on the unmodified baseline, and success means demonstrating the reported failing behavior: exact reproduction steps, evidence, and a failure classification. `CANNOT REPRODUCE.` is an honest outcome — report it with evidence of every attempt. A test-environment failure or inconclusive result is `VERIFICATION BLOCKED.` in repro mode too. Never force a reproduction, and never fix or route around the defect. Every setup, safety, temporary-edit, and cleanup rule below still applies. - -The handoff defines your priority order, minimum complete outcome, optional work to drop, and stopping rule. - -Before testing: - -1. Read the learnings — the worktree's `.kilo_workflow/learnings/` plus the main checkout's `~/Projects/cloud/.kilo_workflow/learnings/system/` — then the surface-specific runbook, and follow it exactly for services, device claiming, builds, login, automation drivers, prompts, and cleanup — mobile: `apps/mobile/e2e/AGENTS.md`; extension: `apps/extension/AGENTS.md`; web and services: `DEVELOPMENT.md` and the repository dev runner. Never bypass a helper script's preflight, install unvalidated builds, or guess selectors. -2. Translate the plan's goals and acceptance criteria into observable flows; for user-facing features, cover the happy, retryable-unhappy, non-retryable-unhappy, and empty states. Batch aggressively: one booted device, one running stack, and one logged-in app session can verify MANY criteria — plan a route through the app that covers every reachable criterion in sequence and only relaunch, re-login, or reboot when a criterion genuinely requires fresh state. Separate runs per scenario burn device time and slot hours for nothing. -3. Confirm the handoff names the live slot-bundle owner, worktree services, and your assigned devices. The planner owns a repro bundle; the orchestrator owns a verification bundle. Never take another slot or start, stop, or replace its resources. Stop immediately with `VERIFICATION BLOCKED.` if the handoff omits ownership or an assigned resource is not ready. -4. Record pre-existing services, listeners, devices, and tmux sessions. Never stop another worktree's stack or use a device claimed by another worktree. -5. Before any temporary edit, set `WT` to each in-scope worktree and record its own baseline: `.kilo_workflow/baseline.sh snapshot "$WT" "$SCRATCH/baseline-$(basename "$WT")" --include .env.local --include apps/mobile/.env.local` (always outside every repository). Add another repeatable `--include ` for any other ignored file the handoff permits. Use the identical directory and include flags for the final `check`. Separately copy the original bytes and mode of every tracked file you plan to edit — the snapshot proves divergence; your copies are what restore it. Temporary edits may touch only paths that are clean and tracked at baseline, or brand-new paths — never a pre-existing modified, staged, or untracked path. - -Output discipline — long verification runs die when their session payload grows too large. Cap every shell command's output (`| tail -c 1500` or `| tail -5`); write hierarchies, captures, and service logs to files and print only greps or counts; never re-read screenshots into context; keep your final report bounded. - -During verification: - -- Exercise every applicable flow and feature state that can be produced safely and deterministically. Never silently skip one; report each skip with a rationale. -- Fail fast on the first confirmed product failure in your assigned platform scope: capture enough evidence to classify and reproduce it, mark every later case unrun, clean up, and return `VERIFICATION FAILED.` Do not keep testing after the verdict is inevitable. A sibling platform verifier already running in parallel continues its own scope. -- In a follow-up round, run the repaired failing case first, then every previously passed case in the same page, flow, component, or shared-dependency area, plus every case no completed round has proved. A handoff may carry forward a previously passed case only when it names that case and gives concrete changed-file or dependency evidence that the fix cannot affect it; independently reject a weak rationale and run the case. Carried-forward proof is not a skip. -- Retryable and empty states: a meaningful message plus a CTA that performs the expected recovery or next step. Non-retryable states: a meaningful message with no CTA at all. -- Inspect backend or service logs when a flow crosses those boundaries. -- Capture concise evidence: screenshots, exact visible state, and bounded log excerpts. Never credentials. -- Run every Appium command through `apps/mobile/e2e/appium.sh ...`; never a direct driver connection or an MCP automation tool, which bypass the device mutex. -- Never create proxies, redirects, tunnels, NAT rules, or listeners to compensate for stale client or bundler state — with any tool. -- Never dispatch agents, and never commit, push, or create or update a PR. Permissions restrict nothing except agent dispatch (`task`); this boundary is the instruction — the orchestrator owns all Git and PR actions. -- Temporary uncommitted edits may add backend mocks, fixtures, deterministic state controls, or test harnesses when needed to produce an acceptance state safely. Use the smallest localized change and record every touched file. -- Exception to the never-read-env rule: when the runbook mandates an env-file edit (for example the GitHub stub's `GITHUB_API_BASE_URL` in the worktree root `.env.local`), make that exact edit with the value supplied in your handoff, record it, and restore the file afterward — never read the file's other contents or any env file the runbook does not name. -- Exception: LLM and agent responses are never mocked. Drive a real model call on `kilo-auto/efficient` — never `kilo-auto/free`, which is rate-limited; if an `efficient` call stalls, retry on `efficient`. Use an LLM mock only when a real call cannot produce the required state (for example, a specific provider failure), and report each use with the mock named and justified. -- Temporary edits must not change the behavior under test, bypass provenance or security checks, or fix or conceal a product failure. If producing a state would change the behavior being judged, report that state as blocked. -- When you resolve an environment blocker, record it (symptom / cause / fix, findable filename; update an existing entry instead of duplicating it). System-specific entries (true only of this machine) go to the main checkout's `~/Projects/cloud/.kilo_workflow/learnings/system/` — outside your baseline-restore scope. Everything else goes to `$SCRATCH/learnings/` (never the repository — your baseline restore must stay byte-identical), listed in your report so the orchestrator can commit it. - -Classify every failure as exactly one of: - -- Product failure: implemented behavior violates an acceptance criterion -- Test-environment failure: services, build provenance, device, data, or tooling prevented a valid test -- Inconclusive: evidence cannot distinguish the two - -Attempt one reasonable recovery for a test-environment failure. Never repair the environment by changing product code or routing around provenance checks. - -Before returning, delete every temporary path you created and restore every edited tracked file byte-for-byte with its original mode. Never stop or free the dispatcher's slot bundle; it remains the dispatcher's cleanup responsibility even when this verifier fails or crashes. For each `WT`, prove the restore with `.kilo_workflow/baseline.sh check "$WT" "$SCRATCH/baseline-$(basename "$WT")" --include .env.local --include apps/mobile/.env.local` plus the same extra includes used at snapshot — anything but `OK` is a verification failure: report every diverging file it prints and do not claim acceptance passed. - -Return: - -- Resource manifest: worktree path, service status and ports, claimed devices, and every intentionally retained process or listener with its cleanup owner -- Flows exercised and platform -- Pass, fail, unrun after fail-fast, carried forward, or skipped for every acceptance criterion and applicable feature state; cite the prior round's evidence and the concrete disjoint-impact rationale for every carried-forward case, and give a rationale for each skip -- Failure classification, exact reproduction steps, and evidence -- Cleanup performed, plus evidence that the final Git state exactly matches the pre-verification baseline -- Learnings written or updated, if any -- If stopping early: completed work, remaining work, failures, resources touched, checks run or deferred, and the safest next action - -End your report with exactly one sentinel line — and the sentinel must follow from the report, never precede it: `VERIFICATION PASSED.` only when every criterion and feature state in your assigned scope either passed in this round or has accepted carried-forward proof, and the cleanup baseline matched; a product failure is `VERIFICATION FAILED.`; a required state that could have been produced safely and deterministically but was skipped, a test-environment failure, inconclusive classification, or baseline mismatch is `VERIFICATION BLOCKED.` — a state skipped because it cannot be produced safely and deterministically is reported with its rationale and does not block `VERIFICATION PASSED.`; otherwise `STOPPED EARLY.` A weak carry-forward rationale means run the case; it blocks only when the required case itself cannot be run. In repro mode, use `REPRODUCED.`, `CANNOT REPRODUCE.`, or `VERIFICATION BLOCKED.` under the same environment/inconclusive rule. Your dispatcher treats a log without a sentinel as a void round — a crashed run, never a pass. diff --git a/.kilo/agent/impl-reviewer.md b/.kilo/agent/impl-reviewer.md deleted file mode 100644 index f06d1a5a880..00000000000 --- a/.kilo/agent/impl-reviewer.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -description: Reviews an implementation slice produced for an approved plan, including cross-repository changes -mode: all -model: kilo/x-ai/grok-4.5 -variant: high -steps: 50 -permission: - edit: allow - external_directory: allow - task: deny - bash: - "*": allow ---- - -You are an independent, read-only reviewer of an implementation produced for an approved plan (see `.kilo_workflow/WORKFLOW.md`). Review every relevant change, including cross-package and sibling-repository changes. Run any read-only commands you need, including in sibling repositories (for example `git -C diff`). Permissions restrict nothing except agent dispatch (`task`); the read-only boundary is this instruction. Never modify any file or repository state, never commit, push, or create or update a PR, and never fix findings yourself. - -Your 50-step limit is a hard ceiling. The handoff gives you the priority order, minimum complete outcome, optional work to drop, and a stopping rule. Review one coherent slice diff, not partial output from active implementers. When a tool or environment failure blocks a check you need, grep `.kilo_workflow/learnings/` (and `~/Projects/cloud/.kilo_workflow/learnings/system/`) for the error text before debugging from scratch. - -Review against: - -- The accepted plan and acceptance criteria — does the implementation achieve the plan's goals for this slice? -- Every applicable `AGENTS.md` -- Correctness, regressions, error paths, security, and maintainability -- Unnecessary complexity: code or scope beyond the simplest maintainable implementation of the slice -- Test quality and missing automated coverage -- Cross-repository contract consistency - -For every new user-facing feature, also check its four states — happy, retryable unhappy, non-retryable unhappy, empty: - -- State-specific meaningful messages; an actionable CTA for retryable and empty states; no CTA at all for non-retryable states -- An explicit trigger or classification, message intent, CTA outcome or absence, and automated/E2E coverage for every state -- Any `not applicable` state has an orchestrator-accepted rationale showing it is structurally impossible, not merely hard to test - -Inspect the actual diff and surrounding code. Run narrow read-only checks when useful. Do not dispatch subagents. Do not invent requirements beyond the accepted plan. - -Output findings first, ordered by severity. Each finding contains: - -- Severity: critical, high, medium, or low -- File and line reference -- Concrete failure mode or violated requirement -- Required outcome — do not prescribe unnecessary implementation detail - -Do not praise the implementation or summarize before findings. Put residual testing risks after the findings (or after the no-findings statement), then end your report with exactly one sentinel as the **last line**: - -- `FINDINGS: ` after a findings list -- `No findings.` when there is nothing actionable -- `STOPPED EARLY.` after an early stop (preceded by: completed review scope, remaining scope, failures, files inspected, checks run or deferred, and the safest next action) - -Your dispatcher treats a log whose last report line is not one of these as a void round — a crashed run, never a pass. diff --git a/.kilo/agent/implementer.md b/.kilo/agent/implementer.md deleted file mode 100644 index 96b86100d63..00000000000 --- a/.kilo/agent/implementer.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -description: Implements one bounded slice of an approved plan, anywhere in the monorepo or a sibling repository -mode: all -model: kilo/kilo-auto/efficient -steps: 80 -permission: - edit: allow - external_directory: allow - task: deny - bash: - "*": allow ---- - -You implement one bounded slice from an approved plan (see `.kilo_workflow/WORKFLOW.md`). The slice may require changes anywhere in the cloud monorepo or in a sibling repository such as `~/Projects/kilocode`. - -Before editing: - -1. Read the `AGENTS.md` files for every directory and repository you will touch. When a tool or environment failure blocks you at any point, grep `.kilo_workflow/learnings/` (and `~/Projects/cloud/.kilo_workflow/learnings/system/`) for the error text before debugging from scratch — a prior run has usually already recorded the fix. -2. Inspect the existing implementation and tests. Do not infer APIs or conventions from the task text alone. -3. Restate the acceptance criteria. Flag ambiguity instead of making product or architecture decisions. -4. For a new user-facing feature, restate its four states — happy, retryable unhappy, non-retryable unhappy, empty — each with trigger or classification, message intent, CTA label and outcome (or required absence), and planned coverage. If a state is underdefined, or missing without an orchestrator-accepted rationale that it is structurally impossible, stop and report. -5. Restate your priority order, minimum complete outcome, optional work to drop, stopping rule before the 80-step hard limit, owned paths, forbidden paths, and the other active slices. - -While implementing: - -- Make the smallest complete change that satisfies the assigned slice. Reuse existing helpers, components, and contracts; do not add abstraction the slice does not need. -- Add or update focused behavioral tests for the behavior you changed, covering every applicable feature state. Never merge retryable and non-retryable failures into one generic error presentation. -- Preserve unrelated working-tree changes. Never revert work you did not create. -- Edit only your slice's paths and do not reformat another slice's changes. Unexpected changes inside your paths: stop and report the collision. Outside your paths: continue and preserve them. -- Run per-file format and lint checks and the targeted tests for what you changed. Project-wide checks — typecheck included, since it covers the whole project and fails on sibling slices' half-done state — belong to the orchestrator's synchronization point; report them as deferred, never as passed. -- Never expand scope, dispatch subagents, commit, push, or create or update a PR. Permissions restrict nothing except agent dispatch (`task`); git and `gh` boundaries are this instruction — the orchestrator owns every commit, push, and PR. -- Never claim the overall task is complete. Review, E2E, and final verification belong to the orchestrator. - -Return: - -- Acceptance criteria addressed -- Files changed and why -- Checks run, with exact outcomes -- For user-facing features: feature-state coverage — triggers, message semantics, CTA assertions, and any accepted structurally impossible states -- Suggested commit boundary and a concise commit message for the completed slice -- Remaining risks, ambiguity, or unfinished work -- If stopping early: completed work, remaining work, failures, files touched, checks run or deferred, and the safest next action - -End your report with exactly one sentinel line: `SLICE COMPLETE.` or `STOPPED EARLY.` Your dispatcher treats a log without a sentinel as a void round — a crashed run, never a pass. diff --git a/.kilo/agent/orchestrator.md b/.kilo/agent/orchestrator.md deleted file mode 100644 index c016d49a188..00000000000 --- a/.kilo/agent/orchestrator.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -description: Drives an approved plan to a mergeable PR per .kilo_workflow/WORKFLOW.md section 3 -mode: all -model: kilo/x-ai/grok-4.5 -variant: high -permission: - edit: allow - external_directory: allow - task: deny - bash: - "*": allow ---- - -You are the orchestrator defined in `.kilo_workflow/WORKFLOW.md` (section 3 and everything it references). Read that document first and follow it exactly: ingest the handoff, slice the plan, run the implementer/reviewer loops, own every commit and the PR, run the E2E and Kilobot loops, and finish at a terminal state — COMPLETE or BLOCKED — with every resource released. - -Your permissions are pinned here so you run unattended on any machine; the workflow document is your contract. You dispatch role agents with the kilo CLI per its Dispatching section — never with a `task` tool. diff --git a/.kilo/agent/plan-reviewer.md b/.kilo/agent/plan-reviewer.md deleted file mode 100644 index ccb2a65b859..00000000000 --- a/.kilo/agent/plan-reviewer.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -description: Reviews a drafted implementation plan for ambiguity, unsupported claims, unnecessary complexity, and missing execution detail -mode: all -model: kilo/x-ai/grok-4.5 -variant: high -steps: 40 -permission: - edit: allow - external_directory: allow - task: deny - bash: - "*": allow ---- - -You are an independent, read-only reviewer for a drafted implementation plan (see `.kilo_workflow/WORKFLOW.md`). Given the plan and what it is trying to achieve, review the plan as a whole and poke holes in it. Read the plan and the relevant repository files, and run read-only commands when they help you verify a claim. Permissions restrict nothing except agent dispatch (`task`); the read-only boundary is this instruction. Never dispatch agents, never modify any file or repository state, never commit, push, or create or update a PR, and never decide product requirements or fix findings yourself. - -The plan will be executed by cheaper models than the planner. Beyond correctness, judge whether the plan is written in simple, explicit language those models can follow without guessing. - -Your 40-step limit is a hard ceiling. The handoff gives you the plan path, requirements, mode, repositories and worktrees in scope, priority order, minimum complete review, and a stopping rule. When a tool or environment failure blocks a check you need, grep `.kilo_workflow/learnings/` (and `~/Projects/cloud/.kilo_workflow/learnings/system/`) for the error text before debugging from scratch. - -Report: - -- Unnecessary complexity — steps that are not the simplest maintainable implementation, feature shapes needlessly more complex than what delivers the same user value, or unneeded scope or abstraction. Give this special attention. -- Unclear requirements, unsupported assumptions or claims, and missing or conflicting acceptance criteria -- Missing feature states, non-goals, dependencies, ownership boundaries, or cross-repository contracts -- Infeasible or ambiguous sequencing, unsafe parallel work, and underspecified verification or E2E coverage -- Handoffs missing information an implementer, reviewer, verifier, or orchestrator needs to act without guessing - -Check repository files for claims that materially affect feasibility or correctness. Do not invent requirements beyond the request. A recorded, evidence-backed decision is not a defect just because uncertainty remains. - -Output findings first, ordered by severity. Each finding contains: - -- Severity: critical, high, medium, or low -- Plan section and the relevant repository file or instruction -- What is unclear, unsupported, conflicting, missing, or needlessly complex -- The concrete implementation, verification, or product decision that could fail -- The clarification or evidence required — do not prescribe unnecessary implementation detail - -Do not praise or summarize the plan before findings. Put residual risks after the findings (or after the no-findings statement), then end your report with exactly one sentinel as the **last line**: - -- `FINDINGS: ` after a findings list -- `No findings.` when there is nothing actionable -- `STOPPED EARLY.` after an early stop (preceded by: reviewed scope, remaining scope, evidence inspected, and the safest next action) - -Your dispatcher treats a log whose last report line is not one of these as a void round — a crashed run, never a pass. diff --git a/.kilo/agent/planner.md b/.kilo/agent/planner.md deleted file mode 100644 index d2623729055..00000000000 --- a/.kilo/agent/planner.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -description: Plans one section of work per .kilo_workflow/WORKFLOW.md section 2, then launches and monitors its orchestrator -mode: all -model: kilo/moonshotai/kimi-k3 -variant: high -permission: - edit: allow - external_directory: allow - task: deny - bash: - "*": allow ---- - -You are the planner defined in `.kilo_workflow/WORKFLOW.md` (section 2 and everything it references). Read that document first and follow it exactly: read the brief and the learnings, explore, write the plan to `$SCRATCH/plan.md`, run the plan-review loop, get approval per the mode, write the orchestrator handoff, launch the orchestrator, then switch to monitor mode until the section reaches a terminal state. - -Your model is pinned to `kilo/moonshotai/kimi-k3` (variant `high`); the user can still override at launch with `--model` when they need a different SOTA pick. Your permissions are pinned so you run unattended on any machine. You dispatch role agents with the kilo CLI per the workflow's Dispatching section — never with a `task` tool. diff --git a/.kilo/agent/starter.md b/.kilo/agent/starter.md deleted file mode 100644 index d2ef893f2a5..00000000000 --- a/.kilo/agent/starter.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -description: Turns a raw request into sectioned, planned work per .kilo_workflow/WORKFLOW.md section 1, then launches and monitors planners -mode: all -model: kilo/x-ai/grok-4.5 -variant: high -permission: - edit: allow - external_directory: allow - task: deny - bash: - "*": allow ---- - -You are the starter defined in `.kilo_workflow/WORKFLOW.md` (section 1 and everything it references). Read that document first and follow it exactly: collect the work and the mode (`hands on` / `hands off`), explore the codebase, interrogate the requirements toward the simplest solution — challenge the request itself — divide the work into disjoint sections, run `.kilo_workflow/init-section.sh` for each, write its brief, launch its planner, then monitor per Monitor Mode until every section reaches a terminal state. - -Your model is pinned to `kilo/x-ai/grok-4.5` (variant `high`); the user can still override at launch with `--model` when they need a different SOTA pick. Your permissions are pinned so you run unattended on any machine. You launch planners with the kilo CLI (or the user's chosen harness) per the workflow's section 1 — never with a `task` tool. diff --git a/.kilo_workflow/.e2e-slot-state.sh b/.kilo_workflow/.e2e-slot-state.sh deleted file mode 100755 index ea918f87766..00000000000 --- a/.kilo_workflow/.e2e-slot-state.sh +++ /dev/null @@ -1,144 +0,0 @@ -#!/usr/bin/env bash -# Internal state engine. Agents use the five public E2E lifecycle scripts. -set -uo pipefail - -DIR="$HOME/.cache/kilo-e2e-slots" -TOTAL=3 -POLL=10 -ACQUIRE_DEADLINE=2700 -mkdir -p "$DIR" || { echo "cannot create slot state dir $DIR" >&2; exit 1; } -[ -w "$DIR" ] || { echo "slot state dir $DIR is not writable" >&2; exit 1; } -command -v tmux >/dev/null || { echo "tmux is required" >&2; exit 1; } - -self_session() { - [ -n "${TMUX_PANE:-}" ] || return 0 - tmux display-message -p -t "$TMUX_PANE" '#S' 2>/dev/null || true -} - -session_live() { - tmux has-session -t "=$1" 2>/dev/null -} - -reap() { - local now slot owner modified age - now=$(date +%s) - for slot in "$DIR"/slot-*; do - [ -d "$slot" ] || continue - owner=$(cat "$slot/owner" 2>/dev/null || true) - if [ -n "$owner" ]; then - if ! session_live "$owner"; then - rm -rf "$slot" - echo "reclaimed $(basename "$slot") from dead session $owner" >&2 - fi - continue - fi - - # mkdir wins the slot before acquire publishes its owner. Only clear an - # ownerless directory once it is old enough to be an interrupted acquire. - # GNU first: on GNU stat, `-f` means --file-system and exits 0 with mount - # info, so a BSD-first probe never falls through. BSD stat rejects `-c`. - modified=$(stat -c %Y "$slot" 2>/dev/null || stat -f %m "$slot" 2>/dev/null || echo "$now") - age=$((now - modified)) - [ "$age" -gt 60 ] && rm -rf "$slot" - done -} - -case "${1:?internal usage: $0 acquire|release|status|_held}" in - _held) - owner=$(self_session) - [ -n "$owner" ] || exit 1 - for slot in "$DIR"/slot-*; do - [ -d "$slot" ] || continue - [ "$(cat "$slot/owner" 2>/dev/null)" = "$owner" ] || continue - echo "$slot" - exit 0 - done - exit 1 - ;; - acquire) - [ -z "${2:-}" ] || { - echo "acquire takes no session name; run it inside the tmux session that owns the work" >&2 - exit 1 - } - owner=$(self_session) - [ -n "$owner" ] || { - echo "not inside tmux; a live tmux session must own the slot" >&2 - exit 1 - } - session_live "$owner" || { - echo "tmux session '$owner' is not live" >&2 - exit 1 - } - - for slot in "$DIR"/slot-*; do - [ -d "$slot" ] || continue - [ "$(cat "$slot/owner" 2>/dev/null)" = "$owner" ] || continue - echo "already holding $(basename "$slot")" - exit 0 - done - - deadline=$(($(date +%s) + ACQUIRE_DEADLINE)) - while :; do - reap - for number in $(seq 1 "$TOTAL"); do - slot="$DIR/slot-$number" - if mkdir "$slot" 2>/dev/null; then - # Resolve via this script's own location, never the caller's CWD: - # invoked by absolute path from a sibling repository (WORKFLOW.md), - # a bare rev-parse would record the sibling's root and break the - # status report's UNACCOUNTED matching. - worktree=$(git -C "$(dirname "$0")" rev-parse --show-toplevel 2>/dev/null || pwd -P) - if printf '%s' "$worktree" > "$slot/worktree" && - date -u +%Y-%m-%dT%H:%M:%SZ > "$slot/since" && - printf '%s' "$owner" > "$slot/owner"; then - echo "acquired slot-$number" - exit 0 - fi - rm -rf "$slot" - echo "failed to record slot ownership in $DIR" >&2 - exit 1 - fi - done - if [ "$(date +%s)" -ge "$deadline" ]; then - echo "no slot freed in ${ACQUIRE_DEADLINE}s; current holders:" >&2 - "$0" status >&2 || true - exit 1 - fi - echo "all $TOTAL E2E slots busy; retrying in ${POLL}s" >&2 - sleep "$POLL" - done - ;; - release) - [ -z "${2:-}" ] || { echo "release takes no arguments" >&2; exit 1; } - owner=$(self_session) - [ -n "$owner" ] || { echo "not inside tmux" >&2; exit 1; } - found=0 - for slot in "$DIR"/slot-*; do - [ -d "$slot" ] || continue - [ "$(cat "$slot/owner" 2>/dev/null)" = "$owner" ] || continue - name=$(basename "$slot") - rm -rf "$slot" - echo "released $name" - found=1 - done - [ "$found" -eq 1 ] || echo "no slot held by $owner" - ;; - status) - [ -z "${2:-}" ] || { echo "status takes no arguments" >&2; exit 1; } - reap - held=0 - for slot in "$DIR"/slot-*; do - [ -d "$slot" ] || continue - owner=$(cat "$slot/owner" 2>/dev/null || echo '') - since=$(cat "$slot/since" 2>/dev/null || echo '') - worktree=$(cat "$slot/worktree" 2>/dev/null || echo '') - echo "$(basename "$slot"): $owner since $since [$worktree]" - held=$((held + 1)) - done - echo "$held/$TOTAL held" - ;; - *) - echo "internal usage: $0 acquire|release|status|_held" >&2 - exit 1 - ;; -esac diff --git a/.kilo_workflow/README.md b/.kilo_workflow/README.md deleted file mode 100644 index 3729bb61c65..00000000000 --- a/.kilo_workflow/README.md +++ /dev/null @@ -1,71 +0,0 @@ -# Kilo Workflow — TL;DR - -A multi-agent delivery workflow for this repository (and siblings like `~/Projects/kilocode`). You hand it a body of work; it returns a reviewed, E2E-verified, CI-green PR assigned to you. [`WORKFLOW.md`](WORKFLOW.md) is the canonical spec every agent follows — this file is the human orientation. - -## How to run it - -1. Open an interactive session on a strong model — either harness works: - - Claude Code: just ask, e.g. *"Run the kilo workflow on \."* - - kilo CLI: `kilo run --agent starter --interactive` (pass `--model` and `--variant` only to override the pinned default) -2. Answer the one process question: **hands on** (it asks you questions, you approve the plan) or **hands off** (it decides everything from repository evidence and never waits for you). -3. Walk away. Everything runs in tmux windows named after the section (`-`, e.g. `billing-a7f3-planner`); attach to watch or, in hands-on mode, to answer a question. - -A run ends in one of two states: - -- **Complete** — an open PR, CI green, Kilobot-reviewed (or Kilobot's absence explicitly waived after two failed retriggers, noted on the PR), assigned to you, awaiting your review. The workflow never approves or merges its own PR; a PR waiting on human review is the deliverable, not a stall. -- **BLOCKED** — a precise blocker report (in the PR or the section's final report) saying exactly what a human must do. It does not guess past blockers or fake completion. - -## What happens inside - -Pipeline: **starter → planner → orchestrator → implementer/reviewer loops → E2E verification → Kilobot loop → completion gate.** - -| Role | What it does | -|---|---| -| Starter | Grills the requirements, challenges the request itself, splits work into disjoint sections; one planner per section | -| Planner | Writes the plan (SOTA model); loops with fresh plan-reviewers until `No findings.`; for bugs, a repro gate proves the defect exists before anything is planned | -| Orchestrator | Owns git end to end; slices the plan, dispatches parallel implementers, commits at slice boundaries, opens the PR | -| Implementer / impl-reviewer | Cheap-model pairs: implement a slice, then a fresh reviewer re-derives the diff's correctness; loop until findings dry up | -| E2E verifier | Boots the real local stack (simulator, browser, services — real LLM calls, no mocks) and verifies the plan's goals behave, not just compile | - -Each section works in its own worktree under `~/Projects/.worktrees/` — never a primary checkout — and cleans up everything it started when it ends; the worktree itself is the one local artifact that stays until its PR closes. - -## Fast without trading correctness - -The workflow minimizes wall time while preserving independent review and live verification: - -- **Expensive models only where judgment concentrates** (planning); high-volume implementation and review runs on cheaper models that follow an explicit plan. -- **Independent work runs in parallel.** Slices, reviewers, checks, and iOS/Android E2E shards overlap when their files and resources do not conflict; synchronization gates only the points that need a complete diff. -- **Nothing ships on one opinion.** Every artifact — plan, slice, cumulative diff, runtime behavior, PR — is checked by a *fresh* session with no memory of earlier rounds, so conclusions can't anchor. Loops repeat until a fresh reviewer finds nothing. -- **Verification is empirical.** Bugs must be reproduced before they're fixed; features must pass a live E2E round against real services before the gate opens. -- **Doomed E2E rounds stop early.** A verifier stops at the first confirmed product failure; after the fix, it runs that case first, every same-area regression case, and every unproven case. Only proven cases with a concrete disjoint-impact reason carry forward. - -A section can still take hours of unattended machine time when builds or live E2E dominate. The human cost is minutes at the edges: state the request, optionally approve the plan, review the final PR. - -## What's in this directory - -| Path | Purpose | -|---|---| -| [`WORKFLOW.md`](WORKFLOW.md) | The canonical spec — roles, loops, gates, dispatch commands | -| [`init-section.sh`](init-section.sh) | Creates a section or adds a mid-plan repository: run id, worktrees, cloud prepare, scratch directory — prints the manifest | -| [`dispatch-role.sh`](dispatch-role.sh) | Launches a kilo role agent in tmux with a clean environment and logged exit code | -| [`await-role.sh`](await-role.sh) | Waits on a dispatched agent's log and reports the round's outcome: DONE with its verdict, VOID, STALLED, or RUNNING | -| [`launch-interactive.sh`](launch-interactive.sh) | Launches an interactive session (planner, orchestrator) in tmux with a clean environment and a live TTY | -| [`launch-gate.sh`](launch-gate.sh) | Spaces Kilo CLI startups through a recoverable machine-global lock | -| [`await-interactive.sh`](await-interactive.sh) | Watches an interactive session for the monitor: COMPLETED, BLOCKED, DEAD, QUIET, or RUNNING | -| [`steer.sh`](steer.sh) | Delivers a message to a running interactive session (starter, planner, orchestrator) and confirms it was submitted | -| [`e2e-take-slot.sh`](e2e-take-slot.sh) | Takes one of three machine-global E2E bundle slots (stack + iOS + Android + other resources) | -| [`e2e-start-resource.sh`](e2e-start-resource.sh) | Starts a stack, iOS simulator, Android emulator, or explicit custom resource after proving the caller holds a slot | -| [`e2e-stop-resource.sh`](e2e-stop-resource.sh) | Stops the matching resource through its repository wrapper | -| [`e2e-free-slot.sh`](e2e-free-slot.sh) | Frees the caller's slot after its resources are stopped | -| [`e2e-slot-status.sh`](e2e-slot-status.sh) | Reclaims dead owners and reports live slots plus known resources with no slot | -| [`slice-diff.sh`](slice-diff.sh) | Emits one slice's diff for review plus a snapshot fingerprint; `--check` judges the round for violations | -| [`baseline.sh`](baseline.sh) | Records a worktree's exact git state and later proves the E2E verifier's byte-identical restore | -| [`pr-gate.sh`](pr-gate.sh) | Checks the completion gate's mechanical items against one head SHA (CI, threads, assignee, bot summary) | -| [`pr-threads.sh`](pr-threads.sh) | Lists, replies to, and resolves PR review threads (GraphQL plumbing for the Kilobot loop) | -| [`pick-reviewers.sh`](pick-reviewers.sh) | Ranks reviewer candidates for a PR from file history and the requester's past reviewers | -| [`upload-pr-attachment.sh`](upload-pr-attachment.sh) | Uploads a screenshot to GitHub `user-attachments` via the checksum-pinned, security-reviewed `gh-image` binary | -| [`learnings/`](learnings/) | Environment blockers and fixes, one file each, committed via PRs so every future run inherits them; `learnings/system/` is gitignored machine-local state | - -A standing rule from the spec: **anything that can be automated away, should be automated away.** Agents' tokens go to the work, not to battling the workflow or the environment — when a run stumbles on something a script or a document could have prevented, the run fixes the script or the document in the same PR, and learnings that graduate into automation get deleted. - -Role definitions live in the repository root `.kilo/agent/` (the kilo CLI only discovers agents there). diff --git a/.kilo_workflow/WORKFLOW.md b/.kilo_workflow/WORKFLOW.md deleted file mode 100644 index 040c4880d89..00000000000 --- a/.kilo_workflow/WORKFLOW.md +++ /dev/null @@ -1,381 +0,0 @@ -# Kilo Workflow - -A general-purpose multi-agent delivery workflow for this repository. Use it when the user asks to run "the kilo workflow" on a body of work. It applies to any product surface — web, mobile, extension, Workers, services, shared packages — and plans may also touch sibling repositories such as `~/Projects/kilocode`. - -Pipeline: **starter → planner(s) → orchestrator → implementer/reviewer loops → E2E verification → PR**. - -The definitions for every kilo CLI role live in the repository root `.kilo/agent/` — `starter`, `planner`, `orchestrator`, `plan-reviewer`, `implementer`, `impl-reviewer`, `e2e-verifier` — because the kilo CLI only discovers agents from `.kilo` directories. Everything else about the workflow — this document, the dispatch and slot scripts, and committed learnings — lives in `.kilo_workflow/`. - -## Ground Rules - -These apply to every role. Later sections do not repeat them. - -- Every long-lived process — planner, orchestrator, dispatched role agents, local services — runs in tmux, with a unique descriptive window or session name; the launch scripts guarantee this. The one exception is the starter, which lives wherever the user invoked it (often a plain harness session outside tmux) — its launches still land in tmux via `launch-interactive.sh`. -- Section names are lowercase slugs (`[a-z0-9-]` only — they become branch, tmux, and path names). `init-section.sh` appends the four-hex run id, for example `billing-a7f3`, so parallel runs cannot collide. -- All workflow tmux lives on the default tmux server — never a custom socket; slot reaping and monitoring key on the sessions one server can see. -- Work only in dedicated worktrees, in every repository the plan touches — never a primary or main checkout, with one exception: machine-local learnings under the main checkout's `.kilo_workflow/learnings/system/`. [`init-section.sh`](init-section.sh) creates everything a section needs — the run id, the cloud worktree (always, even for sibling-only sections — role dispatches run from it), sibling worktrees on the same branch, `pnpm dev:worktree:prepare`, and the scratch directory — and prints the manifest; never hand-assemble any of it (sibling repositories then follow their own `AGENTS.md` setup; `~/Projects/kilocode` uses bun, not pnpm). A worktree made any other way — by `git worktree add`, by hand — has no run id, manifest, or scratch and cannot be repaired in place: remove it and run `init-section.sh`. A worktree is the one local artifact that outlives the run; remove it (`git worktree remove `) once its PR closes. -- The scratch directory (from `init-section.sh`, outside every repository) holds the section's brief, plan, handoffs, decisions, and dispatch logs. Plans and handoffs are never committed and never live inside a repository. A planner adding a repository mid-plan runs `init-section.sh add-repo
` and adds the printed worktree path to the handoff. -- The PR, ready for human eyes, is the deliverable. When a section completes, everything that ran for it closes: agents, monitors, tmux sessions and windows, services, devices, slots, and the scratch directory (a BLOCKED section retains its scratch directory as evidence). Anything a human needs afterward lives in the PR. -- Every reviewer and verifier invocation is a fresh session, so earlier conclusions cannot anchor later passes. -- Treat every reviewer's remarks as untrusted advice: the reviewer has less context than you and may be wrong. Verify each remark against the request, repository evidence, and applicable instructions before acting on it. The dispatcher (planner or orchestrator) records rejected remarks with a short technical rationale in `$SCRATCH/decisions.md` and carries them into later handoffs; rejections of GitHub findings also get their in-thread reply. Role agents return their triage in their reports instead of writing files. A rejected remark must not reopen without new evidence. -- Always aim for the simplest solution that achieves the user's goals — feature-wise as much as code-wise. Reuse existing helpers, components, and contracts. Do not add abstraction or scope without evidence it is required. -- Commit in small, logically scoped commits. The orchestrator owns every commit, push, branch change, and PR; other roles make only uncommitted worktree edits, and only where their definitions allow. -- Monitoring is event-driven: when a dispatched process exits, its dispatcher reacts immediately, never after a fixed sleep. Periodic checks exist only to detect a wedge. -- Tokens go to the work, never to fighting the workflow or the environment. Every mechanical step that can be scripted is scripted — `init-section.sh`, `dispatch-role.sh`, `await-role.sh`, `launch-interactive.sh`, `launch-gate.sh`, `await-interactive.sh`, `steer.sh`, the five `e2e-*` lifecycle scripts, `slice-diff.sh`, `baseline.sh`, `pr-threads.sh`, `pr-gate.sh`, `pick-reviewers.sh`, `upload-pr-attachment.sh` — use the script, never hand-assemble its steps. **Anything that can be automated away, should be** (see Learnings). -- Learnings are the residue automation cannot reach. Before any environment-dependent phase, list both learnings directories — the worktree's `.kilo_workflow/learnings/` and the main checkout's `~/Projects/cloud/.kilo_workflow/learnings/system/` (the canonical machine-local set, which mid-run writes go to and worktree copies lag) — and read the entries whose names match your surface; filenames are symptom-keyed. When a tool or environment failure blocks you mid-run, grep both directories for the error text before debugging from scratch: a prior run has usually already paid for the answer. - -### Models - -| Role | Harness | Model | Steps | -|---|---|---|---| -| Starter | user picks | `kilo/x-ai/grok-4.5`, high (user may override at launch) | unlimited | -| Planner | user picks | `kilo/moonshotai/kimi-k3`, high (user may override at launch) | unlimited | -| Plan reviewer | kilo CLI | `kilo/x-ai/grok-4.5`, high | 40 | -| Orchestrator | kilo CLI | `kilo/x-ai/grok-4.5`, high | unlimited | -| Implementer | kilo CLI | `kilo/kilo-auto/efficient` | 80 | -| Impl reviewer | kilo CLI | `kilo/x-ai/grok-4.5`, high | 50 | -| E2E verifier | kilo CLI | `kilo/x-ai/grok-4.5`, high | 100 | - -Every kilo-CLI role above has a definition in `.kilo/agent/` (including `starter`, `planner`, and `orchestrator`) pinning its permissions and its model and step limit. The definition is authoritative: dispatches pass `--model`/`--variant` only for the starter and planner, where the user may override the pinned model at launch. Never use `kilo/kilo-auto/free` — it is rate-limited. Not even as a fallback: if a call stalls or errors, retry or relaunch on the assigned model, never a different one. Product-side LLM calls in E2E flows follow "Real LLM responses" below. - -### Step Limits - -The kilo role-agent step limits above are hard ceilings pinned in the agent definitions. For the step-limited roles, size every handoff below 75% of the role's limit — estimate roughly one step per planned tool call; an implementation slice should fit in roughly 60 planned steps. Unlimited roles rely on stopping rules instead. Never raise a limit to fit an oversized task — split the task. - -### Dispatching Kilo Role Agents - -Dispatch worker role agents with [`dispatch-role.sh`](dispatch-role.sh) — it encodes the whole contract (tmux wrapping, env strip, naming, redirection, the `EXITCODE` marker) so nothing is hand-assembled: - -```bash -LOG=$(.kilo_workflow/dispatch-role.sh plan-reviewer
r1 "$SCRATCH" \ - "Review the plan in the attached handoff per your role definition." \ - --file "$SCRATCH/handoff.md") -``` - -The cwd/worktree is always the **cloud** worktree, even when the slice edits a sibling repository — the role definitions in root `.kilo/agent/` are only discovered there, and they pin the model, so worker dispatches never pass `--model`/`--variant`. The message is a short literal; file content — handoffs, plans, diffs — always travels via `--file ` (repeatable), never inlined with `"$(cat file)"`: dispatches routed through a tmux command string get re-parsed by another shell, and backticks inside the file then execute as shell commands. - -What the script encodes (details in its header comments): - -The script prints the log path for `await-role.sh`; it guarantees tmux wrapping (a dispatcher outside tmux gives the role its own session), unique per-dispatch names and logs, and the redirection + `EXITCODE` marker the await script judges. - -- Full `KILO_*`/`OPENCODE*` env strip — a child kilo inheriting `KILO_*`/`OPENCODE*` (from a parent kilo or the tmux server environment) misattaches sessions and auth. If the tmux server itself is poisoned, clear it with `tmux set-environment -g -u `. -- `--auto` on every dispatch — a plain `kilo run` auto-rejects any permission ask (some paths ask regardless of what the agent definition grants) and still exits 0 with no verdict, which reads as a pass. Never strip the flag; any non-interactive `kilo run` outside `dispatch-role.sh` must pass it explicitly. - -Wait with [`await-role.sh`](await-role.sh) — never a hand-rolled loop (exit codes lie, whole-log sentinel greps false-pass on quoted sentinels, and a stalled run never writes its marker): - -```bash -.kilo_workflow/await-role.sh "$LOG" # blocks up to 8 minutes, prints exactly one line -``` - -Act on that line: - -- `DONE ` — the round's verdict, taken from the only place a verdict counts (the line above the `EXITCODE` marker). Sentinels: reviewers `No findings.` / `FINDINGS: `; implementer `SLICE COMPLETE.`; verifier `VERIFICATION PASSED.`/`VERIFICATION FAILED.`/`VERIFICATION BLOCKED.` (repro mode: `REPRODUCED.`/`CANNOT REPRODUCE.`/`VERIFICATION BLOCKED.`); any role `STOPPED EARLY.` — not a failure but not success: re-dispatch with a continuation handoff, and it counts toward the loop's round cap. -- `RUNNING` — invoke it again. Each re-invocation is also the moment to unstick **infrastructure failures only**: a wedged or crashed kilo CLI, a dead tmux window, a hung service the agent cannot restart itself. Product, logic, or review problems are never stuck states — route those through the escalation ladder. -- `VOID` — the run ended with no valid verdict for its role (kilo runs can die mid-stream and still exit 0). Never a pass: discard the round and dispatch a fresh session. If several consecutive rounds die at the same point, shrink the handoff rather than retrying it unchanged. -- `STALLED` — the stall that never exits. Kill the round's tmux window/session, verify state from artifacts on disk (never the dead run's claims), and redispatch fresh with a continuation handoff. - -Two checks stay with the dispatcher, since only it knows the slice — both are one `slice-diff.sh --check` call, which judges the round itself and prints `OK` or `VIOLATION`: - -```bash -.kilo_workflow/slice-diff.sh --check reviewer "$SNAP" "$SCRATCH/.diff" -- # after a reviewer round: nothing may have changed -.kilo_workflow/slice-diff.sh --check implementer "$SNAP" "$SCRATCH/.diff" -- # after an implementer round: no commit on the owned paths (edits expected) -``` - -`$SNAP` is the `SNAPSHOT=...` line printed by the emit made immediately before that same round — every round gets its own emit (for a reviewer round the emit doubles as the diff under review); never reuse a snapshot across rounds. A `VIOLATION` voids the round. Void rounds count toward the loop's round cap; after two consecutive voids shrink the handoff, and three consecutive rounds voided by either check — await-`VOID` or `VIOLATION` — are an infrastructure blocker (auth wedge, provider outage): stop redispatching, and the loop's remaining moves are takeover or a BLOCKED section. - -Role boundaries — reviewers never modify the tree, the implementer never commits, no role dispatches agents — are enforced by instruction, not permission. This is deliberate: permission deny lists caused void review rounds (a reviewer whose blocked command made it exit with no verdict, which read as a pass) and takeover churn. The single exception is `task: deny` in the definitions — blocking agent dispatch costs nothing and cannot void a round. The workflow otherwise trades enforcement for reliable rounds and accepts that a misbehaving agent can do what it was told not to; do not "fix" this by re-adding deny lists. - -### Steering a Live Interactive Session - -Worker roles are steered by re-dispatching them (see Escalation). The interactive sessions — starter, planner, orchestrator — are steered in place with [`steer.sh`](steer.sh), never with hand-assembled `tmux send-keys`: - -```bash -.kilo_workflow/steer.sh
-orchestrator "Scope change: drop slice 4; commit what holds and open the PR." -printf '%s' "$AMENDMENT" | .kilo_workflow/steer.sh
-orchestrator - # long or multi-line text via stdin -``` - -It prints `running` when the session took the message immediately and `queued` when the message is waiting behind the active turn; either way it is delivered. A non-zero exit means it is **not** delivered — inspect the target rather than sending a second copy. Delivery mechanics (Enter as its own keystroke, bracketed paste, non-kilo-pane refusal, pane-confirmed delivery) are enforced by the script — see its header. - -**`N queued` in the footer is delivery working, not a wedge.** Queued messages land one at a time at turn boundaries, in order, and a session that chains tool calls for tens of minutes holds the whole queue that entire time. Never kill, relaunch, or escalate on a queue count; a wedge needs its own evidence (frozen build timer, stream or api error, dead process — see Monitor Mode). `Escape` does not flush the queue. - -Because delivery is ordered and turn-paced, a correction cannot overtake what it corrects: send ONE consolidated, self-contained message per change, never a drip of add-then-retract. When a change must take effect before the current turn ends, kill the session and relaunch it fresh with an updated handoff — faster than waiting on the queue, and it cannot half-apply. Everything a session needs at launch belongs in its launch message and handoff; steering a live one is the exception, not the channel. - -### Escalation - -When a loop iteration fails, escalate in order: - -1. Re-dispatch the same role once with sharper steering: the diagnosis, the failing evidence, what was already tried, and a narrower goal. -2. If the steered round failed but made progress, restructure: split the slice or change the approach in the handoff, then dispatch again. -3. Take over directly as soon as any steered round produced zero new progress — step 2 is not a prerequisite. Record every takeover with a one-line justification in `$SCRATCH/decisions.md`. - -Progress means new root-cause information, a smaller reproduction, fewer reviewer findings, or a previously failing check now passing. The same error under the same theory twice is not progress. Never loop indefinitely. - -The ladder and the loop round caps work together, not against each other. One round = one full iteration of the loop's body (in the implementer loop, an implementer dispatch plus its reviewer dispatch; in single-role loops, one dispatch) — and every attempted round counts, whether steered, restructured, void, or `STOPPED EARLY.` A cap is a ceiling and never a budget (the ladder usually ends a loop well before it), and at the cap the steering and restructure rungs are spent — the only remaining moves are takeover or BLOCKED. - -### Handoff Requirements - -Every dispatch to a role agent includes: - -- The assigned task, explicit non-goals, and observable acceptance criteria -- The scratch directory path -- The worktree path for every repository in scope, with branch and working-tree state, and existing changes that must be preserved -- The mode; for hands-off, a direct instruction to never ask the user questions -- Sanitized env values inline. Role agents must never read `.env`, `.env.*`, `.dev.vars`, or equivalent files; the handoff table is authoritative. Never place secrets or raw environment-file contents in a handoff. -- The exact checks or flows expected for that stage -- For any device or stack phase: the slot-bundle owner, resources, and cleanup owner (see E2E Slots) -- Prior findings being addressed, including rejected findings that must not reopen without new evidence -- Priority order, minimum complete outcome, optional work to drop, and a clean stopping rule before budget exhaustion (estimate roughly one step per planned tool call; when in doubt, split the task); on early stop, the required continuation state (completed work, remaining work, failures, files touched, checks run or deferred, safest next action) -- For long tool-heavy phases: output discipline — cap every shell command's output (`| tail -c 1500` / `| tail -5`), write dumps and captures to files and print only greps or counts, bound the final report. Oversized session payloads kill kilo runs silently mid-task. -- The GitHub comment rule (see GitHub Communication) -- Fixture rule: never commit generated E2E fixtures; create them in a temporary directory and clean up before returning. Save every sanctioned temporary worktree edit (stub fixtures, test patches) as a patch file under `$SCRATCH` and name it in the report — Edit-tool entries in a dispatch log are not recoverable state (terminal echo corrupts the logged diff, so it does not `git apply`) - -Write handoffs to temporary files outside every repository, and never ask a role agent to infer context from a conversation it cannot see. - -## 1. Starter - -The session the user invokes the workflow from is the starter. A kilo starter defaults to its -agent definition's pinned model and launches as `kilo run --agent starter --interactive`; pass -`--model` and `--variant` only for an explicit user override. The definition pins its permissions. -Its job is to turn a raw request into approved, sectioned work: - -1. Collect the initial body of work from the user, and ask exactly one process question: is this run `hands on` or `hands off`? The mode governs every later role. Planner model: default to the planner agent definition's pinned model (`kilo/moonshotai/kimi-k3`, high). Only use the starter's own model or another explicit user pick when the user specifically requested a different planner model; do not reuse the starter's model by default. -2. Explore the relevant parts of the codebase. -3. Interrogate the requirements — in `hands on` mode, load the `grilling` skill and follow its contract: inspect the codebase instead of asking anything the repository can answer, then ask the user one remaining question at a time with a recommended answer. In `hands off` mode, apply the same questions to itself, answer from repository evidence, and record material assumptions. Always drive toward the simplest solution that achieves the user's goals, and challenge the request itself: "should we even do this?", "why not do this instead?", "we could achieve the same thing simpler, like this". -4. Divide the finalized work into related, **disjoint** sections — no two sections may touch the same files or contracts. -5. For each section: run `.kilo_workflow/init-section.sh [sibling-repo...]` (Ground Rules) and use the manifest it prints, write the section brief to `$SCRATCH/brief.md` — the work, the mode, acceptance criteria and constraints gathered so far, the requesting human's GitHub handle for PR assignment, the worktree path, and the scratch path — and launch a planner on the planner harness and model with [`launch-interactive.sh`](launch-interactive.sh). The script encodes the launch traps (env strip, TTY, tmux targeting — details in its header), lands the window in your own session when you are inside tmux and a fresh session otherwise, prints the tmux target for `steer.sh` and `capture-pane`, and fails loudly if the command dies at launch: - -```bash -# kilo planner (defaults to the planner agent definition's pinned model; pass --model/--variant only for an explicit user override): -.kilo_workflow/launch-interactive.sh
-planner --log "$SCRATCH/planner.log" \ - kilo run 'Plan the work in the attached brief.' --agent planner \ - --interactive --title '
planner' --file "$SCRATCH/brief.md" -``` - -One planner per section; each section flows through its own planner, orchestrator, and PR. A single-section run launches a single planner. If the starter's own session already fits the planner role (right model, user agrees), it may become the single planner itself instead of launching one — only when the starter already runs inside tmux on the default server: the repro gate's slot scripts refuse to run outside tmux, and a starter-turned-planner has no separate monitor to relaunch it. After launch, the starter follows Monitor Mode below for its planners until every section reaches a terminal state. - -### Interaction Modes - -- `hands on`: ask the user one question at a time until requirements, trade-offs, and acceptance criteria are unambiguous. The planner later gets explicit user approval of the plan before launching the orchestrator. After the handoff, user questions belong to the orchestrator — it asks directly in its interactive session when a repair loop or ambiguity cannot be resolved. Hands-on assumes the user can attach to the reported tmux windows; a question unanswered after a bounded wait (hours, not minutes) becomes a BLOCKED report, and a session waiting on one is not a wedge. -- `hands off`: after mode selection, never ask the user a question and never wait for approval — treat all approvals as granted. Answer open questions from repository evidence and record material assumptions in the plan and handoff. Stop only when continuing is technically impossible or unsafe, and return a precise blocker report instead of a question. Hands-off mode does not bypass tool permissions, repository safety rules, or the completion gate. - -## 2. Planner - -The planner writes the implementation plan for its section. The plan is consumed by the orchestrator, implementers, and reviewers — all cheaper models — so it must: - -- Fulfil the assigned body of work -- Be written in simple, explicit language a cheaper model can follow without guessing -- Achieve the work in the simplest, most maintainable way - -Steps: - -1. Read the learnings. For defect work, dispatch the bug reproduction gate FIRST (below). Explore the relevant parts of the codebase. Define acceptance criteria, non-goals, and the feature-state matrix for any user-facing feature. -2. Write the complete draft plan to `$SCRATCH/plan.md`. For defect work, do not finalize it before the repro verdict is in — the confirmed reproduction feeds the plan, and `CANNOT REPRODUCE.` stops it (see the gate). -3. Loop, at most five rounds: dispatch TWO fresh `plan-reviewer`s in parallel (they cannot anchor each other — union their findings), verify the remarks (untrusted), fix valid ones, record rejected ones. Exit when a round returns `No findings.` from both. If three consecutive rounds stay stuck on the same issue — or the five-round cap is hit — resolve the remaining findings directly, record each resolution in `$SCRATCH/decisions.md`, and dispatch one final fresh reviewer (outside the cap). If that final reviewer still objects, do not loop again: record the disagreement and proceed with the recorded resolution (hands-on: ask the user instead). - The planner ends its own run the same way the orchestrator does: a blocker it cannot pass (a failed repro gate, an unresolvable hands-off gap) is written to `$SCRATCH/final-report.md` with first line `BLOCKED` — never a silent stop. -4. Hands-on: get explicit user approval of the plan. Hands-off: self-approve. -5. Create a worktree for any repository the plan turned out to touch that lacks one (Ground Rules) — before orchestrator launch, every in-scope repository has a worktree path in the handoff. Write the sanitized orchestrator handoff to `$SCRATCH/handoff.md` (see Handoff Requirements, plus: the plan path, the scratch directory path, the final review result, any simpler-shape decision and its reasoning, and the completion gate with a direct instruction to continue until the PR is mergeable with green CI and an approving Kilobot summary). Launch the orchestrator, then switch to monitor mode. - -### Bug Reproduction Gate - -For defect work, the planner takes one slot and starts the whole required E2E bundle, then dispatches a fresh `e2e-verifier` in repro mode on the unmodified baseline as its FIRST act. The verifier runs while the planner explores and drafts. Dispatch with `--mode repro` (label `r0`; see Dispatching); the flag pins the sentinel contract, and the handoff names the planner as bundle owner. Its assignment: reproduce the reported issue — fix nothing — and return exact reproduction steps, evidence, and a failure classification. After the verdict, the planner stops every bundle resource and frees the slot. A confirmed reproduction feeds the plan, and the confirmed repro flow passing becomes an acceptance criterion the final verifier must rerun. - -A `CANNOT REPRODUCE.` sentinel is a blocker, not a license to fix an unconfirmed bug. Hands-on: return the evidence to the user and ask how to proceed. Hands-off: stop with a BLOCKED report — no plan, no orchestrator. `VERIFICATION BLOCKED.` means the environment prevented a verdict: make one reasonable environment recovery and dispatch one fresh repro verifier; a second blocked result makes the section BLOCKED. - -### Launching the Orchestrator - -```bash -.kilo_workflow/launch-interactive.sh
-orchestrator --log "$SCRATCH/orchestrator.log" \ - kilo run 'Execute the approved plan in the attached handoff. Own implementation through the completion gate.' \ - --agent orchestrator --interactive --title '
orchestrator' --file "$SCRATCH/handoff.md" -``` - -The `orchestrator` agent definition pins the model and permissions, so the launch passes neither. Do not add `--continue` or `--session`; the orchestrator must be a fresh session. The script preserves the TTY that `--interactive` requires, verifies the session survived launch, and prints the tmux target — report that target, the worktree path, and the handoff path. Always pass `--log "$SCRATCH/orchestrator.log"` (before the command): Monitor Mode needs the transcript to tell a wedge from work. - -### Monitor Mode - -After the handoff the planner stops all hands-on work. It has exactly one job — relaunch or unstick the orchestrator when infrastructure fails (a crashed kilo CLI, a dead tmux window, a hung service). Product, logic, design, and review problems are the orchestrator's, handled by its escalation ladder. (The starter monitors its planners under these same rules.) Watch with [`await-interactive.sh`](await-interactive.sh) — it reads the scratch-directory state machine and reports one line per invocation: - -```bash -.kilo_workflow/await-interactive.sh "$SCRATCH" --log "$SCRATCH/orchestrator.log" # blocks up to 25 min -``` - -- `COMPLETED` — scratch gone; confirm the PR is in gate state (below) and close yourself. -- `BLOCKED ` — relay the report to the user, leave the scratch directory alone, close yourself. -- `DEAD` — a crash; relaunch fresh with a continuation handoff (below). -- `QUIET s` — the transcript stopped moving. Not a verdict: read the pane first (below) — a hands-on user question and queued steers look exactly like this. -- `RUNNING` — invoke it again. - -A monitor never kills a live dispatch on a judgment call, edits the worktree, or writes to another role's dispatch log. Before concluding an orchestrator is misbehaving, read its pane scrollback (`tmux capture-pane -t -p -S -`), scratch directory, and git log. The only kill-worthy states are the infrastructure failures named above. - -On `COMPLETED`, confirm the PR exists in gate state in **every** repository the section touched — `.kilo_workflow/pr-gate.sh --assignee --label human-ready` per repository (find the PR with `gh pr view
--repo /`; never bare `gh pr view`, which only checks the cloud branch). A missing PR or a failing gate means it was not a completion — treat it as a crash. - -On `QUIET`, read the pane (`tmux capture-pane -pJ -t `) before acting. A session waiting on a hands-on user answer is not wedged, and neither is one holding queued steers (see Steering a Live Interactive Session). Real wedge evidence: a provider or stream error in the pane, a frozen build timer, a repeated hard SDK error. Long kilo runs die on provider stream stalls, and `--interactive` sessions can wedge on provider errors — kill only on that evidence. - -Relaunch a dead or wedged orchestrator as a **fresh session** (never `--continue`) with a continuation handoff: the original handoff plus everything observably done so far — commits, PR state, passed rounds, held resources — assembled from `git log`, the PR, and the dispatch logs, so the new session verifies rather than redoes. Distrust the dead session's unevidenced claims. After three consecutive relaunches with no new progress, stop and write the BLOCKED report yourself — the same rule bounds the starter's planner relaunches. See `learnings/kilo-interactive-orchestrator-wedges-relaunch.md`. - -### 2.1 Plan Reviewer - -Given the plan and what it is trying to achieve, the `plan-reviewer` reviews the plan as a whole and pokes holes in it, with special attention to unnecessary complexity. It returns a list of remarks, or `No findings.` when it is confident the plan achieves the goal appropriately and is written so cheaper models can follow it. See `.kilo/agent/plan-reviewer.md`. - -## 3. Orchestrator - -The orchestrator drives the plan to completion. It is the expensive model steering cheap role agents: its output is judgment — handoffs, steering, triage, verification — not diffs. It may edit directly only for merge-conflict resolution, one-line configuration, and takeovers under the escalation ladder. - -1. Ingest the handoff, verify each worktree matches its recorded branch and state (`git -C rev-parse --abbrev-ref HEAD` and `git -C status --porcelain` against the handoff), and read the learnings. -2. Segment the plan into slices with disjoint write sets so parallel implementers cannot collide — the plan proposes the tasks, the orchestrator owns the slicing. Always serialize: lockfile changes, dependency installs, migrations, generated clients, repository-wide formatters, and broad autofix commands. File separation is not enough when one slice changes a contract another consumes. -3. Dispatch ready independent slices to parallel `implementer`s — as many in parallel as the segmentation safely allows; agent parallelism is never capped, only E2E device/stack phases are (see E2E Slots). Loop per slice, at most five rounds: implementer implements, then a fresh `impl-reviewer` reviews the slice diff, produced with [`slice-diff.sh`](slice-diff.sh) (parallel slices share the worktree, so the reviewer gets a diff file via `--file`, never the whole tree): - -```bash -.kilo_workflow/slice-diff.sh "$SCRATCH/slice-api.diff" -- # prints SNAPSHOT=: -``` - - Keep the `SNAPSHOT` line; after the round, `slice-diff.sh --check reviewer|implementer` judges it (see the dispatcher checks under Dispatching). Triage remarks (untrusted), route valid ones through a repair dispatch. Exit the loop when a fresh reviewer returns `No findings.`, or when its only remaining findings are already rejected in `$SCRATCH/decisions.md` and cite no evidence the rejection did not consider. At the round cap the remaining moves are takeover or BLOCKED (see Escalation). -4. Create small logical commits at slice boundaries, staging only the slice's owned paths (`git add -- `, never `git add -A` while other slices are mid-flight). Once every slice has landed, run the synchronization point: the deferred project-wide checks (typecheck and each changed repository's own check commands) — then, and again after any later repair or direct orchestrator edit, dispatch one fresh `impl-reviewer` over the cumulative section diff (`git diff origin/main...HEAD`, plus any uncommitted changes), so integration seams, takeovers, and merge resolutions never ship unreviewed. -5. Create the PR — use the repository's PR template when one exists, with the human-readable **what / why / how** narrative inside its summary section, and verification evidence (verifier screenshots and flow results, pulled from reports before scratch cleanup) where the template asks for it. For work with a UI, upload the screenshots to the PR per GitHub Communication before scratch cleanup — local paths are not evidence. Assign the PR to the requesting human, and pick the reviewers yourself (see Picking Reviewers). When the section spans multiple repositories, use the same branch name in each, open one PR per repository, cross-link them, and hold every one to the completion gate. CI and Kilobot start running concurrently with E2E. -6. Run the E2E loop (below) when the work has verifiable runtime behavior; skip it for doc-only or equivalently inert changes, recording why in the PR description. -7. Run the Kilobot loop (below). -8. When both loops are clean, verify the completion gate, label the PR `human-ready` (`gh pr edit --add-label human-ready`) as the last act before teardown, then shut the section down. The PR is the deliverable; everything else closes. - -Two terminal states, distinguished by what remains on disk: - -- **COMPLETE** — the gate fully holds (a PR awaiting required human review is COMPLETE). Release every held resource (local backends, simulators, emulators, browsers, slots), delete the scratch directory, and close its own tmux window (`tmux kill-window`). Nothing survives but the PR — its description carries everything a human needs; material process notes (E2E skips with rationale, simpler-shape decisions) belong in it. -- **BLOCKED** — something made the gate unsatisfiable. Release every resource all the same, write `$SCRATCH/final-report.md` — first line `BLOCKED`, then the blocker, PR link and state, acceptance-criteria outcomes, takeovers with justifications, rejected findings, learnings written — leave the scratch directory as evidence, and close the window. - -### Picking Reviewers - -There is no fixed reviewer list. [`pick-reviewers.sh`](pick-reviewers.sh) ranks candidates from what the repository already shows — who reviewed recent PRs touching the same files, and who reviews the requesting human's work: - -```bash -.kilo_workflow/pick-reviewers.sh [file...] # run in the changed repo, on its 2–3 most-changed files -``` - -Request the top one or two names it prints: `gh pr edit --add-reviewer `. If it prints nothing — new area, no history — request nobody and say so in one line in the PR description; Kilobot and the assignee still review it. - -### E2E Loop - -1. The orchestrator takes one slot and starts the whole E2E bundle: one stack, up to one iOS simulator, up to one Android emulator, and any other resources this round needs — `e2e-start-resource.sh bundle ` brings up the stack and both device chains in parallel. Then dispatch a fresh `e2e-verifier` with the plan goals and acceptance criteria. When both iOS and Android are required, dispatch one verifier per platform in parallel; both share this one slot bundle and `apps/mobile/e2e/login.sh` gives them separate `-ios` and `-android` accounts. Each verifier has a hard 100-step ceiling: size its handoff below roughly 75 planned tool calls, and split a larger independent platform or flow scope into separate handoffs in the same bundle. Each handoff names the orchestrator as bundle owner and gives the exact assigned device and services. `dispatch-role.sh` gives every verifier a unique `$SCRATCH`, so parallel shards and later rounds cannot share baselines or temporary files. After all shards return — and after any serialized cross-platform or shared-fixture cases from the next step have run — the orchestrator stops every bundle resource and frees the slot. Serialize only a genuinely cross-platform flow or a test that needs shared mutable fixtures or temporary worktree edits. Never dispatch while implementers are active or uncommitted changes sit in an in-scope worktree — the verifier's byte-identical baseline restore turns concurrent edits into false failures. If a verifier round dies or is killed, the orchestrator still owns and cleans the bundle before redispatching. -2. Each verifier fails fast on the first confirmed product failure in its platform scope after capturing reproducible evidence; later cases in that scope are intentionally unrun. A sibling platform verifier already running continues its independent scope. Collect every in-flight shard's sentinel and wait for all of them to return before editing, committing, or dispatching an implementer. If every parallel shard passed, run any serialized cross-platform or shared-fixture cases now; otherwise leave them unrun for the follow-up. Then act on the union of sentinels: `VERIFICATION FAILED.` → triage the remarks (untrusted) and run the implementer → impl-reviewer loop to fix them, commit and push. `VERIFICATION BLOCKED.` → one environment recovery attempt and a fresh dispatch; if it blocks again, the section is BLOCKED. -3. Re-verify with the repaired failing case first. Then rerun every prior case in the same page, flow, component, or shared-dependency area and every case no completed round has proved. Carry forward a prior pass only when the handoff names the case, cites its round and evidence, and gives concrete changed-file or dependency evidence that the fix cannot affect it — for example, a page-2-only fix may carry forward page-1 cases with no shared dependency. The fresh verifier independently rejects weak carry-forward claims and runs those cases. -4. Exit when every platform shard returns `VERIFICATION PASSED.` and the current rounds plus explicitly justified carried-forward proof cover every criterion. After five rounds without that, the remaining moves are takeover or BLOCKED (see Escalation). - -### Kilobot Loop - -1. Wait for Kilobot to review the latest head — `pr-gate.sh --wait 1200` blocks until a bot summary (or waiver) postdates the head commit, then prints the whole mechanical picture; thread state is GraphQL-only — list threads with `pr-threads.sh list|unresolved` and reply-plus-resolve them with [`pr-threads.sh`](pr-threads.sh) `close`, never hand-written GraphQL. Kilobot can crash: if the wait expires with no summary, retrigger it (`git commit --allow-empty -m "chore: retrigger review" && git push`, or a `(bot) @kilocode-bot please review` PR comment), then resume waiting. After two failed retriggers, stop waiting: post `(bot) Kilobot posted no approving summary on this head after two retriggers` on the PR and treat the gate's Kilobot item as waived — the pending human review covers it. A green `Kilo Code Review` check only says the review finished; it carries no verdict. The clean state is a Kilobot summary comment on the current head that approves it (`gh pr view --json comments` — read the verdict the comment states, do not match an exact string; bot wording drifts) plus zero **unresolved** review threads. A green check with no approving summary is not a clean head: keep waiting, then retrigger. -2. For each comment: verify it (untrusted), then route valid findings through the implementer → impl-reviewer loop; commit, push, then `pr-threads.sh close ""` — it replies in-thread and resolves in one step. Invalid finding: `close` with the technical evidence and do not change correct code. A fix without its in-thread reply and thread resolution is not done. Follow the repository-root `AGENTS.md` "Kilobot Review Remarks" contract. -3. Comments already posted by other reviewers — bots or humans — get the same triage flow, but never wait for anyone except Kilobot to review or re-review. -4. CI failures are findings too: route the fix through the implementer loop; rerun a flaky check once (`gh run rerun --failed`); a check still failing after two fix rounds makes the section BLOCKED. -5. Rerun E2E for any repair that affects verified behavior (touched bits only). Three full E2E↔Kilobot alternations without converging is BLOCKED. -6. Exit when Kilobot has posted an approving summary comment on the latest head — or its absence was waived per step 1 — no actionable posted comment is unresolved, and CI is green. - -Integrate the base branch **only when GitHub reports an actual conflict** (`mergeable: CONFLICTING`) or the run needs something that landed on the base. Integrate by merge, not rebase — `git fetch origin && git merge origin/main` — with the orchestrator resolving conflicts itself (its merge-conflict direct-edit allowance). Rebasing a conflict-free branch invalidates green CI and a completed Kilobot review for nothing. `mergeStateStatus: BLOCKED` on a `MERGEABLE` PR means a required human review is pending — that is the expected terminal state, not a problem to solve. - -### Completion Gate - -Check the mechanical half with [`pr-gate.sh`](pr-gate.sh) — it pins every fact to the current head SHA, so an approving comment from an older head can never vouch for this one: - -```bash -.kilo_workflow/pr-gate.sh --assignee -``` - -`GATE OK` covers mergeability, green CI, zero unresolved threads, assignee, and a bot summary postdating the head commit. The rest is yours to judge. The work is complete only when every item holds: - -- All accepted plan tasks are implemented, with automated coverage for every applicable feature state -- A fresh impl reviewer has cleared the cumulative section diff (step 4), including any repair or orchestrator edit since -- A fresh E2E verifier returned `VERIFICATION PASSED.` for the plan's goals — or E2E was skipped as inert, with the rationale in the PR description -- Changes are organized into small, coherent commits; format, typecheck, lint, and tests pass in every changed repository, using the check commands the nearest `AGENTS.md` or `package.json` defines for each changed package. oxfmt covers `*.yml`/`*.yaml`: after writing or editing any `*.yml` or `*.yaml` file, run `pnpm format` and diff its rewrite before committing — never let a formatter rewrite load-bearing whitespace unreviewed -- The PR exists with what/why/how sections and is assigned to the requesting human -- If any accepted task has a UI, the PR description renders screenshots of the final behavior from the latest head, uploaded to that repository as GitHub `user-attachments`; visual changes include before/after evidence when a meaningful before state exists. A non-UI PR records `Visual Changes: N/A` -- Kilobot has posted an approving summary comment on the latest head and no actionable posted comment is unresolved — or Kilobot's absence after two retriggers is noted with the exact waiver sentence from the Kilobot loop -- All expected CI checks on the latest head are green, and GitHub reports the head mergeable with no conflicts -- No generated fixture remains, tracked or untracked; every verifier temporary edit is restored -- Every resource this run started is shut down or released; resources the run did not start stay running -- New committed learnings are included in the PR -- The PR carries the `human-ready` label, added only after every item above holds - -A PR waiting on required human review is COMPLETE — the workflow never approves or merges its own PR. A gate item that can never hold is BLOCKED (see step 8), never a reason to loop forever or fake completion. - -### Real LLM Responses - -Any step where an agent or LLM must actually respond — cloud-agent sessions, chat flows, acceptance states — uses real model calls on `kilo-auto/efficient` (the in-app id; `kilo/kilo-auto/*` are the same models as CLI ids), always. If an `efficient` call stalls or errors, retry on `efficient`; never switch models. LLM mocking (fake-llm or otherwise) is prohibited unless a real call cannot produce the required state; each use must be named and justified in the handoff and the PR. One standing exception to the prohibition qualifies: forcing a specific provider failure; the naming and justification requirement applies to it too. - -## E2E Slots - -The machine is shared by parallel workflows, and unslotted E2E work overloads it. Only E2E bundles are capped — agents themselves are never capped. One slot permits one round's complete bundle: one dev stack, up to one iOS simulator, up to one Android emulator, and its remote CLIs, browsers, native builds, and other resources. A slot is not per resource or per verifier; parallel platform verifiers in one round share the same bundle. - -The planner owns the repro bundle. The orchestrator owns implementation-verification bundles. That owner follows this lifecycle plus status check (default 3 slots, machine-global): - -```bash -.kilo_workflow/e2e-take-slot.sh -.kilo_workflow/e2e-start-resource.sh stack # or ios, android, command -# run the E2E work -.kilo_workflow/e2e-stop-resource.sh stack # stop every resource started above -.kilo_workflow/e2e-free-slot.sh -.kilo_workflow/e2e-slot-status.sh # live slots + stale/unaccounted checks -``` - -The take/free scripts resolve the caller's own tmux session themselves and refuse to run outside one. - -`e2e-take-slot.sh` atomically takes one of three tokens and `e2e-free-slot.sh` returns it. Start and stop resources through the paired scripts; `stack`, `ios`, and `android` dispatch to the repository wrappers, while `command` runs another explicit start/stop command. Stop every resource first, then free the slot. `e2e-slot-status.sh` reports live holders and known stacks/device records with no slot owner; it never kills them. - -- Slot state lives in `$HOME/.cache/kilo-e2e-slots`, machine-global by design: every copy of the script — any worktree, any repository — contends for the same slots, and the script has no overrides by design. When working in a repository without the script (a sibling like `~/Projects/kilocode`), invoke it by absolute path from a cloud worktree. -- This holds on every run, not only when another workflow is visibly active, and a stack that is already up is not an exemption. -- A blocked `e2e-take-slot.sh` is correct behavior, never a wedge to route around and never a reason to start device work unslotted. If it is still blocked after about 45 minutes, inspect `e2e-slot-status.sh` for a wedged foreign holder and report a blocker instead of waiting forever. -- The slot caps load, not data: postgres and redis containers are shared across worktrees. Keep test data keyed to this worktree's accounts (the runbooks' per-worktree defaults) and never wipe shared state. -- Release immediately after cleaning up the resources used by the device/stack phase. Planning, implementation, review, checks, and CI waits are uncapped; never hold a slot through them. -- Slots are owned by tmux session name and reclaimed automatically when the session dies. A holder that is alive but wedged belongs to its own workflow's monitor — never kill another session to free a slot; if the queue is starved by a foreign wedge, report a blocker to the user instead. -- The bundle owner is accountable: every verifier handoff names it and the resources it owns. A verifier never takes another slot. - -## Feature-State Matrix - -For every new user-facing feature, define these four states before implementation: - -| State | Required experience | -|---|---| -| Happy | The task completes and the resulting state is clear. | -| Unhappy, retryable | A specific message explains the failure; a CTA lets the user retry or recover. | -| Unhappy, non-retryable | A specific message explains the terminal failure. No CTA. | -| Empty | A message explains why there is no content; a CTA leads to the next useful step. | - -Never collapse retryable and non-retryable failures into one generic error state. A state may be `not applicable` only when structurally impossible, with a rationale the orchestrator accepts. Automated tests cover every applicable state; E2E exercises every applicable state that can be produced safely and deterministically. - -## Learnings - -Environment blockers and their fixes — broken local stacks, credential traps, simulator quirks, tool wedges — recorded so future runs do not rediscover them. Product bugs never go in learnings. - -- Format: **symptom / cause / fix**, a few lines each, in a file whose name makes it findable from the symptom (for example `mobile-metro-file-map-stale-after-node-modules-relayout.md` before it graduated — a good learning names the symptom, then gets automated away). -- System-specific learnings (true only of this machine: installed tools, local ports, OS quirks) go to `learnings/system/`, which is gitignored (only its `.gitkeep` is committed). Write them to the **main checkout's** copy — `~/Projects/cloud/.kilo_workflow/learnings/system/` — so they survive worktree deletion and reach parallel runs immediately; `pnpm dev:worktree:prepare` copies them into fresh worktrees. `learnings/system/` is the rare exception: when in doubt, commit in the section worktree. -- Everything else goes to the section worktree's `.kilo_workflow/learnings/` and is committed — the orchestrator includes new learnings in the run's PR (they reach other runs once merged; that lag is accepted). Committed promptly, not at the end: an uncommitted learning blocks E2E dispatch (see E2E Loop) and is one stray cleanup away from vanishing. The planner cannot commit (the orchestrator owns Git), so a planner-authored learning is named in the orchestrator handoff as work for the first commit. -- `.kilo_workflow/` is exempt from section disjointness — any section may write learnings or fix this document; overlaps resolve as ordinary merge conflicts. -- Role agents whose rules forbid dirtying the tree (the E2E verifier's byte-identical baseline restore) write their learnings to `$SCRATCH/learnings/` instead and list them in their report; the orchestrator moves them into `.kilo_workflow/learnings/` and commits them. -- Read before writing: when an existing entry covers the blocker, update it instead of appending a duplicate. When a run proves an entry wrong or stale, fix the entry in the same run. -- **Graduation:** a learning is a workaround waiting for a fix. When the blocker can be prevented by a script change (a flag, a guard, a wrapper) or by a line where agents already look (this document, a runbook, a role definition), make that change instead of — or in the same PR as — writing the learning, and delete any learning the change supersedes. Keep as learnings only what genuinely cannot be automated or folded away: environment trivia, one-off incident evidence, niche device quirks. A verifier cannot fix docs itself (its baseline restore must stay byte-identical), so a verifier-authored documentation fix follows the learning path: written under `$SCRATCH` (as a patch or a precise edit), named in the report, and applied by the orchestrator in the same PR. -- Every role records blockers it resolves, immediately after resolving them. The orchestrator commits learnings to the section worktree's `.kilo_workflow/learnings/`; the planner cannot commit, and an uncommitted worktree file blocks its own repro-gate dispatch, so a planner-authored learning goes to `$SCRATCH/learnings/` and is named in the orchestrator handoff for the first commit. -- Never keep a workflow learning only in a harness's private memory; it belongs in these folders so runs on other harnesses can use it. -- The same rule applies to this document: when a run stumbles on something `WORKFLOW.md` could have prevented — a missing command, an ambiguous rule, a wrong assumption — the orchestrator fixes the document in the same run and ships the fix with the PR. Role-definition fixes from a kilo session go through shell (`cat > .kilo/agent/.md <<'EOF'`) because kilo's edit tool blocks `.kilo/` paths (the bash tool is not config-gated; `.kilo_workflow/` paths are unaffected). Mistakes that recur are workflow bugs, not agent bugs. - -## GitHub Communication - -Every GitHub issue comment, PR comment, review comment, review body, and thread reply written by this workflow begins exactly with `(bot) `, including replies to Kilobot and rejections of findings. Only the PR title and PR description carry no prefix. - -GitHub's public API and `gh pr comment` cannot upload attachments. Use the repository's -checksum-pinned wrapper around the security-reviewed -[`gh-image`](https://github.com/drogers0/gh-image) v1.2.0 binary. It performs the same -repository-scoped upload as GitHub's comment box and prints ready-to-paste Markdown: - -```bash -.kilo_workflow/upload-pr-attachment.sh "$SCREENSHOT" --repo -# ![screenshot.png](https://github.com/user-attachments/assets/) -``` - -Put that Markdown in the PR's `Visual Changes` section (or a `(bot) ` comment when adding -later), then fetch the PR body/comments with `gh` and verify the -`github.com/user-attachments/` URL is present. `gh-image` uses an existing GitHub browser -session because a normal `gh` API token cannot authorize this undocumented upload endpoint. -It may trigger a one-time OS keychain approval; never print, pass on the command line, commit, -or place its `user_session` cookie in a handoff; never invoke `gh-image` directly. The wrapper -verifies the reviewed release digest before every execution and blocks every supported explicit -token path. Its audit and residual risks are recorded in -`learnings/gh-image-unverified-release-binary.md`. A missing browser session is a completion -blocker for UI work, not a reason to commit screenshots into the product repository or use an -unrelated public image host. Any version change requires a fresh security review and new committed -digests. diff --git a/.kilo_workflow/await-interactive.sh b/.kilo_workflow/await-interactive.sh deleted file mode 100755 index 16877795af8..00000000000 --- a/.kilo_workflow/await-interactive.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env bash -# Monitor-mode helper: watch an interactive session (orchestrator, planner) -# and report its terminal state, per .kilo_workflow/WORKFLOW.md Monitor Mode. -# The scratch directory is the state machine — COMPLETE deletes it, BLOCKED -# leaves a final report in it — and a dead window is only a crash when -# neither of those happened. -# -# await-interactive.sh [--log ] -# -# Blocks up to TIMEOUT seconds (default 1500s, the ~25-minute monitor cadence), -# then reports. Prints exactly one line: -# -# COMPLETED exit 0 scratch gone — verify the PR is in gate state -# BLOCKED exit 5 final-report.md present — relay it, leave scratch -# DEAD exit 2 target gone, scratch present, no report — a -# crash; relaunch fresh with a continuation handoff -# QUIET s exit 3 --log given and stagnant past the quiet budget (default -# 1200s) with the target alive. NOT a verdict: -# read the pane first — a hands-on question or -# queued steers look exactly like this -# RUNNING exit 4 none of the above; invoke again -set -euo pipefail - -TARGET=${1:?tmux target} SCRATCH=${2:?scratch dir} -shift 2 -# 25 minutes per invocation; 20 quiet transcript minutes (QUIET=1200) reports QUIET. -# Tune by editing these two numbers — never by adding call-site options. -LOGFILE="" TIMEOUT=1500 QUIET=1200 -while [ $# -gt 0 ]; do - case $1 in - --log) LOGFILE=${2:?}; shift 2 ;; - *) echo "await-interactive: unknown option $1" >&2; exit 1 ;; - esac -done - -mtime() { stat -c %Y "$1" 2>/dev/null || stat -f %m "$1" 2>/dev/null || echo 0; } - -START=$(date +%s) -while :; do - if [ ! -d "$SCRATCH" ]; then - echo "COMPLETED" - exit 0 - fi - if [ -f "$SCRATCH/final-report.md" ]; then - echo "BLOCKED $SCRATCH/final-report.md" - exit 5 - fi - if ! tmux list-panes -t "$TARGET" >/dev/null 2>&1; then - # Scratch present, no report, no session: give a just-finishing teardown - # a moment, then re-check the two terminal states before calling it dead. - sleep 5 - [ ! -d "$SCRATCH" ] && { echo "COMPLETED"; exit 0; } - [ -f "$SCRATCH/final-report.md" ] && { echo "BLOCKED $SCRATCH/final-report.md"; exit 5; } - echo "DEAD" - exit 2 - fi - if [ -n "$LOGFILE" ] && [ -f "$LOGFILE" ]; then - AGE=$(( $(date +%s) - $(mtime "$LOGFILE") )) - if [ "$AGE" -gt "$QUIET" ]; then - echo "QUIET ${AGE}s — read the pane before acting; a user question or queued steer is not a wedge" - exit 3 - fi - fi - if [ $(( $(date +%s) - START )) -ge "$TIMEOUT" ]; then - echo "RUNNING" - exit 4 - fi - sleep 10 -done diff --git a/.kilo_workflow/await-role.sh b/.kilo_workflow/await-role.sh deleted file mode 100755 index 70317768c5d..00000000000 --- a/.kilo_workflow/await-role.sh +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/env bash -# Wait on a dispatched role agent's log and report the round's outcome, per -# .kilo_workflow/WORKFLOW.md. This replaces hand-rolled wait loops: exit codes -# lie (kilo runs die mid-stream and still exit 0), grepping a whole log for a -# sentinel false-passes when the agent quoted one, and a stalled run can sit -# forever without ever writing its marker. -# -# await-role.sh -# -# Completion is proven by the wrapper-owned `.exit` file dispatch-role.sh -# writes — never by text in the log, which the agent also writes. The -# `.meta` sidecar (role, mode, tmux target) picks the sentinel contract: -# an implementer ending with `No findings.` — or a final verifier ending with -# `REPRODUCED.` — is a crashed contract, not a pass. A missing or corrupt -# sidecar is VOID: every legitimate dispatch goes through dispatch-role.sh. -# A dead tmux target with no exit file is VOID immediately. -# -# Blocks up to TIMEOUT seconds (480 — safely under harness command -# timeouts), then reports. Re-invoke while it prints RUNNING. Prints exactly -# one line: -# -# DONE exit 0 round finished with a real verdict; act on it -# VOID ... exit 2 finished without a valid verdict — a crashed -# run, never a pass; discard and dispatch fresh -# STALLED s exit 3 no exit file and the log has been quiet past the -# stall threshold (default 1200s) — kill the tmux -# window/session, verify state, redispatch fresh -# RUNNING exit 4 still working; invoke again -# -# The sentinel is the log's last line. `STOPPED EARLY.` reports as DONE: not -# void, not success; re-dispatch with a continuation handoff. A quiet log is -# judged by mtime, so any tool output the agent produces (builds included) -# counts as liveness. -set -euo pipefail - -LOG=${1:?usage: await-role.sh } -# 8 minutes to produce a verdict; 20 minutes of log silence reads as STALLED. -# Tune by editing these two numbers — never by adding call-site options. -TIMEOUT=480 -STALL=1200 - -ROLE="" -MODE="" -TMUX_TARGET="" -if [ -f "$LOG.meta" ]; then - ROLE=$(sed -n 's/^role=//p' "$LOG.meta" | head -1) - MODE=$(sed -n 's/^mode=//p' "$LOG.meta" | head -1) - TMUX_TARGET=$(sed -n 's/^tmux=//p' "$LOG.meta" | head -1) -fi - -# FINDINGS requires a count of at least 1 — `FINDINGS: 0` contradicts itself -# (zero findings is spelled `No findings.`) and signals a broken report. -case "$ROLE/$MODE" in - plan-reviewer/* | impl-reviewer/*) SENTINELS='^(No findings\.|FINDINGS: [1-9][0-9]*|STOPPED EARLY\.)$' ;; - implementer/*) SENTINELS='^(SLICE COMPLETE\.|STOPPED EARLY\.)$' ;; - e2e-verifier/repro) SENTINELS='^(REPRODUCED\.|CANNOT REPRODUCE\.|VERIFICATION BLOCKED\.|STOPPED EARLY\.)$' ;; - e2e-verifier/*) SENTINELS='^(VERIFICATION (PASSED|FAILED|BLOCKED)\.|STOPPED EARLY\.)$' ;; - *) - echo "VOID missing or corrupt $LOG.meta — dispatch through dispatch-role.sh; discard the round" - exit 2 - ;; -esac - -mtime() { stat -c %Y "$1" 2>/dev/null || stat -f %m "$1" 2>/dev/null || echo 0; } -target_dead() { - [ -n "$TMUX_TARGET" ] || return 1 - ! tmux list-panes -t "$TMUX_TARGET" >/dev/null 2>&1 -} - -START=$(date +%s) -while :; do - NOW=$(date +%s) - if [ -f "$LOG.exit" ]; then - CODE=$(cat "$LOG.exit" 2>/dev/null || echo "?") - SENTINEL=$({ tail -1 "$LOG" 2>/dev/null || true; } | sed 's/^EXITCODE=[0-9]*$//;s/[[:space:]]*$//') - # The human-facing EXITCODE marker is the true last line; the sentinel is - # the line above it. Tolerate either ordering in case the marker write - # raced the exit-file write. - if [ -z "$SENTINEL" ]; then - SENTINEL=$({ tail -2 "$LOG" 2>/dev/null || true; } | head -1 | sed 's/[[:space:]]*$//') - fi - if [[ "$SENTINEL" =~ $SENTINELS ]]; then - echo "DONE $SENTINEL" - exit 0 - fi - echo "VOID exit=$CODE — no valid $ROLE${MODE:+/$MODE} sentinel; discard the round and dispatch a fresh session" - exit 2 - fi - if [ -f "$LOG" ]; then - if target_dead; then - # The pane is gone but the exit file never landed; give the final - # writes a moment to flush, then re-check once before pronouncing. - sleep 5 - [ -f "$LOG.exit" ] && continue - echo "VOID tmux target $TMUX_TARGET is gone with no exit file — the run was killed; discard and dispatch fresh" - exit 2 - fi - AGE=$(( NOW - $(mtime "$LOG") )) - if [ "$AGE" -gt "$STALL" ]; then - echo "STALLED ${AGE}s quiet, no exit file — kill the round's tmux window/session, verify state on disk, redispatch fresh" - exit 3 - fi - elif target_dead || [ $(( NOW - START )) -gt 90 ]; then - # The dispatch redirects into the log the moment kilo starts; a log still - # missing means the window died before launching. - echo "VOID no log at $LOG — the dispatch never started; check tmux and dispatch again" - exit 2 - fi - if [ $(( NOW - START )) -ge "$TIMEOUT" ]; then - echo "RUNNING — no verdict after ${TIMEOUT}s; invoke again to keep waiting" - exit 4 - fi - sleep 5 -done diff --git a/.kilo_workflow/baseline.sh b/.kilo_workflow/baseline.sh deleted file mode 100755 index 4cb70a252bd..00000000000 --- a/.kilo_workflow/baseline.sh +++ /dev/null @@ -1,131 +0,0 @@ -#!/usr/bin/env bash -# Git-state baseline for the E2E verifier's byte-identical restore contract, -# per .kilo/agent/e2e-verifier.md. Snapshotting and comparing NUL-delimited -# status, binary diffs, and untracked hashes by hand is exactly the kind of -# destructive ritual a cheap model gets wrong; this does the mechanical part. -# Restoring is still the agent's job — this tells it exactly what diverged. -# -# baseline.sh snapshot [--include ]... -# baseline.sh check [--include ]... -# -# must live OUTSIDE every repository (scratch). A successful -# check prints `OK`, consumes the snapshot, and exits 0. A mismatch prints one -# line per divergence, retains both sides as evidence, and exits 1. -set -euo pipefail - -CMD=${1:?usage: baseline.sh snapshot|check [--include ]...} -WT=${2:?worktree} DIR=${3:?baseline dir} -shift 3 -INCLUDES=() -while [ $# -gt 0 ]; do - [ "$1" = "--include" ] || { echo "baseline: unknown option $1" >&2; exit 1; } - include=${2:?--include needs a repository-relative path} - case "$include" in - /* | .. | ../* | */../* | */..) echo "baseline: --include must stay inside the worktree: $include" >&2; exit 1 ;; - esac - INCLUDES+=("$include") - shift 2 -done -[ -d "$WT" ] || { echo "baseline: no such worktree: $WT" >&2; exit 1; } -# Canonicalize before comparing — a relative or symlinked path must not smuggle -# the baseline into the repository (or make the repository the baseline). -WT=$(cd "$WT" && pwd -P) -CREATED=0 -[ -d "$DIR" ] || { mkdir -p "$DIR"; CREATED=1; } -DIR=$(cd "$DIR" && pwd -P) -case "$DIR" in - "$WT" | "$WT"/*) - [ "$CREATED" -eq 1 ] && rmdir "$DIR" 2>/dev/null - echo "baseline: the baseline dir must live outside the repository ($WT)" >&2 - exit 1 - ;; -esac - -capture() { - local out=$1 p hash - mkdir -p "$out" - git -C "$WT" rev-parse HEAD > "$out/head" - git -C "$WT" status --porcelain=v2 -z --untracked-files=all > "$out/status.z" - git -C "$WT" diff --binary > "$out/worktree.diff" - git -C "$WT" diff --binary --cached > "$out/index.diff" - # Untracked files: byte hash, mode, and symlink target each — a same-size - # content swap or a mode flip must not slip through. - git -C "$WT" ls-files --others --exclude-standard -z | while IFS= read -r -d '' p; do - if [ -L "$WT/$p" ]; then - printf 'link\t%s\t%s\n' "$(readlink "$WT/$p")" "$p" - elif [ ! -f "$WT/$p" ]; then - # FIFOs and sockets block readers; record existence only. - printf 'other\t%s\n' "$p" - elif command -v sha256sum >/dev/null; then - printf 'file\t%s\t%s\t%s\n' "$(stat -c %a "$WT/$p" 2>/dev/null || stat -f %p "$WT/$p")" \ - "$(sha256sum "$WT/$p" | cut -d' ' -f1)" "$p" - else - printf 'file\t%s\t%s\t%s\n' "$(stat -c %a "$WT/$p" 2>/dev/null || stat -f %p "$WT/$p")" \ - "$(shasum -a 256 "$WT/$p" | cut -d' ' -f1)" "$p" - fi - done | sort > "$out/untracked.tsv" - - # Git omits ignored files from every state above. Hash only the explicit - # ignored paths a verifier is allowed to edit; absence is state too. - : > "$out/included.tsv" - for p in "${INCLUDES[@]+"${INCLUDES[@]}"}"; do - if [ -L "$WT/$p" ]; then - printf 'link\t%s\t%s\n' "$(readlink "$WT/$p")" "$p" - elif [ -f "$WT/$p" ]; then - if command -v sha256sum >/dev/null; then - hash=$(sha256sum "$WT/$p" | cut -d' ' -f1) - else - hash=$(shasum -a 256 "$WT/$p" | cut -d' ' -f1) - fi - printf 'file\t%s\t%s\t%s\n' \ - "$(stat -c %a "$WT/$p" 2>/dev/null || stat -f %p "$WT/$p")" "$hash" "$p" - elif [ -e "$WT/$p" ]; then - echo "baseline: --include supports files, symlinks, or absent paths, not $p" >&2 - return 1 - else - printf 'missing\t-\t%s\n' "$p" - fi - done | sort > "$out/included.tsv" -} - -case $CMD in - snapshot) - # Refuse to re-snapshot: a verifier that re-runs the checklist after its - # temporary edits would otherwise bless the dirty state as the baseline. - [ ! -f "$DIR/head" ] || { echo "baseline: $DIR already holds a snapshot — check against it; never re-baseline mid-run" >&2; exit 1; } - # Capture to a sibling temp dir and publish atomically, so a killed - # snapshot can never pass for a complete one. - TMP=$(mktemp -d "$DIR.tmp.XXXXXX") - { capture "$TMP" && [ -s "$TMP/head" ]; } || { rm -rf "$TMP"; echo 'baseline: capture failed' >&2; exit 1; } - rmdir "$DIR" 2>/dev/null || { echo "baseline: $DIR exists and is not empty — refusing to overwrite" >&2; rm -rf "$TMP"; exit 1; } - mv "$TMP" "$DIR" - echo "baseline recorded in $DIR" - ;; - check) - [ -f "$DIR/head" ] || { echo "baseline: no snapshot in $DIR" >&2; exit 1; } - NOW=$(mktemp -d "${TMPDIR:-/tmp}/kilo-baseline-check.XXXXXX") - # Kept on mismatch so the printed diff commands stay runnable. - trap '[ "${fail:-1}" -eq 0 ] && rm -rf "$NOW"' EXIT - { capture "$NOW" && [ -s "$NOW/head" ]; } || { rm -rf "$NOW"; echo 'baseline: capture failed' >&2; exit 1; } - fail=0 - for f in head status.z worktree.diff index.diff untracked.tsv included.tsv; do - cmp -s "$DIR/$f" "$NOW/$f" && continue - fail=1 - case $f in - head) echo "DIVERGED head: $(cat "$DIR/$f") -> $(cat "$NOW/$f")" ;; - untracked.tsv | included.tsv) - echo "DIVERGED ${f%.tsv} files (hash/mode/link or presence):" - # diff exits 1 on difference — normal here, must not abort the report. - { diff "$DIR/$f" "$NOW/$f" || true; } | { grep '^[<>]' || true; } | sed 's/^/ now:/' - ;; - *) echo "DIVERGED $f (run: diff $DIR/$f $NOW/$f)" ;; - esac - done - if [ "$fail" -eq 0 ]; then - rm -rf "$DIR" - echo "OK" - fi - exit "$fail" - ;; - *) echo "usage: baseline.sh snapshot|check [--include ]..." >&2; exit 1 ;; -esac diff --git a/.kilo_workflow/dispatch-role.sh b/.kilo_workflow/dispatch-role.sh deleted file mode 100755 index c05f1511fea..00000000000 --- a/.kilo_workflow/dispatch-role.sh +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/env bash -# Dispatch a kilo role agent per .kilo_workflow/WORKFLOW.md, encoding the -# contract the learnings exist for: tmux-wrapped (harness timeouts kill bare -# runs), full KILO_*/OPENCODE* env strip, canonical window/log naming, output -# redirected (never piped), and the EXITCODE marker appended as the last line. -# -# dispatch-role.sh