Skip to content

Agent: coding ​

Every coding workspace receives task-directed coding-check guidance: select a useful bounded baseline from repository context and report the actual result. run_check provides typed receipts where the bound workspace, executor and ledger support them; known pre-execution capability limitations permit ordinary shell evidence without a receipt claim. Explicit legacy startup-smoke policies retain the separate compatibility contract. Takes a task from Slack, scopes fast, implements the change in its sandbox, pushes the branch, and submits a typed PR description — Switchboard renders the body and opens the PR itself (pr-description.md item 5). Its unit of delivery is a pushed branch plus a validated PrDescription, not prose; the PR URL comes from code.

  • Code: src/agents/registry.ts (CODING_SYSTEM; resident-path variant CODING_SYSTEM_RESIDENT; seeded-sandbox variant CODING_SYSTEM_SEEDED), src/tools/attach.ts (attach_file: a run's file into the conversation)
  • Docs: The agents and their toolsets
  • Budgets: 90 min / 64k tokens (the turn guard is derived: 540, six a minute — harness-pi.md item 15) · no built-in effort — set it per deployment/channel/user/thread/message through the config layers (routing-and-config.md item 2; e.g. config set channel efforts.coding=medium — the wall clock is the real budget, and a 31-min live run at the model's default effort spent 97 % of it thinking between one-line greps) · toolset full

Behavior ​

  1. Scope first, hard rule (≤5 tool calls): identify the target repo and surface; genuinely ambiguous → ask ONE question and stop the turn. Never clones multiple repos or maps the org to avoid asking.

  2. Workflow: get the repo in place (cold path: clone the bound repo with gh repo clone or git clone "$GIT_DOOR_REMOTE", ≤1 repo/task; resident path: the warm worktree is already attached — see 7) → branch → select and record a task-relevant bounded baseline when applicable → implement matching surrounding style → record scoped verification → commit → push the branch → submit the typed description (see 3). Switchboard renders the PR body at the pushed head and opens (or updates) the PR itself; the agent never opens a PR — with gh or any API call — and both prompts carry an explicit never-merge/never-approve instruction mirroring the review prompt's (a write-scoped token puts a merge one command away). The identity rewrite runs before the open or edit (record 0062; src/execution/identityRewrite.ts): the run's commits — the paginated compare base...tip (compareRange), less the branch's start state the dispatch recorded at attach (CodingPrTarget.startState, read against the first attached full head and acknowledged in the existing ledger before writable tools open. A restart restores that immutable run/requester/thread/repository/ref/base/unit/step-bound receipt without reading the advanced branch. A legacy resume that already pushed but has no receipt keeps an UNKNOWN start state and fails closed; invalid evidence or a failed durable acknowledgment opens no writable tools; a pushed branch OTHER than the recorded one is subtracted at the head the run's own first push found it at — CodingPrTarget.pushedStart, off the push's own status line: [new branch] is an empty start, old..new bounds the start at old and the rewrite refuses a range that no longer holds that head, since the history below it is not the run's to rewrite — and a pushed branch with neither record has an UNKNOWN start state and fails closed) — must each carry an allowed author (the requester pair, the bot pair, or a start-state author pair on a commit whose (author pair, author date, message) fingerprint equals a start commit's), the bot pair as committer, and only the requester's or the bot's Co-Authored-By trailers, as exact (name, email) pairs; from the first commit that fails, the chain is rebuilt over the Git Data API (POST /git/commits with the same tree, the rebuilt parents, foreign co-author lines dropped, the author kept when it passes and otherwise corrected with the original date, the committer sent explicitly as the bot pair; then a forced ref move) and the compare re-read — a tip that moved is rebuilt once more, a third disagreement is unreadable. On unreadable (over 300 commits, an unknown start state, a failed read, a ruleset's 422/409) nothing is opened or edited and the reply says why (a pr_not_opened note) — a run the dispatch resolved no repository or branch for at attach recorded no start state at all (the read was never fired, even when the post-step later discovers the repo from the workspace's own origin, pr-description.md item 5), so it fails closed the same way with a reason saying no read was attempted, never that one failed; otherwise the pull request opens at the rebuilt tip — the body rendered there, head.sha pinned after the open with a mismatch running the rewrite once more, and a second rewrite that answers unreadable leaving the already-open pull request as it is with a warning on the reply and a pr_head_unverified note naming the reason, never a success claim over an unverified head — the reply note and the pr_opened event carry the count re-authored, an [identity] line carries the identities replaced, and the requester's bound login is added as assignee after GET /assignees/<login> answers 204 (a 404 skips it with one log line). The same rewrite runs before the post-step EDITS an open pull request the run's unproven push heads (the wind-down endings of pr-description.md item 5): on unreadable the body is not edited and the note says why; a tip the rewrite settled is where the body renders. An edit of the thread's own pull request that follows no push (CodingPrTarget.ownPr — the run pushed nothing, that path's premise) judges nothing and edits the body without a rewrite: there are no run commits, and a rewrite there could only re-author commits the run did not create. The author env is on (authorEnvEnabled; execution.md item 5 carries the four variables): a bound requester's pair is read and allowed as author, and a rewrite corrects a failing author to it — the flag stays as the rollback lever to the bot-only rollout, under which the requester pair is not read and a rewrite sets the bot pair.

  3. Typed PR description by default: every PR ships from a PrDescription object submitted through submit_pr_description (pr-description.md) — the user never has to ask for review guidance. Right after implementing, the agent understands the change better than anyone, so it brings that context forward. The prompt's content contract (PR_DESCRIPTION_TEMPLATE in registry.ts) maps every rendered field to its object field — the renderer owns the labels and the folds, the agent never authors PR-body markdown. The body is a fixed-size map for the reader with everything for agents collapsed under it (docs/decisions/0050): title (the PR title's single source), tldr (two sentences for a reader with zero context, ≤300 visible characters), why (the problem and the motivation with the triggering issue/request hyperlinked, ≤400), pointers (Where to look, 1 to 7 rows of { label ≤60, text ≤160, risk? ≤100, anchor } in reading order, each anchor a { path, from, to } line range at the pushed head, rendered as a link and never as embedded code), feedbackWanted (≤200), risk (blast radius, rollback, the split considered over 400 changed lines, ≤300), verified (one line for a person, ≤200); collapsed: decisions (0 to 10, rationale ≤400), validation (1 to 30 criteria with proofs), agentNotes (optional, ≤2000). Every cap counts visible characters (a link's target excluded) and the tool refuses an over-cap object naming every field over its cap with the count, the visible characters to remove and a prefix that fits (pr-description.md item 5), so the map is the same size for a 3-file and an 80-file PR and the resubmit is a copy. Rules baked into the prompt: default for EVERY PR (not on request) — including a PR that already exists when the run pushes, whoever opened it (a person, dependabot, an earlier run): after every push the agent reads the PR's current title and body (github_pull_get reads a PR number; gh pr view where gh exists), judges them against the change at the pushed head, and resubmits the object describing the PR as it now is, carrying forward what is still true and anchoring the pointers at the new head — an out-of-date description is a bug and "someone else's PR" is never a reason to leave it (enforced, not only stated: a run that pushed onto an open PR without resubmitting gets one bounded description turn asking for it, and a push still without one is flagged by the post-step — pr-description.md item 5); prose unwrapped (no hard line breaks); the triggering issue/request always hyperlinked; validation states exactly what was run, never fabricated. The craft — which seven pointers, the mechanical anchor rules, what goes below the fold — lives in the first-party pr-description skill (skills.md item 11, skills/pr-description/SKILL.md); the prompt instructs loading it with use_skill BEFORE authoring the pointers, so the rules live in one place and every coding run's load is a visible skill_use event. The resident variant's diff digest informs the object's content (which files the pointers must name, what belongs in risk) rather than pasted markdown.

    • The map (supersedes the Tour, and before it the prose Changes and How to review sections): the reader's part of the body is capped by the schema field by field — a TL;DR, the why, at most seven pointers { label, text, risk?, anchor } rendered as N. [label](permalink) text ⚠ risk, the feedback wanted, the risk, one line of verification — so an 80-file PR gets the same rows as a 3-file PR and the author chooses the main files; decisions, the validation criteria and notes for agents render collapsed under it. Each anchor is a (path, from, to) line range at the pushed head; the sha is a render-time input the SYSTEM supplies, so the regenerate-on-every-repush rule is code: after a later push the agent resubmits with corrected anchors and the body is re-rendered at the new head. Pointers are links, never bare permalinks: a bare permalink is what GitHub embeds as a code block, and the map is a map, not a copy of the diff. The craft lives in the first-party pr-description skill (skills.md item 11, skills/pr-description/SKILL.md); the prompt keeps the field list and instructs loading that skill with use_skill BEFORE authoring the pointers. The author writes it right after implementing, so it costs no extra model call and carries the most context anyone has. The shape and its numbers are argued in docs/decisions/0050.
  4. Reads task context itself (gh issue view, gh pr view) — requires the GitHub App's Issues permission (see execution.md §5).

  5. Maintains the status-card checklist (outcomes, never commands; ✱ from the item's first command, ✓ only once its result is read — the one rule of run-visibility.md item 2).

  6. Reports faithfully: failing tests and skipped steps are stated plainly; the final message leads with the outcome, and Switchboard appends the PR link when it opens (or updates) the PR. The ordinary coding answer is one canonical projection: runner-confirmed publication status first, the actual PR API result when one exists, then the agent's attributed work report (changes, failed or skipped checks and retained work). That report is not write authority: an unsupported model claim is visibly unverified even when no write was accepted. The newest pending, unknown or rejected write qualifies any earlier confirmed head; a matching remote tip or PR lookup is only observation. After restart, a bound accepted checkpoint remains publication authority when the remote still holds its head, including when an older branch push receipt survives. A newer durable accepted receipt replaces an older pushed-head event on the same ref; a later identity rewrite or salvage event keeps its newer head. A metadata-only PR update is named only after the API edit succeeds. Wind-down, stop, freshness and audience guards keep their own composers. A run that pushed but submitted no description is reported honestly by the dispatcher instead of with a claimed PR: when the pushed branch already heads an open PR (a follow-up that repushed an existing PR's own branch) the reply names that PR as updated by the push, otherwise it carries the branch compare URL (pr-description.md item 5).

  7. Resident-path variant (resident-repos.md §31): when the run lands in a resident repo environment, the dispatcher swaps in CODING_SYSTEM_RESIDENT via RunOptions.system (the AgentDef is never mutated). It replaces scope-first/clone with: the workspace is a READY git worktree at the thread's ready ref, which may be a protected base or an owned publication branch, with deps installed and build warm — no cloning, no installing, no repo discovery. gh is not in the resident image: relayed GitHub read tools provide issue, PR and file data, while git fetch uses the configured Git Door and the revocable read bearer; publication uses a runner-owned publish_branch effect whose credential never enters the model shell. The door limits reads to the bound repository and writes to one owned branch. The prompt refuses credential inspection and alternate routes after an auth refusal. A trusted publication binding keeps its exact owned ref; an existing PR without that binding stays local with no alternate branch; an ordinary task without a binding creates a new branch from the ready checkout, even when that checkout is non-default. An existing PR publishes through publish_branch with its exact owned ref and full authorized old-head lease; absent trusted publication authority or isolated transport leaves the commit local. The agent calls diff_digest to shape the description, then submits it — the bot opens the PR, so the prompt carries no PR-creation API call at all.

  8. The unit contract (agent-ship.md item 13): a coding run started for a plan unit — a plan runner's child, or the by-hand receipt rendered with contract render and pasted into the request — finds a ## Contract block in its first user turn, rendered by OpenSwitchboard from the plan itself and never assembled by the agent (an agent that assembles its own brief chooses what to leave out), under fixed sub-headings in a fixed order: ### First instruction (the rebase of the unit's branch onto the merged parent; a conflict ends the unit), ### Unit (the unit's own plan section verbatim), ### Spec rows (the spec items the unit names with their current proof bindings), ### Agent rules (the repository's AGENTS.md, else CLAUDE.md, else none) and ### Guards (specs:check, specs:coverage --test-guard, hygiene:check, decisions:check, one line each on what they refuse). Both prompts (UNIT_CONTRACT, one text shared by CODING_SYSTEM and CODING_SYSTEM_RESIDENT, so the sandbox and resident children read the same rule) name the block and its sub-headings in that order, make it outrank the free-text task beside it, put the first instruction first, require every listed test scenario added as a test, every named spec row updated so its proof binding resolves, the agent rules followed and no named guard weakened, and state the rule the review holds the diff to: a test scenario the unit listed and the diff did not add is a finding at minor severity — the same severity as a spec contradiction (agent-review.md item 17). The agent never edits the plan record; a unit it finds wrong, or a criterion it could not prove, is said in the handoff (item 9) and in the final message (until decided, a child reports a follow-up, never proposes a unit). A run with no such block — every task-string run — is unchanged: the paragraph describes a block that is not there.

  9. The unit handoff (agent-ship.md item 14): the contract's return edge, as data. A coding run that carries a ## Contract block submits, through submit_handoff — the same tool path as the description, in the full toolset right after submit_pr_description and never side-effect-free — one typed Handoff { deviations: [{ from, to, why }], followUps: [{ what, where }], unproven: [{ criterion, why }], landed?: [{ what, where }] }: where it departed from the unit as written and why, what it found and did not do and where it belongs, which of the unit's test scenarios or criteria it could not prove, and — optionally — what of the unit was already on the base when it began and the pull request or commit that carries it (the fact that ends a unit with nothing left to push already_landed, agent-ship.md item 12: the run pushes nothing of its own and opens no pull request). The tool validates the object (parseHandoff, src/core/ship/handoff.ts: the three lists present, at most HANDOFF_MAX_ITEMS entries each, every field a non-empty string of at most HANDOFF_MAX_FIELD_CHARS once trimmed, unknown keys dropped) and answers a bad one with a string error naming the path, never a throw; a valid one reaches the dispatcher's sink — the last valid call wins, an invalid call leaves the valid one standing, and a resumed run restores the one its earlier generation submitted, as it restores the description — and is acknowledged with its counts; with no sink listening the tool says so rather than acknowledging a recording that never happened. Both prompts (UNIT_HANDOFF, one text shared by CODING_SYSTEM and CODING_SYSTEM_RESIDENT, right after the contract paragraph) tie the call to the block, place it once after submit_pr_description and before the final message, name the three lists with their fields and the optional landed list with its (what, where) — the way a unit with nothing left to push ends done: push nothing of your own and open no pull request — say that the run records it and the parent posts it to the unit's board issue where a person decides each row's disposition and that the agent never edits the plan's ledger, require an empty handoff submitted as three empty lists — never skipped, because a missing handoff reads as an unfinished run, not as nothing to say — and forbid the call without a block. The contract's first instruction splits the fast gates' receipts (GATE_RECEIPTS, agent-ship.md item 13; the gates themselves are the coding prompts' own FAST_GATES_BEFORE_PUSH paragraph, item 13 below — the changed-set forms; the full suite and the full typecheck are CI's gate, never the child's criteria): each gate command's exit line is pasted into the PR description's validation table as the row's proof — the handoff carries no verified list — and a gate the run could not execute goes under this handoff's unproven and is never claimed clean. Where it lands: on the run's record (run-history.md item 2, redacted like every stored string) for every coding run that submitted one — a plain coding run with the block pasted into its request (the by-hand receipt) posts nowhere, having no unit issue — and, for a ship coding round whose contract names the unit's board issue, on that issue as a comment the parent posts (agent-ship.md item 14).

  10. Files the person should see reach the conversation (src/tools/attach.ts): a coding run that renders a screenshot (playwright screenshot), a PDF or a recording posts it with attach_file { path, comment? } — the workspace file, whole, into the requesting thread, where an image renders inline; a link to a file on GitHub is not a picture. The tool is full-only (the read-only and workspace-less agents never post files) and never side-effect-free. It touches neither host nor platform: the bytes come off the Executor seam's readBytes (execution.md item 19, capped at 10 MiB) and go out through the channel's attachFile (Slack: slack-channel.md item 10; the CLI harness writes the file to a temp dir the reply names), which the dispatcher binds into the tool context exactly when the requesting channel has one — a child run's opened thread forwards it by method. Each missing half is named, never papered over: a channel without uploads ("this conversation's channel takes no file uploads — link to the file instead"), an executor without a byte read, a missing or empty or over-cap file (nothing posted), a refused upload with the platform's message. The lead is the comment or, absent, the file's own name; the upload is titled by the last path segment. With an artifact store (execution.md item 20, record 0033) the same tool moves the file BY REFERENCE and the bot process never holds the bytes: the dispatcher binds ToolContext.artifacts (the store, the run's id, a per-run sequence, the run page's link and the channel's reply) whenever artifacts: is configured, and the channel's uploadTicket (ChannelIO.uploadTicket({ name, size }) → { url, complete(lead) }, Slack's files.getUploadURLExternal / files.completeUploadExternal) when the channel has one. The chain, each step naming its own failure and stopping the rest: stat -c %s in the workspace measures the file (stat's words on failure; empty refused; over 1 GiB — Slack's own ceiling, MAX_ARTIFACT_BYTES — refused by name before anything is minted); the budget is checked before any mint — each transfer is one command under the 20-minute bash cap clipped to the run's remaining clock like any command, and a file that cannot move inside that at 1 MiB/s plus ten seconds of setup (transferBudgetMs) is refused naming the bytes, the time and the budget (a 1 GB file with three minutes left is refused; a 3 MiB screenshot goes, clipped); the container curl -fsS -Ts the file to a presigned PUT with the derived Content-Type (signed into the URL) under the key runs/<runId>/out/<seq>-<basename>; the bot HEADs the object and refuses a missing one or a size other than the one measured (nothing posted, no event, no ticket); the artifact event (direction: "out", naming the call that posted it — callId, from ToolContext.callId, live-view.md item 26) is published once the store holds the verified object; then the channel: with a ticket the container streams the same file from disk to the ticket's URL (curl -fsS --upload-file … -X POST, never a buffered --data-binary, which reads a 1 GiB file into memory and fails) and the bot completes the share with the lead (a failed POST or a refused complete carries the platform's words and says the file is kept on the run page — the event stands); without one (the CLI harness, HTTP, MCP) the tool takes the store-only path: it posts the lead and the file's own link — the run page's artifact proxy /runs/<id>/artifacts/<key>, carrying the run's live token while the run is live, or the key alone when the deployment has no public URL — through reply, and its result names the key and says no channel upload happened. The command strings carry the presigned query (the access key id, the signature) and never the secret access key, the Slack token or the copy bearer. With a store the inline readBytes path is not taken; without one it runs exactly as before. Inbound (execution.md item 20): a file the person dropped on the thread that the inline path could not carry is already in ./attachments/<index>-<basename> when a coding run's turn names it (Attached files are in ./attachments/: 1-clip.mp4 (312 MB, video/mp4)), staged by the dispatcher before the model's first read and by the runner before a steered follow-up is read; both coding prompts say so in the shared paragraph — read it from there, never ask for a re-upload — and a workspace-less agent's turn names the file and points at agent:coding. Both coding prompts carry one shared paragraph naming the tool, the screenshot case and the rule that text stays in the message — and the destination rule: a request for screenshots that names no destination, or a visual change, means every capture goes to BOTH the thread (attach_file) and the pull request (an explicit attach_file { path, publishForPr: true } call publishes an inspected/redacted PNG after thread delivery, and the returned public URL is embedded with Markdown image syntax in the description's validation section or a PR comment); only a request naming one destination narrows it, and a subset attached with the rest linked is never acceptable. Thread-only requests omit publishForPr. The PR-image boundary owns authorization, PNG-only 10 MiB content checks, private-bucket isolation, retention and cleanup: the public URL lasts for the artifact retention window (default 30 days), not forever. Ordinary attachments remain private. A refused or unavailable publication is reported; if the preflight also refused attachment, the capture is still attached privately without publication. It is never bypassed with an assets-branch push or a tokened run-artifact URL; captures never enter the implementation diff.

  11. The harness (harness-pi.md): a coding run is pi in the run's container — the prompt above as pi's system prompt, the full toolset relayed to the bot (src/tools/toolsets.ts: the recorders, attach_file, diff_digest, the skills, the GitHub reads and issue writes, web_fetch, the session tools) with pi's own read, bash, edit, write, grep, find and ls as its workspace tools (the prompt gains one note mapping the native read_file/write_file names onto them), the budgets of item 15 there, the coding preset's tool rules at the gate (item 7 there), and the PR post-step unchanged; the model turns go through the model proxy on the run's bearer. A verified provider transport failure — a model-gateway 5xx or HTML page, overload or reset — is held and retried on growing backoff inside the run's loop lease (execution.md item 29; harness-pi.md item 6), with a plane recovery able to release the same hold early. Spending that retry window ends the run failed with failure: provider_transient. A pi-local cut stream retries without a provider-down hold; exhausting its local retry lease records failure: model_stream_incomplete. In either case, a coordinator child checkpoints its WIP branch before teardown (agent-ship.md item 8).

  12. Seeded-sandbox variant (execution.md item 26): when the run lands in a sandbox seeded from the resident's snapshot, the dispatcher swaps in CODING_SYSTEM_SEEDED via RunOptions.system: the repository is ALREADY CLONED at /workspace/checkout on the thread's branch, dependencies installed — no cloning, no installing, no repo discovery — and, unlike the resident's, gh and Docker are present, so the workflow is the cold prompt's with the clone step gone: branch, implement, diff_digest, then submit_pr_description — the bot opens the PR only after a verified publication. The runner-owned publish_branch effect inspects the selected checkout at /workspace/checkout through the executor, then sends one typed publication request with a separate effect credential; the model-shell bearer cannot obtain a Git Door write grant. The same effect uses the attached worktree path on a resident. A missing checkout or unavailable typed transport leaves commits local and reports publication unavailable rather than trying a shell push (execution.md item 5, model-proxy.md item 13). The factory never wires the legacy credential-file refresher into a model workspace. A door refusal is reported once, with the branch state and any unpushed commits named by the workspace observation, instead of a model-side sleep or App-token refresh loop.

  13. Checks by cost — the push comes before the expensive ones (CHECKS_BY_COST in src/agents/registry.ts, carried verbatim by every coding prompt; the unit contract's first instruction says the same for a plan child, agent-ship.md item 13). Every check has a cost class by duration — seconds (a formatter or linter on the touched files, one test file, a docs, link or spec check, one package's typecheck) or minutes (the whole suite, a build, an install, an end-to-end or full verification) — and the agent knows a command's class before running it, from the project's own scripts and CI, from the last run, or by the class alone. A change is proved by the cheapest check that can prove it, matched to its scope and scoped to the changed set — the tests nearest the change, the touched project's typecheck, the changed files' formatting, never the whole tree; the rule states the principle plainly — CI runs the tests, types, formatting and verification selected for the push, so the run does not repeat the full gates: it validates and fixes its own change before pushing, at the changed-set scope — and passing the full test suite and the full typecheck is not part of the run's criteria — CI runs them when the change needs them, because on a shared machine they cost minutes every other run pays for; the branch is pushed as soon as the change exists and those checks pass, before any expensive check, because an unpushed tree does not survive the run's end; beyond the changed set the run uses judgement about what the change needs rather than a checklist, every fix a further commit and push. An operation whose expected duration does not fit the budget left, less a commit, a push and the description, is not started: the agent pushes what there is and names what is unverified. The description's validation lists exactly what ran and names the CI checks still pending. At the wind-down note the run commits and pushes what compiles, says what does not, then answers; CHECKS_BY_COST and the unit contract's first instruction both carry the sentence, because the pushed head at the wind-down is what a renewal reads (decision 0046). Both carry one more (TIMEOUT_ON_LONG_COMMANDS): state a timeout on any command expected to run longer than a minute, because a timeout that reaches past the loop's end is refused before the command runs rather than cut midway (harness-pi.md items 7 and 15) — a command with no timeout is never refused, so the push always runs. Stack-agnostic on purpose: no runner, package manager or language is named. The fast gates (FAST_GATES_BEFORE_PUSH in src/agents/registry.ts, one constant carried verbatim by every coding prompt right after the checks-by-cost rule — and by no other preset's; issue 1796): the paragraph beside it that does name its commands, because children handed only the classes chose wrong in both directions and every ask that was not a plan unit had to repeat the paragraph by hand — before every push, each gate scoped to the changed set and never the whole project: npx vitest run on the touched test files by name, once (never --changed, never a directory: on a moving base that is most of the suite), tsc --noEmit -p the touched tsconfig under NODE_OPTIONS=--max-old-space-size=6144, npx prettier --check on the changed files, npm run hygiene:check and npm run specs:check, then judgement on what else the change needs rather than a longer checklist — with CI selecting the relevant parts of npm run verify on the push, never in the run and the head pushed early for CI to judge, fixes as further commits. Those commands are Switchboard defaults: another repository uses its documented equivalents for the same changed-set proofs and CI gate, records the actual commands, and leaves any check it cannot run unproven. An equivalent check is not a unit deviation. The receipts routing is the contract's alone (item 9): the paragraph never mentions the handoff, so a plain coding run reads a rule it can follow whole, and a plan child reads the paragraph once — the contract's first instruction points at it instead of re-stating it (agent-ship.md item 13). The rebase before every push (REBASE_BEFORE_PUSH in src/agents/registry.ts, one constant carried verbatim by every coding prompt right after the fast gates — the same bytes in every coding variant, a ship fix round's child among them, and by no other preset's; record 0071 mechanism one, issue 1747): immediately before each push the run fetches its base branch, rebases its branch onto it, resolves any conflict in one bounded model round using the thread's context and the repository's AGENTS.md (a generated file is regenerated, never hand-merged), and if resolved re-runs the fast gates on the rebased tree and only then pushes — always, not configurable; only a conflict it cannot resolve inside that bound ends the round — so every head that reaches review is current with its base when it lands and no unit ends merge-ready behind a sibling that merged first; the ship contract's first instruction points at this paragraph too (REBASE_POINTER, agent-ship.md item 13) instead of re-stating it, and a replayed child whose base moved during its round shows the fetch, the rebase and the gates before the push in that order (rebaseBeforePushScript, src/load/scriptedProvider.ts).

  14. Task-directed baseline by default. The cold, resident and seeded prompts share one instruction: inspect repository guidance and task scope; prefer run_check with purpose: baseline for the cheapest useful check before intended edits, then purpose: verification for checks after changes. Where the typed path is unsupported and the command is known not to have started, ordinary permitted shell execution remains available without a typed receipt claim; policy refusal, unknown execution and persistence failure cannot authorize fallback. Report unavailable checks and actual failures separately. No repository-specific test command is required. This is workflow guidance, not a structural pre-edit gate; the result is factual execution evidence, not assertion coverage or publication authority. A historical baseline remains history after edits; a legacy startup smoke is a task baseline only when its scope is relevant. See coding-checks.md for receipt and recovery semantics.

Roadmap (gaps) ​

  • [gap] (pr-description.md) The panel renderer. The description-as-data pipeline is live (items 2/3 above: the agent submits submit_pr_description, the system renders at the pushed head and opens/edits the PR, the object rides the run stream as a pr_description event); what remains of "rendered per surface" is the run page's review panel rendering the same object — tracked as the panel-renderer gap in pr-description.md.

Validation criteria ​

CriterionProof
12: a cold runner inspects the actual /workspace/checkout even when /exec starts in /workspace; only the owned clean ref, exact source and accepted Door transition count, while a foreign owner or ref cannot publish[unit] src/core/publicationEffect.test.ts::publish_branch — a runner-owned Git Door effect::publishes the owned branch from a cold /workspace checkout and refuses another owner or ref
14: every coding prompt shares adaptive baseline selection, orders its workflow before implementation, reports failures and unavailable checks honestly, and separates startup smoke/history from changed-tree verification and publication[unit] src/agents/registry.test.ts::coding prompts: adaptive task baseline before edits::*
Before writable tools open, the first identity read uses the attached full SHA and waits for a durable state acknowledgment. A pushed run restores its original inherited commit fingerprints on restart without rereading its advanced branch. Foreign or malformed bindings and failed acknowledgments refuse writable execution; legacy pre-restart pushes without a receipt remain unknown.[unit] src/core/dispatch/runLoop.test.ts::a resume with the answer in hand (the \finish` plan)::awaits the frozen attachment read and its durable acknowledgment before opening writable tools*; ::refuses writable tools when the identity baseline acknowledgment is*; ::refuses writable tools without a durable ledger for the first identity baseline; ::rejects a foreign restored identity baseline without rereading the advanced branch; ::restores the original acknowledged identity baseline after a native push and restart; ::a resumed run whose ledger row records a pre-restart push*`
The identity rewrite first requires the compared tip to equal the caller's observed tip, then moves the ref only if it still points to that compared tip. After a successful rewrite, the post-step publishes the rebuilt tip as the last pushed head and the child's final head; publication checks can bind the exact live PR head. A concurrent ref move refuses the rewrite.[unit] src/execution/githubPulls.test.ts::githubPulls — the identity rewrite's Git Data calls (record 0062)::forceMoveRef updates the branch only at its expected head; a moved ref refuses; src/execution/identityRewrite.test.ts::rewriteRunCommits — the run's commits carry only the allowed identities (record 0062)::refuses a branch that moved before the compare without writing; src/core/codingPrPostStep.test.ts::runCodingPrPostStep — the identity rewrite before the open (record 0062)::*; src/core/dispatch/runLoop.test.ts::a resume with the answer in hand (the \finish` plan)::records the identity rewrite's final pushed head for the coordinator's exact-head read`
Budgets and toolset as specified; no built-in effort; the turn guard derived from the wall clock[unit] src/agents/registry.test.ts::coding: full toolset, 90 min, no built-in effort (config layers decide), ::the turn cap is a runaway guard derived from the wall clock…::*; the budget mechanics are harness-pi.md item 15's rows.
11: a transport-class provider failure is held and retried on growing backoff within the run's loop lease; a successful retry completes the turn, while spending the retry window marks the record failure: provider_transient under a stable ending and a coordinator child pushes its WIP before teardown[unit] src/core/harness/pi/harness.test.ts::runPiHarness — a run on pi from the first file to the answer::a 5xx then success: the transient failure is retried after the ladder's first backoff — pi is re-prompted and the retry's answer is the run's, ::transport failures stay held on backoff inside the lease; exhausting that retry budget ends by type without exposing a gateway page, while a non-transient error is never retried; src/core/dispatch/runLoop.test.ts::runLoop — the model turn and everything that rides on it::a coding child whose transport retry budget is exhausted commits and pushes its interrupted workspace before teardown
11: a pi-local stream cut retries within the lease without claiming provider-down; exhaustion ends with failure: model_stream_incomplete so Ship can re-run an empty round-0 child[unit] src/core/harness/pi/harness.test.ts::runPiHarness — a run on pi from the first file to the answer::an incomplete local stream that spends the retry lease ends with its own typed failure, ::an incomplete OpenAI Responses stream retries locally and completes the original turn; src/core/dispatch/runLoop.test.ts::runLoop — the model turn and everything that rides on it::an exhausted local stream records model_stream_incomplete without claiming a provider failure; src/core/ship/coordinator.test.ts::the transient re-run — round 0 dies on a provider transient with nothing pushed (issue 1932)::an incomplete local model stream gets one round-0 retry without being reported as provider-down
Resident variant: ready worktree, no clone/install/gh instructions; Git through the run-bearer door with no legacy credential-store instruction; push-then-submit, PR creation not its job; fallback prompt unchanged[unit] src/agents/registry.test.ts::resident prompt variants (incl. ::resident coding sends Git through the run-bearer door, never the legacy credential store, ::resident coding names relayed GitHub read tools instead of a shell API call, ::resident coding creates a new branch without trusted publication authority, ::resident coding publishes through a runner-owned effect rather than the model shell, ::coding variant pushes the branch and submits the description; PR creation is not its job); selection wiring in src/core/dispatcher.test.ts::repo/ref resolution + resident prompt selection ….
Seeded variant (item 12): names the seeded checkout, forbids a second clone and any install, keeps gh; push-then-submit[unit] src/agents/registry.test.ts::seeded prompt variants::*
Push then submit, never open/merge/approve (both prompts): the branch is pushed, the description submitted through submit_pr_description, the PR is never opened by the agent, the curl POST /pulls step is gone, and never-merge/never-approve is explicit[unit] src/agents/registry.test.ts::coding prompts: push then submit_pr_description (opening the PR is the system's job) (4: ::both coding prompts instruct pushing the branch, then submitting the typed description, ::neither coding prompt tells the agent to open the PR itself, ::the resident prompt no longer carries the curl POST /pulls instruction, ::both coding prompts forbid merging and approving (mirrors the review prompts' wording)).
Typed description by default (both prompts): every field of the map and the folds mapped to its object field with its cap, no markdown-body authoring instructions, honesty rules stated[unit] src/agents/registry.test.ts::coding prompts: the PR-description content contract (submitted object) (::both coding prompts map every rendered field of the map and the folds to its object field, with the caps, ::no markdown-body authoring instructions remain (the renderer owns the labels and the folds), ::both prompts state the rules that keep the description honest, ::both prompts require re-reading and resubmitting the description after every push to an existing PR, whoever opened it). [agent] agent:coding fix <issue URL> with NO description ask → the bot-opened PR's body has every label of the map and the folds, the issue hyperlinked, and validation matching what actually ran.
The map supersedes the Tour and the prose Changes / How to review; the prompt maps every field of the map and the folds with its cap and mandates loading pr-description before authoring the pointers, and does not duplicate the craft; the skill pins the fields, the caps, the choice rule, the anchor rules, never-embed and the fold; the repush rule is code (resubmit → re-render at the new head)[unit] src/agents/registry.test.ts::coding prompts: the PR-description content contract (submitted object) (4); src/skills/prDescriptionSkill.test.ts (8, incl. anchor verification + never-embed); render-at-head in src/core/dispatcher.test.ts::description submitted + head observed → the PR opens from typed values…. [agent] open a PR via a routed coding ask; the run page shows a 📚 skill pr-description row before the description is submitted; the body arrives map-shaped — no heading, **Why:**, **Where to look** with every pointer a link whose sha equals gh pr view --json headRefOid, no embedded code block, the folds collapsed. Then a follow-up in the same thread that changes code → after the repush every pointer's sha equals the NEW head.
End-to-end: issue → PR[agent] @switchboard agent:coding investigate and fix <issue URL> on a small real issue. Expect: it reads the issue itself (no 403, no asking you to paste it), one repo cloned (cold path) / attached to the warm resident worktree (resident path), a bot-opened PR whose diff addresses the issue, final message = outcome with the PR link appended by Switchboard.
Ambiguity → one question, then stop[agent] Give a task naming no repo with several plausible candidates; expect exactly one clarifying question and a stopped turn — not a survey of the org.
Ordinary coding final prose keeps the change/validation report alongside runner-confirmed ref/full head and actual PR outcome. No accepted write means no push is confirmed even when the model claims otherwise; a newer pending or rejected write leaves the older accepted head historical across a trimmed restart. Only the canonical answer is stored and delivered; wind-down, stop and audience guards remain separate.[unit] src/core/dispatch/runLoop.test.ts::a resume with the answer in hand (the \finish` plan)::reports a runner-confirmed push and the real PR outcome instead of the coding model's stale no-push answer, ::keeps the invented push report distinct from unconfirmed publication, ::keeps the neutral work report distinct from unconfirmed publication, ::keeps an older durable accepted head historical after a newer…, ::does not turn an unresolved publication attempt into a claim that nothing was pushed`
A compaction checkpoint is historical after a later accepted push on the same ref; a later checkpoint remains current. Ordinary branches and adopted PRs report the latest accepted full head in the answer, stored record and channel reply, retaining the work report and actual PR outcome.[unit] src/core/dispatch/runLoop.test.ts::runLoop — the model turn and everything that rides on it::reports the latest accepted head after compaction on…
An ordinary unit's accepted checkpoint survives restart, including when an earlier branch push receipt exists: the PR opens, the full head is confirmed and the record projects the checkpoint. A later durable branch receipt supersedes an older retained event on each ref; a producer-backed rewrite or accepted salvage remains last, but a receiptless event is never publication authority, including when the observed remote matches it; adopted-PR receipts obey the same ordering without bypassing their exact-head fence.[unit] src/core/dispatch/runLoop.test.ts::a resume with the answer in hand (the \finish` plan)::restores an ordinary unit's accepted checkpoint after restart with earlier branch receipt…, ::restores the latest durable branch receipt without regressing…, ::restores accepted receipts on other refs without replaying their earlier heads, ::restores an adopted PR's durable accepted receipt past…`
A coordinator child's accepted owned-ref push stays eligible for its PR post-step and description turn after an auxiliary ref is pushed or its push is rejected; the final door fence checks the observed owned ref's receipt or accepted checkpoint and only its own rejection. The same holds across restart with an accepted checkpoint. A raw retained push event, with or without an older accepted receipt, cannot confirm a new publication.[unit] src/core/dispatch/runLoop.test.ts::a resume with the answer in hand (the \finish` plan)::opens the unit PR from the owned receipt even after an auxiliary accepted push, ::offers a description turn for the owned receipt after an auxiliary accepted push, ::restores an ordinary unit's accepted checkpoint after restart with earlier branch receipt…, ::offers the description turn and opens the owned PR after a rejected auxiliary push, ::publication provenance probe: a retained event alone is not accepted write authority…`
Honest failure reporting[agent] Ask for a change in a repo whose tests are broken at HEAD; the final message must state the failure plainly rather than claim success.
No placeholder narration[agent] Replies must never contain bracketed placeholders (e.g. "[title and details from the issue]") in place of real command output. A follow-up mis-routed to general produces exactly this; guarded by thread stickiness (routing-and-config.md).
The identity rewrite (item 2): a clean chain opens untouched; a foreign author, committer or co-author is rebuilt from the first offender with the same trees and messages, parents remapped, the original dates, the committer the bot pair; start pairs are kept by fingerprint through a rebase and refused on new content; a boundary start state keeps every commit at or below the branch's pre-push head — a human commit planted under the run's is never rebuilt and keeps its author — and a range without that head, or one an abbreviation leaves ambiguous, is unreadable with no writes; no binding and the flag off rewrites to the bot pair; over 300 commits, an unknown start state, a failed read or a ruleset refusal is unreadable without a ref move; a tip moved before the compare is refused without writes, and a tip moved after our ref update is unreadable without another ref move[unit] src/execution/identityRewrite.test.ts::rewriteRunCommits — the run's commits carry only the allowed identities (record 0062)::*, src/execution/identityRewrite.test.ts::readBranchStartState — the branch's commits over the base at attach (record 0062)::*, src/execution/githubPulls.test.ts::githubPulls — the identity rewrite's Git Data calls (record 0062)::*
The post-step's rewrite (item 2): on unreadable nothing opens and the note says why; on rewritten the pull request opens at the rebuilt tip with the count on the note and pr_opened; a head mismatch after the open runs the rewrite once more, and a second unreadable there leaves the open pull request with a warning on the reply and a pr_head_unverified note; the assignee is added after a 204 and skipped after a 404; a pushed branch other than the recorded one is judged over an empty start state only when the run's first push created it, over a boundary at the pre-push head that push moved it from, and over an unknown one (fail closed) with neither record; a missing start-state record fails closed with a reason saying no read was attempted; the unproven-push edits run the same rewrite — unreadable refuses the edit with a pr_not_opened note, a rewritten tip is where the body renders — and the edit of the thread's own pull request after no push runs none[unit] src/core/codingPrPostStep.test.ts::runCodingPrPostStep — the identity rewrite before the open (record 0062)::*
The unit contract (item 8): both coding prompts name the ## Contract block and every fixed sub-heading in the contract module's order, make it outrank the free-text task with the rebase first and the plan never edited, require the listed scenarios added and the named rows and guards kept, state the listed-scenario rule at minor severity as a spec contradiction's, and carry the text byte-identically across the sandbox and resident variants[unit] src/agents/registry.test.ts::coding prompts: the unit contract (agent-coding item 8)::*
A coding run dispatched with a contract carries the rendered block as the last text part of its first user turn, the request's text first; a run without one runs its messages as given[unit] src/core/dispatcher.test.ts::coding PR post-step (docs/reference/specs/pr-description.md)::a coding run dispatched with a contract carries the rendered block as its first user turn's last text part…, src/core/ship/codingChild.test.ts::withContractInFirstUserTurn — the unit's contract in the child's first turn (item 13)::*
The unit handoff (item 9): both coding prompts name submit_handoff once after the description and before the final message, tie it to the ## Contract block, name the three lists with their fields and the optional landed list with its (what, where) as the way a unit with nothing left to push ends done, say the run records it and the parent posts it to the board issue for a person's disposition, require an empty handoff submitted as empty lists and forbid the call without a block, route an unprovable criterion to the handoff, and carry the text byte-identically across the sandbox and resident variants[unit] src/agents/registry.test.ts::coding prompts: the unit handoff (agent-coding item 9)::*
The unit handoff (item 9): the contract's first instruction routes each gate command's exit line to the PR description's validation table as the row's proof, and a gate the child could not run to this handoff's unproven list, never claimed clean[unit] src/core/ship/contract.test.ts::the first instruction points at the preset's fast gates instead of re-stating them…
submit_handoff (item 9): in the full toolset right after submit_pr_description and nowhere else, never side-effect-free; a valid handoff forwarded trimmed with a counted acknowledgement; an empty one accepted and forwarded; the optional landed list in the schema, forwarded and counted when given and absent when not; a malformed one a string error naming the path with the sink untouched; the last valid call wins and an invalid call leaves it standing; no sink → the truth, never a false "recorded"[unit] src/tools/submit.test.ts::submit_handoff tool::*
A plain coding run's submitted handoff rides its run record, redacted, and posts nowhere; a run that submitted none carries no handoff key[unit] src/core/dispatcher.test.ts::coding PR post-step (docs/reference/specs/pr-description.md)::a submitted handoff rides the run record, redacted; a coding run that submitted none carries no handoff key
An in-flight submit_handoff is run again on a resume like the other submit_* recorders — it only sets dispatcher state[unit] src/core/runLedger/resume.test.ts::settlementFor — D4::the default is the restart result…
attach_file (item 10): in the full toolset only, never side-effect-free, its description naming the byte cap[unit] src/tools/attach.test.ts::attach_file toolset wiring::*
attach_file (item 10): posts the bytes under the file's own name with the comment (or the name) as the lead; a channel without uploads is named and nothing is read; an executor without a byte read is named and nothing is posted; a missing, over-cap or empty file and an empty path are each named with nothing posted; a refused upload carries the platform's message[unit] src/tools/attach.test.ts::attach_file::*
The dispatcher binds the requesting channel's attachFile into a coding run's tool context exactly when the channel has one: the tool posts through it, or says the channel takes no files[unit] src/core/dispatch/runLoop.test.ts::runLoop — the model turn and everything that rides on it::a coding run's attach_file posts the workspace file through the channel's attachFile…
A child run's channel forwards attachFile to the opened thread when it has one, and offers none when it does not[unit] src/core/dispatch/spawn.test.ts::spawnChild — the one path a child run is born through::the child's channel forwards attachFile to the opened thread…
Store path (item 10): stat → presigned PUT (the derived type in the header, the runs/<runId>/out/<seq>-<basename> key) → HEAD → artifact event (carrying the call's callId) → ticket → POST → complete, each command under the 20-minute cap and the result naming the size; two files of one name take keys 1- and 2-; a HEAD of another size (the error also carries curl's own report of the PUT — the status answered and the bytes sent — or no such clause when the output holds none), a failed PUT, a failed POST and a refused complete each stop the chain where the spec says and carry the platform's words (the event stands once the store holds the file); a refused ticket, a missing file and an empty file are named with nothing minted; over 1 GiB refused by name; a 1 GB file with three minutes left refused naming the budget while a 3 MiB screenshot goes clipped, and the write-up reserve refuses everything; no ticket → the lead and the run page's link through reply; the inline path is not taken with a store and unchanged without; command strings carry the presigned query and never the secret key or the copy bearer[unit] src/tools/attach.test.ts::attach_file through the artifact store::*
The dispatcher binds the store path (this run's keys, the channel's uploadTicket, its reply) when the deps carry a store: the executor PUTs and POSTs, the record carries the artifact event, the ticket is completed; without a ticket the lead goes through reply carrying the file's proxy link tokened with this run's live token; readBytes is never called[unit] src/core/dispatch/runLoop.test.ts::runLoop — the model turn and everything that rides on it::with an artifact store the run's attach_file moves the file by reference…
Store-only path (item 10, the ticketless channels): the lead carries the file's proxy link and the result names the key; without a public URL the lead names the key on the run page; the CLI harness, HTTP and MCP channels each carry reply and no uploadTicket, and through the harness the lead lands on its stream; no ticket is minted[unit] src/tools/attach.test.ts::attach_file through the artifact store::a channel without an upload ticket gets the lead and the file's proxy link…, …::without a public URL the lead still names the file and its key…, …::the three ticketless channel shapes — the CLI harness, HTTP, MCP…
The file link: /runs/<id>/artifacts/<key> under the public base URL, each key segment encoded, ?t=<token> when a token is given; undefined without a public URL[unit] src/core/dispatch/reply.test.ts::artifactLink::*
A child run's channel forwards uploadTicket to the opened thread when it has one, and offers none when it does not[unit] src/core/dispatch/spawn.test.ts::spawnChild — the one path a child run is born through::the child's channel forwards uploadTicket to the opened thread…
The CLI harness's attachFile writes the bytes under one temp dir per conversation (children included) and prints the lead with the path[unit] src/cli.test.ts::askExitCode — what the…::ConsoleIO.attachFile…
Both coding prompts route files the person should see through attach_file — the screenshot case named, a link called not a picture, text kept in the message, whole files up to 1 GiB with a store and 10 MiB otherwise — in one shared paragraph; the review prompts do not name the tool[unit] src/agents/registry.test.ts::…::both coding prompts route files the person should see through attach_file…
Both coding prompts carry the destination rule (item 10): screenshots go to both places, all of them — attached with attach_file and deliberately published with publishForPr: true, then embedded on the pull request via its returned public URL, never an assets push or the PR's diff — unless the request names one destination; never a subset attached and the rest linked[unit] src/agents/registry.test.ts::…::a screenshot request with no destination named means both the thread and the pull request…
End-to-end (item 10): a coding run screenshots a page and attaches it; the PNG renders inline in the Slack thread beside the run's reply[agent] The live row of execution.md item 19
End-to-end (item 10, the destination rule): "take screenshots of X" with no destination named yields every capture both in the thread and on the PR[agent] Follow PR images — live acceptance after deployment: N images attached in the thread AND the same N rendered on the PR through token-free published URLs. This remains deployment-gated; unit tests are not a live receipt.
End-to-end (item 10, the store path at its ceiling): a coding run attaches a 1 GiB file and the thread receives it whole; the bot never carries the bytes[agent] on a deployment with artifacts: on: ask a coding run in a switchboard thread to create a 1 GiB random file (head -c 1073741824 /dev/urandom > out/probe.bin), attach it with attach_file and report size, sha256, the call's wall time and the tool's result verbatim; expect the file in the thread, an artifact row on the run page, the result attached probe.bin (1073741824 bytes) to the conversation and the run page, a wall time within the transfer budget, and /healthz process.rssMb within tens of MB of idle while it moves; receipts on the receipts issue
13: every coding prompt carries the checks-by-cost rule verbatim — the two classes, the cheapest proving check, the push before the expensive ones, the budget test, the honest validation — and names no runner, package manager or language; the review prompts do not carry it[unit] src/agents/registry.test.ts::coding prompts: checks by cost — push before the expensive ones (agent-coding item 13)::*
13: the rule says CI selects tests, types, formatting and verification on the push — the full suite and typecheck are not the run's criteria — and scopes every check to the changed set, judgement beyond it, still naming no runner or package manager[unit] src/agents/registry.test.ts::coding prompts: checks by cost — push before the expensive ones (agent-coding item 13)::the rule keeps full gates out of the run and lets CI select checks from the changed set
13: the fast-gates paragraph (FAST_GATES_BEFORE_PUSH) names the five changed-set gates by command — the touched test files by name (never --changed, never a directory), tsc --noEmit -p the touched tsconfig under the heap flag, npx prettier --check on the changed files, npm run hygiene:check, npm run specs:check — names npm run verify as the local full gate that CI selects from and orders the early push, and never mentions the handoff or the validation table (the receipts stay the contract's)[unit] src/agents/registry.test.ts::coding prompts: the fast gates before every push (agent-coding item 13, issue 1796)::the paragraph names the five changed-set gates by command…
13: the named fast-gate commands are Switchboard defaults; another repository uses its documented equivalents for the same changed-set proofs, records actual commands and leaves a check it cannot run unproven. An equivalent command is not a unit deviation.[unit] src/agents/registry.test.ts::coding prompts: the fast gates before every push (agent-coding item 13, issue 1796)::the paragraph names the five changed-set gates by command…
13: every coding prompt carries the fast-gates paragraph exactly once — the same bytes across the three variants, after the checks-by-cost rule — and no other preset's prompt carries it[unit] src/agents/registry.test.ts::coding prompts: the fast gates before every push (agent-coding item 13, issue 1796)::every coding prompt carries the paragraph exactly once…, …::no other preset carries it: a review pushes nothing, and the workspace-less presets run no gates
13: the rebase-before-push paragraph (REBASE_BEFORE_PUSH) orders the fetch of the base, the rebase onto it, one bounded conflict-resolution round with the thread context and AGENTS.md, the fast gates on the rebased tree and only then the push — in that order; only a conflict unresolved inside the bound ends the round, and no rival contract stop rule remains — always, not configurable[unit] src/agents/registry.test.ts::coding prompts: the rebase before every push (agent-coding item 13, record 0071 mechanism one)::the paragraph resolves a pre-push conflict in one bounded round, then orders the fast gates and push; src/agents/registry.test.ts::coding prompts: the unit contract (agent-coding item 8)::the block outranks the free-text task, the rebase comes first, and carries no rival conflict stop rule; src/core/ship/contract.test.ts::renderContract — one block under \## Contract`, fixed sub-headings in a stable order::renders the five sections in order under the one heading, the unit body verbatim, each spec row with its proof rows, the rules, the guards`
13: every coding prompt carries the rebase-before-push paragraph exactly once — the same bytes across the three variants, right after the fast gates — and no other preset's prompt carries it[unit] src/agents/registry.test.ts::coding prompts: the rebase before every push (agent-coding item 13, record 0071 mechanism one)::every coding prompt carries the paragraph exactly once…, …::no other preset carries it: a review pushes nothing, and the workspace-less presets push nothing
13: a replayed child whose pre-push rebase conflicts in the coordinator resolves it inside one bounded round, continues the rebase, runs the fast gates and pushes, in that order, the push the round's last tool[unit] src/load/scriptedProvider.test.ts::rebaseBeforePushScript — a child whose pre-push rebase conflicts (agent-coding item 13, record 0071)::resolves the coordinator conflict inside one bounded round, then runs gates and pushes
13: CHECKS_BY_COST and the unit contract's first instruction both carry the timeout clause — a timeout on any command expected to run longer than a minute, one past the loop's end refused before the command runs, never cut midway[unit] src/agents/registry.test.ts::coding prompts: checks by cost — push before the expensive ones (agent-coding item 13)::every coding prompt carries the checks-by-cost rule verbatim…, src/core/ship/contract.test.ts::renders the five sections in order under the one heading, the unit body verbatim, each spec row with its proof rows, the rules, the guards
13: every coding prompt's workflow proves with the cheapest checks, pushes, then hands selected tests, typecheck and verification to CI — the push step before the CI step, no step telling the run to run the expensive checks itself, the old "quick enough to run" step gone[unit] src/agents/registry.test.ts::coding prompts: checks by cost — push before the expensive ones (agent-coding item 13)::every coding prompt's workflow proves the change, pushes, then lets CI run selected checks
13: every coding workspace uses the runner-owned publish_branch effect when present and keeps the commit local when that transport is unavailable; no prompt instructs a model-shell push[unit] src/agents/registry.test.ts::resident prompt variants::every coding workspace refuses shell publication without a runner-owned effect
A coding follow-up in a thread with a review verdict sees its findings by id — the review run's record rendered as data in the prompt's thread-artifacts block right after its notes, never in the conversation (session-log.md item 9)[unit] src/core/dispatcher.test.ts::a follow-up seeds from its session (docs/reference/specs/session-log.md item 9)::a coding follow-up is handed the verdict a review run recorded in the thread since its previous run…, src/core/dispatch/threadArtifacts.test.ts::threadArtifactsFor — the records read off the thread's page::a coding follow-up reads the runs newer than its previous run…
13, live: a plan child pushes its unit before the project's full verification and the pushed head is what the description and the review read[agent] agent:ship in <owner/repo>: plan <path>.md units U<n> on a unit whose Verification line names the project's full check → the unit thread's card shows the push (the PR open) before the step that runs the full check; the run record's tool calls show the push preceding it; the description's validation names the cheap checks that ran and hands the full check to CI