@agentskit/harness 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,41 @@
1
+ # ADR-0027: Keep-pushing loop as composition over Orca
2
+
3
+ - Status: Accepted
4
+ - Date: 2026-09-11
5
+
6
+ ## Context
7
+
8
+ Teams want a coding-agent loop that drains one person's Linear queue around the clock: pick a ticket, freeze a
9
+ verifiable contract, run a worker in an isolated worktree, review, merge, repeat — while never exceeding the
10
+ machine, never guessing when acceptance criteria are missing, and surviving provider usage limits. The harness already
11
+ owns the deterministic pieces (dispatch ledger, machine pressure, delivery gates, evidence). Orca already owns
12
+ scheduling, worktrees, terminals, Linear access and the visibility surface. Nothing owned the composition.
13
+
14
+ ## Decision
15
+
16
+ 1. The loop lives in this package as **Adapter + Composition** (`src/adapters/{command,orca-cli,providers,linear-orca,
17
+ github-cli,code-review}.ts`, `src/loop/*`). The kernel is unchanged and never imports it (`pnpm test:boundaries`).
18
+ 2. Every project-specific value lives in `loop.config.yaml` (zod-validated). The code carries no repository, person,
19
+ path or model id. Env variable **names** may appear in config; values never do.
20
+ 3. External systems are reached only through argv-based, timeout-bounded process execution injected as a
21
+ `CommandRunner`. Adapters never spawn a shell; composition supplies the real runner; tests supply fakes.
22
+ 4. Orca is the single scheduler and visibility surface: two automations (`<prefix>-tick`, `<prefix>-deliver`), each
23
+ gated by a read-only `--precheck` that exits 0 only when work exists, run an agent prompt whose sole job is to
24
+ invoke `ak-harness loop <stage>`. No cron, launchd or daemon of our own.
25
+ 5. Model routing is tiered per role (`orchestrator`, `reviewer`, `builder`, `watcher`); within a tier the first
26
+ provider with usage available wins. Usage comes from `orca account list`; an auth/quota failure marks the provider
27
+ cooling down (exponential, never before the reset Orca reports) and the next candidate is tried.
28
+ 6. Autonomy envelope: the loop may claim tickets, freeze contracts, dispatch workers, nudge them, review, squash-merge on
29
+ a clean review plus green checks, move Linear states and clean worktrees. It must not merge PRs touching
30
+ `selfEditPaths`, must not force-push or bypass approvals, must not recover leases (human only, ADR-0021), and must
31
+ escalate with one deduplicated comment when a contract has no executable acceptance criterion or when fix rounds
32
+ are exhausted.
33
+
34
+ ## Consequences
35
+
36
+ - `yaml` and `zod` become runtime dependencies of the package.
37
+ - The loop is verifiable offline: every stage has fixture-driven tests and `--dry-run` prints the exact argv.
38
+ - Provider CLIs (`claude`, `codex`, `grok`, `opencode`) are configuration, not code; adding one is a YAML entry with a
39
+ TUI template, an optional headless template and an `agentskit-review` provider id.
40
+ - Windows is supported through Node's process model; WSL is detected and capped to one worker because host
41
+ antivirus load is invisible from the distro.
@@ -0,0 +1,45 @@
1
+ # ADR-0028: MCP is an adapter-only boundary
2
+
3
+ ## Status
4
+
5
+ Accepted — 2026-09-11.
6
+
7
+ ## Context
8
+
9
+ MCP (Model Context Protocol) is a useful integration surface for read-only
10
+ inspection and, later, gated tool calls. The keep-pushing loop and kernel must
11
+ remain usable without any MCP host, SDK, or network dependency. MODULE-BOUNDARIES
12
+ and ADR-0026 already classify MCP as an adapter concern; 0.6.0 needs an
13
+ explicit decision before any public bridge ships.
14
+
15
+ ## Decision
16
+
17
+ 1. **Adapter-only.** MCP lives under `src/adapters/mcp.ts` (and future adapter
18
+ modules). Kernel modules do not import MCP clients, hosts, or transports.
19
+ 2. **Default-deny policy.** Every tool call goes through an allowlist
20
+ (`allowTools`) and a `PolicyGate` (or compatible `evaluate`). Missing
21
+ allowlist entry or non-`allow` decision returns `{ status: 'blocked' }`
22
+ without invoking the underlying call.
23
+ 3. **Read-only profile first.** The initial public surface is a policy-gated
24
+ call bridge intended for read-only / inspection tools. Mutating MCP tools
25
+ require a later ADR covering authorization, idempotency, and audit evidence.
26
+ 4. **Harness works without MCP.** Default loop config keeps `mcp.enabled: false`.
27
+ No MCP process is started by install, tick, or deliver.
28
+ 5. **Not wired into loop tick/deliver in 0.6.0.** The bridge is exported for
29
+ composition and future CLI/doctor use. Tick and deliver must not call it in
30
+ this release.
31
+
32
+ ## Consequences
33
+
34
+ - Consumers can plug MCP behind the same policy gate used by session tool
35
+ recording without coupling the kernel to a vendor host.
36
+ - Loop automation remains deterministic and offline-testable when MCP is off.
37
+ - A later release may wire a read-only MCP profile into doctor or a dedicated
38
+ CLI after eval coverage; mutating tools stay out until gated separately.
39
+
40
+ ## Alternatives considered
41
+
42
+ - **Kernel MCP host:** rejected; violates ADR-0026 and forces an optional
43
+ network dependency into deterministic gates.
44
+ - **Wire MCP into tick/deliver now:** rejected for 0.6.0; needs allowlist
45
+ provenance, audit events, and eval coverage first.
package/docs/LOOP.md ADDED
@@ -0,0 +1,276 @@
1
+ # Keep-pushing loop (`ak-harness loop`)
2
+
3
+ The loop drains one person's Linear queue through Orca worktrees, 24/7, with role-based model routing and
4
+ usage-aware fallback. The harness supplies the deterministic parts (claims, machine pressure, gates, evidence);
5
+ Orca supplies scheduling, worktrees, terminals, Linear access and visibility. Everything project-specific lives in
6
+ `loop.config.yaml` — switching project or person is a config change, never a code change.
7
+
8
+ Status: **complete (phases 1–5).** Config, doctor, adapters, the dispatch tick, the deliver stage and the Orca
9
+ automation installer are in place. See [ADR-0027](ADR-0027-keep-pushing-loop.md).
10
+
11
+ ## Commands
12
+
13
+ ```bash
14
+ ak-harness loop validate -f loop.config.yaml # schema check + effective config + hash
15
+ ak-harness loop doctor -f loop.config.yaml --json # readiness report (exit 1 when a blocking check fails)
16
+ ak-harness loop doctor --no-probe # skip provider probe commands
17
+ ak-harness loop precheck tick # exit 0 when a slot, a builder and a candidate exist (Orca --precheck)
18
+ ak-harness loop tick --dry-run --max 1 # plan the next dispatch, print the exact orca argv, write nothing
19
+ ak-harness loop tick # dispatch up to <free slots> workers
20
+ ak-harness loop contract ENG-123 [--refresh|--dry-run] # freeze or show the orchestrator contract for one issue
21
+ ak-harness loop precheck deliver # exit 0 when a dispatched issue is in flight
22
+ ak-harness loop deliver [--dry-run] [--issue ENG-123] # drive dispatched workers to merge
23
+ ak-harness loop install [--yes|--force|--skip-rehearsal|--skip-local-config|--dry-run|--plain] # guided: overlay → checks → rehearsal → confirm
24
+ ak-harness loop uninstall [--dry-run] # remove them
25
+ ak-harness loop status # what Orca knows: enabled, trigger, provider, latest run
26
+ ak-harness loop hook # one status line for a SessionStart hook; never mutates
27
+ ak-harness loop debrief [--issue ENG-123] [--since 24h] # human-facing: what is in flight, held, escalated
28
+ ak-harness loop watch [--issue ENG-123] [--once] [--interval 30] # poll delivery/PR; DONE|FAILED|ACTION_REQUIRED
29
+ ak-harness loop retro [--since 7d] [--json|--learnings] # weekly digest + calibration suggestions
30
+ ```
31
+
32
+ ### Debrief and watch
33
+
34
+ `loop debrief` is the human companion to the automations: a read-only Markdown (or `--json`) snapshot of
35
+ in-flight dispatches, delivery phase (waiting for PR / review / fix round / ready to merge), holds that need a
36
+ person, recent escalations and provider cooldowns. It does not call Orca or `gh` — only the loop state directory —
37
+ so it is safe to run from a SessionStart hook or a chat agent that needs context before acting.
38
+
39
+ `loop watch` polls `delivery.json` (and optionally the live PR via `gh`) and prints line-oriented events:
40
+
41
+ | Event | Meaning |
42
+ |---|---|
43
+ | `DONE` | Delivery finished as merged (or the PR is MERGED) |
44
+ | `FAILED` | stuck / abandoned / failed / PR closed without merge |
45
+ | `ACTION_REQUIRED` | held for a human, incomplete review twice, or fix-round findings |
46
+ | `PROGRESS` | still moving (waiting for PR, review pending, …) |
47
+
48
+ Use `--once` for a single snapshot; omit it to block until a terminal outcome (or `--timeout <seconds>`).
49
+
50
+ ## Running 24/7 with Orca
51
+
52
+ `loop install` is guided and rendered with Ink when stdin/stdout are terminals (plain lines otherwise). When no
53
+ `loop.config.local.yaml` exists next to the config it first offers to create one: it lists the Linear team members from
54
+ Orca, asks whose queue this machine drains and, optionally, how much RAM to keep free and the worker ceiling, then
55
+ writes the gitignored overlay and reloads. It then runs the doctor and the automation-environment checks (harness and review CLIs on
56
+ PATH, `gh auth status`, checkout registered in Orca, queue owner), stops on any failed check unless `--force`,
57
+ offers a dry-run tick rehearsal (one orchestrator call, nothing written), lists the exact automations it will
58
+ create, and asks for confirmation before touching Orca. `--yes` accepts every prompt for scripted setups; without
59
+ a TTY the command refuses unless `--yes` or `--dry-run` is given; `--plain` keeps the old check-free behaviour.
60
+
61
+ It creates two automations in the Orca runtime, each bound to the main checkout as an existing workspace
62
+ with session reuse:
63
+
64
+ | Automation | Trigger | Precheck (plain command, exit 0 = run) | Prompt |
65
+ |---|---|---|---|
66
+ | `<prefix>-tick` | `schedule.tick` | `<harnessCommand> loop stage tick -f <config>` (runs the tick, exits 1) | never reached under `runner: precheck` |
67
+ | `<prefix>-deliver` | `schedule.deliver` | `<harnessCommand> loop stage deliver -f <config>` (runs deliver, exits 1) | never reached under `runner: precheck` |
68
+
69
+ With the default `schedule.runner: precheck` the **precheck command is the stage itself** (`ak-harness loop stage
70
+ tick|deliver`): it runs the harness, prints the JSON report and always exits 1, so Orca records every run as
71
+ `skipped_precheck` with the report in `precheckResult.stdout` and never opens an agent session. This is deliberate:
72
+ Orca caps a precheck at 600 s, so `contract.timeoutMs` (default 300 s) plus dispatch must fit one run; an Orca-launched agent starts in bypass-permissions mode and waits for a human to accept the warning, which leaks one
73
+ stuck session per run. `schedule.runner: agent` keeps the legacy behaviour for providers that run unattended.
74
+ Runs and their output are visible in Orca's Automations view and via `loop status`.
75
+
76
+ `schedule.harnessCommand` must resolve inside Orca's environment: install the harness globally
77
+ (`npm i -g @agentskit/harness`) or set an absolute command. `loop install` warns when it cannot find it on this shell's PATH.
78
+
79
+ Optional SessionStart hook for Claude Code (`.claude/settings.json` of the target project):
80
+
81
+ ```json
82
+ { "hooks": { "SessionStart": [{ "hooks": [{ "type": "command", "command": "ak-harness loop hook -f loop.config.yaml", "timeout": 10 }] }] } }
83
+ ```
84
+
85
+ It prints `loop: installed (2/2, last run …)` or the install command; it never installs or changes anything.
86
+
87
+ ## Deliver
88
+
89
+ For every issue the loop dispatched (`<stateDir>/issues/<id>/dispatch.json`) and has not finished:
90
+
91
+ | Situation | Action |
92
+ |---|---|
93
+ | No PR, worker terminal alive and active | wait |
94
+ | No PR, idle ≥ `workerIdleTimeoutMin` | one check-in via `terminal send`; idle again after that → **stuck**: lease released, issue → `returnState` + `blocked`, worktree kept |
95
+ | No PR, terminal gone (> 5 min after dispatch) | **stuck** as above |
96
+ | PR touches `selfEditPaths` | **held**: one PR comment, no review, no merge |
97
+ | PR conflicting | rebase instruction to the worker, once per head (does not count as a fix round) |
98
+ | CI red | failing check names to the worker; counts as a fix round |
99
+ | CI pending / required check missing | wait |
100
+ | CI green, head not reviewed | `agentskit-review --pr … --block <minSeverity> --result … [--post]` with the first available reviewer candidate |
101
+ | Review findings ≥ floor | findings to the worker; counts as a fix round; same head is never re-reviewed |
102
+ | Fix rounds exhausted (`maxFixRounds`) | **blocked**: Linear comment + label + `returnState`, PR comment, lease released, worktree and PR kept |
103
+ | Review clean, `merge.auto` | `gh api PUT …/merge` with `sha=<reviewed head>` (GitHub refuses if the head moved) → Linear attach + comment + `doneState`, worktree removed when `cleanupWorktree` |
104
+ | PR merged outside the loop | same completion path |
105
+ | PR closed without merge | **abandoned**: lease released, issue → `returnState`, worktree kept |
106
+
107
+ State lives in `<stateDir>/issues/<id>/delivery.json` (reviews per head, fix rounds, nudges) and every decision is
108
+ appended to `<stateDir>/events.ndjson`. `--dry-run` reports the decision for each issue without touching anything.
109
+
110
+ ## One tick
111
+
112
+ 1. **Intake** — `orca linear list-issues` once per configured state, filtered and ordered locally; issues already
113
+ leased, linked to a live worktree or sitting on their branch are *busy* and skipped.
114
+ 2. **Admit** — `assessSlots` (machine) and the harness dispatch ledger (`<stateDir>/coordination`) decide how many
115
+ workers may start; a claim is atomic per issue/worktree/branch.
116
+ 3. **Contract** — the orchestrator (first available candidate across `models.orchestrator` tiers) reads the issue and
117
+ the repository read-only and returns a JSON contract between `<<<LOOP_CONTRACT … LOOP_CONTRACT>>>`. Issue text is
118
+ wrapped as untrusted data. An auth/quota failure on one candidate marks it cooling down and the next is tried.
119
+ Contracts are cached under `<stateDir>/issues/<id>/contract.json` while the issue is unchanged.
120
+ 4. **Dispatch or escalate** — a contract with at least one executable outcome and no blocking ambiguity becomes a
121
+ worker: `orca worktree create --linear-issue <url> --base-branch <base> --no-parent` (no `--agent`: Orca's agent
122
+ launcher runs Claude in bypass-permissions mode and waits for a human), then `orca terminal create --command
123
+ "<providers.<id>.tui>"`, `terminal wait --for tui-idle`, and the brief goes in through `terminal send`. The issue
124
+ moves to `In Progress` and one dedicated comment is posted. Otherwise the loop posts one
125
+ `needs-info` comment (deduped by contract digest), adds the label, and moves on without consuming a slot.
126
+
127
+ Every side effect is recorded in `<stateDir>/events.ndjson`; `dispatch.json` per issue links the worktree,
128
+ terminal handle, provider/model and lease for the deliver stage.
129
+
130
+ Start from [`loop.config.example.yaml`](../loop.config.example.yaml) at the package root.
131
+
132
+ ## What the doctor checks
133
+
134
+ | Check | Source | Blocking |
135
+ |---|---|---|
136
+ | `orca.version` / `orca.runtime` | `orca --version`, `orca status --json` | yes — the loop cannot run without a reachable Orca runtime at or above `orca.minVersion` |
137
+ | `provider.<id>` | PATH lookup, `orca account list` usage windows, `orca agent hooks status`, env key names, optional probe, cooldown store | no — reported per provider |
138
+ | `routing.<role>` | tiers from `models.<role>` filtered by provider availability | yes — a role with no available provider blocks |
139
+ | `machine.slots` | `sampleMachine` + `adaptiveConcurrency`, free RAM reserve, WSL cap, running worktrees | no — 0 free slots is a warning, not a failure |
140
+ | `linear.queue` | `orca linear list-issues` per configured state, filtered and ordered locally | yes — an unreachable Linear blocks |
141
+
142
+ ## Model routing
143
+
144
+ `models.<role>` is a list of tiers; each tier is a list of `provider/model`. Tiers are tried in order and, inside a
145
+ tier, providers in declaration order. The first provider that is **available** wins. A provider is available when
146
+ its binary is on PATH, its auth is not known to be missing, no Orca usage window is at or above
147
+ `cooldown.exhaustedPercent`, it is not cooling down, and its optional probe passed.
148
+
149
+ Providers authenticate through their own CLI login (`claude login`, `codex login`, `grok login`); only providers
150
+ declared `auth: api-key` need an environment variable, and the loop never reads its value. Orca has no per-run model flag; the chosen model is rendered into `providers.<id>.tui` (for example
151
+ `codex -m {model} --full-auto`) and launched in the worker terminal.
152
+
153
+ When a provider runs out of usage the loop records a cooldown in `<stateDir>/provider-cooldowns.json`:
154
+ `initialMin` doubling up to `maxMin`, never earlier than the reset instant Orca reported.
155
+
156
+ ## Machine slots
157
+
158
+ `maxAgents = max(floor, min(adaptiveConcurrency(ceiling), ramBound, wslCap?))` where `ceiling` defaults to
159
+ `cpus / 2`, `ramBound` fits `agentRssMb` agents into free RAM minus `minFreeRamGb`, and the WSL cap applies only
160
+ inside a WSL distro (host Defender load is invisible there). The loop never lowers a running worker; it only
161
+ decides whether to start another.
162
+
163
+ ## Integrations used by the loop
164
+
165
+ The loop talks to the rest of AgentsKit through **adapters and CLI seams**, not by importing those packages into
166
+ the published dependency tree. `@agentskit/harness` stays dependency-light (`commander`, `ink`, `react`, `yaml`,
167
+ `zod`); Doc Bridge and Code Review are optional at runtime.
168
+
169
+ ### Doc Bridge (context for the orchestrator)
170
+
171
+ | Item | Detail |
172
+ |---|---|
173
+ | Adapter | `src/adapters/doc-bridge.ts` → `createDocBridgeContextProvider` |
174
+ | Loop wiring | `resolveDocContext` in `src/loop/contract.ts` |
175
+ | Trigger | `.doc-bridge/index.json` exists under `project.root` |
176
+ | Knob | `contract.maxContextReferences` (default `6`; `0` disables) |
177
+ | Behaviour | Query is `"<issue id> <title>"`. Up to N deterministic references are appended to the orchestrator prompt. Missing or malformed index → **no refs** (loop continues). |
178
+ | Boundary | No `@agentskit/doc-bridge` import; the adapter only reads the local index contract ([ADR-0003](ADR-0003-doc-bridge-context-binding.md)). Index build/refresh stays with Doc Bridge (`pnpm docs:bridge:index` in repos that use it). |
179
+
180
+ ### Code Review (`agentskit-review`)
181
+
182
+ | Item | Detail |
183
+ |---|---|
184
+ | Adapter | `src/adapters/code-review.ts` → `runCodeReview` / `buildReviewArgv` |
185
+ | Loop wiring | Deliver stage, after CI is green and the head has not been reviewed |
186
+ | CLI | `delivery.review.cli` (default `agentskit-review` from `@agentskit/code-review`) |
187
+ | Knobs | `mode` (`trusted-local` recommended so CLI logins work), `transport` (`headless` for current Grok CLI), `profile` (`fast` fits a 600 s Orca stage; `full` needs batching), `votes`, `concurrency`, `minSeverity`, `deadlineMs`, `maxCalls`, `post` |
188
+ | Provider/model | First available candidate from `models.reviewer` tiers |
189
+ | Verdicts | exit `0` → clean (merge path); `1` → findings ≥ floor (fix round); `2` / timeout / incomplete → wait and retry |
190
+ | Boundary | Argv + `--result` file only; the harness never embeds the review SDK. |
191
+
192
+ ### Eval battery (package quality, not the live loop)
193
+
194
+ `evals/manifest.json` plus `runEvalBattery` / `pnpm test:eval-battery` score harness components (`doc-bridge`,
195
+ `code-review`, `orca-worktree`, `memory`, …) under a frozen fixture provider. That is the **library** eval gate.
196
+ The live SDLC loop does **not** call the eval battery on every PR; it calls `agentskit-review` on each head.
197
+
198
+ Loop-focused Vitest coverage lives under `test/loop-*.test.ts`, `test/review.test.ts`, and `test/doc-bridge.test.ts`.
199
+
200
+ ## AgentsKit ecosystem map (what the harness has vs what the loop uses)
201
+
202
+ Legend: **Loop** = wired into `ak-harness loop …` today · **Kernel** = public API / contracts available to callers ·
203
+ **Compat** = pinned in `compatibility/manifest.json` · **Absent** = no seam yet.
204
+
205
+ | Capability | Status | Where | Notes for SDLC |
206
+ |---|---|---|---|
207
+ | **Doc Bridge** | Loop + Kernel + Compat | `adapters/doc-bridge.ts`, contract stage | Optional context on contract freeze. Playbook/docs surfaces appear as Doc Bridge scopes when the index includes them. |
208
+ | **Code Review** | Loop + Compat | `adapters/code-review.ts`, deliver | Live adversarial review before auto-merge. |
209
+ | **Orca / Linear / GitHub** | Loop | `adapters/orca-cli.ts`, `linear-orca.ts`, `github-cli.ts` | Scheduler, worktrees, queue, PR merge. |
210
+ | **Coding-agent CLIs** | Loop | `adapters/providers.ts`, `models.*` tiers | Claude / Codex / Grok / OpenCode via TUI + headless templates — not an AgentsKit agent registry. |
211
+ | **Memory** | Loop + Kernel + Compat | `kernel/memory.ts`, `loop/memory.ts` | Approved learnings (`loop learning promote`) shrink Doc Bridge/issue text in contract + brief. |
212
+ | **Eval** | Kernel + Compat | `kernel/eval.ts`, `evals/manifest.json` | Battery + `assessAgentEval`. **Not run inside tick/deliver**. |
213
+ | **Runtime (process / Docker)** | Kernel | `execution/runtime.ts` | Tool runtimes with attestation for kernel/agent sessions. Loop workers run as Orca terminals + provider TUIs instead. |
214
+ | **Plugin / context registry** | Kernel | `kernel/plugins.ts`, `CONTEXT_PROVIDER_SLOT` | Typed slots so Doc Bridge / Playbook / custom providers plug in without kernel changes. Loop uses Doc Bridge directly today. |
215
+ | **Playbook practices** | Loop (via Doc Bridge scopes) | `contract.briefScopes` → worker brief | Titles/paths for `playbook` / `for-agents` scopes listed in the brief when indexed. |
216
+ | **RAG (`@agentskit/rag` / os-rag)** | Kernel adapter (opt-in) | `adapters/rag-context.ts` | Argv/`ContextProvider` seam; enable via `rag.enabled` + `contract.contextProviders`. No hard dep. |
217
+ | **AgentsKit agent registry** | Composition (opt-in file) | `loop/agent-registry.ts` | Optional `agents.registry.yaml` role→TUI/argv overlay. Not OS marketplace. |
218
+ | **MCP / event bridge** | Kernel adapter + ADR-0028 | `adapters/mcp.ts` | Policy-gated tool bridge; **not** wired into tick/deliver in 0.6.0. |
219
+ | **`@agentskit/core` / `@agentskit/eval` / `@agentskit/memory`** | Compat pins only | `compatibility/manifest.json` | Upstream packages are compatibility-tested; harness does **not** depend on them at runtime. Callers adapt them through the seams above. |
220
+
221
+ Compatibility report for 0.4.0 was **fail-closed** on code-review quality baselines and the no-Harness pilot cohort; see `compatibility/report.md`. Refresh after each release (qualification + pinned revisions).
222
+
223
+ ## What can be added to help the SDLC
224
+
225
+ **0.6.0 ships the backlog below** (memory, doctor freshness/review probe, brief scopes, deliver smoke, agent registry, RAG provider, MCP seam+ADR, Docker verify config, weekly retro automation). Remaining work is dogfooding and deeper MCP/OS registry integrations.
226
+
227
+ Ordered by leverage for a keep-pushing loop (config/adapters first; no kernel redesign required for the early items).
228
+
229
+ | Priority | Addition | Why it helps | Suggested shape |
230
+ |---|---|---|---|
231
+ | P0 | **Memory in contract + brief** | Stop re-deriving the same repo facts; carry approved decisions across tickets | On contract freeze / worker brief, `recall` from an `AgentMemoryAdapter` (backed by `@agentskit/memory` or a file store under `<stateDir>`). Persist only human-`promoteLearnings` / retro-approved records. |
232
+ | P0 | **Doc Bridge freshness gate** | Orchestrator context goes stale when the index is old | Doctor check: index exists, `contentHash` age, optional `docs:bridge:index` hint when missing. |
233
+ | P1 | **Playbook / for-agents snippets in the brief** | Workers skip repo conventions that already exist as docs | Resolve Doc Bridge (or a Playbook `ContextProvider`) with scopes `playbook` + `for-agents` and render a bounded “must follow” block into `renderWorkerBrief`. |
234
+ | P1 | **Eval smoke on deliver (optional)** | Catch harness/component regressions before merge on harness itself | Config flag to run a **bounded** subset of `evals/manifest.json` (or project `ak-verify`) as an extra deliver gate — not a full battery on every product PR. |
235
+ | P1 | **Review transport/doctor** | Incomplete reviews burned the pilot (login / model id / stage budget) | Doctor probes `agentskit-review --help`, configured transport, and a one-lens trusted-local dry call; surface `review-tool-errors` in retro. |
236
+ | P2 | **RAG context provider** | Large codebases exceed Doc Bridge’s deterministic top-N | New adapter implementing `ContextProvider` over `@agentskit/rag` / os-rag; same freeze-into-contract rules as Doc Bridge (hash + cap). |
237
+ | P2 | **AgentsKit agent/skill registry** | Reuse named agents (reviewer, security, docs) instead of free-form CLI templates | Optional `agents.registry` file or OS dispatcher lookup → map role → argv/TUI. Keep fail-closed when the registry entry is missing. |
238
+ | P2 | **MCP tools behind policy** | Controlled access to issue trackers, browsers, internal APIs | MCP adapter + `createPolicyGate` allowlist; record hashed tool events. Requires ADR (MODULE-BOUNDARIES already flags this). |
239
+ | P3 | **Kernel runtime for untrusted tools** | Sandbox one-off scripts the worker must not run on the host | Offer Docker tool runtime as an opt-in for verify commands; loop default stays Orca worktree. |
240
+ | P3 | **Weekly Linear retro automation** | Close the continuous-improvement loop without a human remembering `loop retro` | Orca schedule → `loop retro --learnings` → comment on a fixed Linear issue (project vs harness sections already split). |
241
+
242
+ Non-goals for the loop: embedding LLM SDKs, reading API key values from config, or making `@agentskit/*` hard dependencies of the published package. New ecosystem pieces enter as adapters/plugins with argv/timeouts and eval coverage ([ADR-0026](ADR-0026-kernel-adapters-boundary.md), [ADR-0027](ADR-0027-keep-pushing-loop.md)).
243
+
244
+ ## Boundaries
245
+
246
+ Adapters (`src/adapters/command.ts`, `orca-cli.ts`, `providers.ts`, `linear-orca.ts`) depend on kernel contracts
247
+ only and receive a `CommandRunner`; they never spawn a shell. Composition (`src/loop/*`) supplies the real
248
+ runner and wires adapters together. See `docs/MODULE-BOUNDARIES.md`.
249
+
250
+ ## Security notes
251
+
252
+ - `loop.config.yaml` holds env variable **names**, never values.
253
+ - Every external call is argv-based with a timeout; `ok: false` envelopes fail closed.
254
+ - Linear text is data. Later phases render it into worker briefs inside delimiters and never execute it.
255
+
256
+ ## Continuous improvement: `loop retro`
257
+
258
+ `ak-harness loop retro --since 7d` reads `<stateDir>/events.ndjson`, every `issues/<id>/{contract,dispatch,delivery}.json`,
259
+ `provider-cooldowns.json` and (unless `--no-orca`) the Orca run records of the two automations, and prints a Markdown
260
+ digest:
261
+
262
+ - **Numbers** — contracts frozen, escalation rate, dispatches by provider, merged/blocked/stuck/abandoned/in flight,
263
+ review outcomes, fix rounds, median dispatch→merge, cooldowns, Orca runs (idle/work/timed out, durations).
264
+ - **Problems** — escalation reasons grouped by shape, blocked/stuck/abandoned issues.
265
+ - **What worked** — merged issues with worker, lead time and fix rounds.
266
+ - **Adjustments — project** — rule-based suggestions about the target project, each with its evidence and the
267
+ `loop.config.yaml` knob to turn (`escalation-rate`, `review-floor`, `fix-rounds`, `stuck-workers`, `review-incomplete`,
268
+ `provider-cooldowns`, `idle-loop`, `lead-time`, or `steady`).
269
+ - **Adjustments — harness** — defects or limitations of the library itself, derived from events the loop only emits
270
+ when its own machinery misbehaved (`worker-relaunch`, `dispatch-failures`, `contract-failures`, `merge-refusals`,
271
+ `review-tool-errors`, `stage-timeout`), with a pointer to the harness issue tracker. `--target project|harness`
272
+ filters one side.
273
+
274
+ The headings follow the harness retro grammar, so `--learnings` prints `LearningRecord`s in `proposed` state; a human
275
+ promotes them with `promoteLearnings` (ADR-0019 keeps that decision human). The calibration loop is: read the digest →
276
+ change one knob in `loop.config.yaml` → next digest measures the effect.
@@ -9,7 +9,7 @@ does not claim that the proposed 0.4.0 boundary has already been accepted.
9
9
 
10
10
  | Class | Meaning | Dependency rule |
11
11
  | --- | --- | --- |
12
- | Kernel | Deterministic contracts, state transitions, policy and metric projections | May use Node standard library and other kernel modules; must not import adapters, provider SDKs, CLI composition, or credentials. |
12
+ | Kernel | Deterministic contracts, state transitions, policy and metric projections | May use Node standard library and other kernel modules; must not import adapters, provider SDKs, CLI composition, credentials, or UI libraries (`ink`/`react` are composition-only). |
13
13
  | Execution support | Local process, filesystem, event, evidence, and run lifecycle plumbing | May depend on the kernel; must expose provenance and fail closed at trust boundaries. |
14
14
  | Adapter | Provider-specific or external-system integration | May depend on kernel contracts; must not be imported by kernel modules. |
15
15
  | Composition | Package and CLI entry points | May compose kernel, execution support, and adapters; consumers use `src/index.ts`. |
@@ -73,6 +73,30 @@ modules, not a provider dependency.
73
73
  | `src/adapters/agent.ts` | Adapter | Structured coding-agent execution with bounded timeout and failure classification | `errors`, `resilience`, `adapter-contract` | Agent/provider callback supplied by caller |
74
74
  | `src/adapters/orca.ts` | Adapter | Safe, idempotent Orca dispatch plan | `errors`, `hash`, `preflight` | Orca CLI arguments; no execution |
75
75
  | `src/adapters/tracking.ts` | Adapter | Idempotent provider-neutral tracking transition | `errors`, `hash` | User-supplied Linear/GitHub/etc. handler |
76
+ | `src/adapters/command.ts` | Adapter | Shell-free command runner seam, PATH executable lookup, Orca JSON envelope parsing | stdlib | Command runner supplied by composition or tests |
77
+ | `src/adapters/orca-cli.ts` | Adapter | Typed `orca … --json` calls: version/status/account/agent hooks, worktree ps/create/set/rm, terminal list/send/wait/read, automations list/create/edit/remove/run | `errors`, `command` | Orca CLI via the injected runner |
78
+ | `src/adapters/providers.ts` | Adapter | Coding-agent CLI availability: binary, auth, Orca usage windows, exhaustion, cooldown, optional probe | `command` | PATH, env var names, `orca account list` payload |
79
+ | `src/adapters/linear-orca.ts` | Adapter | Linear queue via `orca linear list-issues`, issue detail, status/comment/label/attach writes with deterministic `--write-id`, `TrackingAdapter` over Orca | `errors`, `hash`, `orca-cli`, `tracking`, `command` | Linear through the Orca CLI |
80
+ | `src/adapters/github-cli.ts` | Adapter | Pull request snapshots, check classification, self-edit path guard, optimistic squash-merge and comments via `gh` | `errors`, `command` | GitHub through the `gh` CLI |
81
+ | `src/adapters/code-review.ts` | Adapter | `agentskit-review` argv, `--result` parsing, severity floor, worker-facing findings | `command` (type-only) | AgentsKit code review CLI via runner |
82
+ | `src/loop/config.ts` | Composition | `loop.config.yaml` schema (zod), loading, model reference parsing | `errors`, `hash`, `model-policy`, `yaml`, `zod` | Project config file |
83
+ | `src/loop/process.ts` | Composition | Real shell-free `CommandRunner` with timeout and output caps | stdlib, `command` (type-only) | Child processes |
84
+ | `src/loop/slots.ts` | Composition | Machine slot assessment: adaptive concurrency, RAM reserve, WSL cap, floor | stdlib, `machine`, `config` | Host CPU/memory |
85
+ | `src/loop/routing.ts` | Composition | Tiered role → provider/model selection over provider availability | `model-policy`, `providers` (type-only), `config` | None |
86
+ | `src/loop/cooldown.ts` | Composition | Provider cooldown store with exponential backoff | stdlib, `providers` | `<stateDir>/provider-cooldowns.json` |
87
+ | `src/loop/doctor.ts` | Composition | Loop readiness report: Orca, providers, routing, slots, workers, queue | adapters, `config`, `cooldown`, `routing`, `slots` | Orca CLI via runner |
88
+ | `src/loop/contract.ts` | Composition | Task contract schema, orchestrator prompt (issue text as untrusted data), marked-JSON parsing, dispatchability assessment, candidate fallback with auth/quota classification, contract cache | `errors`, `hash`, `resilience`, `doc-bridge`, `config`, `routing` (type-only), `zod` | Headless coding-agent CLI via runner |
89
+ | `src/loop/brief.ts` | Composition | Worker prompt: frozen contract + repository rules + protected paths + done signal | `contract`, `config`, `linear-orca` (type-only) | None |
90
+ | `src/loop/tick.ts` | Composition | One keep-pushing tick: intake, admit (slots + dispatch ledger claim), contract, dispatch into an Orca worktree, Linear transition, escalation, precheck | adapters, `coordination`, `errors`, `hash`, `brief`, `config`, `contract`, `cooldown`, `doctor`, `routing`, `slots` | Orca CLI + Linear via runner; `<stateDir>` files |
91
+ | `src/loop/deliver.ts` | Composition | Deliver stage per dispatched issue: PR detection, self-edit hold, conflict/CI/review fix rounds via terminal, review at head, optimistic squash-merge, Linear Done, cleanup, stuck/abandoned escalation | adapters, `coordination`, `errors`, `config`, `cooldown`, `doctor`, `routing`, `tick` | Orca, Linear, GitHub, agentskit-review via runner; `<stateDir>` files |
92
+ | `src/loop/install.ts` | Composition | Orca automation specs (`<prefix>-tick`, `<prefix>-deliver`) with read-only prechecks, idempotent create/edit by name, uninstall, status and the SessionStart hook line | `command`, `orca-cli`, `providers`, `errors`, `config`, `cooldown`, `doctor`, `routing` | Orca automations via runner |
93
+ | `src/loop/guided-install.ts` | Composition | Interactive install: doctor + environment preflight, dry-run rehearsal, confirmation, install, status; readline IO injected | `command`, `orca-cli`, `config`, `doctor`, `install`, `tick`, stdlib readline | Terminal prompts; Orca via runner |
94
+ | `src/loop/local-config.ts` | Composition | Per-machine overlay wizard: Linear team members via Orca, queue owner and machine tuning answers, YAML rendering and reload | `orca-cli`, `config`, `yaml` | Orca via runner; writes `loop.config.local.yaml` |
95
+ | `src/loop/retro.ts` | Composition | Retro digest over `events.ndjson`, per-issue state, cooldowns and Orca runs; rule-based calibration suggestions; Markdown that `parseRetro` can lift into learnings | `orca-cli`, `hash`, `learning`, `config`, `contract`, `cooldown`, `deliver`, `install`, `tick` | Orca via runner (optional); `<stateDir>` files |
96
+ | `src/loop/debrief.ts` | Composition | Human-facing read-only snapshot of what the loop is working on (in-flight, holds, escalations, cooldowns) | `config`, `contract`, `cooldown`, `deliver`, `retro`, `tick` | `<stateDir>` files only |
97
+ | `src/loop/watch.ts` | Composition | Poll delivery (+ optional live PR) and emit DONE/FAILED/ACTION_REQUIRED/PROGRESS for agents or humans | `command`, `github-cli`, `config`, `deliver`, `tick` | optional `gh` via runner; `<stateDir>` files |
98
+ | `src/loop/ui/components.tsx` | Composition | Ink components: check rows, sections, banner, spinner, select/confirm/text prompts | `ink`, `react`, `doctor` (type-only) | Terminal |
99
+ | `src/loop/ui/terminal.tsx` | Composition | `createRichIO`: Ink-backed IO for TTYs with a plain-text fallback | `ink`, `react`, `components`, `guided-install` (type-only) | Terminal |
76
100
 
77
101
  ## Allowed dependency directions and exceptions
78
102
 
@@ -125,16 +149,25 @@ Orca, and tracking. No adapter implementation is re-exported wholesale.
125
149
 
126
150
  ## External integration inventory
127
151
 
128
- | Integration | Current location | Side effects | 0.4.0 boundary |
152
+ How these seams are wired into the keep-pushing loop (and what is still only a kernel contract) is summarized in
153
+ [`LOOP.md`](LOOP.md#integrations-used-by-the-loop) and the [AgentsKit ecosystem map](LOOP.md#agentskit-ecosystem-map-what-the-harness-has-vs-what-the-loop-uses).
154
+
155
+ | Integration | Current location | Side effects | Boundary |
129
156
  | --- | --- | --- | --- |
130
157
  | Doc Bridge | `src/adapters/doc-bridge.ts` | Reads a local index | Keep behind `ContextProvider`; measure context hit/quality separately. |
131
158
  | Orca | `src/adapters/orca.ts` | None; produces argv and lifecycle projections only | Keep lease/worktree/issue-lock/SHA planning provider-neutral; execution belongs to the orchestrator. |
132
159
  | Linear/GitHub/other tracker | `src/adapters/tracking.ts` callback | Caller-owned network mutation | Require idempotency key and explicit tracking authorization. |
160
+ | Orca CLI (loop) | `src/adapters/orca-cli.ts`, `src/adapters/linear-orca.ts` via `CommandRunner` | Read-only `--json` calls in the doctor; dispatch/mutation arrive in later loop phases | Argv only, never a shell string; every call bounded by a timeout; envelope `ok:false` fails closed. |
161
+ | Coding-agent CLIs | `src/adapters/providers.ts` | PATH lookup and optional probe command | Env keys are names only; usage comes from Orca, never from provider SDKs. |
162
+ | GitHub | `src/adapters/github-cli.ts` via `CommandRunner` | `gh pr view/list`, `gh api PUT …/merge` with `sha=<reviewed head>`, `gh pr comment` | Merge is refused by GitHub when the head moved; every call argv-based and bounded. |
163
+ | AgentsKit code review | `src/adapters/code-review.ts` via `CommandRunner` | `agentskit-review --pr … --result <file> [--post]` | Exit codes 0/1/2 plus the private result file decide clean/findings/incomplete; the floor is `--block`. |
133
164
  | Process runtime | `src/execution/runtime.ts` | Starts child processes | Execution support; policy and evidence gates remain kernel decisions. |
134
165
  | Docker runtime | `src/execution/runtime.ts` | Starts Docker containers | Optional sandbox selected by config, never a mandatory kernel dependency. |
135
166
  | LLM provider/model | Caller/plugin | Provider call and token spend | Bind provider/model in experiment metadata; do not embed SDKs in kernel. |
136
- | Memory backend | Caller/plugin; `memory.ts` contract | Backend reads/writes | Keep record validation in kernel; backend adapter owns persistence. |
137
- | MCP/event bridge | Not implemented | Future network/event effects | Add as adapters only after a separate ADR and eval coverage. |
167
+ | Memory backend | Caller/plugin; `memory.ts` + `loop/memory.ts` | Backend reads/writes; loop file store under `stateDir` | Only human-promoted approved records enter loop prompts; prefer-over-DocBridge shrinks tokens. |
168
+ | RAG context | `src/adapters/rag-context.ts` | Optional argv / injected query | No hard `@agentskit/rag` dep; same ContextProvider freeze rules as Doc Bridge. |
169
+ | Agent registry (file) | `src/loop/agent-registry.ts` | Reads YAML | Role→TUI/argv overlay; fail closed only when `agents.requireRegistry`. |
170
+ | MCP/event bridge | `src/adapters/mcp.ts` + [ADR-0028](ADR-0028-mcp-adapter-boundary.md) | Policy-gated tool calls | Adapter-only in 0.6.0; **not** wired into tick/deliver. |
138
171
 
139
172
  ## Review status
140
173