Skip to content

fix(clients): remote pairing keeps the scopes the link grants - #197

Merged
Jacksondr5 merged 1 commit into
j5/peering-5-connections-uifrom
j5/peering-6-pairing-scopes
Sep 30, 2026
Merged

Jacksondr5 merged 1 commit into
j5/peering-5-connections-uifrom
j5/peering-6-pairing-scopes

Conversation

@Jacksondr5

@Jacksondr5 Jacksondr5 commented Sep 21, 2026 •

Copy link
Copy Markdown
Owner

Stack 7/8. Depends on #177. Merge in order, retargeting to j5/main as predecessors land.

Full stack:

Testing: scripts/j5/pr-env.sh <pr> --peer (#255) serves this PR's build as two peered servers, Local and Remote. Rebased onto j5/main on 2026-09-30, after the Effect rc.115 upstream sync and the Playbooks and Crews migrations landed; peer migrations are now 023–027 and the FORK.md case is 48. A test state directory that ran an earlier cut of this stack recorded the peer tables under other ids, so rerun pr-env.sh with --fresh for such environments.


Stack 6 of the cross-server peering build. Base: #177 (Connections UI). Unblocks the "Peer with…" dialog for a remote environment paired from the web client.

Problem

Testing #177 on two servers showed the readiness notice "This connection to … cannot manage access (it lacks access:write)". It was correct. Every client pinned AuthStandardClientScopes when pairing a remote environment (apps/web/src/connection/platform.ts, the mobile equivalent, and the shared pairing and DPoP exchanges), and the server grants what is requested rather than what the link carries. A startup URL's administrative grant was therefore silently narrowed, the remote session never held access:write, and the peer-credential route from #173 would refuse it. Peering from the web client to any remote environment was impossible.

Upstream has the same diagnosis and an open maintainer fix: pingdotgg#9785 "fix(auth): preserve explicitly granted pairing scopes" (juliusmarminge, last pushed 2026-09-19). Community PR pingdotgg#4496 reported it first, in July.

What this does

Takes the client hunks of pingdotgg#9785 verbatim, so a later upstream sync either merges cleanly or drops the fork's copy:

  • ClientPresentation loses its scopes field.
  • The pairing, bearer-refresh, and DPoP exchanges send no scope, so the server issues exactly what the pairing link carries. An administrative startup URL yields an administrative remote session; a link from j5 auth pairing create still yields standard scopes.
  • Desktop secondary backends keep requesting the standard set, with upstream's comment explaining why.
  • Also from fix(auth): preserve explicitly granted pairing scopes pingdotgg/t3code#9785, after review: registry.ts compares catalog entries by reference (Stream.changesWith) so re-pairing an unchanged environment rebinds its observers to the replacement supervisor. Without it, upgrading a saved standard-scope connection to administrative scope for peering left consumers on the old credential. Upstream's observer test comes with it.
  • Client-runtime tests: the presentation fixtures drop the field, the pairing test asserts no scope is sent, and two new cases prove a read-only and an administrative grant survive pairing unchanged (mirroring upstream's tests).

Not taken from pingdotgg#9785: its server-side validate-before-consume, the CLI --scope flags, and browser re-pair session replacement. Peering does not need them.

FORK.md

Recorded under "Temporary in-place patches (watching upstream for a fix)". Watch signal: pingdotgg#9785 or its successor merges into upstream main. Exit: take upstream's versions of these files outright at that rebase, including any revision upstream makes before merging; the fork holds no other delta in them.

Surfaces

  • Web and desktop: remote pairing now inherits the link's grant.
  • Mobile: same change to its presentation layer; no peering UI there yet.
  • Existing sessions keep their recorded scopes. To test, remove the remote environment and re-add it with a fresh startup URL.

Verification

  • vp test run on onboarding.test.ts, layer.test.ts, resolver.test.ts, supervisor.test.ts: 76 passed.
  • Scoped typecheck of packages/client-runtime, apps/web, apps/mobile: clean.
  • Lint on the changed files: clean.

Built by Claude Fable 5.1 in Claude Code.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Behavior Changes
    • Pairing and token exchanges now use the permissions granted by the pairing link, rather than requesting permissions from the client.
    • Desktop secondary connections continue to request the standard permissions.
  • Bug Fixes
    • Re-pairing an environment with replacement credentials now updates active connections to use the new credentials.
    • Connection updates are filtered to avoid notifying observers when an environment entry has not changed.

@Jacksondr5

Copy link
Copy Markdown
Owner Author

Astra review (gpt-6-astra, 2026-09-24) of this PR after the rebase onto current j5/main. Findings and what changed:

  • Confirmed: the client hunks of upstream fix(auth): preserve explicitly granted pairing scopes pingdotgg/t3code#9785 were taken, but not its registry.ts change: Stream.changes compares catalog entries structurally, so re-pairing an unchanged environment kept consumers on the old supervisor and credential. Fixed by taking upstream's Stream.changesWith reference comparison and its re-pair observer test. The DPoP scope tests upstream added did not apply to the fork's test file and were not taken; the pairing test here asserts no scope is sent and that read-only and administrative grants survive.
  • Everything else checked out: optional name fields, no labels in agent envelopes, bound SQL parameters, sender label rendered as React text, receipt replay before label persistence, and the rebase seams in the delivery worker, runtime layer, send service, handler tests, and client-runtime modules.

Built by Claude Fable 5.1 in Claude Code.

@Jacksondr5
Jacksondr5 marked this pull request as ready for review September 24, 2026 07:08
@coderabbitai

coderabbitai Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

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

Important

Review skipped

We couldn't safely recover the incremental review. No full review was started, and the last reviewed checkpoint was preserved. Retry later, or explicitly request a full review by commenting @coderabbitai full review.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: Jacksondr5/j5code/.coderabbit.yaml

Review profile: CHILL

Plan: Essentials

Run ID: 9aaf449e-99b3-4b82-9e1d-aad34426ab8b

📥 Commits

Reviewing files that changed from the base of the PR and between 1033829 and dca6209.

📒 Files selected for processing (12)
  • FORK.md
  • apps/mobile/src/connection/platform.ts
  • apps/web/src/connection/platform.ts
  • packages/client-runtime/src/authorization/layer.test.ts
  • packages/client-runtime/src/authorization/service.ts
  • packages/client-runtime/src/connection/onboarding.test.ts
  • packages/client-runtime/src/connection/onboarding.ts
  • packages/client-runtime/src/connection/registry.test.ts
  • packages/client-runtime/src/connection/registry.ts
  • packages/client-runtime/src/connection/resolver.test.ts
  • packages/client-runtime/src/connection/supervisor.test.ts
  • packages/client-runtime/src/platform/capabilities.ts
💤 Files with no reviewable changes (7)
  • packages/client-runtime/src/connection/resolver.test.ts
  • packages/client-runtime/src/connection/onboarding.ts
  • packages/client-runtime/src/authorization/service.ts
  • packages/client-runtime/src/authorization/layer.test.ts
  • packages/client-runtime/src/connection/supervisor.test.ts
  • packages/client-runtime/src/platform/capabilities.ts
  • apps/mobile/src/connection/platform.ts

Included review availability: 0 reviews are currently available. Your included PR review attempts over the past 7 days set your current allowance at 5 reviews per hour.


📝 Walkthrough

Walkthrough

Client pairing and DPoP exchanges no longer request a fixed scope set. Pairing tests cover grants with different scopes. The connection registry also updates observers when a bearer environment is re-paired with replacement credentials.

Changes

Pairing and connection updates

Layer / File(s) Summary
Remove fixed scopes from client pairing
packages/client-runtime/src/platform/capabilities.ts, apps/mobile/src/connection/platform.ts, apps/web/src/connection/platform.ts, packages/client-runtime/src/connection/onboarding.ts, packages/client-runtime/src/authorization/service.ts, packages/client-runtime/src/connection/onboarding.test.ts, packages/client-runtime/src/authorization/layer.test.ts, packages/client-runtime/src/connection/resolver.test.ts, packages/client-runtime/src/connection/supervisor.test.ts, FORK.md
ClientPresentation no longer includes scopes. Pairing and DPoP exchanges omit the configured scope set. Pairing tests check that read-only and administrative grants appear in fetched session state.
Update observers after credential replacement
packages/client-runtime/src/connection/registry.ts, packages/client-runtime/src/connection/registry.test.ts
followStream filters catalog updates by entry reference. A test checks that a prepared connection observer receives the original and replacement bearer credentials after re-pairing.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Medium

Suggested reviewers: juliusmarminge

Merge Risk: ⚪ Minimal · up to dca62

Remote pairing preserves the link’s granted permissions, and observers follow replacement credentials. No identified issue blocks merging after normal checks.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning PR #197 implements the client subset of #9785. ClientPresentation no longer carries scopes. Remote pairing and DPoP exchanges omit fixed scope requests. Tests cover omitted scope requests and preser… Implement or include the remaining #9785 coding requirements: validate requested scopes before consuming bootstrap or pairing credentials, add deduplicated repeatable --scope support to the specified CLI commands, and implement and test b…
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 4 files. (1 skipped: 1 … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Out of Scope Changes check ✅ Passed The changed client-runtime, web, mobile, and test files support the client-side scope-preservation objective in #9785. The registry change supports the linked re-pairing observer requirement. The `FOR…
Title check ✅ Passed The title clearly and concisely describes the main change: remote pairing preserves the scopes granted by the pairing link.
Description check ✅ Passed The description is detailed and covers the problem, implementation, upstream impact, affected surfaces, testing, and scope limits. It omits the template's explicit Checklist section and uses alternati…
Full details: Linked Issues check

Explanation

PR #197 implements the client subset of #9785. ClientPresentation no longer carries scopes. Remote pairing and DPoP exchanges omit fixed scope requests. Tests cover omitted scope requests and preservation of read-only and administrative grants. EnvironmentRegistry.followStream compares catalog entries by reference and the registry test covers observer rebinding for an unchanged environment. However, directly linked issue #9785 also requires server-side scope validation before credential consumption, repeatable CLI --scope options, and browser-session replacement with reload and rollback behavior. The PR summary explicitly excludes those changes, so the full linked issue is not satisfied.

Resolution

Implement or include the remaining #9785 coding requirements: validate requested scopes before consuming bootstrap or pairing credentials, add deduplicated repeatable --scope support to the specified CLI commands, and implement and test browser-session replacement, reload, and rollback behavior.

Full details: Docstring Coverage

Explanation

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

✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

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

@bryantderosier bryantderosier left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The client changes look right. I'd fix one FORK.md slip before merge.

Should fix

  • FORK.md: this hunk drops the apps/mobile/src/state/thread-order.test.ts sentence from the "CI test fixtures reflect runtime contracts" entry. That test delta still exists on j5/main, so the inventory would no longer record a live upstream edit. (inline)

I checked the rest: the six client-runtime/web/mobile hunks match upstream pingdotgg#9785. The server still limits requested scopes to what the link grants, default links stay standard, and desktop-secondary still pins the standard scopes. Admin-link sessions keeping the admin scope is the direction upstream is taking.

Comment thread FORK.md Outdated
- **Committed Stop wins.** `RestartContinuation.ts` refuses continuation at admission and execution; `ProjectionStore.ts` includes completed `run_interrupt_request` items for active recovery runs. `Orchestrator.ts` checks the same durable Stop while holding the thread dispatch lock: a new steer cannot enter a stopped run, and a previously accepted non-delegated steer cannot become an automatic follow-up after Stop. This also applies to human steers; an explicit new message can start new work. The mailbox keeps its own disposed-cohort policy. Tests cover Stop before steer admission, after admission but before a normal provider completion, and restart recovery before provider acknowledgement. Upstream interrupted/failed terminal handling is otherwise adopted. Watch signal: upstream resolves `TODO(interrupt-hardening)` and honors committed Stop in restart and completed-steer follow-up. Remove these guards once equivalent behavior is proven.
- **MCP idle HTTP connection race**. Files: `apps/server/src/server.ts`, `apps/server/src/mcpHttpConnections.ts`, and its focused test. The Node server disables keep-alive only for `/mcp` responses, avoiding reuse of a connection racing Node's idle timeout. Responses and streams finish normally; MCP session identity is independent of the HTTP connection. Tool POSTs are never automatically retried because a failed response can follow a committed side effect. Nine focused transport tests and live real-provider acceptance cover this seam. Watch signal: the upstream Node/provider transport prevents this idle-reuse failure while preserving streaming, session identity and exactly-once tool effects. Exit: remove the delegate and helper when equivalent transport behavior is verified.

- **CI test fixtures reflect runtime contracts**. `apps/server/src/entrypoint.test.ts` and `provider/AntigravityInstallation.test.ts` expect canonical filesystem paths, including macOS `/private/var`; `textGeneration/CodexTextGeneration.test.ts` preserves `PATH` when its fake CLI receives an explicit environment. `orchestration-v2/FoundationPersistence.test.ts` supplies the turn-item list inspected by J5's restart admission guard; this is a fixture consequence of that guard, not an independent upstream defect. `apps/mobile/src/features/review/shikiReviewHighlighter.test.ts` fixes the clock during its cold-init comparison so Shiki's 500 ms tokenization budget cannot make runner load change token boundaries; the assertion and runtime budget remain intact. `apps/mobile/src/state/thread-order.test.ts` uses the existing complete thread-shell fixture instead of casting a partial object that lacks the lineage read by section membership. Watch signal: upstream supplies these portable fixtures and deterministic timing. Exit: remove each matching test delta when that correction lands. `orchestration-v2/testkit/OrchestratorScenario.ts` awaits the replay gate's arrival notification instead of exhausting a fixed event-loop counter; the gate testkit and scenario tests cover early/late arrival, frame hold/release, missing labels and the former polling path. The Claude composed-prompt assertion remains the separate case 8 contract.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This looks like a rebase slip. The replacement line drops the sentence about apps/mobile/src/state/thread-order.test.ts using the complete thread-shell fixture, but that delta is still on j5/main (f229e78), so the inventory would stop recording a live upstream edit. I'd restore that sentence and keep the new "Remote pairing keeps the scopes" bullet as the only addition.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Done in 66fffa00fd: the thread-order sentence is restored; the new bullet is the only addition.

@Jacksondr5
Jacksondr5 force-pushed the j5/peering-6-pairing-scopes branch 2 times, most recently from 66fffa0 to dca6209 Compare September 24, 2026 19:39
@Jacksondr5
Jacksondr5 force-pushed the j5/peering-6-pairing-scopes branch from dca6209 to eab1c4e Compare September 30, 2026 03:31
@Jacksondr5
Jacksondr5 force-pushed the j5/peering-6-pairing-scopes branch from eab1c4e to 0c204ba Compare September 30, 2026 04:18
@vercel

vercel Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
j5-code Ready Ready Preview Sep 30, 2026 2:37pm UTC

Request Review

@github-actions github-actions Bot added size:XXL 1,000+ effective changed lines (test files excluded in mixed PRs). and removed size:S 10-29 effective changed lines (test files excluded in mixed PRs). labels Sep 30, 2026
@Jacksondr5
Jacksondr5 force-pushed the j5/peering-6-pairing-scopes branch from 97321aa to 0ca0422 Compare September 30, 2026 04:54
@github-actions github-actions Bot added size:S 10-29 effective changed lines (test files excluded in mixed PRs). and removed size:XXL 1,000+ effective changed lines (test files excluded in mixed PRs). labels Sep 30, 2026
Every client pinned the standard scope set when pairing a remote
environment, so a startup URL's administrative grant was silently narrowed
and the remote session could never hold access:write. The peer-credential
route requires that scope, so the "Peer with…" dialog could not introduce a
remote environment from the web client.

Take the client hunks of upstream PR pingdotgg#9785 verbatim:
ClientPresentation loses its scopes field and the pairing, bearer-refresh and
DPoP exchanges send no scope, so the server issues exactly what the link
carries. Desktop secondary backends keep requesting the standard set, as
upstream does. FORK.md records the patch under the temporary in-place
patches, with the exit being to take upstream's files once pingdotgg#9785 merges.

Built by Claude Fable 5.1 in Claude Code.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@Jacksondr5
Jacksondr5 force-pushed the j5/peering-6-pairing-scopes branch from 0ca0422 to 0495271 Compare September 30, 2026 14:36
@Jacksondr5
Jacksondr5 merged commit 531e8f0 into j5/main Sep 30, 2026
37 checks passed
@Jacksondr5
Jacksondr5 deleted the j5/peering-6-pairing-scopes branch September 30, 2026 15:01

This branch was successfully deployed

1 active deployment
Preview — 04952718 Deployed Sep 30, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S 10-29 effective changed lines (test files excluded in mixed PRs). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants