Skip to content

Reading diff

Every PR review run carries the change as a reviewer reads it — a review_artifact run event holding the full git diff, produced by the run's own executor and guaranteed on the record. A second artifact, meat.dev's abridged reading diff (the conceptual meat of agent-written code: style, imports and mechanical noise dropped; concepts, algorithm choices and architecture kept), is produced after the review, on the bot host, over the complete diff GitHub serves for the PR — on demand through review abridge <run id>, or automatically when review.readingDiff.provider is meat. meat never runs inside an execution container, and no container ever holds the Anthropic credential: the bot spends its own key, the same one its Anthropic provider reads. This is the data half of the review panel (the run-page slideout renders it beside the Tour).

Shaped on a measurement against the real API over one 357-line PR diff: Opus 4.8 abridged to 45 % kept in 96 s with everything kept load-bearing; Sonnet 5 kept 87 % in 240 s — meat needs an Opus-class model, and its latency is why it never sits on a review's path.

  • Code: src/core/readingDiff.ts (resolveReadingDiff, readingDiffCommand, parseMeatJson, capDiff, sanitizeArtifactText, produceReadingDiff, startReviewReadingDiff, READING_DIFF_CAP, MEAT_MODEL_DEFAULT, MEAT_TIMEOUT_S_DEFAULT); src/core/reviewAbridge.ts (ReviewAbridger, reviewAbridgerFromConfig, autoAbridgeOnPersist, AbridgeRefusal); src/core/meatProcess.ts (meatOnHost, MEAT_BINARY); src/core/commands/review.ts (review.abridge); GithubApi.compareDiff in src/execution/githubApi.ts; anthropicApiKey in src/providers/anthropic.ts; the review_artifact event and PrDescriptionArtifact in src/core/runEvents.ts; src/core/reviewDescription.ts (submittedPrDescriptionArtifact, parsedPrDescriptionArtifact, findSubmittedPrDescription, startReviewDescription, SUBMITTED_LOOKUP_LIMIT); the PR facts on src/core/repoContext.ts (RepoContext.prDescription, PR_BODY_CAP); the coding post-step's publish in src/core/codingPrPostStep.ts; dispatcher wiring in src/core/dispatch/runLoop.ts; the persist hook and the shared abridger in src/index.ts; config AppConfig.review.readingDiff (src/config.ts); the binary in the bot image (Dockerfile).
  • Tests: src/core/readingDiff.test.ts, src/core/reviewDescription.test.ts, src/core/repoContext.test.ts (PR title and body for the description artifact), src/core/codingPrPostStep.test.ts, src/core/reviewAbridge.test.ts, src/core/meatProcess.test.ts, src/core/commands/review.test.ts, src/core/dispatcher.test.ts::reading-diff artifact on review runs, src/execution/githubApi.test.ts::compareDiff — the unified diff of base...head, src/providers/anthropic.test.ts::anthropicApiKey — the one credential getter, src/deploy/botImageMeat.test.ts, src/core/runRecord.test.ts::capEvent on a review_artifact, src/core/authz/policy.test.ts, src/core/commandConformance.test.ts, src/core/runEventLines.test.ts, src/core/runFriction.skillUse.test.ts (side-fact invisibility), src/channels/runTimeline.test.ts; the panel: web/src/modules/pr-review/prReview.test.ts, web/src/lib/prReviewCollector.test.ts, web/src/lib/reviewAbridge.test.ts, web/src/pages/runPage.test.ts (PR-review panel wiring).
  • Web: web/src/modules/pr-review/ (the liftable module — types.ts, files.ts, tour.ts, FileList.vue, DescriptionBlock.vue, TourList.vue, LabelTip.vue, ReadingDiffView.vue, PrReviewPanel.vue), web/src/lib/prReviewCollector.ts (the adapter), web/src/lib/reviewAbridge.ts (the abridge control), the slideout in web/src/pages/RunPage.vue; the preview fixture scripts/web-preview-reading-diff.ts and the description in scripts/web-preview.ts (/runs/review-1).

Behavior

  1. The switch. AppConfig.review.readingDiff = { provider: "git" | "meat" | "off", meatModel?, meatTimeoutS? }, overridden by env SWITCHBOARD_READING_DIFF=git|meat|off (an operator flips a deployed bot without a config rebuild; an unrecognized env value is ignored). Absent everything → git: every review records the full diff and the abridged one is on demand. meat → git PLUS an automatic abridged diff once the review's record is durable (item 10). off → no artifact, so nothing to abridge either. meatModel is meat's -model; with provider: meat and none set it is MEAT_MODEL_DEFAULT = claude-opus-5 (the Opus-class floor above). meatTimeoutS (default 240) is meat's own runtime budget on the host (item 6). Whether abridging can happen at all is a capability (capabilities.md item 1): readingDiffAbridge is on iff the meat binary resolves on the bot host's PATH (meatOnPath, probed once at startup), the Anthropic provider's credential is set (the one getter of item 6), and the provider is not off. Computed once with the rest, printed in the startup [capabilities] line and by setup init, carried in the dashboard seed so the panel can hide the action; off → review abridge is hidden on every surface (item 9) and no abridger is built — the Null Object of an off deployment, whose defence-in-depth answer names the three facts.
  2. One command, injection-closed, git only. git diff --no-color --end-of-options 'origin/<base>...HEAD' (base falls back to origin/HEAD), the range shellQuoted into one inert token — the same discipline as diff_digest. The command never names meat: no execution container runs it (item 6), whatever the provider.
  3. The baseline never throws into the run, and honors the stream's hygiene contract. A git failure (exit N:/fatal: first line, executor throw) or an empty diff yields no artifact and never a throw. Every string the artifact carries is control-stripped and redactSecrets-redacted FIRST (sanitizeArtifactText), then capped at READING_DIFF_CAP (120 000 chars, …[N more chars], truncated: true, never splitting a surrogate pair) — a review of a PR that accidentally commits a credential must not carry it onto the run page or the record. The run record's own byte budget stays the outer bound.
  4. Guaranteed baseline — no timeout races in the pipeline. For a review run with a resolved PR, startReviewReadingDiff fires right before the model loop — the run's own executor runs the (read-only) command beside the model's tools; the event publishes straight to the registry (like input/run_meta; the status card never sees it). The dispatcher joins the baseline just before the answer publish — a join on a seconds-long command started minutes earlier, not a timeout — so every PR review's record deterministically carries a reading diff. Coding runs and PR-less reviews never produce one. The production is a background span on the run's stream (tracing.md items 4 and 18): run.reading_diff (outcome: published / none), concurrent with the loop and never a counted term; the join before the answer is run.reading_diff_join (finishing up). Nothing about the abridged diff happens in the run: with provider: meat the run still records only the git baseline.
  5. ONE abridge path. ReviewAbridger.abridge({ runId, model?, force? }) is the single way an abridged diff is produced: the review abridge command (item 9) and the provider: meat auto mode (item 10) both call it with the same inputs, so a review's abridged diff is identical whichever way it was asked for — meat itself caches by the hash of model + diff (MEAT_CACHE under <dataDir>/meat-cache), so a repeat costs nothing. The binary is spawned in exactly one module (meatProcess.ts); a structural test holds it. Its state machine, per run: absent → running → done | failed; force moves done or failed back to running. abridge on absent starts the production and answers the in-progress marker { state: "running", startedAt }; on running answers the marker again (one production per run at a time, force included); on done answers the stored artifact's summary with reused: true and spends nothing; on failed answers the remembered reason. running and failed live in the bot process — a restart forgets them: a lost job is absent again and re-runnable on demand, a lost failure is retried on the next ask — while done IS the stored artifact and survives restarts. Refusals are named and never a production failure: an unknown run is not_found, a run with no git reading diff (a coding run, a review with none) is conflict (run <id> carries no reading diff — only PR review runs record one).
  6. Input completeness — meat reads the whole diff, never a cut one. The recorded git artifact is capped (item 3) and may be truncated, so it cannot be meat's primary input: a cut diff would mislead the abridging, and refusing on it would make the feature dead exactly on the large PRs that need it. meat's input is the complete unified diff GitHub renders for base...headGET /repos/{owner}/{repo}/compare/{base}...{head} with Accept: application/vnd.github.diff (GithubApi.compareDiff, the read credential the github tools already use; repo and head from the run's run_meta, base from the git artifact's baseRef; read up to COMPARE_DIFF_MAX_CHARS). The recorded diff serves only as the fallback, when that fetch fails (a GithubApiError — 404 for an unknown ref, 406 when GitHub will not render the comparison as a diff —, a transport error, a diff cut at the cap, an empty diff, no GitHub credential, a run_meta without repo and head) AND the artifact is not truncated; otherwise the production fails by name, spending nothing: diff unavailable: <compare failure> and the recorded diff is truncated (e.g. diff unavailable: GitHub compare failed (406) and the recorded diff is truncated). The meat artifact records which input it read — input: "github-compare" | "recorded" — and inputBytes, plus the model it ran with. meat on the host (meatOnHost): meat -json -model <m> with the diff on its stdin and exactly four environment variables — PATH and HOME from the host, MEAT_CACHE, and ANTHROPIC_API_KEY = the bot's own credential through the ONE getter anthropicApiKey(config.providers, secrets) (the first type: anthropic provider's apiKeyEnv, default ANTHROPIC_API_KEY, as a Secret from the process's Secrets — the same function the Anthropic provider's client reads; reveal()ed only into the child's environment, never process.env at a call site; PATH and HOME come from publicEnv()); none of the bot's other secrets reach meat. No credential → refused before any spawn. Failures by name: the binary missing, a nonzero exit (its code + meat's first stderr line), output that is not meat's -json shape ({smart_diff, summary, input_tokens, output_tokens}, parsed strictly), the budget passing (the child is killed: meat did not finish within Ns); every reason redacted. The binary lives in the bot image only (a static Go build from a pinned commit of boldsoftware/meat, Dockerfile, asserted with meat -h at build time); neither execution image installs it.
  7. The PR's description as data — one artifact kind, two origins. review_artifact artifact: "pr_description" = { origin: "submitted" | "parsed", repo, pr, headSha?, fromRunId?, title, body, tldr?, tour: RenderedTourStep[], remaining, decisions, complete, problems, truncated } (pr-description.md items 1 and 6 for the shapes): every Tour anchor carries the sha its permalink was rendered at, and headSha is the head the artifact was produced at — the render head for submitted, the reviewed head for parsed — so a reader compares the two to know whether a step points into the head it is looking at. Persisted at its source: the coding PR post-step publishes the submitted object right after pr_opened — the same object the body was rendered from, with the PR number GitHub answered and the render head, complete: true, the rendered body — so the Tour as data is in the coding run's record, not only rendered into the PR. Read by every PR review: startReviewDescription fires beside startReviewReadingDiff and publishes exactly one artifact — the submitted object when the run store holds one for exactly this repo, PR and reviewed head (findSubmittedPrDescription, run-history.md item 43; its at re-stamped, fromRunId naming the coding run — an object for another head is never passed off as this head's), else the body GitHub holds (RepoContext.prDescription, captured off the resolver's one GET /pulls/{n}: title, body capped at PR_BODY_CAP = 65 536 chars with truncated) parsed back through parsePrDescriptionMarkdown with origin: "parsed", else nothing (no facts: the head fetch failed). Hygiene like the diff: every string leaf — title, body, prose, paths, notes — is control-stripped and redactSecrets-redacted; the parsed body is sanitized FIRST and then parsed, so every parsed leaf is redacted by construction; a body the resolver cut is parsed as it is with the cut named as the first problem. The store read runs in the background and the dispatcher joins it before the answer under run.pr_description_join (finishing up, like run.reading_diff_join; tracing.md item 18), so the artifact is in the record deterministically; a store failure degrades to the parsed body, never a throw into the run. Coding runs publish it only from the post-step; the switch in item 1 does not govern it (no exec is involved). The field is origin, not source: the input event's source is an object, and a literal-typed twin would make source a discriminant of the whole RunEvent union.
  8. The artifact is appended to the STORED record. meat's smart_diff and one-line summary go through the same hygiene as the baseline (item 3: sanitizeArtifactText first, capDiff after) into a second review_artifact (poweredBy: "meat", summary, meatTokens, input, inputBytes, model) appended to the finished record through the one rewrite path of run-history.md item 44 — the next seq after every event the record ever held, eventCount bumped for a new artifact and kept for a force replacement (which drops the previous meat artifact, never accumulates), the byte budget refit, the whole record put. The per-event cap leaves a review_artifact alone (its diff is the producer's to cap; its summary is one line). Readers prefer the meat artifact when both exist (item 12). A record that is gone, would not fit, or that the store does not keep is a named failed, never a resurrection.
  9. review abridge <id> [--model m] [--force] [--wait] (command-registry.md) — the on-demand path, one registration on every surface: chat, CLI, POST /api/review.abridge (body { "id": "<run id>", "model"?: "<m>", "force"?: true, "wait"?: true }), the MCP tool review_abridge. Action review:write — the command spends one Opus-class call and rewrites a stored record: admins through all, anyone granted it by name, never a baseline (review:abridge is not a word the closed action vocabulary of authorization.md item 2 has; write is the class). The run must also be one the caller may see: runs:read on the run's own attributes, like every runs.* point read — a deny is not_found, byte-identical to a missing run. Hidden without runHistory (the record it appends to). The answer is the state, never the diff (the diff is on the record: runs events <id>, the run page): { id, state: "running", startedAt } — the in-progress marker a panel polls by asking again —, { id, state: "done", reused, artifact: { model, summary, input, inputBytes, diffChars, truncated, meatTokens } } (the summary wrapped as untrusted on machine surfaces: it is model prose), { id, state: "failed", reason, at }. --wait blocks for the outcome (the CLI's shape); in chat the acknowledgement is the marker and the outcome lands as a second reply through settle (command-registry item 26). Refusals: not_found (unknown or invisible run), conflict (no git reading diff), unavailable (no run history).
  10. Auto mode is the same path, after the record is durable, detached. With provider: meat, the run-history writer's onPersisted hook (the final record's successful put — never the provisional tombstone) calls abridge({ runId }) through autoAbridgeOnPersist as a detached host task: the review's reply, its record and its finish are already done, so the pipeline never waits on meat and nothing about a review changes with the switch except that its record later gains the meat artifact. Refusals are silent (every non-review run persists through the same hook); a failed outcome is warned with the run id and reason ([reading-diff] <id> meat did not land: <reason>). git and off do nothing here. The shared ReviewAbridger (one per process, src/index.ts) means an operator's review abridge during an automatic production sees running, not a second spend. A restart mid-production loses the job (item 5): the run is abridged on demand later; nothing is retried at boot.
  11. A side fact, not a step. The friction analyzer treats review_artifact — both kinds — like skill_use: invisible to eventCount, steps and turn timing; parseRunEventLines accepts it (reading_diff + string diff + known poweredBy; pr_description + known origin, string repo/title/body, numeric pr, array tour); the timeline deliberately folds both to nothing until the panel renders them.
  12. The pr-review module renders it (web/src/modules/pr-review/ — a deliberately liftable, props-only Vue module; see its README). Contract: PrReviewData = { pr: PrRef, readingDiffs: ReadingDiff[], description?: PrDescriptionData } plus an optional AbridgeControl prop — the module imports nothing from the app's lib/pages or anything runs-shaped (one generic component, ExpandableText, excepted and named in its README), so the whole folder can be shared into another product with the host writing its own adapter. PrReviewPanel is a header over two columns that scroll on their own. The header: the PR's title (data.description.title when the host knows it, else owner/repo#N) linking to the PR; one line of facts — the reference, the head sha, the base, N files, +A −D, the producer badge, a truncated badge when the producer capped the diff, the files-on-GitHub link — every GitHub link built only from shape-verified values (prLinks: owner/name + positive integer + 7–40-hex sha; anything else renders as text); producer tabs when both diffs exist (preferredDiff: abridged first; a segmented control — the selected tab ink on the page ground in an accented track, the other muted with a hover; with one producer no tab bar, the facts line's badge names it); the producer label and truncated carry a tooltip that says what they mean (poweredByExplanation, truncatedExplanation — the latter names the character count the recorded diff was cut at), on the tab and on the badge alike, one setting for all (LabelTip: wraps at a measure, opens below, collision-aware, a short delay); a wrap toggle; a close control for a closable host. The left column (FileList, 19rem wide, hidden below md) opens on the PR's description when the host knows it — the description block (DescriptionBlock: the TL;DR and, after it, the What & why, folded to five lines by ExpandableText with a fade and a Show more; under it, for a description parsed from the PR body that came up short, the muted note description read from the PR body, ; no Tour appended when the body had none) and the Tour (TourList: Tour · N steps, each step a native button — number, title and the one-line description each clamped to two lines, Look for: when present, the anchor as path:from–to in mono clamped to one line and truncating from the left so the file name stays, the note under it on its own line — with a visible focus ring; the clamps are fixed: nothing hover- or active-driven changes an element's size (the list holds still under a reader clicking through it), and what a clamp hides rides ONE LabelTip per step whose trigger is the step element itself — focusable, so keyboard focus opens it as hover does — saying only what the step cannot show (stepTip: title and description whole while their clamp hides lines; the full anchorLabel while the anchor is cut; the stale explanation), disabled when there is nothing to add; overflow is measured, and remeasured when the steps' text changes in place (a deep watch) or the list resizes; the clamp is visual, so the accessible name already carries the whole text; a warning badge when anchor.sha is neither the reviewed head nor a prefix of it, its accessible name — and the step's tooltip — reading anchored at <sha7>, the review is at <sha7>; lines may have moved; then Remaining changes, path and note each). No description → neither section renders and the files start at the top; a description without a Tour → the block alone. Each step is placed against the shown diff and the full one (placementOf, with the full diff's truncated flag): clicking a step in the shown diff calls scrollTo(path, from, to), marks the step active (aria-current="step", the same mark the lit rows carry — a left bar and a tint in --pr-review-mark) and its file current; a step whose file only the full diff carries reads not in the reading diff · open full diff and the jump switches to the full diff first, as does a step whose lines the abridged diff dropped; a step in no diff while the full diff on record is truncated is beyond — the file may lie past the cut, as it does when a large PR's later files fall after the cap — and reads beyond the recorded diff · open on GitHub ↗ as a link to the file at the reviewed head with the lines selected (fileLink: from a shape-verified repo, sha and path — no empty, . or .. segment, no backslash or control character, each segment URL-encoded — else a muted, inert step reading beyond the recorded diff), never not in this diff; a step in no diff with the whole diff on record is muted and inert (a Tour whose every anchor is missing still lists every step, all muted); a step whose lines no diff carries lands on the file and reads lines not in this diff (placementNote). A Remaining path opens its file the same way; one in no diff links to GitHub the same way when the full diff was cut, else is muted. Under the description come the files — status icon, path, +A −D, viewed mark; clicking an entry scrolls the diff to the file, and the current entry follows the diff's scroll (scroll-spy, currentFileAt). The right column (ReadingDiffView) is the producer's summary as a labelled lede block (Summary · <producer>, the sentence at a measure, under it the same hairline a file header carries), then one section per file (parseFiles): a sticky one-line header (collapse, status, path — old → new for a rename —, counts or binary, the viewed checkbox) over diff2html's line-by-line rendering of that file, styled by the library's own stylesheet (imported whole, so the gutter, the inline prefix and the code cell track upstream) with the colors from the app's tokens in both themes; the library escapes diff content — hostile diff text is never markup. Long lines scroll inside the file's code area, never the panel; wrap folds them. Viewed folds a file (header stays, body hidden) and dims its entry — one set of paths the panel keeps for its lifetime, shared by both columns and across the producer tabs. The view exposes scrollTo(path, fromLine, toLine) — lights the rows the new-side line range covers (rowsInRange: deletions interleaved with the range ride along), unfolds a folded file and measures only after the unfold has rendered, then scrolls so the first lit row sits 30 % down the column, never under the file's sticky header (the header plus a line of room is the floor); the light — a left bar and a tint in --pr-review-mark laid over the row's own colour, one 900 ms pulse on arrival (a second, cell-filling inset shadow fading from 40 % to clear: a two-shadow box-shadow list in both keyframes, interpolable in every engine, no registered custom property) that prefers-reduced-motion drops — holds until the next jump; resolves false and leaves the previous light alone for a file or range the diff does not carry — and scrollToFile(path). --pr-review-mark is the panel's third hue beside --pr-review-ins / --pr-review-del (--ui-info by default, the host may retune it): a lit range of added lines in the insertion green would vanish into them. The slideout's accessible name is the hidden dialog title (panelTitle), since the panel replaces the dialog's visible header. OpenSwitchboard's adapter is web/src/lib/prReviewCollector.ts — the one place run shapes meet the module: run_meta → the PR identity, review_artifact reading_diff → the diffs (one per producer, a later artifact replaces its producer's earlier one — a same-run re-review), review_artifact pr_description (item 7) → description (descriptionFrom: title, origin, the Tour's steps with their anchors' shas, the remaining files, complete, truncated, headSha; the TL;DR is the artifact's or the body's first paragraph (firstParagraph), the What & why is the body's ## What & why section (markdownSection, fences respected); a later artifact replaces the earlier; a malformed one — bad title or origin, a step without a title, an inverted or non-positive range, a bad sha, a remaining entry without a note — changes nothing; a description alone lights nothing), malformed frames ignored. The run page feeds the collector the same frames as the timeline (seeded history and live SSE alike) and, once ready (at least one diff), shows a Reading diff button on the run-meta line that opens the panel in a slideout. Coding runs and reviews with the provider off never light the button. The abridge control (web/src/lib/reviewAbridge.ts, item 9's surface from the page): on a stored run when capabilities.readingDiffAbridge is on, the page hands the panel a control; the panel shows it where the tabs would be while the git diff stands alone — the Abridge with meat button (tooltip: what it does, one model call), then Abridging… usually 1–3 minutes with a spinner, or the failure's reason with a Retry; start() POSTs /api/review.abridge { id } as JSON same-origin ({ id, force: true } only on a retry after the server's own failed — a stored failure is what force recomputes; a transport or page-side failure is retried without it, so the command's idempotency answers a stored artifact instead of spending a new call), reads running and polls with the same POST every POLL_INTERVAL_MS up to MAX_POLLS, and on done pages GET /api/runs.events?id=… into the collector — at most MAX_PAGES pages, and a cursor that does not advance ends the read as a named failure — so the meat artifact lands like any other frame and the tabs take the control's place with the reading diff selected; a non-2xx answer fails with the body's error (else HTTP <status>), a thrown fetch with its message, a done with no meat artifact on the record names that; dispose() on unmount stops the polling, and no frame read after it reaches the collector. Capability off, or both producers present → nothing renders (no disabled button).

Deploy prerequisites for the abridged diff

Built in: the meat binary ships in the bot image. What an installation supplies: run history (the record the artifact is appended to), the Anthropic provider the bot already has (meat spends its key), and a GitHub credential (the App, or GH_TOKEN) for the compare diff — without one, only reviews whose recorded diff is whole can be abridged. provider: meat is then one config line (or SWITCHBOARD_READING_DIFF=meat).

Roadmap (gaps)

  • [gap] The panel's button: review abridge on the run page (the HTTP form of item 9, polled until done), and the meat artifact's input/inputBytes shown beside its badges — the web module is owned separately.
  • [gap] Landing rate: a count of failed productions and their reasons on the runs index or in friction report (today: the warn line of item 10 and the artifacts on the records).

Validation criteria

CriterionProof
Resolve: default git + default meat budget; config selects provider/model/budget (floored, positive); off → null; env overrides config; unknown env ignored; meat without a model is claude-opus-5, git names none[unit] src/core/readingDiff.test.ts::resolveReadingDiff (config + env)::* (4)
Command: exact git string, origin/HEAD fallback, hostile ref quoted inert, never names meat[unit] src/core/readingDiff.test.ts::readingDiffCommand::* (4)
meat JSON parsed strictly; error line / non-JSON / no smart_diff throw[unit] src/core/readingDiff.test.ts::parseMeatJson::* (2)
Cap with note + truncated flag; never splits a surrogate pair[unit] src/core/readingDiff.test.ts::capDiff::*, src/core/readingDiff.test.ts::produceReadingDiff::caps the diff…, ::never splits a surrogate pair at the cap
Stream hygiene: secrets redacted + ANSI stripped before the cap, on the baseline and on meat's diff and summary[unit] src/core/readingDiff.test.ts::produceReadingDiff::redacts secrets and strips ANSI…, src/core/reviewAbridge.test.ts::ReviewAbridger — failures and the append::the appended diff and summary are redacted…
Baseline producer: git artifact; git failure / executor throw / empty diff → null, never a throw[unit] src/core/readingDiff.test.ts::produceReadingDiff::* (6)
startReviewReadingDiff: the baseline publishes and resolves true; provider: meat still runs ONE git command and publishes only git; off runs nothing; a failure resolves false, never rejects[unit] src/core/readingDiff.test.ts::startReviewReadingDiff (the baseline, guaranteed)::* (4)
Wiring: a review of a resolved PR carries the baseline with seq before the answer's; provider: meat → the record carries git only and no meat command reaches the executor; off and coding runs publish none[unit] src/core/dispatcher.test.ts::reading-diff artifact on review runs::* (4)
Spans: the baseline is a run.reading_diff child of the root with outcome published/none and the diff's exec under it; without a root nothing is measured (tracing.md item 18)[unit] src/core/readingDiff.test.ts::reading diff spans (docs/reference/specs/tracing.md items 17/18)::* (3)
5 — one path: abridge runs absent→running→done and appends the meat artifact; idempotent (done/reused, no spend) until --force, which replaces; a second call while running is running, --force included; unknown → not_found, no git artifact → the named conflict; the persist hook calls the same abridge[unit] src/core/reviewAbridge.test.ts::ReviewAbridger.abridge — the one path::* (4), src/core/reviewAbridge.test.ts::autoAbridgeOnPersist — provider: meat is the same path, after the record is durable::meat → abridge({ runId }) on persist…
5 — one spawn site: no source file other than meatProcess.ts spawns the binary[unit] src/core/meatProcess.test.ts::meat is spawned in one module::no other source file names the binary as a child process
6 — input order: the compare diff first (input: github-compare, inputBytes); 404 → the whole recorded diff with a warning; 406 + truncated → the named refusal, nothing spent; a diff cut at the cap, no credential, a run_meta without repo/head, a transport error → the same order and wording[unit] src/core/reviewAbridge.test.ts::ReviewAbridger — input completeness (compare first, recorded only when whole)::* (5)
6 — compareDiff: GET /repos/<repo>/compare/<base>...<head> with the diff media type on the read token, percent-encoded refs, cut at the cap with complete: false, GitHub's 404/406 as GithubApiError; the in-memory double answers seeded comparisons[unit] src/execution/githubApi.test.ts::compareDiff — the unified diff of base...head::* (5)
6 — meat on the host: the diff on stdin, -json -model <m>, exactly PATH/HOME/MEAT_CACHE/ANTHROPIC_API_KEY in the child env; no credential → refused before a spawn; missing binary, nonzero exit (code + first stderr line), non-JSON, the budget (killed, named) → named failures; reasons redacted[unit] src/core/meatProcess.test.ts::meatOnHost::* (8)
6 — the one credential getter: the first anthropic provider's apiKeyEnv (default ANTHROPIC_API_KEY); undefined without a provider or an unset/empty variable, never a fallback[unit] src/providers/anthropic.test.ts::anthropicApiKey — the one credential getter::* (2)
6 — the binary in the bot image only: a pinned Go stage, CGO_ENABLED=0 go install meat.dev/cmd/meat@<40-hex sha>, one COPY --from=meat, command -v meat + meat -h asserted, copied before USER switchboard; neither execution image names meat[unit] src/deploy/botImageMeat.test.ts; [agent] CI image / build bot (check:image) green at the PR head
8 — the append: next seq after every existing event, eventCount bumped for a new artifact and kept for a replacement, the summary survives the event cap, a gone record / stored: false → named failures[unit] src/core/reviewAbridge.test.ts::ReviewAbridger — failures and the append::* (4), src/core/runRecord.test.ts::capEvent on a review_artifact::does not shrink the summary…
9 — the command: running marker then done summary (untrusted-wrapped, the diff never in the answer); --wait; --force + --model reach abridge; conflict / not_found wording; the run must be visible (a private run is not_found to an outsider holding the grant); no grant → unauthorized before anything runs; no history → unavailable; settle posts the outcome after a running ack; hidden without runHistory[unit] src/core/commands/review.test.ts::review.abridge::* (9)
9 — the policy row review:write command [has-grant(review:write)]: admins allowed; a plain chat user, a member, a run-scoped token, no grants, dispatch-only denied[unit] src/core/authz/policy.test.ts (the row's allow/deny cases; coverage)
9 — every surface: the derived forms, the MCP schema, --help, the authorization matrix and identical invoke JSON for review abridge <id> and each option variant[unit] src/core/commandConformance.test.ts (registry-driven; the seeded review rev-1)
10 — auto mode: provider: meatabridge({ runId }) on persist; git/off (config or env) → nothing; refusals silent; a failure warned with id and reason[unit] src/core/reviewAbridge.test.ts::autoAbridgeOnPersist — provider: meat is the same path, after the record is durable::* (2)
Stream + friction: accepted when well-formed, skipped otherwise; diagnosis identical with/without side facts[unit] src/core/runEventLines.test.ts::…::accepts \review_artifact`…; src/core/runFriction.skillUse.test.ts`
Module: prLinks builds links only from shape-verified values (hostile repo/sha → text, never a URL); preferredDiff abridged-first; panelTitle title → reference → repo → label; the label explanations and their tooltip triggers on tabs and badges; parseFiles path/status/counts/rename/binary per file; currentFileAt and rowsInRange geometry; the stylesheet is diff2html's own with the gutter/prefix rules present; the header carries title, facts, producer and truncation badges, tabs, close only when closable; the file list is a table of contents (click selects and scrolls) seating the description and tour slots first; viewed folds the file and marks the entry from either column and survives a tab switch; the chevron folds on its own; wrap toggles; scrollTo lights the range and scrolls, unfolds a viewed file and measures after the render, false off-diff without unlighting; empty states (no diff, no files); hostile diff text is escaped, never markup[unit] web/src/modules/pr-review/prReview.test.ts (53, cd web && npm test)
12: the Tour's pure half — anchorLabel (path:from–to, one number for one line), staleAnchor (a prefix is the same head; unknown on either side is not stale) and staleExplanation, originNote (parsed + incomplete only; ; no Tour when empty), placementOf (shown / full / beyond when the full diff was cut / absent), placementNote (where the jump goes, or that it cannot), stepTip (title and description whole), fileLink (the file at the head with the lines selected, from shape-verified values only — hostile paths, a bad repo or sha → none), filePaths[unit] web/src/modules/pr-review/prReview.test.ts::the Tour's pure helpers::*
12: the description in the panel — no description → no block, no Tour, files at the top; the block folds TL;DR + What & why in ExpandableText (5 lines, the column's ground) with the origin note only for a parsed incomplete description, a Tour-less description alone; the Tour lists every step (number, medium title, two-line description, Look for, mono anchor) with its placement and the stale badge (accessible name; the step, not the badge, is the tooltip trigger); a click in the shown diff lights the lines, scrolls, marks the step active (the clamp kept) and the file current; a step only the full diff carries — or whose lines the abridged diff dropped — switches to the full diff first; an absent step is muted and inert and an all-absent Tour still lists every step; lines no diff carries land on the file and say so; Remaining paths open their file the same way, muted when in no diff; steps are native buttons with a focus ring[unit] web/src/modules/pr-review/prReview.test.ts::the PR description in the panel::*
12: the Tour holds still and jumps clearly — no hover-driven class exists in the template and hovering changes no element's classes; title, description and anchor keep their clamps on the active step; the step's one tooltip opens on keyboard focus of the step itself and carries the title and description whole, the full path and the stale explanation — only what is hidden, quiet when nothing is (the stale note excepted); an in-place change to a step's text is remeasured and the tooltip follows the words; the jump lands the first lit row at 30 % of the column (a short column falls back to the header plus a line), keeps the light through a file click, unfolds a viewed file first; the lit rows and the active step wear the same --pr-review-mark bar and tint, a third hue the panel defines, with the arrival pulse a plain box-shadow keyframe (no @property) dropped under reduced motion; a step past a truncated full diff is a GitHub link at the head with the lines selected (beyond, the truncated badge shown, a new file before the cut still found), muted and unlinked without a repo and head, and reads not in this diff only when the whole diff is on record; a Remaining path past the cut links the same way; the left column is 19rem[unit] web/src/modules/pr-review/prReview.test.ts::the Tour holds still, jumps clearly, and says when a file lies past the recorded diff::*
12: the abridge control in the panel — no control or both producers → nothing; absent → the button with its tooltip, a click starts; running → spinner + Abridging… usually 1–3 minutes, no button; failed → the reason + Retry that starts again; the meat artifact arriving on the data replaces the control with the tabs, reading diff selected[unit] web/src/modules/pr-review/prReview.test.ts::the abridge control::*
Adapter: run_meta → PR identity, review_artifact reading_diff → one diff per producer (later replaces earlier), pr_description → the description (title, TL;DR from the artifact or the body's first paragraph, the What & why section, Tour with shas, remaining, origin, completeness, head; later replaces earlier; a malformed one changes nothing; never ready on its own), malformed frames ignored, never throws; ready only once a diff exists; firstParagraph and markdownSection[unit] web/src/lib/prReviewCollector.test.ts (8)
The page's abridge action: start POSTs { id } as JSON same-origin, polls with the same POST after a delay, on done pages the record into the sink; a reused done skips the poll; done with no meat artifact fails naming it; failed carries the reason and a retry after the server's failure sends force: true, after a transport or page-side failure no force; a non-2xx fails with the body's error else the status, a thrown fetch with its message, an unknown state too; start while running is a no-op; dispose stops the polling and no frame reaches the sink after it; MAX_POLLS rounds give up; paging stops on a cursor that does not advance or past MAX_PAGES, a named failure[unit] web/src/lib/reviewAbridge.test.ts (8)
Wiring: history seed and live SSE both light the Reading diff button exactly when artifacts exist; the slideout renders the panel with the PR link and the rendered hunks, and the dialog's aria-labelledby resolves to a rendered title; a seeded pr_description names the dialog by the PR's title and renders the TL;DR and the Tour; with readingDiffAbridge on and only the git diff the panel offers Abridge with meat and a click POSTs /api/review.abridge for this run, off → nothing[unit] web/src/pages/runPage.test.ts::PR-review panel wiring (5)
Live: a review run's GET /runs/:id/events carries one review_artifact poweredBy: "git" with the PR's real diff; review abridge <id> on it answers running then done with input: github-compare, and the record then carries a poweredBy: "meat" artifact the panel shows as a second tab[agent] post-deploy: agent:review <PR URL>, then review abridge <run id> --wait from Slack or the CLI, then probe the run's events.
7: the submitted builder carries the golden's title, tldr, tour (anchors stamped with the render sha), remaining, decisions and the rendered body, complete; every string leaf — title, prose, anchor path, decision, body — is control-stripped and redacted[unit] src/core/reviewDescription.test.ts::submittedPrDescriptionArtifact::carries the golden's title…, ::every string leaf is control-stripped and redacted…
7: the coding post-step publishes the submitted artifact right after pr_opened with the PR number GitHub answered, the render head on every anchor and the very body it put on GitHub; no PR opened → no artifact[unit] src/core/codingPrPostStep.test.ts::runCodingPrPostStep (callable with explicit inputs)::the golden description → a \submitted` pr_description artifact…, ::no PR opened (open throws, or nothing to open) → no pr_description artifact`
7: the PR facts ride on the repo context from the one GET /pulls/{n} — explicit and inherited PR alike; a null body is empty; the body is capped at PR_BODY_CAP with truncated, never splitting a surrogate pair; no title or a failed fetch → unset[unit] src/core/repoContext.test.ts::PR title and body for the description artifact::*
7: the parsed builder: the golden body parses back complete with the reviewed head beside the anchors' render sha; sanitized BEFORE parsing (a token never reaches tldr, a step or the body; ANSI stripped); a shapeless body → first paragraph, empty tour, complete: false; a truncated body names the cut first[unit] src/core/reviewDescription.test.ts::parsedPrDescriptionArtifact::*
7: the lookup finds the newest submitted artifact for exactly this repo, PR and head across the repo's records — skipping parsed copies, other PRs, heads and repos — a review run's copy counting with the coding run kept as fromRunId, the newest within a record winning, bounded by SUBMITTED_LOOKUP_LIMIT[unit] src/core/reviewDescription.test.ts::findSubmittedPrDescription::*
7: startReviewDescription prefers the stored object for the reviewed head (once, re-stamped, fromRunId), parses the body when none / another head / the store throws, publishes nothing without a PR or facts, contains a throwing publish — never rejects[unit] src/core/reviewDescription.test.ts::startReviewDescription::*
7: wiring — a review with facts and no stored object carries the parsed artifact before the answer (redacted, reviewed head beside the anchors) beside the diff; a stored object for the reviewed head is published instead, re-stamped, naming the coding run; one for another head → parsed; no facts → the diff alone; a coding run publishes none at start[unit] src/core/dispatcher.test.ts::reading-diff artifact on review runs::no submitted object in the store…, ::the store holds the submitted object for the reviewed head…, ::the store's submitted object is for ANOTHER head…, ::no PR facts (the head fetch failed)…, ::a coding run publishes no artifact — neither…
7: stream + side fact — parseRunEventLines accepts the kind and skips a bad origin / tour / pr; the analyzer's diagnosis is identical with both artifact kinds present; the timeline folds it to nothing[unit] src/core/runEventLines.test.ts::…::accepts a \pr_description` review_artifact…; src/core/runFriction.skillUse.test.ts::…::a review_artifact — the reading diff and the pr_description alike — is invisible to friction; src/channels/runTimeline.test.ts::…::folds a pr_description artifact to nothing too…`
7: live — a review of a PR opened by agent:coding carries pr_description with origin: "submitted" and fromRunId = the coding run; a review of a hand-written PR carries origin: "parsed" with its Tour steps' anchors at the body's permalink sha[agent] post-deploy: agent:review <PR URL> on one PR of each kind, then probe GET /runs/:id/events for the artifact.