Copi — bots/copilot
Conversational iterion assistant: the DSL, the Cxxx diagnostics, run/resume semantics, backends. Read-only by construction. Newest run first.
2026-08-31 — host-event resumes survive the HTTP acknowledgement
- Observed defect: Studio could acknowledge an executed Copi action with
action-completed, then immediately cancel Copi because the resumed engine inherited the completed HTTP request context. Its target watch stopped with it, breaking thetarget → Copi → CLIsupervision chain. - Repair:
handleDeliverHostEventnow detaches the engine lifetime while preserving request-scoped values, matching the existing human-answer path. Copi also recognizes host receipts bykind, correlates a completed action, verifies it through read evidence and retains the confirmed result in its brief before requesting the next safe step. - Verification: the server lifecycle regression closes the HTTP response before a deterministic resumed turn finishes. On the live
:4893Studio, Copi01a05819-7366-7ede-8142-e9c5623f22bfrequested watchb2e82db0-3185-449f-b4b1-2e1fbb393873for town_planner01a04ee1-e661-71e6-a130-a222f994862d; its failure event woke Copi, then the exact production receipt{action, args, message}was delivered. Copi correlatedaction: run.watch, re-read the target, retained the confirmed watch in its brief and returned to its chat pause without cancellation. - Prompt migration: the prior Copi requested its watch be stopped before the on-disk prompt hash changed; it remains parked as evidence, while the fresh Copi owns the active watch. This avoids a stale assistant silently failing its next resume on the workflow hash guard.
2026-08-30 — complete diagnostic evidence without an unguarded shell
- Observed defect: Copi could inspect bounded Iterion run events but could not search the live workspace by content, routinely missed project-owned evidence under
state/, and received no warning when a large source read crossed a downstream 256 KiB clipping boundary. DB-backed project state was unreachable. Separately, the ambientPOSTGRES_PASSWORD=shortsregisteredshortsas a global literal secret and erased thousands of unrelated source occurrences from persisted observability. - Workspace evidence: claw
read_filenow returns at most 240 KiB with an explicit line continuation marker and acceptsstart_line/line_count.workspace_grepis confined to the active workspace, searches ignored project artifacts such asstate/, and excludes credential files and internal run stores before scanning. - Live evidence: native Bash and Grep remain denied by default. Copi alone declares a
diagnostic_shellalias under an explicitaskrule. On Claude Code its declared, single-line Bash verification request is mapped to that alias only after explicit-deny screening, so the operator approves the full exact command. The bridge permits bounded diagnostics and source- nonmutating targeted tests/validation, never Git/source writes; a multiline command or any other node remains native Bash → deny. - Redaction: short/simple ambient values are no longer eligible for global substring taint. Credential-file denies, launch-env name redaction and the permission boundary still keep those values out of ordinary model inputs; distinctive generated passwords/tokens continue through known-value redaction.
- Verification: tool tests pin explicit large-file continuation, credential-safe search and outside-workspace refusal; permission tests pin native Bash/Grep deny versus diagnostic-shell ask; secretguard tests cover both the
shortscollision and a distinctive ambient secret. The Copi bot validates with only its intentional C128sandbox:nonewarning.
2026-08-29 — tool-pair-safe persistent conversation (run copi-toolpair-fix-smoke-20260829)
- Status: validated on the Tabarria Studio at
:4893; the smoke run was cancelled deliberately after its third successful chat pause. - Versions: Copi from
feat/assistant-epic· Iterionbeb4568ac569-dirty, rebuilt statically with the tool-pair repair. - Method: three real operator turns with
reviewer=on, OpenAI claw forcopi/revise, Claude Code forreview,sandbox:none, no supervisors and no host actions. Turns two and three explicitly referred to the preceding answer to exercise the sharedassistant_conversationslot. - Result: all three passes completed
copi → review → revise → compose → chat; the same claw session id (ee4063e8-…) survived every turn, revise's request grew from 6 to 12 to 18 messages, and no OpenAI function-output 400 or fallback occurred. The final persisted envelope contains 20 messages and seven exactly matchedtool_use/tool_resultids. - Engine hardening: compaction now prunes orphaned tool blocks in both directions without widening the retained window; loading repairs historical envelopes; request construction validates the invariant;
ask_userkeeps only its explicitly pending id. Fallback restores the pre-attempt snapshot under the effective named slot instead of wiping the conversation. The exact 4893 summary/result boundary and tight-budget shrink are covered deterministically inpkg/backend/model/session_toolpairs_test.go.
2026-08-29 — durable conversational continuity
- Observed defect: Copi used
session: inherit_if_availableonclaw, but claw returned no_session_id; every ordinary chat resume therefore ran fresh. The edge wiring was dead andcontext_briefwas the only memory. - Host projection: the manifest's chat node now requests a canonical
conversation_historyprojection. The server rebuilds it from the existing run events, keeps the newest eight messages under an estimated 12k-token cap, and injects it as an ephemeral host input. It is not copied intohuman_answers_recordedor an artifact, so events remain the authority. - Verbatim continuity: claw now packs its compacted
api.Messagehistory into the existing backend-session store forsession: persist. Copi and its private revise pass sharesession_slot: assistant_conversation, ensuring the next turn continues from the answer shown to the operator rather than the pre-review draft. - Fail-safe: a provider-fingerprint change discards the opaque session and falls back to the bounded projection. A present-but-empty continuity key is now a visible
session_degradedevent instead of an Info-only fresh start. - Verification: deterministic tests cover host-input non-persistence, named-slot hand-off, claw envelope round-trip/size cap, history bounds and missing-session observability;
iterion validate bots/copilot/main.botpasses (only the pre-existing C128 sandbox opt-out warning).
2026-08-29 — cross-project failed-bot delegation
- Status: deployed on 4893/4894; deterministic validation and a zero-LLM live delegation smoke pass.
- Scope: Copi
0.1.7, Featurly2.4.0, generic run provenance and launch contracts. No bot id is introduced inpkg/. - Contract: Copi stays read-only and emits
run.launchwith a catalog worker plussource_run_id. The host resolves the failed bot's owner repo, injects a boundedrun_failureenvelope and operator instructions, pins a snapshot commit, requires the worker's declaredworktree:auto, and forcesmerge_into:none. - Isolation: dirty/untracked source content is captured through a temporary Git index without changing the operator checkout/index. A deterministic per-attempt worker run id makes the run store's unique create the CAS for one active repair per failure fingerprint. Consumer re-pin and merge remain separate confirmed operations.
- Return path: the Studio watches worker
finished|failed|cancelledoutcomes and the existing safe host-event wake returns the result to Copi. - Verification: Go tests cover provenance persistence, install sidecars, dirty-tree snapshot isolation and outcome-kind selection; Vitest covers the closed action payload and host-selected terminal watch. Both modified bots pass
iterion validate. Live runrepair-b5b67a91d83531344432d38b-1resolved legacy run01a0451b-dead-700f-8fcb-99b60c155c78toiterion-bots, snapshotted all 38 dirty/untracked entries at hidden commit092ceea8a0d6, ran in an isolated worktree and finished without changing the source checkout or its HEAD (e972a8a308d2).
2026-08-29 — durable target-run watch and safe automatic diagnostic wake
- Status: implementation and deterministic validation complete; live failure smoke is pending deployment below.
- Scope: Copi
0.1.6, generic chat manifest protocol, local/Mongo control plane, Studio action/UX. Existing native-ticket watches are unchanged. - Contract:
run.watchcreates a rooted run-tree → assistant-run link indiagnoseorproposemode. Descendant actionable outcomes are included; successful child completion stays silent and root Done alone resolves it. Outcome events are the fast path and a durable tree sweep is the backstop. Episodes use a CAS lease, concrete outcome-run provenance, failure fingerprint, cooldown, maximum count and conversation-budget guard. - Wake boundary: delivery is accepted only while the assistant is
paused_waiting_humanon its manifest chat node. Running turns stay pending; mid-turnask_userandpaused_operatorare not resumed. Cancelling or finishing the assistant stops its watches; minimizing the dock does not. - Authority: the resume carries a JSON
host_eventfield, not the chatmessage. The transcript renders it as an automatic host event and Copi's prompt forbids treating it as explicit intent.auto_safeis rejected: a browser/localStorage action policy is not server authority. - Verification: FS store tests cover idempotent episodes, one winner under concurrent claims and lease recovery. Server tests pin chat-vs-ask_user/ paused_operator eligibility, the 90% budget guard and stable fingerprints. Studio tests pin host-selected assistant ids, rejection of
auto_safe, and the absence of an operator message for watch wake-ups. Go package tests, Copi validation, TypeScript and targeted Vitest pass.
2026-08-29 — closing an assistant conversation stops its owned run
- Status: deterministic validation complete; production Studio rebuilt and reloaded on ports 4893 and 4894. Browser close smoke pending an operator gesture so no existing run is cancelled without confirmation.
- Scope: Studio assistant lifecycle; Copi bundle remains
0.1.5on branchfeat/assistant-epic. - Trigger: closed conversation tabs could leave Copi runs in
paused_waiting_human. The close path readrunIdand status only from an in-memory snapshot, fire-and-forgotcancelRun, swallowed every error, then deleted the tab and store immediately. A not-yet-hydrated or stale tab sent no cancellation at all. Bot switching dropped the persisted id without any cancellation, and a single conversation exposed only Minimise, no stop control. - Implementation: close, new-session and bot-switch now share one cancel-before-dispose contract. It prefers the conversation-owned run id, never gates on client status, treats 404/410 as already gone, waits for HTTP acceptance, and retains the owner with a persistent Retry toast on real failures. A synchronous memory-only guard deduplicates gestures; destructive live/resumable disposal asks for confirmation. The single-conversation strip now exposes a visible Close conversation and stop its run cross. The dock's minus remains pure minimisation.
- Verification: pure tests cover id resolution, confirmation classification, delayed acceptance, 404/410 and 5xx. Provider tests exercise absent snapshots, deduplication, retained tabs on error and bot switching. Component tests pin the single-tab affordance and minimisation semantics. The complete Studio suite passes (192 files, 1,700 tests), as do TypeScript and the production build; both live servers load the rebuilt asset containing the new close contract.
2026-08-29 — assistant resume handles workflow source drift
- Status: validated locally and deployed to the Studio instances on ports 4893 and 4894.
- Versions: bot 0.1.4 → 0.1.5 · branch
feat/assistant-epic. - Trigger: Copi emitted an explicit
run.resumefor cancelled run01a044da-f33b-712c-b167-0b6ed6795c66. The server correctly refused because the current workflow hash differed from the launch hash, but the assistant card reduced that guard toAPI error 400plus aRetrybutton that could only repeat the same unforced request. - Implementation: assistant resume now mirrors the Pipelines board's two-step source-drift path. It first sends
{}; the exact source-change verdict transitions the card to a warning with Resume with updated workflow; that second explicit gesture sends{force:true}. Other resume errors remain ordinary errors. - Authority boundary:
forceis not accepted in the Copi action contract. The validator strips a model-supplied value, and only host context from the second operator gesture can add it to the API request. Auto-action policy may attempt the normal resume, but can never auto-force through source drift. - Verification: request-boundary tests prove model
force:trueis stripped and host force is honored; action-card tests exercise the 400 → warning → second gesture → forced resume sequence; the existing Pipelines tests keep the same behavior pinned. Typecheck passes.
2026-08-29 — cross-review closes the loop before publication
- Status: validated — deterministic suites plus one live non-empty-review turn after the static rebuild/reload of both studios.
- Versions: bot 0.1.3 → 0.1.4 · branch
feat/assistant-epic. - Trigger: with
reviewer: on, the chat displayed Copi's draft followed by a separate « Revue croisée » block. Copi never received that feedback: the graph wascopi → review → compose, andcomposeconcatenated the two strings deterministically. - Implementation: the reviewed path is now
copi → review → revise → compose → chat.reviseuses Copi's model, fallback ladder and tools; it inherits Copi's backend session when one is exposed, with the explicit question/draft/brief as the cold-session fallback. It challenges the private critique and returns the only operator-visible answer.composeis now a plain delivery projection, never a text concatenator, and carries the revisedcontext_briefinto the next turn. - Integrity: the revision pass may change conversational prose, quick replies and rolling memory only. Typed host actions, editor proposals and companion-file replacements remain the first Copi pass's published requests, so an editorial model cannot replace a host-policy-checked action after the fact. The prompt requires the prose to stay aligned with those immutable requests and preserves deterministic validation verdicts verbatim.
- Verification: the graph contract checks the same-family author session, private critique edge and absence of critique concatenation. The runtime E2E supplies a deliberately wrong draft plus a non-empty critique and proves that the chat receives only Copi's corrected answer; after resume it proves the next turn gets the revised brief and session rather than the draft state.
- Live result: run
copi-review-refine-20260829on the:4894project produced a substantive reviewer critique, enteredrevise, rechecked the review withruns.read, and parked onchatwith onlyoutputs.revise.reply. Neither the original draft nor the critique/« Revue croisée » label appeared in the delivered question payload. This CLI replay intentionally tested the editorial topology, not card resolution (host-attested page context is added by the Studio send path, not by a raw CLI--var initial_message). Claw exposed no resumable session id on this turn, exercising the explicit question/draft/brief fallback successfully.
2026-08-29 — run diagnosis uses host-attested context (:4893, :4894)
- Status: validated — deterministic suites plus one live Copi turn after a static rebuild and reload of both studios.
- Versions: bot 0.1.2 → 0.1.3 · branch
feat/assistant-epic. - Trigger: Copi answered that it could not inspect a failed run in two studios. On
:4893, run01a04943-36a8-7629-ba07-9d309787fab7existed in the server-selected global project store but Copi globbed only the workspace. On:4894,native:d5fc94b2-ca40-4cb5-9056-ea02fb8dfdafwas a task reference; Copi incorrectly treated its UUID as a run id instead of followinglast_run_idto cancelled run01a044da-f33b-712c-b167-0b6ed6795c66. - Root cause: the assistant had typed pointers but no host-resolved facts; its run-debug skill suggested a glob that cannot cross the workspace boundary, while the claw node exposed only workspace reads. The optional reviewer could identify the mistake only when it happened to reach the external store, and could not repair the answer already returned.
- Implementation:
- the Studio resolves attached/active
run,node, andcardreferences, plus explicitnative:task mentions, at send time and stamps a bounded<resolved-assistant-context>containing task state,last_run_id, run status, failing node, error code and error; - a bot-agnostic
runs.readcapability exposes read-onlyrun_get,run_events, andruns_listover the currentRunStore; claw calls stay in-process, non-sandboxed delegate calls use stdio, and sandbox/cloud calls use the existing ephemeral host-MCP listener with the run tenant pinned in the token grant; - Copi and its reviewer use those tools and are explicitly forbidden from reconstructing or globbing
.iterionstore paths; - a selected Pipelines drawer contributes its exact card/run typed reference, so navigate-then-send quick replies resolve after the drawer is active.
- the Studio resolves attached/active
- Isolation: no store directory, workdir, run inputs or arbitrary task body is added to the prompt. Local project isolation remains host-owned; cloud reads are tenant-scoped even though MCP token requests do not carry an operator JWT.
- Verification: Go tests cover the resolver, capability gate, bounded run projection, in-process tools, HTTP tenant pinning and backend/runtime wiring. Studio typecheck plus context/page/dock tests pass. A Copi botreplay golden requires the stamped failed status and failing node without a live model key.
- Live result: studios
:4893and:4894were reloaded on Iterionbeb4568a; both resolver calls returned the expected run/task facts. Live run01a04cb1-e8b3-71b1-90d4-d699dabe8965on:4893calledrun_getandrun_events, citedrun_failedseq. 141, identified internal nodeseal_experienceand missingexperience-map.a2.candidate.json, and warned against a blind Resume. It did not glob or reconstruct.iterion. - Live-run hardening: that replay exposed an oversized raw event page (~770 KB). Before finalizing,
run_eventswas changed to a 192-KB bounded diagnostic projection (max 250 events/page) that omits inputs, prompts and arbitrary tool/node outputs; a regression test seeds all three and proves they do not cross the capability boundary. - Follow-up regression: a task id typed directly in prose (
#native:d5fc…) from the generic Pipelines page bypassed the first implementation, which only resolved active or attached chips. The dock now asks the host resolver on every free-text send; the host recognizes boundednative:mentions, resolves them as cards and followslast_run_id. A dock test pins this generic-page launch shape. Model-visible run-tool errors are also sanitized at the runops boundary, so a missing id reports onlyrun not foundand never the filesystem store path. - Follow-up live result: after rebuilding and reloading both studios, run
01a04cc2-e099-784d-8f05-01b2407497econ:4894replayed the exact generic-page question. Copi followed the card to01a044da-f33b-712c-b167-0b6ed6795c66, calledrun_getand two pages ofrun_events, and correctly distinguished cancellation from failure: twocontext canceleddelegate errors aroundplan_macroled torun_cancelledevents 240 and 246.
2026-08-28 — cross-review without memory, reviewer probing the Studio API (runs 01a04999, 01a049e1, 01a049e4)
- Status: validated — two fixes to the reviewer's input and prompt, measured on two follow-up runs.
- Versions: bot 0.1.1 → 0.1.2 · iterion
fbd56fc6(worktree onfeat/assistant-authoring-files; the fix lands onfix/copi-reviewer-context→feat/assistant-epic). - Method: studio shorts (
:4894, Copi loaded from the worktree via--bots-path),reviewer: on, Copi onclaw+openai/gpt-5.6-sol, reviewer onclaude_code+claude-fable-5(Claude Code 2.1.251). Two-turn scenario in the leading-question shape: « Je vais relancer la tâche pipeline native:… avec un reset, c'est bien ça ? » then « ok go ». Launched with the dock's own request shapes (POST /api/runswithbot_id+vars, thenPOST /api/runs/{id}/resumewith{answers: {message}, force: true}), so the runs sit in the studio's store. - Result:
- before (
01a04999, operator-reported): reviewer $0.93/turn; one deniedBash curl http://127.0.0.1:4894/api/v1/…per turn; critique opening with "I can't reach the Studio API from here"; at turn 2 « "ok go" ne porte aucun contexte » — it had been handed only the last message. - after v1 (
01a049e1: brief on the edge + boundary stated in the prompt): $0.53 / $0.22; the turn-2 critique is contextual (« l'opérateur a déjà dit ok go … le brief lui-même dit que la confirmation a eu lieu »); still one denied call per turn —ToolSearch select:Glob,Grep, then aBash lson turn 2. - after v2 (
01a049e4:ToolSearchallow-listed,assistant_actionspassed): $0.80 / $0.15; turn 2 is an EMPTY critique ("Nothing to contest") — the reviewer saw the explicitpipeline.task.resetrequest matching the confirmed ask. No Bash attempt on either turn.
- before (
- Value: the reviewer now judges the answer as one message of the thread and can tell a typed proposal from an omission. Silence at turn 2 is the whole point: a reviewer that manufactures critique on a confirmed action trains the operator to skip it.
- Findings / misses:
session: fresh+ an edge carrying onlyoperator_message/replyis a reviewer with no memory. Copi's owncontext_brief, rewritten the same turn, held exactly the missing context — it now rides the edge. Assumed structural limit: the brief is authored by the model under review, so a shared misunderstanding is ratified, not caught. A raw bounded transcript is the follow-up if that ever bites.- Under
claude_codethe node'stools:list is inert; the deny gate is the only boundary, and a prompt that does not state it gets probed every turn. Stated, Fable 5 stopped curling the API. - Claude Code 2.1.251 ships NO Glob/Grep (
ToolSearchanswers "No matching deferred tools found"), so the workflow'sGloballow is inert on that backend — the reviewer can onlyReadexact paths. The prompt now gives the store layout (<workspace>/.iterion/or~/.iterion/projects/<encoded-workdir>/, cards atdispatcher/issues/native__<uuid>.json) so a Read is a lookup, not a guess — the v2 reviewer lost four Reads guessingissues/<uuid>.json.ToolSearchis allow-listed because a denied loader makes the model conclude the tool is gone and reach for Bash. - Copi (
gpt-5.6-sol) emittedrun.reseton01a04999turn 1 — not in the catalogue; the Studio would have rejected it. One line in the Host-actions catalogue ("there is NOrun.reset"); both follow-up runs usedpipeline.task.reset. - Scrubber side-effect worth knowing: the shorts project's
POSTGRES_PASSWORDequals its directory name, so every path the reviewer touched reads…/video/__ITERION_SECRET_env_POSTGRES_PASSWORD__/…in the events. The masking works; the password should be rotated.
- Engine hardening: none needed — everything sat in the bot. Candidate: expose the resolved store dir to prompts (a
{{vars.store_dir}}or an engine-provided ref) so a chat bot stops inferring it from the cwd. - Lessons for next run: the leading-question test (22/08 below) needs a SECOND turn of the « ok go » shape — that is what exposes a reviewer without memory, and an empty turn-2 critique is the pass condition, not a failure to engage. Reviewer cost is dominated by its reads ($0.80 when it reads skills and probes paths, $0.15 when it has what it needs), so the store-layout hint is a cost lever as much as a correctness one.
2026-08-28 — declared companion-file proposal (run 01a04962)
- Status: validated — paused normally after one Copi turn; no file was saved.
- Versions: bot 0.1.0 · iterion
8d8704bb(branchfeat/assistant-authoring-files, stacked onfeat/assistant-epic). - Method: local
legendary-film-chaptereditor marker with a complete livemain.bot, a server-minted snapshot of 15 declared companion files, and an explicit request for one exact comment replacement infilm_pipeline/matter.py. Reviewer off; Copi onclaw+openai/gpt-5.6-sol. - Result: Copi made one
read_filecall and emitted one boundedfile_changesreplacement with the exact host session/revision andfile_changes_intent: explicit. The 4894 preview endpoint resolved it to a real before/after diff. A final disk read proved the original comment was still present; commit was deliberately not called. 522 unpriced tokens were reported for the answering node.
Value and finding
The complete path works model → typed artifact → host-bound hash → server preview without giving Copi a write tool or asking it to copy a SHA-256. The server snapshot exposed exactly the manifest perimeter, including the three workspace files needed by this project.
The active main.bot was 101,157 characters and therefore travelled inline on this turn. That is correct for a live unsaved buffer and still under the 160-KB safety ceiling, but it dominates prompt size even when only a companion script is edited. A future optimization can make an unchanged saved editor document referenceable; V1 must keep inlining the unsaved buffer because the run cannot otherwise read it honestly.
2026-08-22 — first dogfood, with cross-review on (runs 01a02a31, 01a02a32, 01a02a39)
- Status: validated — after three attempts, two of which failed on real defects the bot's tests could not have caught.
- Versions: bot 0.1.0 · iterion
1ae6b850(branchfeat/assistant-epic) - Method:
iterion run bots/copilot/main.bot --store-dir "$PWD/.iterion" --var reviewer=on --var initial_message=…. Copi onclaw+openai/gpt-5.6-sol; reviewer onclaude_code+claude-fable-5. No board writes, no worktree (worktree: none),sandbox: none. - Result: converged to the chat pause on every successful turn. 3 answering turns + 3 review turns. $2.46 – $2.57 per reviewed turn, essentially all of it the reviewer.
Value — the reviewer earns its place, but not on every turn
Turn 1 ("explique C176") returned an empty critique. That is the designed verdict for a sound answer, and it was correct: Copi's answer was accurate, with file:line citations that matched what a human reading the same code found independently.
Turn 2 was a leading question — "je vais mettre sandbox: none et permission: off sur tous mes bots de chat, c'est bien ça ?". The reviewer bit, and usefully: it caught Copi inverting the semantics of permission: deny, citing docs/permissions.md:38. Third run, same question shape: the reviewer confirmed the answer and then added the caveat Copi had left out — that a permission: boundary is only real on claude_code, claw and pi, quoting docs/permissions.md:157-159.
So: silence when there is nothing to say, a specific and sourced objection when there is. That is the behaviour the prompt asks for, and it held.
The economics are the open question. $2.46 to return an empty critique is a real number, and on a standing conversation it compounds. Off-by-default is right; whether an operator would ever leave it on for a long session is not established by three turns.
Findings — three defects, all found by running, none catchable by the tests
tools: [list_files]— unknown tool, run dies on the first node.list_filesis not a registered claw tool; the registry rejects it at execution.iterion validatecompiled it happily: it does not check tool names against the registry. The canonical builtins areread_file, write_file, glob, grep, file_edit, web_fetch, bash(pkg/backend/tool/claw_builtins.go:91-98), plusskillwith askills:block. ACxxxdiagnostic for unknown tool names on claw nodes would have turned a failed run into a compile error — worth a ticket.concat()is the ARRAY primitive; string joining is+.composeandgateboth usedconcat(a, b, c)on strings. It compiled, and turn 1 passed —if()short-circuits, and that turn's critique was empty, so the faulty branch never evaluated. The failure surfaced only on the turn where the reviewer had something to say, i.e. the first turn where the feature did its job. Pinned byTestCopilot_CrossReview_ComposesBothHalves, which drives the branch with a non-empty critique.{{input.x}}on an EDGE resolves against the RUN's inputs, not the source node's input — while the compile-time check validates against the source node's schema. C034 rejects{{input.reviewer}}onchat -> copibecausechat_inputhas no such field; at run time the same reference oncopi -> gatereads the run inputs. The bot worked by coincidence, because--var reviewer=onpopulates both. Replaced with{{vars.reviewer}}, which says what is meant. The compiler and the runtime disagreeing about whatinputmeans is an engine issue, not a bot one — worth a ticket.
Engine hardening
- Nothing committed to
pkg/from this run. Two candidate tickets above (unknown-tool diagnostic;{{input.x}}edge-vs-compile divergence). - Related and already filed: #476 — grok and kimi cannot enforce
permission:, which is why Copi's model ladder runs on claw rather than the CLI forfaits an operator would prefer.
Lessons for next run
- The reviewer must not share the answering model's family, and on this host that is not free.
anthropic/…on claw is not usable when the only Anthropic credential is the Claude Code OAuth forfait (iterion modelsreports every Anthropic row usable via claude_code alone). The reviewer's primary would have failed every turn and fallen through to the openai rung — Copi's own family — turning cross-review into a mirror, silently. It runs onbackend: "claude_code"for that reason, which is legal because claude_code enforces the gate and the node issession: fresh. - A leading question is the test that matters. "Explain X" produced an empty critique three times; "I'm going to do X, right?" is what made the reviewer speak. Any future evaluation of this feature should use the second shape.
- Measure the reviewer's cost against a real session before recommending it to anyone. Three turns is not a sample.
- Do not put the run store in
/tmpon this host — it is a 16 GB tmpfs. An earlier studio in this same session filled it and took the machine's shell with it. SeeHANDOFF-worktree-pool.md.
