docs/specs/2026-07-31-evidence-affecting-paths-assessment.md

Evidence-affecting path list — assessment

Status: In force. This is the evidence gate's PR-diff backstop list, designated as such by ADR-0005 and linked as the inventory from CONTRIBUTING.md and the pull request template. It stands until a maintained machine-consumed list supersedes it; the governance-docs ticket (#305) closed without producing one. Individual entries stay revisable — see "Judgment calls needing confirmation in review". (Origin: wayfinder ticket #303, autonomy map #298.) Amended 2026-08-23 by ADR-0014: every include-list entry now carries a tier, blocking or advisory. Read "Tiers" before using this list for anything; a hit no longer means one thing. Definition applied: CONTEXT.md, Assurance baseline — "An evidence-affecting change includes acquisition, parsing, extraction, storage, hashing, signing, trusted time, manifests, verification, redaction, export, reporting, AI analysis, and software distribution when it can alter an evidentiary result or its interpretation."

Purpose

This list is the mechanical PR-diff backstop for the evidence gate (decision record: #299). The primary trigger is the evidence-affecting label applied at triage; this list catches PRs whose diff touches an evidence path without the label. It is expressed as globs so a routine or CI step can match it against git diff --name-only. It was drafted as the reviewed source that a machine-consumed file would be seeded from; #305 closed without relocating or superseding it, so this document is the list itself until one is produced.

Method note: produced by a three-slice parallel survey of the codebase (main services; db/shared/ ai/extraction; extension/renderer/distribution edges), each reading files rather than classifying by name, followed by an adversarial completeness/overbreadth challenge. The challenge pass added six paths both surveyors missed, overturned one exclusion on a factual error, and removed two inconsistent inclusions — those changes are already folded into the list below.

Amendments since the survey: src/shared/urlPatterns.ts added to Acquisition (#227) — the ignored-URL matcher moved there out of the already-listed src/main/services/captureServer.ts, and the rename-detection-off rule only catches the move itself, not later edits to the new home. src/renderer/lib/api/** added to the exclusion list (#229) — the renderer query layer split out of src/renderer/lib/queries.ts into per-domain modules, so the reasoning recorded against that one file now applies to the directory. The call is unchanged (still excluded), and judgment call 2 below is restated against the new path; queries.ts itself is a re-export barrel that the last PR of that sequence deletes. extension/src/popup/PopupApp.tsx, extension/src/popup/pageStatus.ts and extension/src/messages.ts added to Acquisition (#387) — the popup's case routing and its capture button moved out of the already-listed extension/src/popup/popup.tsx, which is now a mount point, and the same rename-detection caveat applies: without these three the backstop would stop firing on the code that actually routes a capture. src/main/services/exclusionPolicy.ts added to Acquisition (#400) — the same move as #227, one step further: the decision about what may enter a case moved out of the already-listed src/main/services/captureServer.ts so that src/main/services/recapture.ts, a producer the capture server never sees, enforces the identical rule rather than a second copy of it. The rename-detection-off rule catches the move itself and nothing after, and this file is now the single place that answer is computed, so a later edit touching only it would otherwise match no entry. src/shared/urlCanonicalize.ts added to Acquisition (#392) — the canonical URL identity the extension attach routes (tag apply, note create, URL lookup) resolve against. It decides which Capture a Tag or Note binds to, and whether an attach request acquires new bytes at all (no canonical match means the supplied payload is ingested), so it shapes what enters a case the same way urlPatterns.ts does; the #227 argument applies unchanged. Shared so the extension and the server agree on the rules by construction rather than by convention.

List format

These lists are the seed for a machine-consumed file, so entries follow fixed rules. Anything seeded from this document should be checked against them.

  • One repository-relative glob per entry. No table row or bullet combines two paths; a rule that needs two paths gets two entries. This is what makes the tables extractable by cell.
  • Paths are repository-relative POSIX paths with no leading ./ or /, so they compare directly against git diff --name-only output. Matching is case-sensitive, and directory separators are always /.
  • A ** segment crosses directory separators. An entry ending in a ** segment means that directory and everything beneath it, recursively. An entry with no wildcard matches exactly one file.
  • Run the diff with rename detection off (git diff --name-only --no-renames), so moving a file out of an evidence path still trips the backstop on the old path.
  • Only the include list feeds the matcher. The exclusion list is a review record of paths deliberately left out, not a subtraction pass applied on top. Blanket exclusion-over-inclusion would be wrong here: src/renderer/components/dashboard/** is excluded while src/renderer/components/dashboard/cases/DataExplorer.tsx and src/renderer/components/dashboard/cases/ImportCaseDialog.tsx are included, and a blanket override would silently drop them. If a future tool does consume both lists, the more specific pattern wins — an exact path beats a directory glob, and among globs the one with the longer literal prefix before its first wildcard beats the shorter.

Tiers

Added 2026-08-23 by ADR-0014. Every include-list entry carries a tier, and the tier decides what a hit costs.

blocking. A change on this path can alter an evidentiary result or its interpretation. The backstop finding is blocking, the PR owes the ADR-0004 obligations (an Evidence impact section, a known-answer test or a written justification, preserved backward verification), it gets human review, and it never auto-merges.

advisory. The path is real evidence surface, but it is shared with a lot of code that is not. A hit produces a one-line reviewer disposition naming which region the diff touched. It owes no Evidence impact section and does not by itself make the PR evidence-affecting. A reviewer who finds the diff does reach evidence behaviour relabels the PR blocking and the full obligations apply.

The evidence-affecting label applied at triage is unchanged and still the primary trigger. The tiers govern the mechanical backstop only. A labelled PR owes the full obligations whatever its diff touches.

Why the tiers exist

ADR-0005 asked for a mechanical backstop because "looks harmless" is the judgment that fails under review pressure. That reasoning holds. What did not hold is treating every entry as equally load-bearing: judgment calls 1, 3 and 10 below predicted, before the pilot ran, that ipcHandlers.ts/ipc.ts wholesale, pnpm-lock.yaml and the distribution block would fire on changes that could not touch an evidentiary result, and pre-authorised dropping them if that proved out.

It proved out. Measured 2026-08-23 over every PR that has carried the label, matching this document's own include list (extracted by cell per the rules above) against git diff --name-only --no-renames for each merge:

Count
PRs labelled evidence-affecting41
of those, merged38
merged PRs in the repository overall349
labelled merges hitting a narrow-block glob26
labelled merges hitting only broad-block globs11
labelled merges hitting no include-list glob at all1

Broad-block means the cross-process contracts block, the software distribution block, or src/main/services/db/**. The eleven include the cross-case recent-activity feed, the UI density setting, the tests/ typecheck gating and its reapply, the preload namespace rename, and two PRs hardening the release supply chain. The one no-hit is #532, a threat-model documentation change labelled by triage judgment alone, which is the label working as designed.

Four of the twenty-six narrow hits are incidental brushes rather than evidence work, and they are the more misleading class because they read as substantive:

  • #786 hit src/renderer/components/export/** by adding data-tour="export" to a div in ExportMenu.tsx. The whole diff is a coach-mark tour.
  • #455 and #357 hit the same glob changing error handling in ExportComplete.tsx and ExportMenu.tsx. Neither shapes a package.
  • #478 hit src/main/index.ts moving window sizing out to resolveWindowSize. That file is listed for the evidence-viewer invariants, webview navigation blocking and sandbox webPreferences, and the diff went nowhere near them. #515 tracks the same carve-out.

So 16 of 38 labelled merges, 42%, touched no path where the change could have altered an evidentiary result.

The tiers do not clear all sixteen. Re-running the same measurement against the tiered list, 13 of 38 now classify advisory or no-hit and 25 stay blocking. The gap is src/main/services/db/**, kept blocking deliberately because it holds the evidentiary records, which leaves #677 (the cross-case recent-activity feed) and #435 firing on a repo read. That is a known residual, not an oversight: narrowing db/** to something that distinguishes a read from a write is a real piece of work and it is not this one.

Known exposure of the advisory tier

Recorded rather than resolved, because the tier assignment is a deliberate trade.

src/main/services/settings.ts is advisory, and it persists the operator identity that src/main/services/certification.ts embeds verbatim in an export certification. A change there can therefore reach a shipped package without a blocking finding. The judgment is that the certification path itself is blocking and is where such a change becomes visible. If an advisory-tier defect ever does reach main through this route, that is the evidence to promote the entry, and it should be promoted rather than argued with.

src/main/index.ts stays blocking despite its only observed fire being incidental. One file's noise does not buy dropping the evidence-viewer invariants, and #515 is the better fix for the specific carve-out that caused it.

Include list

Acquisition (extension → capture server)

PathTierWhy
extension/src/background.tsblockingOrchestrates MHTML/screenshot/text capture; caches response headers anchored into the signed manifest
extension/src/content.tsblockingIn-page capture: screenshot stitching, DOM mutation during capture, selector matching over page text
extension/src/toast.tsblockingInjects DOM before/during capture — its markup can land inside captured bytes
extension/src/utils/api.tsblockingBuilds the multipart capture payload that becomes hashed evidence and manifest fields
extension/src/utils/headers.tsblockingDeterministic header normalization anchored into the hash-chained manifest
extension/src/popup/popup.tsxblockingRoutes a capture to a case (chain-of-custody routing)
extension/src/popup/PopupApp.tsxblockingThe popup itself since #387: picks the case a capture is routed to, and gates the capture button on the ignore rules
extension/src/popup/pageStatus.tsblockingDerives what the popup tells the operator about a page, including whether a capture would be refused
extension/src/messages.tsblockingThe popup/background contract carrying the block reason and the rules-loaded flag the capture route gates on
extension/manifest.jsonblockingAcquisition permissions; ships in the release zip
src/main/services/captureServer.tsblockingIngest endpoint: upload validation, case routing, ingest-time selector matching
src/shared/urlPatterns.tsblockingThe ignored-URL matcher both sides run: it decides what never enters a case at all, and names the rule recorded as the reason for the absence
src/shared/urlCanonicalize.tsblockingCanonical URL identity for the extension attach routes: decides which Capture a Tag or Note binds to, and whether an attach request ingests its payload or binds to an existing Capture
src/main/services/exclusionPolicy.tsblockingResolves the per-case exclusion list into the answer the acquisition routes check before ingest, so it decides what those routes refuse. Not every route asks: the pipeline self-test (POST /api/captures/test) is a documented exception, so read the call sites rather than assuming coverage is total
src/main/services/serverToken.tsblockingAuthenticates ingest; weakening admits spoofed captures into the evidence chain
src/main/services/session.tsblockingActive-case state deciding which case a capture is filed under
src/main/services/recapture.tsblockingRecapture queue re-acquiring URLs as new evidence
src/main/services/backgroundRenderer.tsblockingHeadless render producing the artifacts that get hashed
src/main/services/consentBlocker.tsblockingSuppresses consent overlays during recapture — alters acquired page content

Integrity core (hash, manifest, signing, trusted time, verification)

Assurance boundary for this block, stated so the rationales do not overclaim: the manifest chain and the RFC 3161 token cover different things, and neither substitutes for the other. The timestamp request's message imprint is the capture's contentHash (buildTimestampRequest in src/main/services/timestamp.ts), so a granted token dates the captured bytes — it says nothing about whether the manifest chain around them is intact. The chain, conversely, covers manifest entries but is signed with the installation-local keypair generated and held by this install (src/main/services/signingKey.ts), so it detects edits made without that key and is not independent of the Operator who holds it.

PathTierWhy
src/main/services/manifest.tsblockingHash-chained, signed audit manifest append/verify. The chain detects edits made without the installation-local signing key; it is not an Operator-independent guarantee
src/main/services/signingKey.tsblockingManifest signing keypair generation/storage/signing — the key the chain's assurance is bounded by
src/main/services/timestamp.tsblockingRFC 3161 TimeStampReq construction (message imprint = the capture's contentHash) and TSA round-trip. The token dates the content; it does not prove manifest-chain integrity
src/main/services/timestampWorker.tsblockingAsync timestamping worker writing manifest entries
src/main/services/trustedTime.tsblockingPer-capture trusted-time status resolution
src/main/services/tsaTrust.tsblockingEmbedded TSA trust anchors shipped in evidence packages
src/main/services/tlsCertChain.tsblockingTLS chain corroboration anchored into signed manifest entries
src/main/services/installationId.tsblockingInstallation identity in manifest entries and certification
src/shared/verify/**blockingVerification core: canonical JSON, chain, signature, RFC 3161 token, package verifier
src/verifier/**blockingStandalone verifier CLI (fs+crypto-only constraint)
src/main/index.tsblockingEnforces evidence-viewer invariants (webview navigation block, sandbox webPreferences); boots DB/capture server/installation id

Capture lifecycle, storage, parsing, extraction

PathTierWhy
src/main/services/captureLifecycle.tsblockingIngest/delete pipeline: hashing, manifest entries, trusted-time reconciliation
src/main/services/captureStore.tsblockingOwner of capture artifact bytes on disk
src/main/services/storage.tsblockingStorage root and case directory layout
src/main/services/mhtmlDecoder.tsblockingMHTML parsing feeding text extraction
src/main/services/dataExtractor.tsblockingIOC extraction pipeline entry
src/main/services/extraction/**blockingSource selection, sanitizer, IOC adapter, validators — determine extracted results
src/main/services/db/**blockingAll evidentiary records: schema, repos, dbAdmin row edit/restore/purge
src/main/services/selectorLifecycle.tsblockingRetro-matching writes selector_matches rows that ride in case archives
src/main/services/safeRegex.tsblockingRegex evaluation deciding those persisted matches (follows the selector cluster call below)

Annotation, redaction, notes-to-evidence binding

PathTierWhy
src/main/services/annotations.tsblockingPersists annotation shapes incl. redaction rects burned into exports
src/main/services/burnAnnotations.tsblockingComposites annotations/redactions onto exported pixels
src/main/services/renderAnnotationsSvg.tsblockingShape rendering for burn-in — a wrong redact rect leaks or destroys content
src/main/services/noteAnchorResolver.tsblockingBinds notes to hash-covered text
src/shared/noteDoc.tsblockingNote document model + text derivation (feeds FTS and exported reports)
src/shared/noteAnchor.tsblockingAnchor model deciding which evidence a note points at
src/renderer/components/captures/annotation/**blockingAnnotation editing incl. the redact tool

Export, reporting, archive

PathTierWhy
src/main/services/export.tsblockingEvidence-package export: hashing, evidence.json, verification, burn-in
src/main/services/pdfExport.tsblockingPDF disclosure rendering
src/main/services/reportHtml.tsblockingForensic report renderer inside packages
src/main/services/certification.tsblockingExport certification document
src/main/services/verifyRunbook.tsblockingBy-hand verification runbook shipped in packages
src/main/services/caseArchive.tsblockingArchive export/import with id remapping
src/main/services/zip.tsblockingEvidence container write
src/main/services/zipRead.tsblockingEvidence container read
src/main/services/csvEscape.tsblockingEscaping behind CSV exports of case data
src/renderer/components/export/ExportDialog.tsxblockingExport options directly shape the package. Narrowed from src/renderer/components/export/** on 2026-08-23. See the tier amendment

AI analysis

PathTierWhy
src/main/services/ai/**blockingAnalysis pipeline + chat client (truncation changes model input, hence output)
src/renderer/components/captures/AnalysisTab.tsxblockingTriggers analysis, selects model/prompt, saves results
src/renderer/components/settings/AIConfig.tsxblockingWrites defaultModel and analysisSystemPrompt — same rationale as AnalysisTab

Interpretation surfaces (renderer)

PathTierWhy
src/renderer/components/captures/ForensicsTab.tsxblockingHash chain / chain status / headers display
src/renderer/components/captures/ProvenanceBadge.tsxblockingIntegrity + trusted-time status rendering
src/renderer/components/captures/getProvenanceColor.tsblockingStatus-to-meaning mapping
src/renderer/components/captures/MhtmlViewer.tsxblockingEvidence rendering invariants (JS disabled, navigation blocked)
src/renderer/components/captures/WaybackTab.tsxblockingPins Wayback corroboration refs
src/renderer/components/captures/CaptureDownloadMenu.tsxblockingPer-capture evidence export surface
src/renderer/components/overview/VerifyBar.tsxblockingCase-level verification display
src/renderer/components/overview/overviewModel.tsblockingThe actual verified/tampered bucketing VerifyBar shows
src/renderer/components/dashboard/cases/ImportCaseDialog.tsxblockingArchive verification display + overrideTamper flow
src/renderer/components/dashboard/cases/DataExplorer.tsxblockingExtraction results display + reprocess trigger
src/renderer/components/settings/DatabaseAdmin.tsxblockingDirect DB administration over evidence tables
src/renderer/components/settings/db/**blockingThe DB administration views behind it (browse, row edit, utilities)
src/renderer/components/settings/OperatorConfig.tsxblockingOperator identity embedded verbatim in export certification

Cross-process contracts and configuration

PathTierWhy
src/main/ipcHandlers.tsadvisoryShapes inputs/outputs of evidence operations (see judgment call 1)
src/preload/index.tsadvisorySingle privileged chokepoint for every evidence channel
src/shared/ipc.tsadvisoryTyped payloads for evidence operations (see judgment call 1)
src/shared/types.tsadvisoryDomain contracts for captures, provenance, trusted time, exports
src/shared/schemas.tsadvisoryZod validation at trust boundaries (upload, manifest entry, settings)
src/shared/constants.tsadvisoryManifest filename/schema version, TSA URL, size caps, default analysis prompt
src/main/services/settings.tsadvisoryPersists TSA URL, operator identity, capture prefs, key encryption
src/main/services/waybackMachine.tsblockingCorroboration lookups pinned as evidence references

Software distribution

PathTierWhy
package.jsonadvisoryelectron-builder build block, dependency pins/overrides
pnpm-lock.yamladvisoryThe actual dependency pin compiled into shipped bundles (see judgment call 3)
.github/workflows/release.ymladvisoryBuilds/publishes installers, updater manifests, extension zip
electron.vite.config.tsadvisoryCompiles the exact app bytes the release workflow publishes
extension/vite.config.tsadvisoryCompiles the exact extension bytes the release workflow publishes
scripts/build-verifier.mjsblockingBuild of the distributed verifier binary
sea-config.jsonblockingSEA definition of that verifier binary
src/main/services/updater.tsadvisoryUpdate delivery

Notable exclusions

A review record of paths deliberately left out, in the same one-glob-per-entry form as the include list — see "List format" for why this list is not applied as a subtraction pass over the includes. It is notable exclusions, not an exhaustive complement of the include list.

PathWhy
src/main/ipcWrap.tsContent-agnostic IPC envelope
src/main/services/logger.tsOperational logging
src/main/services/logSafe.tsRedacts logs, not evidence — the definition's "redaction" is the annotation burn-in path
src/main/services/sessionLog.tsOperational logging
src/main/services/bugReport.tsBug-report zip deliberately excludes captures and the DB
src/main/services/diagnostics.tsOperational telemetry
src/main/services/thumbnails.tsDerived UI previews, never exported or hash-covered
src/main/services/openrouter.tsAPI-key test + model catalog only; cannot alter analysis output. Distinct from the included src/main/services/ai/openrouter.ts chat client
src/main/services/deepLink.tsNavigation plumbing
src/main/services/extensionPath.tsExtension setup plumbing
src/renderer/lib/api/**High-churn typed passthrough; evidence parameters originate in included dialogs and are enforced in main (but see judgment call 2)
src/renderer/lib/queries.tsSame rationale — the re-export barrel left behind by the split, deleted by the last PR of #229
src/renderer/components/captures/AddUrlsBox.tsxRemoved by the challenge pass as inconsistent with the standard applied to src/renderer/lib/queries.ts and src/renderer/components/settings/CapturePreferences.tsx — UI-side plumbing whose values are enforced in main
src/renderer/components/captures/useVerifyMutation.tsSame rationale as src/renderer/components/captures/AddUrlsBox.tsx above
src/renderer/components/captures/CaptureViewer.tsxRenderer chrome
src/renderer/components/captures/CaptureList.tsxRenderer chrome
src/renderer/components/captures/CaptureDetailsPanel.tsxRenderer chrome
src/renderer/components/settings/CapturePreferences.tsxCapture-preference UI; the values it writes are enforced in main
src/renderer/components/layout/**Renderer chrome
src/renderer/components/dashboard/**Renderer chrome, except the two files listed in the include list — see the precedence rule in "List format"
src/renderer/components/tags/**Renderer chrome
src/renderer/components/search/**Renderer chrome
src/renderer/components/status/**Renderer chrome
src/renderer/components/ui/**Shared UI primitives
src/renderer/components/notes/**Note-editing UI; the evidence binding lives in the included src/shared/noteAnchor.ts / src/main/services/noteAnchorResolver.ts
src/renderer/components/selectors/**Selector-management UI; matching lives in main
src/renderer/hooks/**Renderer chrome
src/renderer/routes/**Renderer chrome
src/renderer/stores/**UI-only state
src/renderer/styles/**Styling
.github/workflows/ci.ymlNo distributed artifacts
.github/workflows/security.ymlNo distributed artifacts
.github/workflows/docs.ymlNo distributed artifacts
scripts/coverage-*.mjsDev tooling
scripts/gen-linux-icons.mjsDev tooling
scripts/rebuild-native.mjsDev tooling
extension/src/fonts/**Popup cosmetics, never touch the captured page
extension/src/popup/popup.cssPopup cosmetics, never touch the captured page
extension/src/popup/popup.htmlPopup shell, never touches the captured page
src/renderer/components/export/ExportMenu.tsxLaunch and confirmation UI. It opens the dialog and reports the result; it shapes no package. Split out of src/renderer/components/export/** on 2026-08-23
src/renderer/components/export/ExportComplete.tsxSame rationale as src/renderer/components/export/ExportMenu.tsx
src/renderer/components/export/ExportProgress.tsxSame rationale as src/renderer/components/export/ExportMenu.tsx
src/main/services/db/diagnosticsRepo.tsRead-only database facts for the Diagnostics panel. Swept in by src/main/services/db/** and named as an accepted false positive by judgment call 8; made explicit on 2026-08-23
src/renderer/components/settings/db/ConfirmDialog.tsxGeneric confirmation dialog. Swept in by src/renderer/components/settings/db/**, same judgment call 8 rationale

Judgment calls needing confirmation in review

  1. src/main/ipcHandlers.ts and src/shared/ipc.ts wholesale. Both also carry pure-UI channels, so the gate fires on many non-evidence PRs. The backstop cannot distinguish a payload-type change to an evidence channel from a new UI list query. Accept the noise, or split the files later. Resolved 2026-08-23: both are advisory. The files are not split; the tier absorbs the noise instead.
  2. src/renderer/lib/api/** excluded while src/preload/index.ts is included. Both are typed passthroughs. As excluded, a hostile change like hardcoding overrideTamper: true in the import mutation (src/renderer/lib/api/cases.ts) would not trip the backstop. Including it gates most renderer PRs. Current call: excluded — the label-at-triage trigger and human review are expected to catch that class.
  3. pnpm-lock.yaml. Included by the same logic as package.json (the lockfile is the actual pin that determines shipped code), but it gates every dependency-bump PR. Current call: include; drop it if dep-bump noise proves unacceptable during the pilot. Resolved 2026-08-23: it did. pnpm-lock.yaml is advisory, still listed.
  4. src/main/services/session.ts / src/main/services/serverToken.ts. Included on misattribution/spoofed-ingest rationales; arguably workflow state and authn rather than evidence processing.
  5. src/main/services/csvEscape.ts. CSV exports are convenience exports, not signed packages; kept because they are still disclosed case data.
  6. Notes cluster (src/main/services/noteAnchorResolver.ts, src/shared/noteDoc.ts, src/shared/noteAnchor.ts). Notes never enter evidence packages; inclusion leans entirely on the "interpretation" clause (what evidence a note points at, and note text in exported reports/archives).
  7. Selector cluster (src/main/services/selectorLifecycle.ts, src/main/services/safeRegex.ts). Originally excluded as "matches never enter the evidence package" — the challenge pass found that factually wrong (src/main/services/caseArchive.ts exports and re-imports selectors and selectorMatches). Included on that basis; if selector matches are ruled non-evidentiary despite riding in archives, drop both.
  8. Accepted glob false positives. src/main/services/db/** sweeps in the read-only src/main/services/db/diagnosticsRepo.ts; src/renderer/components/settings/db/** sweeps in the generic src/renderer/components/settings/db/ConfirmDialog.tsx. Accepted: a per-file list would silently miss future files added to these directories. Resolved 2026-08-23: the globs stay, and both named files are now explicit exclusions. The precedence rule in "List format" makes the specific exclusion win over the directory glob, so future files in those directories are still swept in.
  9. extension/src/toast.ts / extension/src/popup/popup.tsx. One step removed from captured bytes (toast markup can be captured; the popup routes evidence to a case). A stricter list could drop extension/src/popup/popup.tsx.
  10. Distribution block (src/main/services/updater.ts, the build configs, .github/workflows/release.yml, pnpm-lock.yaml). Included under the definition's "software distribution" clause. If release PRs get their own review channel instead, this whole section could move out of the backstop. Resolved 2026-08-23: the section is advisory rather than moved out, except scripts/build-verifier.mjs and sea-config.json, which define the binary a receiving party runs and stay blocking.