Skip to content

Run every cross-version wire suite, and start the job for every module they pair - #22041

Open
brennanb2025 wants to merge 10 commits into
mainfrom
brennanb2025/wire-gate-agent-status-coverage
Open

brennanb2025 wants to merge 10 commits into
mainfrom
brennanb2025/wire-gate-agent-status-coverage

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 5 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​203 $\color{#cf222e}{\Huge{\mathbf{−}}}$​4 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​199
Prod 3 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​38 $\color{#cf222e}{\Huge{\mathbf{−}}}$​15 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​23

The problem

The cross-version wire compatibility PR job is Orca's check that a change still works against the last released build: it extracts the newest release tag and runs old code against new code (a paired desktop and remote host, or a database written by one build and opened by another). Two gaps meant it could stay green while seeing nothing.

Three suites in tests/e2e/cross-version-wire/ ran on no machine. The unit-test shards exclude that directory (a shallow clone has no release tags to extract), so the job's own file list is the only thing that runs it. That list was hand-maintained and had fallen behind: orchestration-delivery-downgrade, agent-session-death-evidence-downgrade and published-field-shape were in the directory but in no list, so they never executed and never reported.

Changes to modules the suites test did not start the job. Which PRs run the job is decided by a separate list of path prefixes (CROSS_VERSION_WIRE_PREFIXES). Nine modules that suites load and pair against a release were missing from it, for example src/main/runtime/orchestration/db/contract-constants.ts (the orchestration schema version the downgrade suite checks) and mobile/src/worktree/agent-row-display.ts (the phone's worktree ps row reader). A PR changing only those files skipped the one job that could catch a break in them.

What changes for developers

  • Every suite in the directory runs on every PR that starts the job. The three that never ran now run and pass.
  • A PR that changes a module a suite pairs now starts the job, including a mobile-only change to the phone's row reader and a migration under src/main/runtime/orchestration/db/schema/.
  • Adding a new suite, or making a suite pair a new module, fails CI until the job will actually run for it, with a message naming the module and the suite that loads it.

No app behaviour changes; this PR only touches CI configuration, tests and one reference doc.

How

  • .github/workflows/pr.yml: the job runs the directory, not a file list. It is the same boundary the shard exclusion uses, so a file cannot fall between them.
  • config/scripts/cross-version-wire-lane-registration.test.mjs (new) holds that shut: every suite is selected by the job, the shards still exclude the directory, and every module a suite (or the versioned-* helper it loads through) directly loads for pairing exists and starts the job. A population floor stops a broken scan from passing by finding nothing.
  • config/scripts/pr-code-change-scope.mjs: the ten missing prefixes, and the job's trigger is computed outside the general "should anything run" check, the same way mobile_web_app already is, because a change to the phone's row reader is a mobile-only diff that would otherwise skip every job.
  • The two newly running downgrade suites get the 180s budget their siblings use, and the release-extraction timeout goes from 45s to 120s because several pinned releases now extract at once.

Why this design

The earlier version of this PR added hand-written pairings for agent status and the SSH relay. Review traced every path by which one build's data reaches a different build and found those pairings guarded nothing real: the agent-status store's snapshot and change messages never leave the process that owns them, and the SSH relay is always the desktop's own build (each build installs it under its own versioned directory and the handshake refuses any other build). A gate that fails on changes no older peer can see blocks legitimate work, so they were removed. The recurring failure underneath was that the trigger list and the suites drifted apart silently. This PR fixes that class instead: the list is checked against what the suites actually load, so it cannot drift again.

What stays uncovered, stated rather than implied: session-tab sync publishes each terminal tab's agent status to paired desktop and mobile clients of any version, and nothing pairs it yet. Pairing it needs a designed suite and is left to a separate PR; docs/reference/remote-wire-compatibility.md lists it as unpaired. The guard sees modules a suite loads directly, not everything those modules import, so a transitive dependency (the orchestration schema directory is the known one) still has to be added by hand; the doc says so.

Cost

  • The job runs 13 suites in about 1.5 minutes on the free ARM runner (74 tests, ~49s of test time); the three newly running suites add about 6s.
  • Over the last 90 days of main, 37 of 6,470 commits (0.57%) would additionally have started the job, about 5% more runs of it, most from edits to the orchestration database facade. In a sample of the 300 newest open PRs, none newly start it.

Verified

  • Removing a prefix the guard needs turns it red, naming the module and the suite; making a suite load an ungated module turns it red; run against main's prefix list, it names exactly the modules this PR adds. Each edit was temporary and reverted.
  • A temporary change under orchestration/db/schema/ starts the job with this PR and did not before.
  • CI on the head is green, including the cross-version job (13 files, 74 tests).

…elay

The gate that decides which PRs run the cross-version wire job listed 23 prefixes
and none of them reached the agent-status store, the worktree/mobile row contract,
or the relay transport. A change to any of those could break a released peer with
nothing in CI to say so.

Add those prefixes, and give them something that can fail: two new pairings against
the newest release, one over the agent-status snapshot, mutation envelope and status
payload, one over the relay handshake and JSON-RPC framing. Reaching a frozen relay
daemon needs src/relay in the extracted tree, so the harness now archives it.

Also fix the lane itself. The unit shards exclude this directory, so the job's vitest
argv was the only thing deciding whether a suite ran, and it was a curated list of
seven paths while the directory held nine. The two unlisted suites executed nowhere;
one could not have passed if it had, because it extracts a checkout no sibling shares
and had no timeout to cover it. The step now names the directory, and a new guard
keeps the two in step.
@coderabbitai

coderabbitai Bot commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 54fe3cf5-c0a6-4128-b279-43916e8c1e9c

📥 Commits

Reviewing files that changed from the base of the PR and between f4f6f76 and d4b6e38.

📒 Files selected for processing (4)
  • config/scripts/cross-version-wire-lane-registration.test.mjs
  • config/scripts/pr-code-change-scope.mjs
  • config/scripts/pr-code-change-scope.test.mjs
  • docs/reference/remote-wire-compatibility.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/reference/remote-wire-compatibility.md

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 1 remain after this review.


📝 Walkthrough

Walkthrough

The PR workflow now runs all tests in the cross-version wire directory. Job classification covers additional runtime, persistence, orchestration, and mobile worktree paths. A registration test checks suite selection, paired-module coverage, and unit-shard exclusion. Two downgrade tests and the checkout process use longer timeout limits. The compatibility documentation describes harness coverage and job selection.

Priority: ⬇️ Low

Merge Risk: 🟡 Moderate · up to d4b6e

The lane still does not test the session-tab status payload against an older reader, and a relay test failure may be misleading. Resolve or explicitly accept these gaps before merging.

Security Architecture Review

Security architecture risk: 🔵 Low · up to d4b6e

The change broadens compatibility testing without an apparent change to production permissions or shipped wire behavior. The newly reached tests run with read-only repository access, but runner-level controls and test outcomes were not established.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — Broader path matching and directory selection increase the PR-controlled test code reached by this CI job, but the inspected job does not gain a write-capable repository token or a deployment step.

Trust Boundaries and Controls

  • observed — PR-controlled path names influence whether the test job runs, not its declared permissions. The job checks out PR code with credentials not persisted; repository test execution remains the relevant trust boundary.

Resilience and Maintainability Implications

  • observed — The registration check fails on missing lane configuration, unselected suites, or paired modules whose changes do not start the job, reducing drift in this compatibility gate.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.45% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 22 functions across 11 files. (1 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the primary changes: running every cross-version wire suite and triggering the job for every paired module.
Description check ✅ Passed The description is detailed and directly explains the problem, changes, rationale, scope, limitations, cost, and verification. It does not include a linked issue, the required Visual Proof entry, or t…
Full details: Docstring Coverage

Explanation

Docstring coverage is 45.45% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 22 functions across 11 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/e2e/cross-version-wire/cross-version-relay-transport-wire.unit.test.ts (1)

156-162: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the verdict frame decoded before parsing it.

This test parses frame?.payload ?? Buffer.alloc(0). If decodeAll returns no frame, parseHandshakeMessage throws on the empty buffer while evaluating the expect argument. The custom message at Line 159 never runs, so the reported failure is a JSON parse error rather than a missing handshake verdict frame. The two neighbouring tests assert frame?.type first; apply the same guard here.

♻️ Proposed guard
       const [frame] = decodeAll(bridge, reply)
+      expect(frame?.type, `${bridge.label} did not read a handshake verdict frame`).toBe(
+        bridge.host.MessageType.Handshake
+      )
       expect(
         bridge.host.parseHandshakeMessage(frame?.payload ?? Buffer.alloc(0)),
         `${bridge.label} could not read ${daemon.label}'s handshake verdict`
       ).toMatchObject({
         type: agreed ? 'orca-relay-handshake-ok' : 'orca-relay-handshake-mismatch'
       })

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 0305f6a6-01a2-4ec5-8e02-fc5e52ece810

📥 Commits

Reviewing files that changed from the base of the PR and between 663d670 and adccab0.

📒 Files selected for processing (11)
  • .github/workflows/pr.yml
  • config/scripts/cross-version-wire-lane-registration.test.mjs
  • config/scripts/pr-code-change-scope.mjs
  • config/scripts/pr-code-change-scope.test.mjs
  • docs/reference/remote-wire-compatibility.md
  • tests/e2e/cross-version-wire/cross-version-agent-status-wire.unit.test.ts
  • tests/e2e/cross-version-wire/cross-version-relay-transport-wire.unit.test.ts
  • tests/e2e/cross-version-wire/orchestration-delivery-downgrade.unit.test.ts
  • tests/e2e/cross-version-wire/release-checkout-tree.ts
  • tests/e2e/cross-version-wire/release-checkout.ts
  • tests/e2e/cross-version-wire/versioned-agent-status-wire.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 4 remain after this review.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ Minor suggestions only — two of the seven newly gated prefixes cannot fail any suite in the lane.

Reviewed changes

  • Wire gate widened — CROSS_VERSION_WIRE_PREFIXES gains seven agent-status, worktree-row and relay prefixes, so the cross-version-wire job now starts for those diffs.
  • Lane runs the whole directory — the cross-version-wire vitest argv drops the curated seven-file list for tests/e2e/cross-version-wire/, so orchestration-delivery-downgrade and published-field-shape execute again; the latter gets the missing 180s budget.
  • Lane registration guard (new) — asserts every .unit.test.ts in the directory is selected, the argv names only on-disk suites, the shard exclusion stays, and the classifier gates the job for each suite, with a population floor.
  • Agent-status pairing (new) — skews the store snapshot, mutation envelope and OSC status payload in both directions and checks the arm set is a superset of the release's.
  • Relay pairing (new) — skews the handshake frame and a desktop→daemon JSON-RPC frame, and pins the two RELAY_VERSION copies together within a build.
  • Checkout support — ARCHIVE_PATHS gains src/relay, CHECKOUT_FORMAT 3→4, extraction timeout 45s→120s so two concurrent fills fit.
  • Docs + classifier tests — remote-wire-compatibility.md documents the new surfaces; pr-code-change-scope.test.mjs asserts each new prefix exists on disk and classifies true.

The new agent-status suite is real coverage: the snapshot/mutation decoders use strict hasOnlyKeys, so an added nested field fails hydration, and the top-level key-parity assertion fails on an added/removed snapshot key. I re-ran the two config/scripts suites here (44 passed) and confirmed every path named in pr-code-change-scope.test.mjs exists.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

Comment thread config/scripts/pr-code-change-scope.mjs Outdated
Comment thread config/scripts/pr-code-change-scope.mjs Outdated
Mobile Checks dispatches on its own paths filter, and it does not list the shared
types mobile imports verbatim: runtime-types (26 mobile importers),
agent-status-types (15) and runtime-worktree-contracts (2). Of 103 host-side
modules named *mobile* outside mobile/, 85 are outside that filter, including the
projection that builds the tab rows a phone reads.

Two of those shared types are gated for cross-version skew by this change. That is
a different failure from mobile's typecheck breaking, so the page now says so
rather than letting a reader infer the phone is covered. The fix belongs in
mobile.yml.
Resolve the lane argv to the directory (main had grown its curated list to ten
files, still missing three suites), union ARCHIVE_PATHS with main's phone row
reader and bump CHECKOUT_FORMAT to 5 since main already took 4, keep main's
orchestration downgrade test and re-apply only its timeout, give the death
evidence downgrade suite the same budget, drop the duplicate
runtime-worktree-contracts prefix main already added, and point the shard
exclusion guard at ci-unit-files.mjs where main moved it.
@greptile-apps

greptile-apps Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[High risk] Expands cross-version wire test coverage and CI lane triggers.

The PR appears safe to merge; no new blocking defect was established.

Findings

  1. P2 Nested status fields go untested ▶
  2. P2 Handshake verdict is simulated ▶
  3. P2 OSC text parser is bypassed ▶
  4. P2 New status arms go untested ▶

Summary

The PR runs the cross-version wire job for additional paired modules and selects the entire cross-version suite directory instead of a curated file list. It adds registration checks for suite selection and path gating, and gives two downgrade tests longer cold-checkout budgets.

Reviews (6) · Last reviewed commit: "ci(cross-version-wire): start the job fo..."

Comment on lines +65 to +88
expect(
store.applyMutation({ parent: { subject, firstObservedAt: 10 } }),
build.label
).not.toBeNull()
const childWorkId = 'cross-version-child-1'
expect(
build.announceChild(store, childWorkId, {
parent: subject,
provider: 'claude',
aliases: [{ segmentId: 'segment-1', aliasKind: 'task_id', alias: 'task-1' }],
fence: { invocationId: 'invocation-1', generation: 1 },
lifetime: 'current',
kind: 'agent',
state: 'working',
membership: 'live',
observedAt: 20,
stoppable: true,
provenance: { source: 'transport', producerId: 'cross-version-fixture' }
}),
build.label
).toMatchObject({ accepted: true, childWorkId })
const snapshot = store.getSnapshot()
const envelope = store.applyMutation({
facts: [{ subject, key: 'cross-version-fact', value: 'published' }]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Nested status fields go untested The fixture creates a parent without a status and follows the snapshot with a facts-only mutation. If a real host publishes a new nested status or child-work field, the older peer’s strict decoder can reject it while this pairing stays green. Exercise status-bearing parents and representative child-work mutations; the separate comparison checks only top-level snapshot keys.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +137 to +156
const parsed = daemon.host.parseHandshakeMessage(frame?.payload ?? Buffer.alloc(0))
expect(parsed).toMatchObject({
type: 'orca-relay-handshake',
version: bridge.host.RELAY_VERSION
})
})

it('answers the peer handshake with a verdict the peer can read', () => {
const { bridge, daemon } = pair()
const agreed = daemon.host.RELAY_VERSION === bridge.host.RELAY_VERSION
const reply = daemon.host.encodeHandshakeFrame(
agreed
? { type: 'orca-relay-handshake-ok', version: daemon.host.RELAY_VERSION }
: {
type: 'orca-relay-handshake-mismatch',
expected: daemon.host.RELAY_VERSION,
got: bridge.host.RELAY_VERSION
}
)
const [frame] = decodeAll(bridge, reply)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Handshake verdict is simulated Changes to relay-handshake.ts start this job, but the pairing chooses and encodes the daemon’s reply itself. It never exercises the daemon’s version and credential checks or the bridge’s refusal handling. Those paths could break a reconnect while the job stays green; pair the actual handlers before treating that file as covered.

Knowledge Base Used: Relay protocol and transport

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +105 to +106
.announce(request),
normalizeStatusPayload: (payload) => modules.types.normalizeAgentStatusPayload(payload)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 OSC text parser is bypassed The claimed OSC pairing calls the structured-object normalizer on both builds. OSC 9999 status arrives as JSON text through the separate parseAgentStatusPayload entry point, so a change confined to that parser can break CLI status ingestion while this suite stays green. Send JSON text from one build through the other build’s OSC parser.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: f729e081-98a7-4ea2-9b5c-4095a65bab97

📥 Commits

Reviewing files that changed from the base of the PR and between 2c35b2d and 8381e66.

📒 Files selected for processing (9)
  • .github/workflows/pr.yml
  • config/scripts/cross-version-wire-lane-registration.test.mjs
  • config/scripts/pr-code-change-scope.mjs
  • config/scripts/pr-code-change-scope.test.mjs
  • docs/reference/remote-wire-compatibility.md
  • tests/e2e/cross-version-wire/agent-session-death-evidence-downgrade.unit.test.ts
  • tests/e2e/cross-version-wire/orchestration-delivery-downgrade.unit.test.ts
  • tests/e2e/cross-version-wire/release-checkout-tree.ts
  • tests/e2e/cross-version-wire/release-checkout.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 3 remain after this review.

Comment thread docs/reference/remote-wire-compatibility.md Outdated

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No new issues in the merge delta — one informational note below.

Reviewed changes

Re-reviewed the commits that landed since the prior pullfrog review (adccab001a): the origin/main merge 8381e66.

  • Re-based the lane guard onto the sharded unit workflow — cross-version-wire-lane-registration.test.mjs now finds the shard step by --shard=, asserts ORCA_BALANCE_UNIT_SHARDS is 1, and checks UNIT_EXCLUDE carries tests/e2e/cross-version-wire/**, because unit-tests.yml / vitest.config.ts now apply the exclusion through that shared constant behind the env flag instead of inline in the step's run.
  • Dropped the duplicate worktree-contracts prefix — the merge removed the src/shared/runtime-worktree-contracts entry this PR had added beside src/shared/agent-status; origin/main already carries that prefix, so the gate's coverage is unchanged and the prior thread on it is retired.
  • Reconciled the checkout config — ARCHIVE_PATHS keeps src/relay and the mobile row reader, and CHECKOUT_FORMAT lands at 5 (base 4 plus this PR's bump).

Verified in-run: config/scripts/cross-version-wire-lane-registration.test.mjs + pr-code-change-scope.test.mjs → 46 passed; the lane argv is the whole directory, matching the shard exclusion.

ℹ️ PR description still counts the pre-existing prefix as newly added

The body's "Added to CROSS_VERSION_WIRE_PREFIXES" list names seven paths, including src/shared/runtime-worktree-contracts, but that prefix ships on origin/main and the merge dropped this PR's duplicate — the PR adds six. A reviewer comparing the description to the diff is left to reconcile the extra entry.

Technical details
# PR body overstates the newly gated prefix set

## Affected sites
- PR description "The gate runs for these paths" list — names `src/shared/runtime-worktree-contracts` as added by this PR.
- `config/scripts/pr-code-change-scope.mjs:178` — `src/shared/runtime-worktree-contracts` exists on `origin/main`; the merge dropped this PR's duplicate entry next to `src/shared/agent-status`.
- HEAD adds six prefixes: `src/shared/agent-status`, `src/main/ssh/relay-protocol`, `src/shared/relay-frame-decoder`, `src/relay/protocol`, `src/relay/relay-frame-decoder`, `src/relay/relay-handshake`.

## Required outcome
- The description's prefix list matches the diff, so a reader is not left reconciling the extra entry.

## Open questions for the human
- None.

Pullfrog  | Fix it ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

…sses versions

The agent-status store's snapshot and mutation envelope never leave the process
that owns the store, and the SSH relay is always the desktop's own build (content-
hashed install directory, per-version socket, exact-version handshake). Pairing
them gated changes no older peer can observe.

What does cross versions is the hook server's status file: after an update or a
downgrade the other build re-admits each persisted row with its own
normalizeAgentStatusPayload. Pair that in both directions over every baseline arm,
and check current still writes every arm and field the baseline writes.

Narrow the lane prefixes to the normalizer's modules, drop the relay prefixes and
src/relay from the extracted tree (CHECKOUT_FORMAT back to main's 4), fold the
classification cases into main's existing list so the scope test fits max-lines,
and correct the wire-compatibility doc.
… suites minimal

A named timeout after the test body makes the formatter re-indent the whole test;
a numeric literal keeps the test-call layout, so each suite diff is two lines.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found — the narrower gate and the new payload pairing are sound, and the prior relay thread is addressed.

Reviewed changes

Re-reviewed the commit that landed since the prior pullfrog review (8381e66): 26321e3, which narrows the PR to the agent-status payload surface.

  • Gate narrowed to three payload prefixes — CROSS_VERSION_WIRE_PREFIXES now adds src/shared/agent-status-types, src/shared/agent-status-field-normalization and src/shared/agent-turn-outcome; the store, relay and worktree-row prefixes from earlier commits are gone. The three are the inputs normalizeAgentStatusPayload uses, so each can change what the hook server re-admits from a persisted status row after an update or downgrade.
  • Single payload pairing replaces three files — cross-version-agent-status-payload.unit.test.ts supersedes the deleted cross-version-agent-status-wire.unit.test.ts, cross-version-relay-transport-wire.unit.test.ts and versioned-agent-status-wire.ts, pairing normalizeAgentStatusPayload against the newest release in both directions.
  • Checkout config reverted — CHECKOUT_FORMAT is back to 4 and src/relay is dropped from ARCHIVE_PATHS, leaving the 45s→120s extraction timeout as the only net change to release-checkout-tree.ts.
  • Lane guard trimmed — the now-redundant "argv names no path that is not a suite" assertion is removed; the directory-argv selection and shard-exclusion checks remain.
  • Docs rewritten — the canonical store and the relay are documented as deliberately ungated with their reasons; the earlier mobile-gap section is gone.

I re-ran pr-code-change-scope.test.mjs and cross-version-wire-lane-registration.test.mjs (44 passed), traced each gated prefix to an assertion that can actually fail on its change, and checked the docs' "the relay is always the desktop's own build" claim against src/main/ssh/ssh-relay-superseded-endpoints.ts (per-version relay directory, exact content-hash handshake) — it holds. The store snapshot/envelope serializers have no production caller, so pairing them "when it ships" is the right call.

ℹ️ PR description and title still describe the pre-26321e3 scope

The title ("agent status, worktree rows and the relay") and the body still promise the relay gate, the src/shared/agent-status store gate, the two deleted suites, src/relay in ARCHIVE_PATHS, the CHECKOUT_FORMAT bump and the mobile-gap analysis. None of those are in the diff at HEAD, so a reviewer reading the description is left to reconcile roughly twice the change that shipped. The code is mergeable as-is; this is a description refresh only.

Technical details
# PR body/title no longer match the diff

## Affected sites
- PR title — names "worktree rows and the relay", neither gated by this PR at HEAD.
- Body "The gate runs for these paths" — lists `src/shared/agent-status`, `src/main/ssh/relay-protocol`, the three `src/relay/*` prefixes; HEAD adds only `src/shared/agent-status-types`, `src/shared/agent-status-field-normalization`, `src/shared/agent-turn-outcome`.
- Body "What is now covered" — describes `cross-version-agent-status-wire.unit.test.ts` and `cross-version-relay-transport-wire.unit.test.ts`, both deleted; the surviving suite is `cross-version-agent-status-payload.unit.test.ts`.
- Body checkout paragraph — says `ARCHIVE_PATHS` gains `src/relay` and `CHECKOUT_FORMAT` goes 3→4; HEAD has neither (format is `4`, same as `main`).
- Body "A neighbouring gap I did not fold in" — the mobile-gap section was removed from `docs/reference/remote-wire-compatibility.md` in this commit.

## Required outcome
- Title and body describe the three-prefix / one-suite payload change that HEAD actually ships, so the description is not read as cover for the dropped relay and store surfaces.

## Open questions for the human
- Was dropping the relay/store gates deliberate (the commit and docs read as intentional) — if so, the body's "Blast radius" figures also need recomputing for the narrower prefix set.

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

it('re-admits every row the writer persisted and reads each shared field as its own', () => {
const { writer, reader } = pair()
// Every arm the release can write; an arm current drops fails the old-writer direction.
for (const state of baseline.states) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 New status arms go untested When a new build adds a status arm, it can persist a row that the older build rejects after a downgrade, losing that status. Both pairing directions test only arms from the baseline release, while the separate arm check allows new ones. The lane can therefore stay green for this break. Test arms written only by the current build against the older reader.

Knowledge Base Used: Agent integrations and hooks

…us's only version boundary

Session-tab sync also carries each terminal tab's agent status to paired clients of
any version; say so where the suite and the reference name what is and is not paired.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 237c9f1f-5037-4bc7-bf86-723573a4dfeb

📥 Commits

Reviewing files that changed from the base of the PR and between 8381e66 and f4f6f76.

📒 Files selected for processing (8)
  • config/scripts/cross-version-wire-lane-registration.test.mjs
  • config/scripts/pr-code-change-scope.mjs
  • config/scripts/pr-code-change-scope.test.mjs
  • docs/reference/remote-wire-compatibility.md
  • tests/e2e/cross-version-wire/agent-session-death-evidence-downgrade.unit.test.ts
  • tests/e2e/cross-version-wire/cross-version-agent-status-payload.unit.test.ts
  • tests/e2e/cross-version-wire/orchestration-delivery-downgrade.unit.test.ts
  • tests/e2e/cross-version-wire/release-checkout-tree.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/reference/remote-wire-compatibility.md

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 0 remain after this review.

it('re-admits every row the writer persisted and reads each shared field as its own', () => {
const { writer, reader } = pair()
// Every arm the release can write; an arm current drops fails the old-writer direction.
for (const state of baseline.states) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Test every state that the selected writer can persist.

Both pairings iterate baseline.states. If the current build adds a state, the new-writer pairing never sends that state to the baseline reader. Line 137 still passes because it permits additional current states. The baseline normalizer would reject the new state, and persisted-row hydration would drop the row. Iterate writer.states in each pairing so the lane detects this downgrade incompatibility. (raw.githubusercontent.com)

The registration test now scans each suite (and the wire helpers suites load
through) for the working-tree modules and release-checkout paths it pairs, and
fails when one does not start the cross-version-wire job. Nine paired modules
did not; they are added as exact paths. The phone's worktree-ps row reader is a
mobile-only diff, so the job is lifted outside should_run like mobile_web_app.

Drops the agent-status payload pairing: the hook server's status file is a
minor path, the suite skipped the new-arm-on-downgrade case it claimed, and it
could not see the file's own version gate. Session-tab sync's agentStatus is
now listed as unpaired instead.
@greptile-apps

This comment has been minimized.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No code issues in the delta — one description refresh below.

Reviewed changes

Re-reviewed the commit that landed since the last pullfrog review (f4f6f76): e8fdd62, which starts the lane for every module its suites pair.

  • Paired-module scanner added (new) — cross-version-wire-lane-registration.test.mjs walks every .ts file in the lane directory for static/dynamic working-tree imports and quoted checkout paths, and fails when a paired module is not in CROSS_VERSION_WIRE_PREFIXES, with a 25-module floor and an existence check so a broken scan cannot pass empty.
  • Nine paired modules gated — rpc/methods/index.ts, runtime-subscription-registry.ts, terminal-retirement-proof-ledger.ts, mobile-session-terminal-retirement(-proof).ts, worktree-identity-migration.ts, orchestration/db.ts, orchestration/db/contract-constants.ts and mobile/src/worktree/agent-row-display.ts. Each is genuinely loaded by a suite, so the prefix can fail for a change.
  • Lane lifted outside should_run — the phone's worktree ps row reader is a mobile-only diff, so the job is set like mobile_web_app instead of staying gated on desktop-relevant changes.
  • Agent-status payload pairing dropped — the suite, its three prefixes and versioned-agent-status-wire.ts are removed; the docs now list session-tab-sync agentStatus as unpaired.
  • Docs tightened — remote-wire-compatibility.md states every paired module must be on the prefix list and names cross-version-worktree-ps-verdict.unit.test.ts as the one paired agent-status path.

Verified in-run: cross-version-wire-lane-registration.test.mjs + pr-code-change-scope.test.mjs → 47 passed; the scanner resolves 30 modules, all on disk and classifying cross-version-wire=true.

ℹ️ Title and body still describe the gates this PR dropped

The title promises "agent status, worktree rows and the relay" and the body's "What is now covered" section lists src/shared/agent-status, the src/relay/* prefixes, two deleted suites and a CHECKOUT_FORMAT bump — none of which are in the diff at HEAD. With e8fdd62 also dropping the agent-status payload pairing, the description now reads as roughly the opposite of what ships.

Technical details
# PR title/body no longer match HEAD

## Affected sites
- PR title — names "agent status" and "the relay"; neither is in `CROSS_VERSION_WIRE_PREFIXES` at HEAD.
- Body "The gate runs for these paths" — lists `src/shared/agent-status`, `src/main/ssh/relay-protocol`, `src/shared/relay-frame-decoder`, `src/relay/protocol`, `src/relay/relay-frame-decoder`, `src/relay/relay-handshake`; none are added by HEAD.
- Body "What is now covered" — describes `cross-version-agent-status-wire.unit.test.ts` and `cross-version-relay-transport-wire.unit.test.ts`, both deleted; `cross-version-agent-status-payload.unit.test.ts` is also deleted in `e8fdd62`.
- Body checkout paragraph — says `ARCHIVE_PATHS` gains `src/relay` and `CHECKOUT_FORMAT` goes 3→4; HEAD has neither.

## Required outcome
- The title and body describe the surface HEAD actually gates (the worktree-row reader plus the terminal / agent-session suites already there), so a reviewer is not left reconciling a different PR.

Pullfrog  | Fix it ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

The orchestration downgrade suite opens a current-schema database with old
code, so a migration under db/schema/ is what it guards. The registration
guard only sees modules a suite loads directly and db.ts reaches the schema
transitively, so gate it by hand and say so where the guard is described.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No critical issues — one minor gap in the newly hand-gated transitive set.

Reviewed changes

Re-reviewed the commits since the prior pullfrog review (e8fdd62): the two origin/main merges and 7a75ff2.

  • Orchestration schema gated by hand — CROSS_VERSION_WIRE_PREFIXES gains src/main/runtime/orchestration/db/schema/, so a change to the schema migrations behind db.ts starts the lane; the orchestration downgrade suite reaches them transitively.
  • Scanner limit documented — the readPairedModules JSDoc and remote-wire-compatibility.md now state that only a suite's direct loads are auto-gated and a module reached through them has to be added to the prefix list by hand.
  • Classifier case added — pr-code-change-scope.test.mjs asserts db/schema/migrate-v42.ts classifies cross-version-wire=true.

Verified in-run: the transitive claim holds (db.ts → orchestration-db.ts → schema/migrate.ts → schema/migrate-v42.ts), and cross-version-wire-lane-registration.test.mjs + pr-code-change-scope.test.mjs pass (47).

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

'src/main/runtime/orchestration/db.ts',
'src/main/runtime/orchestration/db/contract-constants.ts',
// Why: the orchestration downgrade suite opens a current-schema database with old code; db.ts reaches this transitively.
'src/main/runtime/orchestration/db/schema/',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

orchestration-schema-version-skew.ts sits one directory up from this new prefix and is imported by the now-gated schema/migrate.ts; it owns resolveOrchestrationMigrationStartVersion, the exact cross-version decision the orchestration downgrade suite exercises. A change to it does not start the lane — classifyPrJobs(['src/main/runtime/orchestration/orchestration-schema-version-skew.ts'])['cross-version-wire'] is false. Consider adding it alongside db/schema/.

Technical details
# The orchestration version-skew resolver is ungated

## Affected sites
- `config/scripts/pr-code-change-scope.mjs:177` — `src/main/runtime/orchestration/db/schema/` was just added; the sibling resolver one directory up is not on the list.
- `src/main/runtime/orchestration/orchestration-schema-version-skew.ts:184` — `resolveOrchestrationMigrationStartVersion`, the function that decides whether a stored DB migrates or keeps its stamp.
- `src/main/runtime/orchestration/db/schema/migrate.ts:1` — imports the resolver; `migrate.ts` lives inside the newly gated directory.
- `tests/e2e/cross-version-wire/orchestration-delivery-downgrade.unit.test.ts:5,25,43` — drives `OrchestrationDb` (which runs the resolver through `migrate`) against a pre-v41 checkout, so the lane can fail for a change here.

## Required outcome
- A change to `orchestration-schema-version-skew.ts` starts the `cross-version-wire` job, the same standard the `db/schema/` prefix was just added for.
- Add a representative case to the `pr-code-change-scope.test.mjs` classifier list so the on-disk path is exercised too.

## Open questions for the human
- The hand-gated transitive list also has no existence guard, and `db/orchestration-db.ts` / `db/attach-orchestration-db-methods.ts` are likewise ungated. Is `db/schema/` the intended coverage boundary, or the transitive closure around `db.ts`?
Suggested change
'src/main/runtime/orchestration/db/schema/',
'src/main/runtime/orchestration/db/schema/',
'src/main/runtime/orchestration/orchestration-schema-version-skew',

@brennanb2025 brennanb2025 changed the title Run the cross-version wire gate on agent status, worktree rows and the relay Run every cross-version wire suite, and start the job for every module they pair Sep 29, 2026
@brennanb2025

Copy link
Copy Markdown
Contributor Author

Review status: ready for your review (not merged)

The problem this now fixes: the cross-version wire job could stay green while seeing nothing. Three suites in tests/e2e/cross-version-wire/ ran on no machine because the job's hand-kept file list had fallen behind, and nine modules the suites pair against a release did not start the job when changed.

What is different for developers: every suite in the directory runs whenever the job does; a change to any module a suite pairs (including a mobile-only change to the phone's worktree ps row reader, and orchestration schema migrations) now starts it; and a new suite or a newly paired module fails CI until the job will run for it.

The PR was rescoped during review, and the title and body are rewritten to match. The original version added agent-status and SSH relay pairings. Tracing every path by which one build's data reaches a different build showed those pairings guarded nothing real: the agent-status store's snapshot and change messages never leave the process that owns them, and the SSH relay is always the desktop's own build (versioned install directory, handshake refuses any other build). They would have failed CI on changes no older peer can see, so they were removed. What replaced them fixes the underlying failure class: the trigger list is now checked against what the suites actually load, so it cannot silently drift again.

Still not covered, on purpose: session-tab sync sends each terminal tab's agent status to paired desktop and mobile clients of any version, and nothing pairs it. That needs a designed suite and belongs in its own PR; the reference doc lists it as unpaired. The guard also only sees modules a suite loads directly, so transitive dependencies (the orchestration database writers behind db.ts) still need adding by hand.

Evidence

  • CI at 7a75ff2098e: 31 checks passed, 7 skipped, 0 failed. The cross-version job ran all 13 suites (74 tests) in about 1.5 minutes, including the three that previously never ran.
  • Removing a prefix the guard needs turns it red, naming the module and the suite that loads it (reproduced by me); making a suite load an ungated module turns it red; against main's list it names exactly the modules this PR adds. All edits were temporary and reverted.
  • Cost: over the last 90 days of main, 0.57% of commits (37 of 6,470) would additionally have started the job; none of the 300 newest open PRs newly start it.
  • Readiness checklist run at the start and the end of the review; the end run found no code issues.
  • Electron QA: not applicable. This PR changes only CI configuration, tests and a reference doc, with no app behaviour to exercise, so no UI check was invented.

Related open work: #19861 changes how the relay negotiates its protocol version. If that lands and a new client can talk to an older relay, the relay becomes a real cross-version surface and should get a pairing then.

Not verified: the combined result against the single newest main commit (#23810, unit shard rebalancing) has not run in CI; a local test merge is clean and it does not touch this job.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant