Skip to content

feat(prd-023e): app-scoped isolated lanes per desktop app#9

Open
thenotoriousllama wants to merge 6 commits into
mainfrom
feat/prd-023e-isolated-lanes
Open

feat(prd-023e): app-scoped isolated lanes per desktop app#9
thenotoriousllama wants to merge 6 commits into
mainfrom
feat/prd-023e-isolated-lanes

Conversation

@thenotoriousllama

Copy link
Copy Markdown
Contributor

Summary

Implements PRD-023e — app-scoped isolated lanes per desktop app: the safety contract that makes "a real Claude Desktop turn route through rflectr without putting OpenAI desktop traffic at risk" an enforceable, tested guarantee rather than an aspiration. This is the layer the original mission identified as the missing piece between "the product path works" and "safe to ship."

Built via the Beekeeper dispatch: typescript-node-worker-bee (implementer, 4 waves) → security-worker-bee (close-out) → quality-worker-bee (QA).

What landed (6 commits)

Wave 1 — lane primitives (feat(desktop-interception): PRD-023e lane primitives)

  • app-targets.ts: DesktopAppLane model + DESKTOP_APP_LANES table (claude-native = app-scoped-proxy preferred; claude-legacy-gateway + codex-desktop = config-only). laneOwnsHost/lanesForHost enforce egress exclusivityapi.openai.com can never satisfy the Claude lane.
  • os-proxy.ts: real WindowsOsProxyAdapter + MacosOsProxyAdapter (snapshot/apply/restore via reg/networksetup), createOsProxyAdapter factory. Hard-rollback seam.
  • rival-apps.ts: detectRunningRivalApps (ChatGPT/Codex), assertNoRivalAppsRunning(override?) — the global-proxy block gate.
  • app-launch.ts: openClaudeAppWithProxy via Electron --proxy-server (per-app, never a system proxy switch); refuses shell:AppsFolder packaging rather than falling back.

Wave 2 — router (feat(server): PRD-023e dashboard endpoints + rival-app block)

  • 4 new endpoints: claude-desktop/revert, codex-desktop/revert, codex-desktop/stop (per-lane, never touches claude-native state), server/kill (closes only owned runtime handles).
  • Rival-app global-proxy block in dashboardClaudeNativeStart: refuses with 409 rival_apps_running when ChatGPT/Codex desktop is running, unless the caller passes rivalOverride: true (explicit override path).
  • Real createOsProxyAdapter() swap in native uninstall (was noopOsProxyAdapter).

Wave 3 — dashboard (feat(dashboard): PRD-023e per-lane state + DTOs + controls UI)

  • DashboardRuntime.lanes (typed per-lane state), LaneRuntimeState, DesktopLaneStatusDto, buildDesktopLaneStatuses.
  • Desktop Apps UI: Revert legacy / Revert Codex / Stop Codex proxy / Kill server buttons. Lanes render as distinct Claude-native / Claude-legacy / Codex sections.

Wave 4 — integration tests (test(desktop): lane isolation + rival-app block integration)

  • tests/desktop-lane-isolation.test.ts (16 tests) tying the lane primitives, rival-app detector, OS-proxy factory, and per-lane runtime status together.

Wave 5a — security (security(os-proxy): harden proxy host/port)

  • Critical remediation: the macOS networksetup interpolation was entirely unquoted; a crafted host read from OS output could inject shell args. Added assertSafeProxyHost/assertSafeProxyPort guards before every interpolation; fails closed.

Wave 5b — QA (docs(qa): PRD-023e quality close-out)

  • Verdict: VERIFIED-WITH-WARNINGS. Report at library/.../prd-023-desktop-apps-native-controls/qa/2026-06-30-prd-023e-quality-closeout.md.

Verification

Gate Result
npm run typecheck exit 0
npm test 94 files / 837 passed / 2 skipped / 0 failed (was 89/763 on main — +74 tests)
Focused PRD-023e suite 63 passed
Security close-out Critical remediated, re-verified green

AC tally (full matrix in the QA report)

11 MET — lanes distinct, per-app-only proxy, app-scoped-or-nothing refusal, egress exclusivity, rival-app block (fires before mutation), per-lane stop isolation, codex-never-native, gates green.

1 PARTIAL — hard-rollback snapshot seam exists and is hardened, but the router doesn't yet populate runtime.lanes (lanes work via fallback to legacy fields).

3 UNMET (documentation/investigation deliverables, none block the safety contract):

  • G1 — Windows launch-options qa/ investigation note (AC-023e-4/14): the mechanism is implemented; the write-up is the gap.
  • G2 — Real observed Anthropic turn (AC-023e-9/10): the safety properties are tested (loopback-only, 403-deny of openai, per-app proxy), but no committed test observes a live turn. This was the explicit "next" item in the original plan.
  • G3 — Per-lane state population (follow-up to the PARTIAL above).

Recommended follow-ups (not in this PR)

  1. G2 — author an opt-in live-observation harness (skipped by default, runs under RFLECTR_LIVE_DESKTOP=1) to regression-protect the real-turn observation. This fully closes the original mission.
  2. G1 — Windows launch-options investigation note.
  3. G3 — populate runtime.lanes in the router start/stop handlers.

Foundation modules for app-scoped isolated lanes; no router/dashboard
wiring yet (wave 2-3).

- app-targets.ts: DesktopAppLane model + DESKTOP_APP_LANES table
  (claude-native app-scoped-proxy, claude-legacy-gateway config-only,
  codex-desktop config-only) with getLaneById/laneOwnsHost/laneForApp/
  lanesForHost. Egress exclusivity: api.openai.com never in the claude
  lane, api.anthropic.com never in the codex lane.
- os-proxy.ts: real WindowsOsProxyAdapter + MacosOsProxyAdapter
  (snapshot/apply/restore via reg query/networksetup), createOsProxyAdapter
  factory; noopOsProxyAdapter unchanged. Hard-rollback snapshot seam.
- rival-apps.ts: detectRunningRivalApps (ChatGPT/Codex), formatRivalAppWarning,
  assertNoRivalAppsRunning(override?) — the global-proxy block gate.
- app-launch.ts: openClaudeAppWithProxy / launchOrRestartClaudeAppWithProxy
  via Electron --proxy-server (per-app, never system proxy); _internals
  testability seam extended.

Tests: 47 new (app-targets 13, os-proxy 18, rival-apps 9, app-launch 7).
typecheck clean; full desktop suite 12 files / 89 tests pass.
- 4 new dashboard endpoints: claude-desktop/revert (legacy gateway
  cleanup via app-session), codex-desktop/revert, codex-desktop/stop
  (per-lane, never touches claude-native state), server/kill (closes
  only owned runtime handles, never user/OS processes).
- Rival-app global-proxy block in dashboardClaudeNativeStart: refuses
  native start with 409 rival_apps_running when ChatGPT/Codex desktop is
  running, unless the caller passes rivalOverride:true (the explicit
  override path).
- Swap noopOsProxyAdapter -> createOsProxyAdapter() in native uninstall
  so real snapshot/restore runs on Windows/macOS (hard-rollback seam).
- DesktopActionResult/Category typed DTO for safe action responses.

Tests: server-router 30 -> 35 (codex stop noop+isolation, server kill,
rival-app block 409 + override, revert endpoints). typecheck clean;
full suite 93 files / 815 tests pass.
- DashboardRuntime.lanes: Partial<Record<LaneId, LaneRuntimeState>>
  (per-lane running/startedAt/proxySnapshot/lastError). Existing flat
  fields kept; router depends on them.
- LaneRuntimeState, DesktopLaneStatusDto, DesktopAppsStatusDto typed DTOs.
- buildDesktopLaneStatuses(runtime): iterates DESKTOP_APP_LANES, derives
  running from lane state or legacy fields (claudeNativeTransport /
  codexProxy). Wired into the settings payload as lanes.
- Desktop Apps UI: Revert legacy (claude-desktop/revert), Revert Codex
  + Stop Codex proxy (codex-desktop/{revert,stop}), Kill server
  (server/kill, warns browser may disconnect). Lanes render as distinct
  Claude-native / Claude-legacy / Codex sections (AC-023e-1).

Tests: server-dashboard 11 (lane status builder, claude-native running
from transport, codex running from proxy, DTO shape, rendered buttons).
typecheck clean; full suite 94 files / 821 tests pass.
…(wave 4)

New tests/desktop-lane-isolation.test.ts (16 tests) tying together the
lane primitives, rival-app detector, OS-proxy factory, and per-lane
runtime status:

- egress exclusivity (AC-023e-5/27): claude lane never owns openai,
  codex lane never owns anthropic; lanesForHost resolution.
- rival-app block (AC-023e-6/24): detectRunningRivalApps + assert-
  NoRivalAppsRunning (throws w/ 'blocked', override path no-throw,
  execSync-failure safe-default).
- per-lane isolation (AC-023e-11..13/28): stopping one lane's runtime
  does not affect the other; both can run independently; codex never
  reports native-interception.
- OS proxy factory (AC-023e-2..3): noop contract + platform-correct
  instance selection; no real OS mutation.

ESM-safe: vi.mock w/ importOriginal (transitive spawn bind requires
real spawn preserved); platform override + restore in afterEach.

Full suite: 94 files / 837 tests pass.
…ation

The macOS networksetup interpolation was entirely unquoted, and the
Windows reg add + read-path host (\S+ regex) accepted arbitrary values.
A crafted or corrupted host read from OS output or an external snapshot
could inject shell arguments.

Add assertSafeProxyHost (strict [A-Za-z0-9._:-] charset) and
assertSafeProxyPort (integer 0..65535) guards before every
interpolation point in WindowsOsProxyAdapter.apply/restore and
MacosOsProxyAdapter.apply/restore. The surrounding try/catch swallows
the throw, so an unsafe value fails closed (command not executed) rather
than injecting. PRD-023e security close-out (Critical finding).

Verified: os-proxy 18 tests + full suite (94 files / 837 tests) green.
quality-worker-bee close-out (report authored inline after agent timeout;
evidence captured from green gates + file:line inspection).

Verdict: VERIFIED-WITH-WARNINGS. The lane/isolation contract is
implemented, tested (94 files / 837 tests), and security-remediated.

AC tally: 11 MET, 1 PARTIAL, 3 UNMET.
- MET: lanes distinct (e-1), per-app-only proxy (e-2/3), egress
  exclusivity (e-5), rival-app block before mutation (e-6), per-lane
  stop isolation (e-11..13), codex never native (e-12), gates (e-15).
- PARTIAL: hard-rollback snapshot seam exists (real OsProxyAdapter
  wired, hardened) but router doesn't yet populate runtime.lanes (G3).
- UNMET: Windows launch-options qa/ note (e-4/14, G1) and committed
  real-Anthropic-turn observation (e-9/10, G2) — both documentation /
  investigation deliverables the original plan scoped as 'later', not
  safety-contract code.

Three honest gaps documented with recommendations; none block the
safety guarantee ('a real Claude turn routes without putting OpenAI
desktop traffic at risk').
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

An error occurred during the review process. Please try again later.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/prd-023e-isolated-lanes

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

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":500,"request":{"method":"PATCH","url":"https://api.github.com/repos/legioncodeinc/rflectr/issues/comments/4840722143","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- review_stack_entry_start -->\n\n[![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/legioncodeinc/rflectr/pull/9?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)\n\n<!-- review_stack_entry_end -->\n<!-- This is an auto-generated comment: review in progress by coderabbit.ai -->\n\n> [!NOTE]\n> Currently processing new changes in this PR. This may take a few minutes, please wait...\n> \n> <details>\n> <summary>⚙️ Run configuration</summary>\n> \n> **Configuration used**: Organization UI\n> \n> **Review profile**: CHILL\n> \n> **Plan**: Pro Plus\n> \n> **Run ID**: `174dc100-40aa-4b61-b26d-73c967f6f31d`\n> \n> </details>\n> \n> <details>\n> <summary>📥 Commits</summary>\n> \n> Reviewing files that changed from the base of the PR and between b57deeed563a5946965207e9ba6de792b28d66e8 and 1b3d764df0b17dbb6dadaa431a49469ac3afa2e6.\n> \n> </details>\n> \n> <details>\n> <summary>📒 Files selected for processing (14)</summary>\n> \n> * `library/requirements/backlog/prd-023-desktop-apps-native-controls/qa/2026-06-30-prd-023e-quality-closeout.md`\n> * `src/claude-desktop/app-launch.ts`\n> * `src/desktop-interception/app-targets.ts`\n> * `src/desktop-interception/os-proxy.ts`\n> * `src/desktop-interception/rival-apps.ts`\n> * `src/server/dashboard.ts`\n> * `src/server/router.ts`\n> * `tests/claude-app-launch-proxy.test.ts`\n> * `tests/desktop-interception-app-targets.test.ts`\n> * `tests/desktop-interception-os-proxy.test.ts`\n> * `tests/desktop-interception-rival-apps.test.ts`\n> * `tests/desktop-lane-isolation.test.ts`\n> * `tests/server-dashboard.test.ts`\n> * `tests/server-router.test.ts`\n> \n> </details>\n> \n> ```ascii\n>  ____________________________________________\n> < Let's git serious about your code quality. >\n>  --------------------------------------------\n>   \\\n>    \\   \\\n>         \\ /\\\n>         ( )\n>       .( o ).\n> ```\n\n<!-- end of auto-generated comment: review in progress by coderabbit.ai -->\n\n<!-- finishing_touch_checkbox_start -->\n\n<details>\n<summary>✨ Finishing Touches</summary>\n\n<details>\n<summary>📝 Generate docstrings</summary>\n\n- [ ] <!-- {\"checkboxId\": \"7962f53c-55bc-4827-bfbf-6a18da830691\"} --> Create stacked PR\n- [ ] <!-- {\"checkboxId\": \"3e1879ae-f29b-4d0d-8e06-d12b7ba33d98\"} --> Commit on current branch\n\n</details>\n<details>\n<summary>🧪 Generate unit tests (beta)</summary>\n\n- [ ] <!-- {\"checkboxId\": \"f47ac10b-58cc-4372-a567-0e02b2c3d479\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Create PR with unit tests\n- [ ] <!-- {\"checkboxId\": \"6ba7b810-9dad-11d1-80b4-00c04fd430c8\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Commit unit tests in branch `feat/prd-023e-isolated-lanes`\n\n</details>\n\n</details>\n\n<!-- finishing_touch_checkbox_end -->\n<!-- tips_start -->\n\n---\n\n\n\n\n<sub>Comment `@coderabbitai help` to get the list of available commands.</sub>\n\n<!-- tips_end -->"},"request":{"retryCount":3,"signal":{},"retries":3,"retryAfter":16}}}

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