Skip to content

fix(server): linked launches inherit the caller's mode, capped by the link - #5

Merged
Synthminds merged 5 commits into
fix/fork-test-suitefrom
fix/linked-launch-inherits-mode
Nov 2, 2026
Merged

Synthminds merged 5 commits into
fix/fork-test-suitefrom
fix/linked-launch-inherits-mode

Conversation

@Synthminds

@Synthminds Synthminds commented Nov 2, 2026 •

Copy link
Copy Markdown
Owner

Problem

t3_thread_launch can be called over an environment link. When the call leaves out runtimeMode, the new thread started at the receiving project's default mode. In this fork that default is approval-required (upstream: full-access). So a full-access agent's linked task silently dropped to approval-required and waited for approvals on the other machine.

That contradicts the tool description ("Omit … modes to inherit yours") and docs/orchestration-v2/orchestrator-mcp-server.md. It also contradicts two upstream tests that fail on this fork's CI (run 41388349181, Test Server 2 and 3):

  • PeerForwarding.test.ts › "launches in a linked environment with the caller's modes, retry-safe";
  • RemoteDelegation.test.ts › "a task named only by environment inherits the parent's model, within the link".

#4 had changed those tests to expect the project default (7f47d29). That runs against the owner's intent, so this PR reverts it.

Change

The rule now:

  • An agent in a thread launches or delegates to a linked environment without naming a mode. The remote thread inherits that agent's own modes, capped by the link's access.
  • An MCP client outside a thread has an approval ceiling, not a running mode. Its launch over a link starts at the far project's default, within its ceiling and the link. That is the same rule as a local launch.

How:

  • Calling side: PeerForwarding marks the forwarded limits inherit: true only for a calling thread. PeerMcpClient then sends T3-Mode-Inherit: 1 beside T3-Mode-Limit.
  • Receiving side: McpHttpServer.withModeLimit sets client.inheritsModes only when three things hold: the caller is a peer link, the header is exactly 1, and a valid T3-Mode-Limit is present. It reads the header per request.
  • Launch: t3_thread_launch skips the project default only for that caller.
  • Mixed versions: they keep today's project default. An old receiver ignores the header, and an old sender never sends it. Nothing is refused or widened.
  • Docs: docs/internals/remote.md states the rule. docs/user/remote-access.md tells users which mode a linked task starts in.

Commits:

Commit What it does
eee5e3b Reverts 7f47d29; first version keyed on linkOrigin
b73f8fe Replaces that with the header, after review found outside clients would also inherit their ceiling
2799d3c Honours the header only from a peer link. Adds a test where the caller's own mode sits below the link, and pins the receiver rules
c8a2539 Wording: "that project's default there", US spelling, and the withModeLimit comment

Scope and approval

Wes chose "Inherit, capped by link" on November 2, 2026. The question was what mode a linked or delegated task starts in when none is named. He also gave autonomy to drive the fork's CI green.

The client-outside-a-thread case follows from that rule: such a client has no mode to inherit, so it keeps the local rule. It is the narrower default.

This PR is stacked on #4 and changes only this one rule.

Verification

Fail-before:

  • 4150b20 (tests reverted): both upstream tests fail with approval-required.
  • eee5e3b: the outside-client case got full-access where approval-required was expected.
  • b73f8fe receiver: a non-link client sending the header got +inherit.

Pass-after (2799d3c):

  • These 5 files, 66/66: PeerForwarding.test.ts, RemoteDelegation.test.ts, project/handlers.test.ts, linkOrigin.test.ts and McpHttpServer.test.ts.
  • tsc --noEmit -p apps/server, vp fmt --check and vp lint are clean.

Mutation proofs (independent reviewer, disposable worktree):

  • M1: the receiver honours non-links. McpHttpServer T3-Mode-Limit test fails.
  • M2: an inheriting launch takes the link's access. PeerForwarding fails on the auto row.
  • M3: the forwarder sends inherit for clients. PeerForwarding fails on the client row.

CI at 2799d3c:

Review:

  • Original reviewer: "revise" at eee5e3b, then approve at b73f8fe.
  • Two fresh lenses at b73f8fe (trust boundary and compatibility; rule and tests): one approve, one revise. 2799d3c addresses them.
  • Mutation reviewer at 2799d3c: approve. c8a2539 applies its three wording notes.

Not checked: a live two-environment link session; T3 Connect or relay handling of the new header.

Merge danger

  • Behaviour change for linked agent work. An agent's linked launch that omits runtimeMode now runs at the agent's own mode, up to the link's access, not at the receiving environment's default. A full-access agent on a full-access link starts full-access work there by default.
  • Wire change: a new header, T3-Mode-Inherit. It is backward-compatible both ways, and honoured only from peer links. It only chooses a default within limits the caller already has.
  • Known limit (pre-existing, not widened). A remote delegate_task does not step an inherited mode down to the far provider's supported modes, unlike a local one. At thread start, an unsupported mode falls back to approval-required (RuntimePolicy.providerRuntimeMode), so the result is narrower, never broader.
  • Merge order. Merge into fix/fork-test-suite (fix: make the SynthOS fork's CI run and pass #4) after fix: make the SynthOS fork's CI run and pass #4's remaining fixes land.

🤖 Generated with Claude Code

https://claude.ai/code/session_01A1D6J2UMPo7YBWQFvbg1br

… link

A thread launched over an environment link without a runtimeMode started
at the receiving project's default for new threads. The fork's default is
approval-required (upstream: full-access), so a full-access caller's task
silently dropped to approval-required on the other side. The tool
description and orchestrator-mcp-server docs promise inheritance, and two
upstream tests (PeerForwarding, RemoteDelegation) pin it.

t3_thread_launch now starts a linked session's launch at the forwarded
ceiling: the remote caller's modes capped by the link's access. Local MCP
clients outside a thread still start at the project default within their
ceiling. This reverts 7f47d29, which had changed the two tests to expect
the project default instead.

Fails before: PeerForwarding "launches in a linked environment with the
caller's modes" received approval-required, expected full-access;
RemoteDelegation "a task named only by environment inherits the parent's
model, within the link" received approval-required, expected
auto-accept-edits. After: those plus project handlers.test and
linkOrigin.test pass, 42/42.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01A1D6J2UMPo7YBWQFvbg1br
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:S labels Nov 2, 2026
@github-actions

github-actions Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ℹ️ No successful main baseline artifact is available yet. This run establishes the initial measurement.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire — 5.0 KiB — 6.8 KiB ✅
Codex Thread snapshot wire — 3.8 KiB — 4.9 KiB ✅
Codex Live turn WebSocket wire — 1.2 KiB — 2.0 KiB ✅
Codex Live turn WebSocket decoded — 21.6 KiB — 24.4 KiB ✅
Codex Live turn messages — 2 — 8 ✅
Claude Total thread wire — 5.0 KiB — 6.8 KiB ✅
Claude Thread snapshot wire — 3.8 KiB — 4.9 KiB ✅
Claude Live turn WebSocket wire — 1.3 KiB — 2.0 KiB ✅
Claude Live turn WebSocket decoded — 21.9 KiB — 24.4 KiB ✅
Claude Live turn messages — 2 — 8 ✅

Baseline: unavailable · PR result: 90458ed · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

Copy link
Copy Markdown
Owner Author

CI on eee5e3b (run 41411301715): Check is red because of four jobs that also fail on this PR's base. #4's own run at 4150b20 (41395174419) fails the same four. None of the failing files is in this diff; it changes only project/handlers.ts, two peer/ tests and one peer/ comment.

Job Failing file Cause (base)
Test scripts/synthos-companion/*.test.mjs, scripts/synthos-service/run.test.mjs Vitest collects node:test files: "No test suite found"
Test Server 2 persistence/reconcileV2PreviewMigration.test.ts (2 cases) expects 19 migrations; SynthOS ships 27
Test Server 3 auth/EnvironmentAuth.test.ts › "accepts cookie sessions on socket upgrades only from the app's own origins" origin check rejects the test's origin
Test Server 7 cli/pair.test.ts (3 cases) CLI argv is gch …, test expects t3 …

What this PR targets now passes in CI. The two tests it changes both run in shards above, before those shards stop at their unrelated failures:

  • PeerForwarding.test.ts passes 6/6 in Test Server 2.
  • RemoteDelegation.test.ts passes 26/26 in Test Server 3.

At 4150b20, Test Server 2 and 3 failed on exactly these two tests. Lint, Build and Typecheck, and Test Server 1, 4, 5, 6 and 8 are green.

Fix. All four are being fixed on #4's branch, fix/fork-test-suite. They are not pushed yet. Once they land there, I'll merge the base into this branch so CI re-runs. I'm not re-running now: each failure is deterministic and reproduces on the base run.


Generated by Claude Code

eee5e3b started any linked launch that omitted runtimeMode at the
forwarded T3-Mode-Limit. For a calling thread that limit is its own
running mode, which is what should carry over. For an MCP client outside
a thread it is only an approval ceiling, so a full-access client launching
through a full-access link got a full-access thread there. The same
client launching locally gets the project default.

The forwarder now marks the limit as inheritable with T3-Mode-Inherit: 1,
and only when the caller is a thread. The receiving /mcp records that
beside narrowedTo. t3_thread_launch skips the project default only for
a caller that sent it. A client outside a thread starts at the far
side's project default, within its ceiling and the link, as it does here.

Mixed versions keep today's default: an old receiver ignores the header,
and an old sender never sends it. docs/internals/remote.md states the
rule.

Fails before: the new client case in PeerForwarding "launches in a
linked environment with the caller's modes" received full-access,
expected approval-required. After: PeerForwarding, RemoteDelegation,
project handlers, linkOrigin and McpHttpServer tests 66/66; tsc, fmt and
lint clean on the changed files.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01A1D6J2UMPo7YBWQFvbg1br
@github-actions github-actions Bot added size:M and removed size:S labels Nov 2, 2026
The independent re-reviews of b73f8fe found three gaps.
- The receiving /mcp accepted T3-Mode-Inherit from any OAuth client, so a
  non-link client could start its own omitted-mode launch at its ceiling
  rather than the project default. That widens no access, but it breaks
  the rule that a client outside a thread keeps the local default.
- No test pinned that an inheriting launch takes the caller's own mode
  rather than the link's access.
- No test pinned the receiver's header rules.

Changes:
- withModeLimit sets inheritsModes only for a linked client.
- PeerForwarding's launch test adds a thread whose mode (auto) sits
  between the box default and the full-access link. It must start at
  auto.
- McpHttpServer's T3-Mode-Limit test gains a peer-link caller and pins
  four rules: inherit is honoured beside a valid limit; ignored without
  one; ignored unless the value is "1"; ignored from a non-link client.
- The MODE_LIMIT_HEADER doc now says a broad limit gains no access,
  rather than nothing.
- docs/internals/remote.md states the receiver rule and mixed-version
  behaviour.
- docs/user/remote-access.md tells users what mode a linked task starts
  in.

Fails before: with the linked check removed (b73f8fe's receiver), the
McpHttpServer T3-Mode-Limit test fails on the non-link row. After:
PeerForwarding 6/6 and the McpHttpServer T3-Mode-Limit test pass. tsc,
fmt and lint are clean on the changed files.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01A1D6J2UMPo7YBWQFvbg1br
The re-review of 2799d3c approved it and noted three wording problems in
its text:
- The user doc said a linked launch that inherits nothing starts at "that
  machine's default". The code applies the project's effective default
  (resolveProjectSettings), so it now says "that project's default there".
- "behaviour" and "honour(ed)" are now US spelling, to match the rest of
  the docs.
- withModeLimit's comment now says the function also applies a peer
  link's T3-Mode-Inherit.

Comments and docs only; fmt and lint clean.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01A1D6J2UMPo7YBWQFvbg1br
@Synthminds
Synthminds marked this pull request as ready for review November 2, 2026 11:35
@Synthminds
Synthminds merged commit 05b04f7 into fix/fork-test-suite Nov 2, 2026
29 checks passed
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-02T11:37:10.209209Z 90458ed Draft marked ready
🔒 Security Review ✅ Completed 2026-11-02T11:37:43.586469Z 90458ed Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

Synthminds added a commit that referenced this pull request Nov 2, 2026
This PR includes:
- #3: GitHub-hosted runners;
- the round-1 and round-2 test-suite fixes, 12 groups;
- #5: linked launches inherit the caller's mode, capped by the link.

CI is fully green at 05b04f7. Round 2 was verified with PASS, with mutation checks on every fix group. The Codex code and security reviews found nothing. No assertion was removed, skipped or loosened.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01A1D6J2UMPo7YBWQFvbg1br
Synthminds pushed a commit that referenced this pull request Nov 2, 2026
Brings in the fork's CI runner and test-suite fixes (#3, #4, #5) so this
PR's CI runs on GitHub-hosted runners against a green base.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01A1D6J2UMPo7YBWQFvbg1br
Synthminds pushed a commit that referenced this pull request Nov 2, 2026
main now carries #3, #4 and #5, which add GitHub-hosted CI and the
fork's test-suite fixes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01A1D6J2UMPo7YBWQFvbg1br
Synthminds pushed a commit that referenced this pull request Nov 14, 2026
Brings in the fork's CI runner and test-suite fixes (#3, #4, #5) so this
PR's CI runs on GitHub-hosted runners against a green base.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01A1D6J2UMPo7YBWQFvbg1br
Synthminds pushed a commit that referenced this pull request Nov 14, 2026
main now carries #3, #4 and #5, which add GitHub-hosted CI and the
fork's test-suite fixes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01A1D6J2UMPo7YBWQFvbg1br
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M 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