feat(runtime-host): add client-selectable host profiles - #2834

Merged
M4n5ter merged 7 commits into
mainfrom
feat/runtime-host-profiles
Aug 12, 2026
Merged

feat(runtime-host): add client-selectable host profiles#2834
M4n5ter merged 7 commits into
mainfrom
feat/runtime-host-profiles

Conversation

@M4n5ter

@M4n5terM4n5ter commented Aug 11, 2026

Copy link
Copy Markdown
Member
English

Summary

  • add a shared Runtime Host profile catalog for Desktop, CLI, and TUI
  • let Desktop switch between Local and remote Runtime Hosts without restarting
  • route remote work through Host-owned Project identities instead of Client-local paths
  • bind each access credential to an exact profile target and keep it outside profile documents
  • reset Host-owned Client projections at the target-generation boundary

Why

Runtime Host is now the execution authority, but Clients could still only select the implicit local Host. This change introduces the smallest shared target model needed for future remote Hosts while preserving a strict boundary: a remote Host path is never treated as a local Client path.

Each Desktop target generation owns its reconnect lifecycle, Session observations, Project policy, and local-path permissions. Switching reports the Host that is actually active even if selection persistence and rollback both fail. CLI and TUI omit Client-side cwd relocation for remote Sessions.

User impact

Desktop users can manage and switch Runtime Host profiles from Workspace settings. CLI and TUI users can select the same profiles with --host and select an existing remote Project with --project. Credentials are accepted through secure storage or environment input, never command-line arguments or profile JSON.

Validation

  • Runtime Host, Storage, CLI, and Desktop typechecks
  • focused Desktop, CLI/TUI, Runtime Host, and Storage regressions: 130 passed
  • complete production build
  • Biome and git diff --check

Refs #2522

简体中文

概要

  • 为 Desktop、CLI 和 TUI 增加共享的 Runtime Host profile catalog
  • Desktop 无需重启即可在 Local 与远程 Runtime Host 之间切换
  • 远程工作通过 Host 持有的 Project identity 路由,不再依赖 Client 本地路径
  • access credential 与精确的 profile target 绑定,并与 profile 文档分离保存
  • 在 target generation 边界清空并重载 Host-owned Client projections

原因

Runtime Host 已成为执行权威,但 Client 仍只能使用隐式的本地 Host。本次变更增加未来远程 Host 所需的最小共享目标模型,同时保留严格边界:远程 Host 路径绝不会被当作 Client 本地路径使用。

Desktop 的每个 target generation 独立持有 reconnect lifecycle、Session observations、Project policy 与本地路径权限。即使 selection 持久化与回滚同时失败,切换结果仍会报告实际活跃的 Host。CLI 与 TUI 不会对远程 Session 执行 Client 侧 cwd relocation。

用户影响

Desktop 用户可以在工作区设置中管理和切换 Runtime Host profiles。CLI 和 TUI 用户可以通过 --host 选择同一套 profiles,并通过 --project 选择已有的远程 Project。Credential 仅通过安全存储或环境变量输入,不会进入命令行参数或 profile JSON。

验证

  • Runtime Host、Storage、CLI 与 Desktop typecheck
  • Desktop、CLI/TUI、Runtime Host 与 Storage 定向回归:130 项通过
  • 完整 production build
  • Biome 与 git diff --check

关联 #2522

@M4n5ter
M4n5ter marked this pull request as ready for review August 12, 2026 00:35
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from 50f0923 to c71fa13CompareAugust 12, 2026 00:45

@Astro-HanAstro-Han 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.

The profile catalog itself looks sound. It keeps connection configuration on the Client, preserves the Runtime Host as the execution authority, and does not add another discovery or spawn path.

I think the remaining problems come from three missing boundaries rather than a collection of unrelated bugs.

  1. A target switch has no identity that follows its requests and results. Reconnecting to Host A and switching from A to B both look like a temporary handler gap. Commands can wait for B's handler, while reads started on A can complete after the switch and overwrite B's renderer state.

  2. Host-owned and Client-owned locations share the same representation. A plain cwd may be treated as a Host execution path, a Client filesystem path, a display value, or an autocomplete root. This breaks remote resume and also lets a remote capability frame supply a Client-local cwd despite advertising hostPathAccess: "none". Bare Project IDs have the same ownership problem when a global default is reused across Host roots.

  3. Desired profile selection, active Host readiness, and persisted credential state are mixed together. A profile or credential read failure currently rewrites the selection to Local, so a temporary storage failure can change execution authority. Profile and credential mutations also have commit points that can leave one side updated without enough metadata to recover the other.

I would keep the catalog, root pinning, credential binding, and per-target lifecycle. The smallest coherent revision is narrower:

  • Give each target generation an epoch. Commands must fail when their epoch is gone. Reconnectable reads may wait or retry only within the same epoch, and renderer reads must check the epoch before committing.
  • Split Client-local cwd from Host-owned execution location. Remote paths must not reach Client realpath, autocomplete, foreign-session scanning, or native capabilities unless the registered offer explicitly permits Host path access.
  • Keep a failed remote selection in an unavailable state. Persist Local only after an explicit user choice.
  • Keep remote Project policy root-scoped. Do not reuse the Client-global default, and hide workspace actions that remote main will reject.
  • Make profile and credential updates recoverable. Do not discard target metadata after a cleanup failure, and finish fallible preparation before committing profile metadata.

The cleaner end state is one Desktop owner publishing {epoch, target, readiness, candidate}. Boot, profile service, IPC, runtime policy, and renderer should consume that snapshot instead of storing separate versions of the active target. Execution locations should also be a discriminated type, so only a client-path can reach Client filesystem APIs.

I left inline comments at the smallest code sites that expose these boundaries.

The current red typecheck and workspace checks appear to reproduce on the base and are not attributed to this diff. The branch is still not green, but those failures are separate from the points above.

Disclosure: Codex performed the code inspection and adversarial analysis using several focused review passes. I (Astro-Han) reviewed and challenged the analysis over multiple rounds, selected the issues included here, and own the final review. I have not independently rerun every reproduction.

Comment threadapps/desktop/src/main/runtime-host-desktop-owner.ts Outdated
Comment threadapps/desktop/src/renderer/app-shell-effects.ts
Comment threadapps/desktop/src/main/runtime-host-native-capabilities.ts Outdated
Comment threadpackages/cli/src/runtime-host-session-driver.ts
Comment threadapps/desktop/src/main/runtime-host-profile-service.ts
Comment threadpackages/runtime-host/src/client/host-profile.ts Outdated

@liugddxliugddx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Follow-up adversarial review with findings anchored to the smallest changed lines. These complement the consolidated review and avoid duplicating existing inline comments.

简体中文

补充对抗性审查:以下问题锚定到最小变更行,作为汇总评论的可逐项 resolve 版本,并避免重复已有行级评论。

Comment threadpackages/cli/src/runtime-host-tui-context.ts
Comment threadapps/desktop/src/main/runtime-host-profile-service.ts Outdated
Comment threadapps/desktop/src/main/runtime-host-profile-service.ts Outdated
Comment threadpackages/cli/src/__tests__/runtime-host-run-command.test.ts Outdated
Comment threadapps/desktop/src/main/__tests__/runtime-host-desktop-owner.test.ts Outdated
Comment threadpackages/runtime-host/src/__tests__/host-profile.test.ts Outdated
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from e6857cb to 51be16bCompareAugust 12, 2026 03:12
@M4n5ter

Copy link
Copy Markdown
MemberAuthor

Addressed both review passes at 51be16b4d and rebased onto the latest main.

The revision now:

  • fences Desktop requests, results, and observations by target generation;
  • separates desired selection from the actually active Host, including the no-active-Host state;
  • keeps remote paths Host-owned across Desktop, CLI, TUI, and Client Capability calls;
  • makes profile targets immutable and credentials exact-target bound with recoverable mutation ordering;
  • removes the low-value remote-resume and object-identity tests called out in review.

Local validation passed: Runtime Host coverage (the only stale-build failure passed after rebuilding its updated dependency), CLI 233/233, Desktop 758/758, all three workspace typechecks, Biome, and git diff --check.

Could you please re-review the current head?

@Astro-HanAstro-Han 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.

The previous generation, path-authority, active-state, and credential-isolation findings are materially addressed in 51be16b4d.

Requests and results are now fenced by target epoch; remote Host paths no longer reach Client filesystem or capability APIs; rollback can represent no active Host; preference-write failure keeps the healthy Host active; and credentials are bound and tested against the exact immutable target. The focused tests now exercise the production boundaries rather than object identity or injected fakes.

I think the two remaining findings share one root cause rather than being isolated guards: the Runtime Host authority model has been updated, but two Client-facing boundaries still encode the former assumption that a valid local state always exists.

At startup, an unreadable selection is represented as Local because the resolution type cannot express “the authority is unknown.” In the project UI, the absence of a Client-visible path is interpreted as an unavailable Project, while Client-local actions remain visible, because the renderer infers capabilities from the old local Project representation instead of consuming capabilities published by the active target.

This creates the same failure in two forms: an unavailable or intentionally absent Client-local value is converted into Local authority instead of remaining an explicit unavailable or Host-owned state. The result is either a silent authority change or UI actions that the active Host cannot support.

From first principles, an unknown selection must not become an execution target, and the renderer should only expose operations granted by the active authority. I would therefore fix these at their natural boundaries rather than adding individual if (remote) checks:

  • Make startup resolution a closed ready | unavailable result. Only ready may contain a target; choosing Local should be a separate explicit transition.
  • Publish a small project-capability projection for the active target, covering directory actions, no-project allowance, local defaults, and path visibility. The renderer should consume that projection rather than infer authority from profile.kind, preferredPath, or failed calls.

I found two concrete consequences of the incomplete boundary:

  1. A failure to read the selection file still synthesizes Local as the selected target, and the startup recovery dialog skips that Local-shaped failure. An existing remote selection can therefore become Local execution authority without an explicit user choice.
  2. The remote Desktop project projection still exposes Client-local actions and permits “No project,” although remote Session creation requires a Host Project. The Settings view also interprets intentionally hidden Host paths as unavailable Projects.

These look like contained follow-ups to the Client state model, not reasons to revisit the profile catalog or Runtime Host architecture. Please feel free to push back if there is an existing authority or product contract that makes either state intentional.

Current CI is green. I am leaving this as a comment rather than Request Changes.

Disclosure: Codex performed the code inspection and adversarial analysis through four independent focused review passes. I (Astro-Han) reviewed the evidence, selected the findings included here, and made the final decision to submit this review. I did not independently rerun every reproduction.

Comment threadapps/desktop/src/main/runtime-host-profile-service.ts
Comment threadapps/desktop/src/main/project-management-service.ts
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from 51be16b to 3f7c664CompareAugust 12, 2026 04:24
@M4n5ter

Copy link
Copy Markdown
MemberAuthor

Addressed the latest authority-boundary review in 3f7c66418 and rebased onto current main.

Startup selection now remains explicitly unavailable on read/decode failure until the user chooses Local. The active target also publishes one Project capability snapshot consumed by Main and renderer surfaces, so remote Hosts no longer expose unsupported Client-directory, no-Project, local-default, or path-based behavior.

Local validation passed: UI 113/113, Desktop 760/760, focused regressions 15/15, Desktop/UI typechecks, Biome, and diff checks. All addressed review threads are resolved.

@Astro-Han@liugddx, could you please re-review the current head?

@M4n5ter
M4n5ter requested a review from Astro-HanAugust 12, 2026 04:25

@Astro-HanAstro-Han 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.

The two findings from my previous round are materially fixed in 3f7c66418. Startup resolution is now a closed ready | unavailable result, and Project capabilities are published by the active target and consumed by the main picker and Settings surfaces. CI is green and the PR is mergeable.

I also asked six fresh reviewers to inspect the entire PR independently, without relying on the previous findings. The core architecture held up well:

  • Runtime Host remains the sole execution authority.
  • Desired profile selection and the actually active target are separate.
  • Requests, results, observations, and Client Capability calls are fenced by target epoch.
  • Remote Host paths no longer enter Client filesystem authority.
  • Credentials are bound to an exact immutable target and remain outside profile documents.
  • Desktop, CLI, and TUI share one profile catalog rather than introducing another discovery or execution path.

The remaining observations mostly reduce to one architectural seam rather than a list of unrelated defects:

Target generation is now the authority for IPC, but it is not yet the single state model consumed by reconnect recovery, renderer projections, execution-location UI, and Project resolution.

For example, the owner knows when a target is connecting, ready, reconnecting, or unavailable, while consumers generally receive only a successful-activation invalidation. This leaves old projections visible during a switch, does not refresh all global projections after a same-target reconnect, and lets some renderer surfaces continue exposing local-path actions. Project identity is similarly canonical on the Host, but Desktop, CLI, and TUI still implement separate ID/alias and cwd rules.

I think the clean end state is one target-state projection owned by the Desktop target owner:

typeClientTargetState=|{readiness: "connecting";epoch: string;desiredProfileId: string;activeTarget?: ResolvedTarget;}|{readiness: "ready";epoch: string;desiredProfileId: string;activeTarget: ResolvedTarget;projectCapabilities: ProjectCapabilities;location: ExecutionLocation;}|{readiness: "unavailable";desiredProfileId: string;error: Error;};

Renderer invalidation, reconnect resynchronization, profile status, local-path actions, and the visible Host/execution location can then consume the same fact instead of inferring it from cached data, profile.kind, or a bare cwd.

The other independent concern is persistence. Profile metadata, selection, credential storage, and the directory lock currently have multiple durable commit points without automatic crash recovery. I would define one visible commit point, keep reads independent of the mutation lock where safe, bound all renderer-to-main input, and make credential replacement an explicit operation on an immutable target.

This is a large PR—82 files and roughly 5.3k changed lines—but I would not split it by Runtime Host, Desktop, CLI, and TUI. Those pieces form one authority change, and independently mergeable layers would create temporary incomplete states. I would instead shrink it through consolidation:

  • publish one target-state projection rather than adding more renderer-specific invalidations;
  • share one Project ID/alias resolver across Desktop, CLI run, and TUI;
  • represent execution location as client_path | host_project, allowing only the first to reach Client filesystem APIs;
  • remove the second location inference from the raw hostProfileId after the profile has already been resolved;
  • remove the write lock from safe read-only resolution and remove the redundant post-rename chmod.

I left three inline comments at the natural owner boundaries rather than filing every downstream symptom separately. All are non-blocking P2 observations. I’m approving the current revision; please feel free to push back if an existing invariant or recovery guarantee changes this analysis.

AI-assisted review disclosure

Codex performed the code inspection and six independent adversarial review passes. I, Astro-Han, reviewed and challenged the evidence, grouped the findings by their underlying invariants, selected the comments included here, and made the final decision to approve. I did not independently rerun every failure sequence described below.

中文对照

上一轮提出的两个问题已经在 3f7c66418 中得到实质修复:启动解析现在是闭合的 ready | unavailable 状态;Project capability 由 active target 发布,并被主项目选择器和设置页面消费。CI 全绿,PR 当前可以合并。

我又安排了六路互相独立的 fresh-eye 对抗性复审。整体架构经受住了审查:Runtime Host 仍是唯一执行权威;desired profile 与 active target 已分离;request、result、observation 和 Client Capability 都有 epoch fencing;远程 Host path 不再进入 Client 文件系统权限;credential 与精确且不可变的 target 绑定;Desktop、CLI、TUI 共用同一个 profile catalog。

剩余问题大多来自同一个架构边界:

target generation 已经是 IPC 的 authority,但还没有成为 reconnect、renderer projection、execution-location UI 和 Project resolution 共同消费的唯一状态模型。

最干净的终态是让 Desktop target owner 发布一个包含 epoch、desired profile、active target、readiness、Project capability 和 execution location 的闭合状态。Renderer 清理与刷新、重连恢复、profile 状态、本地路径操作和 Host/location 展示都消费这个事实,不再从旧缓存、profile.kind 或裸 cwd 推断。

另一个相对独立的问题是 persistence:profile metadata、selection、credential store 和目录锁目前存在多个 durable commit point,但没有自动的崩溃恢复协议。建议明确唯一可见提交点,让安全的只读解析不受 mutation lock 阻塞,统一限制 renderer-to-main 输入大小,并把 credential replacement 建模为 immutable target 上的独立操作。

这个 PR 的确很大,但不建议按 Runtime Host、Desktop、CLI、TUI 拆开,因为它们共同组成一个 authority change,分开合并会制造不安全的中间状态。更合适的是通过合并重复规则和删除平行推断来缩小复杂度。

下面只在三个自然 owner 上留下 inline comments,不把同一根因展开成十个局部问题。它们都是非阻塞的 P2。我会批准当前版本;如果已有不变量或恢复保证能够推翻这些分析,也欢迎作者直接说明或讨论。

AI 辅助审查披露

Codex 负责代码检查和六路独立的对抗性复审。Astro-Han 复核并质疑了相关证据,按共同不变量归并 findings,选择了最终评论,并作出批准决定。Astro-Han 没有独立重新执行下面描述的每一个失败序列。

Comment threadapps/desktop/src/main/runtime-host-desktop-owner.ts
Comment threadpackages/cli/src/runtime-host-run-command.ts Outdated
Comment threadpackages/runtime-host/src/client/host-profile.ts Outdated
Introduce a shared profile catalog for Desktop, CLI, and TUI, with credentials bound to an exact Host target. Desktop can switch targets live while keeping reconnect and session observation ownership isolated per target generation.
Remote targets operate through Project identities and do not inherit Client-local filesystem authority.
Keep Desktop projections, project policy, and rollback state aligned with the Runtime Host that is actually active. Remote clients no longer interpret Host-owned paths as local filesystem authority, including TUI and non-interactive resume flows.
Keep regression coverage at the public catalog and CLI boundaries while removing assertions that freeze internal credential key encoding and generic parser plumbing. Replace the stateful CLI fake with a capture-only dependency so the test reflects command ownership.
Represent remote connectivity as a discriminated TLS transport and bind credentials to that transport identity. This preserves a clear extension point for SSH and explicitly acknowledged insecure transports without ambiguous URL or boolean combinations.
Fence Desktop requests and projections by target generation, and represent desired selection independently from the active Host. Keep remote paths Host-owned across Desktop, CLI, TUI, and Client Capability calls.\n\nMake profile targets immutable and credentials exact-target bound so partial mutations remain explicit and recoverable without cleanup heuristics.
Keep unreadable Runtime Host selections unavailable until the user explicitly chooses Local. Publish target-scoped Project capabilities so renderer surfaces expose only actions supported by the active Host.
Publish target lifecycle transitions so Desktop clears stale projections during switches and reconnects. Canonicalize Project aliases across clients, and make the atomic profile document the visible persistence commit while keeping safe reads independent of writer serialization.
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from 3f7c664 to 112cef2CompareAugust 12, 2026 05:28
@M4n5ter

M4n5ter commented Aug 12, 2026

Copy link
Copy Markdown
MemberAuthor

Addressed the three follow-up P2 threads in 112cef2f7 and rebased onto current main.

  • Desktop now publishes the complete target lifecycle, clears stale Host projections before readiness, and refreshes them after activation or same-target reconnect
  • Project ID/alias resolution is shared across Host, Desktop, CLI run, and TUI, with Clients forwarding the canonical Project ID
  • Profile reads no longer wait on mutation locks; the atomic profile document is the visible commit point; credential input is bounded; and the redundant post-rename chmod is removed

I kept the existing fail-loud writer-lock policy instead of auto-stealing stale directories. The inline response records the TOCTOU reason and the resulting recovery contract.

Local validation after rebasing: all four affected workspace typechecks passed, the 52 focused boundary tests passed, Biome and diff checks passed. Before the rebase, the complete Runtime Host, CLI, and Desktop test suites also passed. CI is green on the rebased head.

@M4n5ter
M4n5ter merged commit 5df53c1 into mainAug 12, 2026
11 checks passed
@M4n5ter
M4n5ter deleted the feat/runtime-host-profiles branch August 12, 2026 05:45
@Astro-Han

Copy link
Copy Markdown
Contributor

A small post-merge follow-up from a fresh review of the final head: I found two P2 issues that may be worth addressing in a focused patch.

  1. Same-target reconnect currently clears Client-owned session and composer state.

    The owner publishes reconnecting → ready within the same target epoch, but the renderer performs a full Runtime Host reset for both events. That clears the active Session, messages, pending attachments, and pending quotes. A normal remote-network interruption can therefore return the user to the new-session screen and discard unsent composer state even though the execution authority did not change.

    The narrow fix is to reserve the full reset for an actual epoch/target-generation change. A same-epoch reconnect should pause and refresh Host-owned projections while preserving the active Session intent and Client-owned drafts.

  2. Some remote surfaces still expose Client-local path actions that Main will reject.

    The Project picker and Projects Settings now consume the target capability snapshot, but the title-bar/command-palette “Open Project Folder” actions and the Skills actions for opening local files/folders or importing a local source remain visible on a remote target. These are normally reachable controls that deterministically fail at the Main boundary.

    It would be cleaner for these surfaces to consume the same active-target local-path capability rather than adding independent remote checks.

Neither issue changes the core Runtime Host authority model, exact-target credential binding, or the merge decision. They look suitable for one focused follow-up rather than reopening the profile architecture.

Please feel free to push back if reconnect is intentionally defined as a full Client workspace reset, or if these local-path actions have another capability source I missed.

AI-assisted review disclosure

Codex performed the post-merge code inspection and delegated adversarial review. Astro-Han reviewed the synthesized evidence, recalibrated the findings to P2, and decided which follow-ups to include. The reproduction paths remain AI-assisted and should be verified while implementing the follow-up.

中文对照(默认折叠)

在最终 head 合并后的 fresh review 中,发现两个可以考虑用独立小补丁处理的 P2:

  1. 同一 target 的重连目前会清除 Client-owned Session 和 composer 状态。reconnecting → ready 没有改变 epoch,但 renderer 对两个事件都执行完整 reset,因此普通网络中断可能清除当前 Session、未发送附件和引用。建议只在 epoch/target generation 真正改变时完整 reset;同 epoch reconnect 只暂停和刷新 Host projection。

  2. 部分 remote surface 仍显示 Main 必然拒绝的 Client-local path 操作。Project picker 和 Projects Settings 已消费 capability snapshot,但标题栏/命令面板的 Open Project Folder,以及 Skills 中打开本地文件、文件夹和导入本地来源的操作仍然可见。建议让这些 surface 消费同一个 active-target local-path capability。

这两个问题不改变 Runtime Host 的核心 authority、exact-target credential binding 或此前的合并判断,适合作为一个 focused follow-up,而不是重新调整 profile 架构。

如果 reconnect 本来就被定义为完整 Client workspace reset,或者这些本地路径操作还有其他 capability 来源,也欢迎直接 push back。

本次由 Codex 完成合并后代码检查和多路对抗性复审;Astro-Han 复核综合证据、将问题重新校准为 P2,并决定纳入哪些 follow-up。具体复现路径仍属于 AI-assisted analysis,建议在实现 follow-up 时再次验证。

@liugddx

Copy link
Copy Markdown
Member

There is one reconnect boundary worth handling in a focused follow-up PR.

runtime-host-profiles:changed carries a target epoch, but the renderer handler currently clears state and starts fire-and-forget refreshes without retaining or checking that epoch. The individual refresh functions also commit unconditionally after await. Because a same-target reconnect keeps the same target epoch, a read that started on candidate A can complete after candidate B is ready and overwrite B's newer projection.

A minimal fix would give each candidate replacement its own generation and fence Host-scoped refresh commits against it, either at the IPC result boundary or in the renderer. The regression should hold an A read pending, reconnect and complete B's refresh, then release A and verify that A cannot replace B's state.

This does not require reverting this PR or reopening the broader profile design. A small follow-up PR covering the generation fence and one behavioral test should be enough.

简体中文

建议用一个聚焦的后续 PR 处理 remaining reconnect 边界。

runtime-host-profiles:changed 虽然携带 target epoch,但 renderer handler 当前只是清空状态并触发一组 fire-and-forget refresh,没有保存或校验该 epoch。各 refresh 在 await 后也会无条件提交。由于 same-target reconnect 复用同一个 target epoch,candidate A 上已经开始的读取可能在 candidate B ready 并完成刷新之后才返回,从而用 A 的旧投影覆盖 B。

最小修复是为每次 candidate replacement 增加独立 generation,并在 IPC result 边界或 renderer commit 前校验 generation。回归测试应挂起 A 的读取,让 B 重连并完成刷新,再释放 A,确认 A 不能覆盖 B 的状态。

这不需要回滚本 PR,也不需要重新展开整个 profile 设计。单独开一个小 PR,完成 generation fence 和一条行为测试即可。

Sign up for freeto 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.

3 participants

@M4n5ter@Astro-Han@liugddx
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat(runtime-host): add client-selectable host profiles - #2834

Merged
M4n5ter merged 7 commits into
mainfrom
feat/runtime-host-profiles
Aug 12, 2026
Merged

feat(runtime-host): add client-selectable host profiles#2834
M4n5ter merged 7 commits into
mainfrom
feat/runtime-host-profiles

Conversation

@M4n5ter

@M4n5terM4n5ter commented Aug 11, 2026

Copy link
Copy Markdown
Member
English

Summary

  • add a shared Runtime Host profile catalog for Desktop, CLI, and TUI
  • let Desktop switch between Local and remote Runtime Hosts without restarting
  • route remote work through Host-owned Project identities instead of Client-local paths
  • bind each access credential to an exact profile target and keep it outside profile documents
  • reset Host-owned Client projections at the target-generation boundary

Why

Runtime Host is now the execution authority, but Clients could still only select the implicit local Host. This change introduces the smallest shared target model needed for future remote Hosts while preserving a strict boundary: a remote Host path is never treated as a local Client path.

Each Desktop target generation owns its reconnect lifecycle, Session observations, Project policy, and local-path permissions. Switching reports the Host that is actually active even if selection persistence and rollback both fail. CLI and TUI omit Client-side cwd relocation for remote Sessions.

User impact

Desktop users can manage and switch Runtime Host profiles from Workspace settings. CLI and TUI users can select the same profiles with --host and select an existing remote Project with --project. Credentials are accepted through secure storage or environment input, never command-line arguments or profile JSON.

Validation

  • Runtime Host, Storage, CLI, and Desktop typechecks
  • focused Desktop, CLI/TUI, Runtime Host, and Storage regressions: 130 passed
  • complete production build
  • Biome and git diff --check

Refs #2522

简体中文

概要

  • 为 Desktop、CLI 和 TUI 增加共享的 Runtime Host profile catalog
  • Desktop 无需重启即可在 Local 与远程 Runtime Host 之间切换
  • 远程工作通过 Host 持有的 Project identity 路由,不再依赖 Client 本地路径
  • access credential 与精确的 profile target 绑定,并与 profile 文档分离保存
  • 在 target generation 边界清空并重载 Host-owned Client projections

原因

Runtime Host 已成为执行权威,但 Client 仍只能使用隐式的本地 Host。本次变更增加未来远程 Host 所需的最小共享目标模型,同时保留严格边界:远程 Host 路径绝不会被当作 Client 本地路径使用。

Desktop 的每个 target generation 独立持有 reconnect lifecycle、Session observations、Project policy 与本地路径权限。即使 selection 持久化与回滚同时失败,切换结果仍会报告实际活跃的 Host。CLI 与 TUI 不会对远程 Session 执行 Client 侧 cwd relocation。

用户影响

Desktop 用户可以在工作区设置中管理和切换 Runtime Host profiles。CLI 和 TUI 用户可以通过 --host 选择同一套 profiles,并通过 --project 选择已有的远程 Project。Credential 仅通过安全存储或环境变量输入,不会进入命令行参数或 profile JSON。

验证

  • Runtime Host、Storage、CLI 与 Desktop typecheck
  • Desktop、CLI/TUI、Runtime Host 与 Storage 定向回归:130 项通过
  • 完整 production build
  • Biome 与 git diff --check

关联 #2522

@M4n5ter
M4n5ter marked this pull request as ready for review August 12, 2026 00:35
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from 50f0923 to c71fa13CompareAugust 12, 2026 00:45

@Astro-HanAstro-Han 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.

The profile catalog itself looks sound. It keeps connection configuration on the Client, preserves the Runtime Host as the execution authority, and does not add another discovery or spawn path.

I think the remaining problems come from three missing boundaries rather than a collection of unrelated bugs.

  1. A target switch has no identity that follows its requests and results. Reconnecting to Host A and switching from A to B both look like a temporary handler gap. Commands can wait for B's handler, while reads started on A can complete after the switch and overwrite B's renderer state.

  2. Host-owned and Client-owned locations share the same representation. A plain cwd may be treated as a Host execution path, a Client filesystem path, a display value, or an autocomplete root. This breaks remote resume and also lets a remote capability frame supply a Client-local cwd despite advertising hostPathAccess: "none". Bare Project IDs have the same ownership problem when a global default is reused across Host roots.

  3. Desired profile selection, active Host readiness, and persisted credential state are mixed together. A profile or credential read failure currently rewrites the selection to Local, so a temporary storage failure can change execution authority. Profile and credential mutations also have commit points that can leave one side updated without enough metadata to recover the other.

I would keep the catalog, root pinning, credential binding, and per-target lifecycle. The smallest coherent revision is narrower:

  • Give each target generation an epoch. Commands must fail when their epoch is gone. Reconnectable reads may wait or retry only within the same epoch, and renderer reads must check the epoch before committing.
  • Split Client-local cwd from Host-owned execution location. Remote paths must not reach Client realpath, autocomplete, foreign-session scanning, or native capabilities unless the registered offer explicitly permits Host path access.
  • Keep a failed remote selection in an unavailable state. Persist Local only after an explicit user choice.
  • Keep remote Project policy root-scoped. Do not reuse the Client-global default, and hide workspace actions that remote main will reject.
  • Make profile and credential updates recoverable. Do not discard target metadata after a cleanup failure, and finish fallible preparation before committing profile metadata.

The cleaner end state is one Desktop owner publishing {epoch, target, readiness, candidate}. Boot, profile service, IPC, runtime policy, and renderer should consume that snapshot instead of storing separate versions of the active target. Execution locations should also be a discriminated type, so only a client-path can reach Client filesystem APIs.

I left inline comments at the smallest code sites that expose these boundaries.

The current red typecheck and workspace checks appear to reproduce on the base and are not attributed to this diff. The branch is still not green, but those failures are separate from the points above.

Disclosure: Codex performed the code inspection and adversarial analysis using several focused review passes. I (Astro-Han) reviewed and challenged the analysis over multiple rounds, selected the issues included here, and own the final review. I have not independently rerun every reproduction.

Comment threadapps/desktop/src/main/runtime-host-desktop-owner.ts Outdated
Comment threadapps/desktop/src/renderer/app-shell-effects.ts
Comment threadapps/desktop/src/main/runtime-host-native-capabilities.ts Outdated
Comment threadpackages/cli/src/runtime-host-session-driver.ts
Comment threadapps/desktop/src/main/runtime-host-profile-service.ts
Comment threadpackages/runtime-host/src/client/host-profile.ts Outdated

@liugddxliugddx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Follow-up adversarial review with findings anchored to the smallest changed lines. These complement the consolidated review and avoid duplicating existing inline comments.

简体中文

补充对抗性审查:以下问题锚定到最小变更行,作为汇总评论的可逐项 resolve 版本,并避免重复已有行级评论。

Comment threadpackages/cli/src/runtime-host-tui-context.ts
Comment threadapps/desktop/src/main/runtime-host-profile-service.ts Outdated
Comment threadapps/desktop/src/main/runtime-host-profile-service.ts Outdated
Comment threadpackages/cli/src/__tests__/runtime-host-run-command.test.ts Outdated
Comment threadapps/desktop/src/main/__tests__/runtime-host-desktop-owner.test.ts Outdated
Comment threadpackages/runtime-host/src/__tests__/host-profile.test.ts Outdated
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from e6857cb to 51be16bCompareAugust 12, 2026 03:12
@M4n5ter

Copy link
Copy Markdown
MemberAuthor

Addressed both review passes at 51be16b4d and rebased onto the latest main.

The revision now:

  • fences Desktop requests, results, and observations by target generation;
  • separates desired selection from the actually active Host, including the no-active-Host state;
  • keeps remote paths Host-owned across Desktop, CLI, TUI, and Client Capability calls;
  • makes profile targets immutable and credentials exact-target bound with recoverable mutation ordering;
  • removes the low-value remote-resume and object-identity tests called out in review.

Local validation passed: Runtime Host coverage (the only stale-build failure passed after rebuilding its updated dependency), CLI 233/233, Desktop 758/758, all three workspace typechecks, Biome, and git diff --check.

Could you please re-review the current head?

@Astro-HanAstro-Han 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.

The previous generation, path-authority, active-state, and credential-isolation findings are materially addressed in 51be16b4d.

Requests and results are now fenced by target epoch; remote Host paths no longer reach Client filesystem or capability APIs; rollback can represent no active Host; preference-write failure keeps the healthy Host active; and credentials are bound and tested against the exact immutable target. The focused tests now exercise the production boundaries rather than object identity or injected fakes.

I think the two remaining findings share one root cause rather than being isolated guards: the Runtime Host authority model has been updated, but two Client-facing boundaries still encode the former assumption that a valid local state always exists.

At startup, an unreadable selection is represented as Local because the resolution type cannot express “the authority is unknown.” In the project UI, the absence of a Client-visible path is interpreted as an unavailable Project, while Client-local actions remain visible, because the renderer infers capabilities from the old local Project representation instead of consuming capabilities published by the active target.

This creates the same failure in two forms: an unavailable or intentionally absent Client-local value is converted into Local authority instead of remaining an explicit unavailable or Host-owned state. The result is either a silent authority change or UI actions that the active Host cannot support.

From first principles, an unknown selection must not become an execution target, and the renderer should only expose operations granted by the active authority. I would therefore fix these at their natural boundaries rather than adding individual if (remote) checks:

  • Make startup resolution a closed ready | unavailable result. Only ready may contain a target; choosing Local should be a separate explicit transition.
  • Publish a small project-capability projection for the active target, covering directory actions, no-project allowance, local defaults, and path visibility. The renderer should consume that projection rather than infer authority from profile.kind, preferredPath, or failed calls.

I found two concrete consequences of the incomplete boundary:

  1. A failure to read the selection file still synthesizes Local as the selected target, and the startup recovery dialog skips that Local-shaped failure. An existing remote selection can therefore become Local execution authority without an explicit user choice.
  2. The remote Desktop project projection still exposes Client-local actions and permits “No project,” although remote Session creation requires a Host Project. The Settings view also interprets intentionally hidden Host paths as unavailable Projects.

These look like contained follow-ups to the Client state model, not reasons to revisit the profile catalog or Runtime Host architecture. Please feel free to push back if there is an existing authority or product contract that makes either state intentional.

Current CI is green. I am leaving this as a comment rather than Request Changes.

Disclosure: Codex performed the code inspection and adversarial analysis through four independent focused review passes. I (Astro-Han) reviewed the evidence, selected the findings included here, and made the final decision to submit this review. I did not independently rerun every reproduction.

Comment threadapps/desktop/src/main/runtime-host-profile-service.ts
Comment threadapps/desktop/src/main/project-management-service.ts
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from 51be16b to 3f7c664CompareAugust 12, 2026 04:24
@M4n5ter

Copy link
Copy Markdown
MemberAuthor

Addressed the latest authority-boundary review in 3f7c66418 and rebased onto current main.

Startup selection now remains explicitly unavailable on read/decode failure until the user chooses Local. The active target also publishes one Project capability snapshot consumed by Main and renderer surfaces, so remote Hosts no longer expose unsupported Client-directory, no-Project, local-default, or path-based behavior.

Local validation passed: UI 113/113, Desktop 760/760, focused regressions 15/15, Desktop/UI typechecks, Biome, and diff checks. All addressed review threads are resolved.

@Astro-Han@liugddx, could you please re-review the current head?

@M4n5ter
M4n5ter requested a review from Astro-HanAugust 12, 2026 04:25

@Astro-HanAstro-Han 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.

The two findings from my previous round are materially fixed in 3f7c66418. Startup resolution is now a closed ready | unavailable result, and Project capabilities are published by the active target and consumed by the main picker and Settings surfaces. CI is green and the PR is mergeable.

I also asked six fresh reviewers to inspect the entire PR independently, without relying on the previous findings. The core architecture held up well:

  • Runtime Host remains the sole execution authority.
  • Desired profile selection and the actually active target are separate.
  • Requests, results, observations, and Client Capability calls are fenced by target epoch.
  • Remote Host paths no longer enter Client filesystem authority.
  • Credentials are bound to an exact immutable target and remain outside profile documents.
  • Desktop, CLI, and TUI share one profile catalog rather than introducing another discovery or execution path.

The remaining observations mostly reduce to one architectural seam rather than a list of unrelated defects:

Target generation is now the authority for IPC, but it is not yet the single state model consumed by reconnect recovery, renderer projections, execution-location UI, and Project resolution.

For example, the owner knows when a target is connecting, ready, reconnecting, or unavailable, while consumers generally receive only a successful-activation invalidation. This leaves old projections visible during a switch, does not refresh all global projections after a same-target reconnect, and lets some renderer surfaces continue exposing local-path actions. Project identity is similarly canonical on the Host, but Desktop, CLI, and TUI still implement separate ID/alias and cwd rules.

I think the clean end state is one target-state projection owned by the Desktop target owner:

typeClientTargetState=|{readiness: "connecting";epoch: string;desiredProfileId: string;activeTarget?: ResolvedTarget;}|{readiness: "ready";epoch: string;desiredProfileId: string;activeTarget: ResolvedTarget;projectCapabilities: ProjectCapabilities;location: ExecutionLocation;}|{readiness: "unavailable";desiredProfileId: string;error: Error;};

Renderer invalidation, reconnect resynchronization, profile status, local-path actions, and the visible Host/execution location can then consume the same fact instead of inferring it from cached data, profile.kind, or a bare cwd.

The other independent concern is persistence. Profile metadata, selection, credential storage, and the directory lock currently have multiple durable commit points without automatic crash recovery. I would define one visible commit point, keep reads independent of the mutation lock where safe, bound all renderer-to-main input, and make credential replacement an explicit operation on an immutable target.

This is a large PR—82 files and roughly 5.3k changed lines—but I would not split it by Runtime Host, Desktop, CLI, and TUI. Those pieces form one authority change, and independently mergeable layers would create temporary incomplete states. I would instead shrink it through consolidation:

  • publish one target-state projection rather than adding more renderer-specific invalidations;
  • share one Project ID/alias resolver across Desktop, CLI run, and TUI;
  • represent execution location as client_path | host_project, allowing only the first to reach Client filesystem APIs;
  • remove the second location inference from the raw hostProfileId after the profile has already been resolved;
  • remove the write lock from safe read-only resolution and remove the redundant post-rename chmod.

I left three inline comments at the natural owner boundaries rather than filing every downstream symptom separately. All are non-blocking P2 observations. I’m approving the current revision; please feel free to push back if an existing invariant or recovery guarantee changes this analysis.

AI-assisted review disclosure

Codex performed the code inspection and six independent adversarial review passes. I, Astro-Han, reviewed and challenged the evidence, grouped the findings by their underlying invariants, selected the comments included here, and made the final decision to approve. I did not independently rerun every failure sequence described below.

中文对照

上一轮提出的两个问题已经在 3f7c66418 中得到实质修复:启动解析现在是闭合的 ready | unavailable 状态;Project capability 由 active target 发布,并被主项目选择器和设置页面消费。CI 全绿,PR 当前可以合并。

我又安排了六路互相独立的 fresh-eye 对抗性复审。整体架构经受住了审查:Runtime Host 仍是唯一执行权威;desired profile 与 active target 已分离;request、result、observation 和 Client Capability 都有 epoch fencing;远程 Host path 不再进入 Client 文件系统权限;credential 与精确且不可变的 target 绑定;Desktop、CLI、TUI 共用同一个 profile catalog。

剩余问题大多来自同一个架构边界:

target generation 已经是 IPC 的 authority,但还没有成为 reconnect、renderer projection、execution-location UI 和 Project resolution 共同消费的唯一状态模型。

最干净的终态是让 Desktop target owner 发布一个包含 epoch、desired profile、active target、readiness、Project capability 和 execution location 的闭合状态。Renderer 清理与刷新、重连恢复、profile 状态、本地路径操作和 Host/location 展示都消费这个事实,不再从旧缓存、profile.kind 或裸 cwd 推断。

另一个相对独立的问题是 persistence:profile metadata、selection、credential store 和目录锁目前存在多个 durable commit point,但没有自动的崩溃恢复协议。建议明确唯一可见提交点,让安全的只读解析不受 mutation lock 阻塞,统一限制 renderer-to-main 输入大小,并把 credential replacement 建模为 immutable target 上的独立操作。

这个 PR 的确很大,但不建议按 Runtime Host、Desktop、CLI、TUI 拆开,因为它们共同组成一个 authority change,分开合并会制造不安全的中间状态。更合适的是通过合并重复规则和删除平行推断来缩小复杂度。

下面只在三个自然 owner 上留下 inline comments,不把同一根因展开成十个局部问题。它们都是非阻塞的 P2。我会批准当前版本;如果已有不变量或恢复保证能够推翻这些分析,也欢迎作者直接说明或讨论。

AI 辅助审查披露

Codex 负责代码检查和六路独立的对抗性复审。Astro-Han 复核并质疑了相关证据,按共同不变量归并 findings,选择了最终评论,并作出批准决定。Astro-Han 没有独立重新执行下面描述的每一个失败序列。

Comment threadapps/desktop/src/main/runtime-host-desktop-owner.ts
Comment threadpackages/cli/src/runtime-host-run-command.ts Outdated
Comment threadpackages/runtime-host/src/client/host-profile.ts Outdated
Introduce a shared profile catalog for Desktop, CLI, and TUI, with credentials bound to an exact Host target. Desktop can switch targets live while keeping reconnect and session observation ownership isolated per target generation.
Remote targets operate through Project identities and do not inherit Client-local filesystem authority.
Keep Desktop projections, project policy, and rollback state aligned with the Runtime Host that is actually active. Remote clients no longer interpret Host-owned paths as local filesystem authority, including TUI and non-interactive resume flows.
Keep regression coverage at the public catalog and CLI boundaries while removing assertions that freeze internal credential key encoding and generic parser plumbing. Replace the stateful CLI fake with a capture-only dependency so the test reflects command ownership.
Represent remote connectivity as a discriminated TLS transport and bind credentials to that transport identity. This preserves a clear extension point for SSH and explicitly acknowledged insecure transports without ambiguous URL or boolean combinations.
Fence Desktop requests and projections by target generation, and represent desired selection independently from the active Host. Keep remote paths Host-owned across Desktop, CLI, TUI, and Client Capability calls.\n\nMake profile targets immutable and credentials exact-target bound so partial mutations remain explicit and recoverable without cleanup heuristics.
Keep unreadable Runtime Host selections unavailable until the user explicitly chooses Local. Publish target-scoped Project capabilities so renderer surfaces expose only actions supported by the active Host.
Publish target lifecycle transitions so Desktop clears stale projections during switches and reconnects. Canonicalize Project aliases across clients, and make the atomic profile document the visible persistence commit while keeping safe reads independent of writer serialization.
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from 3f7c664 to 112cef2CompareAugust 12, 2026 05:28
@M4n5ter

M4n5ter commented Aug 12, 2026

Copy link
Copy Markdown
MemberAuthor

Addressed the three follow-up P2 threads in 112cef2f7 and rebased onto current main.

  • Desktop now publishes the complete target lifecycle, clears stale Host projections before readiness, and refreshes them after activation or same-target reconnect
  • Project ID/alias resolution is shared across Host, Desktop, CLI run, and TUI, with Clients forwarding the canonical Project ID
  • Profile reads no longer wait on mutation locks; the atomic profile document is the visible commit point; credential input is bounded; and the redundant post-rename chmod is removed

I kept the existing fail-loud writer-lock policy instead of auto-stealing stale directories. The inline response records the TOCTOU reason and the resulting recovery contract.

Local validation after rebasing: all four affected workspace typechecks passed, the 52 focused boundary tests passed, Biome and diff checks passed. Before the rebase, the complete Runtime Host, CLI, and Desktop test suites also passed. CI is green on the rebased head.

@M4n5ter
M4n5ter merged commit 5df53c1 into mainAug 12, 2026
11 checks passed
@M4n5ter
M4n5ter deleted the feat/runtime-host-profiles branch August 12, 2026 05:45
@Astro-Han

Copy link
Copy Markdown
Contributor

A small post-merge follow-up from a fresh review of the final head: I found two P2 issues that may be worth addressing in a focused patch.

  1. Same-target reconnect currently clears Client-owned session and composer state.

    The owner publishes reconnecting → ready within the same target epoch, but the renderer performs a full Runtime Host reset for both events. That clears the active Session, messages, pending attachments, and pending quotes. A normal remote-network interruption can therefore return the user to the new-session screen and discard unsent composer state even though the execution authority did not change.

    The narrow fix is to reserve the full reset for an actual epoch/target-generation change. A same-epoch reconnect should pause and refresh Host-owned projections while preserving the active Session intent and Client-owned drafts.

  2. Some remote surfaces still expose Client-local path actions that Main will reject.

    The Project picker and Projects Settings now consume the target capability snapshot, but the title-bar/command-palette “Open Project Folder” actions and the Skills actions for opening local files/folders or importing a local source remain visible on a remote target. These are normally reachable controls that deterministically fail at the Main boundary.

    It would be cleaner for these surfaces to consume the same active-target local-path capability rather than adding independent remote checks.

Neither issue changes the core Runtime Host authority model, exact-target credential binding, or the merge decision. They look suitable for one focused follow-up rather than reopening the profile architecture.

Please feel free to push back if reconnect is intentionally defined as a full Client workspace reset, or if these local-path actions have another capability source I missed.

AI-assisted review disclosure

Codex performed the post-merge code inspection and delegated adversarial review. Astro-Han reviewed the synthesized evidence, recalibrated the findings to P2, and decided which follow-ups to include. The reproduction paths remain AI-assisted and should be verified while implementing the follow-up.

中文对照(默认折叠)

在最终 head 合并后的 fresh review 中,发现两个可以考虑用独立小补丁处理的 P2:

  1. 同一 target 的重连目前会清除 Client-owned Session 和 composer 状态。reconnecting → ready 没有改变 epoch,但 renderer 对两个事件都执行完整 reset,因此普通网络中断可能清除当前 Session、未发送附件和引用。建议只在 epoch/target generation 真正改变时完整 reset;同 epoch reconnect 只暂停和刷新 Host projection。

  2. 部分 remote surface 仍显示 Main 必然拒绝的 Client-local path 操作。Project picker 和 Projects Settings 已消费 capability snapshot,但标题栏/命令面板的 Open Project Folder,以及 Skills 中打开本地文件、文件夹和导入本地来源的操作仍然可见。建议让这些 surface 消费同一个 active-target local-path capability。

这两个问题不改变 Runtime Host 的核心 authority、exact-target credential binding 或此前的合并判断,适合作为一个 focused follow-up,而不是重新调整 profile 架构。

如果 reconnect 本来就被定义为完整 Client workspace reset,或者这些本地路径操作还有其他 capability 来源,也欢迎直接 push back。

本次由 Codex 完成合并后代码检查和多路对抗性复审;Astro-Han 复核综合证据、将问题重新校准为 P2,并决定纳入哪些 follow-up。具体复现路径仍属于 AI-assisted analysis,建议在实现 follow-up 时再次验证。

@liugddx

Copy link
Copy Markdown
Member

There is one reconnect boundary worth handling in a focused follow-up PR.

runtime-host-profiles:changed carries a target epoch, but the renderer handler currently clears state and starts fire-and-forget refreshes without retaining or checking that epoch. The individual refresh functions also commit unconditionally after await. Because a same-target reconnect keeps the same target epoch, a read that started on candidate A can complete after candidate B is ready and overwrite B's newer projection.

A minimal fix would give each candidate replacement its own generation and fence Host-scoped refresh commits against it, either at the IPC result boundary or in the renderer. The regression should hold an A read pending, reconnect and complete B's refresh, then release A and verify that A cannot replace B's state.

This does not require reverting this PR or reopening the broader profile design. A small follow-up PR covering the generation fence and one behavioral test should be enough.

简体中文

建议用一个聚焦的后续 PR 处理 remaining reconnect 边界。

runtime-host-profiles:changed 虽然携带 target epoch,但 renderer handler 当前只是清空状态并触发一组 fire-and-forget refresh,没有保存或校验该 epoch。各 refresh 在 await 后也会无条件提交。由于 same-target reconnect 复用同一个 target epoch,candidate A 上已经开始的读取可能在 candidate B ready 并完成刷新之后才返回,从而用 A 的旧投影覆盖 B。

最小修复是为每次 candidate replacement 增加独立 generation,并在 IPC result 边界或 renderer commit 前校验 generation。回归测试应挂起 A 的读取,让 B 重连并完成刷新,再释放 A,确认 A 不能覆盖 B 的状态。

这不需要回滚本 PR,也不需要重新展开整个 profile 设计。单独开一个小 PR,完成 generation fence 和一条行为测试即可。

Sign up for freeto 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.

3 participants

@M4n5ter@Astro-Han@liugddx
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(runtime-host): add client-selectable host profiles - #2834

Merged
M4n5ter merged 7 commits into
mainfrom
feat/runtime-host-profiles
Aug 12, 2026
Merged

feat(runtime-host): add client-selectable host profiles#2834
M4n5ter merged 7 commits into
mainfrom
feat/runtime-host-profiles

Conversation

@M4n5ter

@M4n5terM4n5ter commented Aug 11, 2026

Copy link
Copy Markdown
Member
English

Summary

  • add a shared Runtime Host profile catalog for Desktop, CLI, and TUI
  • let Desktop switch between Local and remote Runtime Hosts without restarting
  • route remote work through Host-owned Project identities instead of Client-local paths
  • bind each access credential to an exact profile target and keep it outside profile documents
  • reset Host-owned Client projections at the target-generation boundary

Why

Runtime Host is now the execution authority, but Clients could still only select the implicit local Host. This change introduces the smallest shared target model needed for future remote Hosts while preserving a strict boundary: a remote Host path is never treated as a local Client path.

Each Desktop target generation owns its reconnect lifecycle, Session observations, Project policy, and local-path permissions. Switching reports the Host that is actually active even if selection persistence and rollback both fail. CLI and TUI omit Client-side cwd relocation for remote Sessions.

User impact

Desktop users can manage and switch Runtime Host profiles from Workspace settings. CLI and TUI users can select the same profiles with --host and select an existing remote Project with --project. Credentials are accepted through secure storage or environment input, never command-line arguments or profile JSON.

Validation

  • Runtime Host, Storage, CLI, and Desktop typechecks
  • focused Desktop, CLI/TUI, Runtime Host, and Storage regressions: 130 passed
  • complete production build
  • Biome and git diff --check

Refs #2522

简体中文

概要

  • 为 Desktop、CLI 和 TUI 增加共享的 Runtime Host profile catalog
  • Desktop 无需重启即可在 Local 与远程 Runtime Host 之间切换
  • 远程工作通过 Host 持有的 Project identity 路由,不再依赖 Client 本地路径
  • access credential 与精确的 profile target 绑定,并与 profile 文档分离保存
  • 在 target generation 边界清空并重载 Host-owned Client projections

原因

Runtime Host 已成为执行权威,但 Client 仍只能使用隐式的本地 Host。本次变更增加未来远程 Host 所需的最小共享目标模型,同时保留严格边界:远程 Host 路径绝不会被当作 Client 本地路径使用。

Desktop 的每个 target generation 独立持有 reconnect lifecycle、Session observations、Project policy 与本地路径权限。即使 selection 持久化与回滚同时失败,切换结果仍会报告实际活跃的 Host。CLI 与 TUI 不会对远程 Session 执行 Client 侧 cwd relocation。

用户影响

Desktop 用户可以在工作区设置中管理和切换 Runtime Host profiles。CLI 和 TUI 用户可以通过 --host 选择同一套 profiles,并通过 --project 选择已有的远程 Project。Credential 仅通过安全存储或环境变量输入,不会进入命令行参数或 profile JSON。

验证

  • Runtime Host、Storage、CLI 与 Desktop typecheck
  • Desktop、CLI/TUI、Runtime Host 与 Storage 定向回归:130 项通过
  • 完整 production build
  • Biome 与 git diff --check

关联 #2522

@M4n5ter
M4n5ter marked this pull request as ready for review August 12, 2026 00:35
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from 50f0923 to c71fa13CompareAugust 12, 2026 00:45

@Astro-HanAstro-Han 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.

The profile catalog itself looks sound. It keeps connection configuration on the Client, preserves the Runtime Host as the execution authority, and does not add another discovery or spawn path.

I think the remaining problems come from three missing boundaries rather than a collection of unrelated bugs.

  1. A target switch has no identity that follows its requests and results. Reconnecting to Host A and switching from A to B both look like a temporary handler gap. Commands can wait for B's handler, while reads started on A can complete after the switch and overwrite B's renderer state.

  2. Host-owned and Client-owned locations share the same representation. A plain cwd may be treated as a Host execution path, a Client filesystem path, a display value, or an autocomplete root. This breaks remote resume and also lets a remote capability frame supply a Client-local cwd despite advertising hostPathAccess: "none". Bare Project IDs have the same ownership problem when a global default is reused across Host roots.

  3. Desired profile selection, active Host readiness, and persisted credential state are mixed together. A profile or credential read failure currently rewrites the selection to Local, so a temporary storage failure can change execution authority. Profile and credential mutations also have commit points that can leave one side updated without enough metadata to recover the other.

I would keep the catalog, root pinning, credential binding, and per-target lifecycle. The smallest coherent revision is narrower:

  • Give each target generation an epoch. Commands must fail when their epoch is gone. Reconnectable reads may wait or retry only within the same epoch, and renderer reads must check the epoch before committing.
  • Split Client-local cwd from Host-owned execution location. Remote paths must not reach Client realpath, autocomplete, foreign-session scanning, or native capabilities unless the registered offer explicitly permits Host path access.
  • Keep a failed remote selection in an unavailable state. Persist Local only after an explicit user choice.
  • Keep remote Project policy root-scoped. Do not reuse the Client-global default, and hide workspace actions that remote main will reject.
  • Make profile and credential updates recoverable. Do not discard target metadata after a cleanup failure, and finish fallible preparation before committing profile metadata.

The cleaner end state is one Desktop owner publishing {epoch, target, readiness, candidate}. Boot, profile service, IPC, runtime policy, and renderer should consume that snapshot instead of storing separate versions of the active target. Execution locations should also be a discriminated type, so only a client-path can reach Client filesystem APIs.

I left inline comments at the smallest code sites that expose these boundaries.

The current red typecheck and workspace checks appear to reproduce on the base and are not attributed to this diff. The branch is still not green, but those failures are separate from the points above.

Disclosure: Codex performed the code inspection and adversarial analysis using several focused review passes. I (Astro-Han) reviewed and challenged the analysis over multiple rounds, selected the issues included here, and own the final review. I have not independently rerun every reproduction.

Comment threadapps/desktop/src/main/runtime-host-desktop-owner.ts Outdated
Comment threadapps/desktop/src/renderer/app-shell-effects.ts
Comment threadapps/desktop/src/main/runtime-host-native-capabilities.ts Outdated
Comment threadpackages/cli/src/runtime-host-session-driver.ts
Comment threadapps/desktop/src/main/runtime-host-profile-service.ts
Comment threadpackages/runtime-host/src/client/host-profile.ts Outdated

@liugddxliugddx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Follow-up adversarial review with findings anchored to the smallest changed lines. These complement the consolidated review and avoid duplicating existing inline comments.

简体中文

补充对抗性审查:以下问题锚定到最小变更行,作为汇总评论的可逐项 resolve 版本,并避免重复已有行级评论。

Comment threadpackages/cli/src/runtime-host-tui-context.ts
Comment threadapps/desktop/src/main/runtime-host-profile-service.ts Outdated
Comment threadapps/desktop/src/main/runtime-host-profile-service.ts Outdated
Comment threadpackages/cli/src/__tests__/runtime-host-run-command.test.ts Outdated
Comment threadapps/desktop/src/main/__tests__/runtime-host-desktop-owner.test.ts Outdated
Comment threadpackages/runtime-host/src/__tests__/host-profile.test.ts Outdated
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from e6857cb to 51be16bCompareAugust 12, 2026 03:12
@M4n5ter

Copy link
Copy Markdown
MemberAuthor

Addressed both review passes at 51be16b4d and rebased onto the latest main.

The revision now:

  • fences Desktop requests, results, and observations by target generation;
  • separates desired selection from the actually active Host, including the no-active-Host state;
  • keeps remote paths Host-owned across Desktop, CLI, TUI, and Client Capability calls;
  • makes profile targets immutable and credentials exact-target bound with recoverable mutation ordering;
  • removes the low-value remote-resume and object-identity tests called out in review.

Local validation passed: Runtime Host coverage (the only stale-build failure passed after rebuilding its updated dependency), CLI 233/233, Desktop 758/758, all three workspace typechecks, Biome, and git diff --check.

Could you please re-review the current head?

@Astro-HanAstro-Han 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.

The previous generation, path-authority, active-state, and credential-isolation findings are materially addressed in 51be16b4d.

Requests and results are now fenced by target epoch; remote Host paths no longer reach Client filesystem or capability APIs; rollback can represent no active Host; preference-write failure keeps the healthy Host active; and credentials are bound and tested against the exact immutable target. The focused tests now exercise the production boundaries rather than object identity or injected fakes.

I think the two remaining findings share one root cause rather than being isolated guards: the Runtime Host authority model has been updated, but two Client-facing boundaries still encode the former assumption that a valid local state always exists.

At startup, an unreadable selection is represented as Local because the resolution type cannot express “the authority is unknown.” In the project UI, the absence of a Client-visible path is interpreted as an unavailable Project, while Client-local actions remain visible, because the renderer infers capabilities from the old local Project representation instead of consuming capabilities published by the active target.

This creates the same failure in two forms: an unavailable or intentionally absent Client-local value is converted into Local authority instead of remaining an explicit unavailable or Host-owned state. The result is either a silent authority change or UI actions that the active Host cannot support.

From first principles, an unknown selection must not become an execution target, and the renderer should only expose operations granted by the active authority. I would therefore fix these at their natural boundaries rather than adding individual if (remote) checks:

  • Make startup resolution a closed ready | unavailable result. Only ready may contain a target; choosing Local should be a separate explicit transition.
  • Publish a small project-capability projection for the active target, covering directory actions, no-project allowance, local defaults, and path visibility. The renderer should consume that projection rather than infer authority from profile.kind, preferredPath, or failed calls.

I found two concrete consequences of the incomplete boundary:

  1. A failure to read the selection file still synthesizes Local as the selected target, and the startup recovery dialog skips that Local-shaped failure. An existing remote selection can therefore become Local execution authority without an explicit user choice.
  2. The remote Desktop project projection still exposes Client-local actions and permits “No project,” although remote Session creation requires a Host Project. The Settings view also interprets intentionally hidden Host paths as unavailable Projects.

These look like contained follow-ups to the Client state model, not reasons to revisit the profile catalog or Runtime Host architecture. Please feel free to push back if there is an existing authority or product contract that makes either state intentional.

Current CI is green. I am leaving this as a comment rather than Request Changes.

Disclosure: Codex performed the code inspection and adversarial analysis through four independent focused review passes. I (Astro-Han) reviewed the evidence, selected the findings included here, and made the final decision to submit this review. I did not independently rerun every reproduction.

Comment threadapps/desktop/src/main/runtime-host-profile-service.ts
Comment threadapps/desktop/src/main/project-management-service.ts
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from 51be16b to 3f7c664CompareAugust 12, 2026 04:24
@M4n5ter

Copy link
Copy Markdown
MemberAuthor

Addressed the latest authority-boundary review in 3f7c66418 and rebased onto current main.

Startup selection now remains explicitly unavailable on read/decode failure until the user chooses Local. The active target also publishes one Project capability snapshot consumed by Main and renderer surfaces, so remote Hosts no longer expose unsupported Client-directory, no-Project, local-default, or path-based behavior.

Local validation passed: UI 113/113, Desktop 760/760, focused regressions 15/15, Desktop/UI typechecks, Biome, and diff checks. All addressed review threads are resolved.

@Astro-Han@liugddx, could you please re-review the current head?

@M4n5ter
M4n5ter requested a review from Astro-HanAugust 12, 2026 04:25

@Astro-HanAstro-Han 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.

The two findings from my previous round are materially fixed in 3f7c66418. Startup resolution is now a closed ready | unavailable result, and Project capabilities are published by the active target and consumed by the main picker and Settings surfaces. CI is green and the PR is mergeable.

I also asked six fresh reviewers to inspect the entire PR independently, without relying on the previous findings. The core architecture held up well:

  • Runtime Host remains the sole execution authority.
  • Desired profile selection and the actually active target are separate.
  • Requests, results, observations, and Client Capability calls are fenced by target epoch.
  • Remote Host paths no longer enter Client filesystem authority.
  • Credentials are bound to an exact immutable target and remain outside profile documents.
  • Desktop, CLI, and TUI share one profile catalog rather than introducing another discovery or execution path.

The remaining observations mostly reduce to one architectural seam rather than a list of unrelated defects:

Target generation is now the authority for IPC, but it is not yet the single state model consumed by reconnect recovery, renderer projections, execution-location UI, and Project resolution.

For example, the owner knows when a target is connecting, ready, reconnecting, or unavailable, while consumers generally receive only a successful-activation invalidation. This leaves old projections visible during a switch, does not refresh all global projections after a same-target reconnect, and lets some renderer surfaces continue exposing local-path actions. Project identity is similarly canonical on the Host, but Desktop, CLI, and TUI still implement separate ID/alias and cwd rules.

I think the clean end state is one target-state projection owned by the Desktop target owner:

typeClientTargetState=|{readiness: "connecting";epoch: string;desiredProfileId: string;activeTarget?: ResolvedTarget;}|{readiness: "ready";epoch: string;desiredProfileId: string;activeTarget: ResolvedTarget;projectCapabilities: ProjectCapabilities;location: ExecutionLocation;}|{readiness: "unavailable";desiredProfileId: string;error: Error;};

Renderer invalidation, reconnect resynchronization, profile status, local-path actions, and the visible Host/execution location can then consume the same fact instead of inferring it from cached data, profile.kind, or a bare cwd.

The other independent concern is persistence. Profile metadata, selection, credential storage, and the directory lock currently have multiple durable commit points without automatic crash recovery. I would define one visible commit point, keep reads independent of the mutation lock where safe, bound all renderer-to-main input, and make credential replacement an explicit operation on an immutable target.

This is a large PR—82 files and roughly 5.3k changed lines—but I would not split it by Runtime Host, Desktop, CLI, and TUI. Those pieces form one authority change, and independently mergeable layers would create temporary incomplete states. I would instead shrink it through consolidation:

  • publish one target-state projection rather than adding more renderer-specific invalidations;
  • share one Project ID/alias resolver across Desktop, CLI run, and TUI;
  • represent execution location as client_path | host_project, allowing only the first to reach Client filesystem APIs;
  • remove the second location inference from the raw hostProfileId after the profile has already been resolved;
  • remove the write lock from safe read-only resolution and remove the redundant post-rename chmod.

I left three inline comments at the natural owner boundaries rather than filing every downstream symptom separately. All are non-blocking P2 observations. I’m approving the current revision; please feel free to push back if an existing invariant or recovery guarantee changes this analysis.

AI-assisted review disclosure

Codex performed the code inspection and six independent adversarial review passes. I, Astro-Han, reviewed and challenged the evidence, grouped the findings by their underlying invariants, selected the comments included here, and made the final decision to approve. I did not independently rerun every failure sequence described below.

中文对照

上一轮提出的两个问题已经在 3f7c66418 中得到实质修复:启动解析现在是闭合的 ready | unavailable 状态;Project capability 由 active target 发布,并被主项目选择器和设置页面消费。CI 全绿,PR 当前可以合并。

我又安排了六路互相独立的 fresh-eye 对抗性复审。整体架构经受住了审查:Runtime Host 仍是唯一执行权威;desired profile 与 active target 已分离;request、result、observation 和 Client Capability 都有 epoch fencing;远程 Host path 不再进入 Client 文件系统权限;credential 与精确且不可变的 target 绑定;Desktop、CLI、TUI 共用同一个 profile catalog。

剩余问题大多来自同一个架构边界:

target generation 已经是 IPC 的 authority,但还没有成为 reconnect、renderer projection、execution-location UI 和 Project resolution 共同消费的唯一状态模型。

最干净的终态是让 Desktop target owner 发布一个包含 epoch、desired profile、active target、readiness、Project capability 和 execution location 的闭合状态。Renderer 清理与刷新、重连恢复、profile 状态、本地路径操作和 Host/location 展示都消费这个事实,不再从旧缓存、profile.kind 或裸 cwd 推断。

另一个相对独立的问题是 persistence:profile metadata、selection、credential store 和目录锁目前存在多个 durable commit point,但没有自动的崩溃恢复协议。建议明确唯一可见提交点,让安全的只读解析不受 mutation lock 阻塞,统一限制 renderer-to-main 输入大小,并把 credential replacement 建模为 immutable target 上的独立操作。

这个 PR 的确很大,但不建议按 Runtime Host、Desktop、CLI、TUI 拆开,因为它们共同组成一个 authority change,分开合并会制造不安全的中间状态。更合适的是通过合并重复规则和删除平行推断来缩小复杂度。

下面只在三个自然 owner 上留下 inline comments,不把同一根因展开成十个局部问题。它们都是非阻塞的 P2。我会批准当前版本;如果已有不变量或恢复保证能够推翻这些分析,也欢迎作者直接说明或讨论。

AI 辅助审查披露

Codex 负责代码检查和六路独立的对抗性复审。Astro-Han 复核并质疑了相关证据,按共同不变量归并 findings,选择了最终评论,并作出批准决定。Astro-Han 没有独立重新执行下面描述的每一个失败序列。

Comment threadapps/desktop/src/main/runtime-host-desktop-owner.ts
Comment threadpackages/cli/src/runtime-host-run-command.ts Outdated
Comment threadpackages/runtime-host/src/client/host-profile.ts Outdated
Introduce a shared profile catalog for Desktop, CLI, and TUI, with credentials bound to an exact Host target. Desktop can switch targets live while keeping reconnect and session observation ownership isolated per target generation.
Remote targets operate through Project identities and do not inherit Client-local filesystem authority.
Keep Desktop projections, project policy, and rollback state aligned with the Runtime Host that is actually active. Remote clients no longer interpret Host-owned paths as local filesystem authority, including TUI and non-interactive resume flows.
Keep regression coverage at the public catalog and CLI boundaries while removing assertions that freeze internal credential key encoding and generic parser plumbing. Replace the stateful CLI fake with a capture-only dependency so the test reflects command ownership.
Represent remote connectivity as a discriminated TLS transport and bind credentials to that transport identity. This preserves a clear extension point for SSH and explicitly acknowledged insecure transports without ambiguous URL or boolean combinations.
Fence Desktop requests and projections by target generation, and represent desired selection independently from the active Host. Keep remote paths Host-owned across Desktop, CLI, TUI, and Client Capability calls.\n\nMake profile targets immutable and credentials exact-target bound so partial mutations remain explicit and recoverable without cleanup heuristics.
Keep unreadable Runtime Host selections unavailable until the user explicitly chooses Local. Publish target-scoped Project capabilities so renderer surfaces expose only actions supported by the active Host.
Publish target lifecycle transitions so Desktop clears stale projections during switches and reconnects. Canonicalize Project aliases across clients, and make the atomic profile document the visible persistence commit while keeping safe reads independent of writer serialization.
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from 3f7c664 to 112cef2CompareAugust 12, 2026 05:28
@M4n5ter

M4n5ter commented Aug 12, 2026

Copy link
Copy Markdown
MemberAuthor

Addressed the three follow-up P2 threads in 112cef2f7 and rebased onto current main.

  • Desktop now publishes the complete target lifecycle, clears stale Host projections before readiness, and refreshes them after activation or same-target reconnect
  • Project ID/alias resolution is shared across Host, Desktop, CLI run, and TUI, with Clients forwarding the canonical Project ID
  • Profile reads no longer wait on mutation locks; the atomic profile document is the visible commit point; credential input is bounded; and the redundant post-rename chmod is removed

I kept the existing fail-loud writer-lock policy instead of auto-stealing stale directories. The inline response records the TOCTOU reason and the resulting recovery contract.

Local validation after rebasing: all four affected workspace typechecks passed, the 52 focused boundary tests passed, Biome and diff checks passed. Before the rebase, the complete Runtime Host, CLI, and Desktop test suites also passed. CI is green on the rebased head.

@M4n5ter
M4n5ter merged commit 5df53c1 into mainAug 12, 2026
11 checks passed
@M4n5ter
M4n5ter deleted the feat/runtime-host-profiles branch August 12, 2026 05:45
@Astro-Han

Copy link
Copy Markdown
Contributor

A small post-merge follow-up from a fresh review of the final head: I found two P2 issues that may be worth addressing in a focused patch.

  1. Same-target reconnect currently clears Client-owned session and composer state.

    The owner publishes reconnecting → ready within the same target epoch, but the renderer performs a full Runtime Host reset for both events. That clears the active Session, messages, pending attachments, and pending quotes. A normal remote-network interruption can therefore return the user to the new-session screen and discard unsent composer state even though the execution authority did not change.

    The narrow fix is to reserve the full reset for an actual epoch/target-generation change. A same-epoch reconnect should pause and refresh Host-owned projections while preserving the active Session intent and Client-owned drafts.

  2. Some remote surfaces still expose Client-local path actions that Main will reject.

    The Project picker and Projects Settings now consume the target capability snapshot, but the title-bar/command-palette “Open Project Folder” actions and the Skills actions for opening local files/folders or importing a local source remain visible on a remote target. These are normally reachable controls that deterministically fail at the Main boundary.

    It would be cleaner for these surfaces to consume the same active-target local-path capability rather than adding independent remote checks.

Neither issue changes the core Runtime Host authority model, exact-target credential binding, or the merge decision. They look suitable for one focused follow-up rather than reopening the profile architecture.

Please feel free to push back if reconnect is intentionally defined as a full Client workspace reset, or if these local-path actions have another capability source I missed.

AI-assisted review disclosure

Codex performed the post-merge code inspection and delegated adversarial review. Astro-Han reviewed the synthesized evidence, recalibrated the findings to P2, and decided which follow-ups to include. The reproduction paths remain AI-assisted and should be verified while implementing the follow-up.

中文对照(默认折叠)

在最终 head 合并后的 fresh review 中,发现两个可以考虑用独立小补丁处理的 P2:

  1. 同一 target 的重连目前会清除 Client-owned Session 和 composer 状态。reconnecting → ready 没有改变 epoch,但 renderer 对两个事件都执行完整 reset,因此普通网络中断可能清除当前 Session、未发送附件和引用。建议只在 epoch/target generation 真正改变时完整 reset;同 epoch reconnect 只暂停和刷新 Host projection。

  2. 部分 remote surface 仍显示 Main 必然拒绝的 Client-local path 操作。Project picker 和 Projects Settings 已消费 capability snapshot,但标题栏/命令面板的 Open Project Folder,以及 Skills 中打开本地文件、文件夹和导入本地来源的操作仍然可见。建议让这些 surface 消费同一个 active-target local-path capability。

这两个问题不改变 Runtime Host 的核心 authority、exact-target credential binding 或此前的合并判断,适合作为一个 focused follow-up,而不是重新调整 profile 架构。

如果 reconnect 本来就被定义为完整 Client workspace reset,或者这些本地路径操作还有其他 capability 来源,也欢迎直接 push back。

本次由 Codex 完成合并后代码检查和多路对抗性复审;Astro-Han 复核综合证据、将问题重新校准为 P2,并决定纳入哪些 follow-up。具体复现路径仍属于 AI-assisted analysis,建议在实现 follow-up 时再次验证。

@liugddx

Copy link
Copy Markdown
Member

There is one reconnect boundary worth handling in a focused follow-up PR.

runtime-host-profiles:changed carries a target epoch, but the renderer handler currently clears state and starts fire-and-forget refreshes without retaining or checking that epoch. The individual refresh functions also commit unconditionally after await. Because a same-target reconnect keeps the same target epoch, a read that started on candidate A can complete after candidate B is ready and overwrite B's newer projection.

A minimal fix would give each candidate replacement its own generation and fence Host-scoped refresh commits against it, either at the IPC result boundary or in the renderer. The regression should hold an A read pending, reconnect and complete B's refresh, then release A and verify that A cannot replace B's state.

This does not require reverting this PR or reopening the broader profile design. A small follow-up PR covering the generation fence and one behavioral test should be enough.

简体中文

建议用一个聚焦的后续 PR 处理 remaining reconnect 边界。

runtime-host-profiles:changed 虽然携带 target epoch,但 renderer handler 当前只是清空状态并触发一组 fire-and-forget refresh,没有保存或校验该 epoch。各 refresh 在 await 后也会无条件提交。由于 same-target reconnect 复用同一个 target epoch,candidate A 上已经开始的读取可能在 candidate B ready 并完成刷新之后才返回,从而用 A 的旧投影覆盖 B。

最小修复是为每次 candidate replacement 增加独立 generation,并在 IPC result 边界或 renderer commit 前校验 generation。回归测试应挂起 A 的读取,让 B 重连并完成刷新,再释放 A,确认 A 不能覆盖 B 的状态。

这不需要回滚本 PR,也不需要重新展开整个 profile 设计。单独开一个小 PR,完成 generation fence 和一条行为测试即可。

Sign up for freeto 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.

3 participants

@M4n5ter@Astro-Han@liugddx
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(runtime-host): add client-selectable host profiles - #2834

Merged
M4n5ter merged 7 commits into
mainfrom
feat/runtime-host-profiles
Aug 12, 2026
Merged

feat(runtime-host): add client-selectable host profiles#2834
M4n5ter merged 7 commits into
mainfrom
feat/runtime-host-profiles

Conversation

@M4n5ter

@M4n5terM4n5ter commented Aug 11, 2026

Copy link
Copy Markdown
Member
English

Summary

  • add a shared Runtime Host profile catalog for Desktop, CLI, and TUI
  • let Desktop switch between Local and remote Runtime Hosts without restarting
  • route remote work through Host-owned Project identities instead of Client-local paths
  • bind each access credential to an exact profile target and keep it outside profile documents
  • reset Host-owned Client projections at the target-generation boundary

Why

Runtime Host is now the execution authority, but Clients could still only select the implicit local Host. This change introduces the smallest shared target model needed for future remote Hosts while preserving a strict boundary: a remote Host path is never treated as a local Client path.

Each Desktop target generation owns its reconnect lifecycle, Session observations, Project policy, and local-path permissions. Switching reports the Host that is actually active even if selection persistence and rollback both fail. CLI and TUI omit Client-side cwd relocation for remote Sessions.

User impact

Desktop users can manage and switch Runtime Host profiles from Workspace settings. CLI and TUI users can select the same profiles with --host and select an existing remote Project with --project. Credentials are accepted through secure storage or environment input, never command-line arguments or profile JSON.

Validation

  • Runtime Host, Storage, CLI, and Desktop typechecks
  • focused Desktop, CLI/TUI, Runtime Host, and Storage regressions: 130 passed
  • complete production build
  • Biome and git diff --check

Refs #2522

简体中文

概要

  • 为 Desktop、CLI 和 TUI 增加共享的 Runtime Host profile catalog
  • Desktop 无需重启即可在 Local 与远程 Runtime Host 之间切换
  • 远程工作通过 Host 持有的 Project identity 路由,不再依赖 Client 本地路径
  • access credential 与精确的 profile target 绑定,并与 profile 文档分离保存
  • 在 target generation 边界清空并重载 Host-owned Client projections

原因

Runtime Host 已成为执行权威,但 Client 仍只能使用隐式的本地 Host。本次变更增加未来远程 Host 所需的最小共享目标模型,同时保留严格边界:远程 Host 路径绝不会被当作 Client 本地路径使用。

Desktop 的每个 target generation 独立持有 reconnect lifecycle、Session observations、Project policy 与本地路径权限。即使 selection 持久化与回滚同时失败,切换结果仍会报告实际活跃的 Host。CLI 与 TUI 不会对远程 Session 执行 Client 侧 cwd relocation。

用户影响

Desktop 用户可以在工作区设置中管理和切换 Runtime Host profiles。CLI 和 TUI 用户可以通过 --host 选择同一套 profiles,并通过 --project 选择已有的远程 Project。Credential 仅通过安全存储或环境变量输入,不会进入命令行参数或 profile JSON。

验证

  • Runtime Host、Storage、CLI 与 Desktop typecheck
  • Desktop、CLI/TUI、Runtime Host 与 Storage 定向回归:130 项通过
  • 完整 production build
  • Biome 与 git diff --check

关联 #2522

@M4n5ter
M4n5ter marked this pull request as ready for review August 12, 2026 00:35
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from 50f0923 to c71fa13CompareAugust 12, 2026 00:45

@Astro-HanAstro-Han 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.

The profile catalog itself looks sound. It keeps connection configuration on the Client, preserves the Runtime Host as the execution authority, and does not add another discovery or spawn path.

I think the remaining problems come from three missing boundaries rather than a collection of unrelated bugs.

  1. A target switch has no identity that follows its requests and results. Reconnecting to Host A and switching from A to B both look like a temporary handler gap. Commands can wait for B's handler, while reads started on A can complete after the switch and overwrite B's renderer state.

  2. Host-owned and Client-owned locations share the same representation. A plain cwd may be treated as a Host execution path, a Client filesystem path, a display value, or an autocomplete root. This breaks remote resume and also lets a remote capability frame supply a Client-local cwd despite advertising hostPathAccess: "none". Bare Project IDs have the same ownership problem when a global default is reused across Host roots.

  3. Desired profile selection, active Host readiness, and persisted credential state are mixed together. A profile or credential read failure currently rewrites the selection to Local, so a temporary storage failure can change execution authority. Profile and credential mutations also have commit points that can leave one side updated without enough metadata to recover the other.

I would keep the catalog, root pinning, credential binding, and per-target lifecycle. The smallest coherent revision is narrower:

  • Give each target generation an epoch. Commands must fail when their epoch is gone. Reconnectable reads may wait or retry only within the same epoch, and renderer reads must check the epoch before committing.
  • Split Client-local cwd from Host-owned execution location. Remote paths must not reach Client realpath, autocomplete, foreign-session scanning, or native capabilities unless the registered offer explicitly permits Host path access.
  • Keep a failed remote selection in an unavailable state. Persist Local only after an explicit user choice.
  • Keep remote Project policy root-scoped. Do not reuse the Client-global default, and hide workspace actions that remote main will reject.
  • Make profile and credential updates recoverable. Do not discard target metadata after a cleanup failure, and finish fallible preparation before committing profile metadata.

The cleaner end state is one Desktop owner publishing {epoch, target, readiness, candidate}. Boot, profile service, IPC, runtime policy, and renderer should consume that snapshot instead of storing separate versions of the active target. Execution locations should also be a discriminated type, so only a client-path can reach Client filesystem APIs.

I left inline comments at the smallest code sites that expose these boundaries.

The current red typecheck and workspace checks appear to reproduce on the base and are not attributed to this diff. The branch is still not green, but those failures are separate from the points above.

Disclosure: Codex performed the code inspection and adversarial analysis using several focused review passes. I (Astro-Han) reviewed and challenged the analysis over multiple rounds, selected the issues included here, and own the final review. I have not independently rerun every reproduction.

Comment threadapps/desktop/src/main/runtime-host-desktop-owner.ts Outdated
Comment threadapps/desktop/src/renderer/app-shell-effects.ts
Comment threadapps/desktop/src/main/runtime-host-native-capabilities.ts Outdated
Comment threadpackages/cli/src/runtime-host-session-driver.ts
Comment threadapps/desktop/src/main/runtime-host-profile-service.ts
Comment threadpackages/runtime-host/src/client/host-profile.ts Outdated

@liugddxliugddx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Follow-up adversarial review with findings anchored to the smallest changed lines. These complement the consolidated review and avoid duplicating existing inline comments.

简体中文

补充对抗性审查:以下问题锚定到最小变更行,作为汇总评论的可逐项 resolve 版本,并避免重复已有行级评论。

Comment threadpackages/cli/src/runtime-host-tui-context.ts
Comment threadapps/desktop/src/main/runtime-host-profile-service.ts Outdated
Comment threadapps/desktop/src/main/runtime-host-profile-service.ts Outdated
Comment threadpackages/cli/src/__tests__/runtime-host-run-command.test.ts Outdated
Comment threadapps/desktop/src/main/__tests__/runtime-host-desktop-owner.test.ts Outdated
Comment threadpackages/runtime-host/src/__tests__/host-profile.test.ts Outdated
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from e6857cb to 51be16bCompareAugust 12, 2026 03:12
@M4n5ter

Copy link
Copy Markdown
MemberAuthor

Addressed both review passes at 51be16b4d and rebased onto the latest main.

The revision now:

  • fences Desktop requests, results, and observations by target generation;
  • separates desired selection from the actually active Host, including the no-active-Host state;
  • keeps remote paths Host-owned across Desktop, CLI, TUI, and Client Capability calls;
  • makes profile targets immutable and credentials exact-target bound with recoverable mutation ordering;
  • removes the low-value remote-resume and object-identity tests called out in review.

Local validation passed: Runtime Host coverage (the only stale-build failure passed after rebuilding its updated dependency), CLI 233/233, Desktop 758/758, all three workspace typechecks, Biome, and git diff --check.

Could you please re-review the current head?

@Astro-HanAstro-Han 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.

The previous generation, path-authority, active-state, and credential-isolation findings are materially addressed in 51be16b4d.

Requests and results are now fenced by target epoch; remote Host paths no longer reach Client filesystem or capability APIs; rollback can represent no active Host; preference-write failure keeps the healthy Host active; and credentials are bound and tested against the exact immutable target. The focused tests now exercise the production boundaries rather than object identity or injected fakes.

I think the two remaining findings share one root cause rather than being isolated guards: the Runtime Host authority model has been updated, but two Client-facing boundaries still encode the former assumption that a valid local state always exists.

At startup, an unreadable selection is represented as Local because the resolution type cannot express “the authority is unknown.” In the project UI, the absence of a Client-visible path is interpreted as an unavailable Project, while Client-local actions remain visible, because the renderer infers capabilities from the old local Project representation instead of consuming capabilities published by the active target.

This creates the same failure in two forms: an unavailable or intentionally absent Client-local value is converted into Local authority instead of remaining an explicit unavailable or Host-owned state. The result is either a silent authority change or UI actions that the active Host cannot support.

From first principles, an unknown selection must not become an execution target, and the renderer should only expose operations granted by the active authority. I would therefore fix these at their natural boundaries rather than adding individual if (remote) checks:

  • Make startup resolution a closed ready | unavailable result. Only ready may contain a target; choosing Local should be a separate explicit transition.
  • Publish a small project-capability projection for the active target, covering directory actions, no-project allowance, local defaults, and path visibility. The renderer should consume that projection rather than infer authority from profile.kind, preferredPath, or failed calls.

I found two concrete consequences of the incomplete boundary:

  1. A failure to read the selection file still synthesizes Local as the selected target, and the startup recovery dialog skips that Local-shaped failure. An existing remote selection can therefore become Local execution authority without an explicit user choice.
  2. The remote Desktop project projection still exposes Client-local actions and permits “No project,” although remote Session creation requires a Host Project. The Settings view also interprets intentionally hidden Host paths as unavailable Projects.

These look like contained follow-ups to the Client state model, not reasons to revisit the profile catalog or Runtime Host architecture. Please feel free to push back if there is an existing authority or product contract that makes either state intentional.

Current CI is green. I am leaving this as a comment rather than Request Changes.

Disclosure: Codex performed the code inspection and adversarial analysis through four independent focused review passes. I (Astro-Han) reviewed the evidence, selected the findings included here, and made the final decision to submit this review. I did not independently rerun every reproduction.

Comment threadapps/desktop/src/main/runtime-host-profile-service.ts
Comment threadapps/desktop/src/main/project-management-service.ts
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from 51be16b to 3f7c664CompareAugust 12, 2026 04:24
@M4n5ter

Copy link
Copy Markdown
MemberAuthor

Addressed the latest authority-boundary review in 3f7c66418 and rebased onto current main.

Startup selection now remains explicitly unavailable on read/decode failure until the user chooses Local. The active target also publishes one Project capability snapshot consumed by Main and renderer surfaces, so remote Hosts no longer expose unsupported Client-directory, no-Project, local-default, or path-based behavior.

Local validation passed: UI 113/113, Desktop 760/760, focused regressions 15/15, Desktop/UI typechecks, Biome, and diff checks. All addressed review threads are resolved.

@Astro-Han@liugddx, could you please re-review the current head?

@M4n5ter
M4n5ter requested a review from Astro-HanAugust 12, 2026 04:25

@Astro-HanAstro-Han 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.

The two findings from my previous round are materially fixed in 3f7c66418. Startup resolution is now a closed ready | unavailable result, and Project capabilities are published by the active target and consumed by the main picker and Settings surfaces. CI is green and the PR is mergeable.

I also asked six fresh reviewers to inspect the entire PR independently, without relying on the previous findings. The core architecture held up well:

  • Runtime Host remains the sole execution authority.
  • Desired profile selection and the actually active target are separate.
  • Requests, results, observations, and Client Capability calls are fenced by target epoch.
  • Remote Host paths no longer enter Client filesystem authority.
  • Credentials are bound to an exact immutable target and remain outside profile documents.
  • Desktop, CLI, and TUI share one profile catalog rather than introducing another discovery or execution path.

The remaining observations mostly reduce to one architectural seam rather than a list of unrelated defects:

Target generation is now the authority for IPC, but it is not yet the single state model consumed by reconnect recovery, renderer projections, execution-location UI, and Project resolution.

For example, the owner knows when a target is connecting, ready, reconnecting, or unavailable, while consumers generally receive only a successful-activation invalidation. This leaves old projections visible during a switch, does not refresh all global projections after a same-target reconnect, and lets some renderer surfaces continue exposing local-path actions. Project identity is similarly canonical on the Host, but Desktop, CLI, and TUI still implement separate ID/alias and cwd rules.

I think the clean end state is one target-state projection owned by the Desktop target owner:

typeClientTargetState=|{readiness: "connecting";epoch: string;desiredProfileId: string;activeTarget?: ResolvedTarget;}|{readiness: "ready";epoch: string;desiredProfileId: string;activeTarget: ResolvedTarget;projectCapabilities: ProjectCapabilities;location: ExecutionLocation;}|{readiness: "unavailable";desiredProfileId: string;error: Error;};

Renderer invalidation, reconnect resynchronization, profile status, local-path actions, and the visible Host/execution location can then consume the same fact instead of inferring it from cached data, profile.kind, or a bare cwd.

The other independent concern is persistence. Profile metadata, selection, credential storage, and the directory lock currently have multiple durable commit points without automatic crash recovery. I would define one visible commit point, keep reads independent of the mutation lock where safe, bound all renderer-to-main input, and make credential replacement an explicit operation on an immutable target.

This is a large PR—82 files and roughly 5.3k changed lines—but I would not split it by Runtime Host, Desktop, CLI, and TUI. Those pieces form one authority change, and independently mergeable layers would create temporary incomplete states. I would instead shrink it through consolidation:

  • publish one target-state projection rather than adding more renderer-specific invalidations;
  • share one Project ID/alias resolver across Desktop, CLI run, and TUI;
  • represent execution location as client_path | host_project, allowing only the first to reach Client filesystem APIs;
  • remove the second location inference from the raw hostProfileId after the profile has already been resolved;
  • remove the write lock from safe read-only resolution and remove the redundant post-rename chmod.

I left three inline comments at the natural owner boundaries rather than filing every downstream symptom separately. All are non-blocking P2 observations. I’m approving the current revision; please feel free to push back if an existing invariant or recovery guarantee changes this analysis.

AI-assisted review disclosure

Codex performed the code inspection and six independent adversarial review passes. I, Astro-Han, reviewed and challenged the evidence, grouped the findings by their underlying invariants, selected the comments included here, and made the final decision to approve. I did not independently rerun every failure sequence described below.

中文对照

上一轮提出的两个问题已经在 3f7c66418 中得到实质修复:启动解析现在是闭合的 ready | unavailable 状态;Project capability 由 active target 发布,并被主项目选择器和设置页面消费。CI 全绿,PR 当前可以合并。

我又安排了六路互相独立的 fresh-eye 对抗性复审。整体架构经受住了审查:Runtime Host 仍是唯一执行权威;desired profile 与 active target 已分离;request、result、observation 和 Client Capability 都有 epoch fencing;远程 Host path 不再进入 Client 文件系统权限;credential 与精确且不可变的 target 绑定;Desktop、CLI、TUI 共用同一个 profile catalog。

剩余问题大多来自同一个架构边界:

target generation 已经是 IPC 的 authority,但还没有成为 reconnect、renderer projection、execution-location UI 和 Project resolution 共同消费的唯一状态模型。

最干净的终态是让 Desktop target owner 发布一个包含 epoch、desired profile、active target、readiness、Project capability 和 execution location 的闭合状态。Renderer 清理与刷新、重连恢复、profile 状态、本地路径操作和 Host/location 展示都消费这个事实,不再从旧缓存、profile.kind 或裸 cwd 推断。

另一个相对独立的问题是 persistence:profile metadata、selection、credential store 和目录锁目前存在多个 durable commit point,但没有自动的崩溃恢复协议。建议明确唯一可见提交点,让安全的只读解析不受 mutation lock 阻塞,统一限制 renderer-to-main 输入大小,并把 credential replacement 建模为 immutable target 上的独立操作。

这个 PR 的确很大,但不建议按 Runtime Host、Desktop、CLI、TUI 拆开,因为它们共同组成一个 authority change,分开合并会制造不安全的中间状态。更合适的是通过合并重复规则和删除平行推断来缩小复杂度。

下面只在三个自然 owner 上留下 inline comments,不把同一根因展开成十个局部问题。它们都是非阻塞的 P2。我会批准当前版本;如果已有不变量或恢复保证能够推翻这些分析,也欢迎作者直接说明或讨论。

AI 辅助审查披露

Codex 负责代码检查和六路独立的对抗性复审。Astro-Han 复核并质疑了相关证据,按共同不变量归并 findings,选择了最终评论,并作出批准决定。Astro-Han 没有独立重新执行下面描述的每一个失败序列。

Comment threadapps/desktop/src/main/runtime-host-desktop-owner.ts
Comment threadpackages/cli/src/runtime-host-run-command.ts Outdated
Comment threadpackages/runtime-host/src/client/host-profile.ts Outdated
Introduce a shared profile catalog for Desktop, CLI, and TUI, with credentials bound to an exact Host target. Desktop can switch targets live while keeping reconnect and session observation ownership isolated per target generation.
Remote targets operate through Project identities and do not inherit Client-local filesystem authority.
Keep Desktop projections, project policy, and rollback state aligned with the Runtime Host that is actually active. Remote clients no longer interpret Host-owned paths as local filesystem authority, including TUI and non-interactive resume flows.
Keep regression coverage at the public catalog and CLI boundaries while removing assertions that freeze internal credential key encoding and generic parser plumbing. Replace the stateful CLI fake with a capture-only dependency so the test reflects command ownership.
Represent remote connectivity as a discriminated TLS transport and bind credentials to that transport identity. This preserves a clear extension point for SSH and explicitly acknowledged insecure transports without ambiguous URL or boolean combinations.
Fence Desktop requests and projections by target generation, and represent desired selection independently from the active Host. Keep remote paths Host-owned across Desktop, CLI, TUI, and Client Capability calls.\n\nMake profile targets immutable and credentials exact-target bound so partial mutations remain explicit and recoverable without cleanup heuristics.
Keep unreadable Runtime Host selections unavailable until the user explicitly chooses Local. Publish target-scoped Project capabilities so renderer surfaces expose only actions supported by the active Host.
Publish target lifecycle transitions so Desktop clears stale projections during switches and reconnects. Canonicalize Project aliases across clients, and make the atomic profile document the visible persistence commit while keeping safe reads independent of writer serialization.
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from 3f7c664 to 112cef2CompareAugust 12, 2026 05:28
@M4n5ter

M4n5ter commented Aug 12, 2026

Copy link
Copy Markdown
MemberAuthor

Addressed the three follow-up P2 threads in 112cef2f7 and rebased onto current main.

  • Desktop now publishes the complete target lifecycle, clears stale Host projections before readiness, and refreshes them after activation or same-target reconnect
  • Project ID/alias resolution is shared across Host, Desktop, CLI run, and TUI, with Clients forwarding the canonical Project ID
  • Profile reads no longer wait on mutation locks; the atomic profile document is the visible commit point; credential input is bounded; and the redundant post-rename chmod is removed

I kept the existing fail-loud writer-lock policy instead of auto-stealing stale directories. The inline response records the TOCTOU reason and the resulting recovery contract.

Local validation after rebasing: all four affected workspace typechecks passed, the 52 focused boundary tests passed, Biome and diff checks passed. Before the rebase, the complete Runtime Host, CLI, and Desktop test suites also passed. CI is green on the rebased head.

@M4n5ter
M4n5ter merged commit 5df53c1 into mainAug 12, 2026
11 checks passed
@M4n5ter
M4n5ter deleted the feat/runtime-host-profiles branch August 12, 2026 05:45
@Astro-Han

Copy link
Copy Markdown
Contributor

A small post-merge follow-up from a fresh review of the final head: I found two P2 issues that may be worth addressing in a focused patch.

  1. Same-target reconnect currently clears Client-owned session and composer state.

    The owner publishes reconnecting → ready within the same target epoch, but the renderer performs a full Runtime Host reset for both events. That clears the active Session, messages, pending attachments, and pending quotes. A normal remote-network interruption can therefore return the user to the new-session screen and discard unsent composer state even though the execution authority did not change.

    The narrow fix is to reserve the full reset for an actual epoch/target-generation change. A same-epoch reconnect should pause and refresh Host-owned projections while preserving the active Session intent and Client-owned drafts.

  2. Some remote surfaces still expose Client-local path actions that Main will reject.

    The Project picker and Projects Settings now consume the target capability snapshot, but the title-bar/command-palette “Open Project Folder” actions and the Skills actions for opening local files/folders or importing a local source remain visible on a remote target. These are normally reachable controls that deterministically fail at the Main boundary.

    It would be cleaner for these surfaces to consume the same active-target local-path capability rather than adding independent remote checks.

Neither issue changes the core Runtime Host authority model, exact-target credential binding, or the merge decision. They look suitable for one focused follow-up rather than reopening the profile architecture.

Please feel free to push back if reconnect is intentionally defined as a full Client workspace reset, or if these local-path actions have another capability source I missed.

AI-assisted review disclosure

Codex performed the post-merge code inspection and delegated adversarial review. Astro-Han reviewed the synthesized evidence, recalibrated the findings to P2, and decided which follow-ups to include. The reproduction paths remain AI-assisted and should be verified while implementing the follow-up.

中文对照(默认折叠)

在最终 head 合并后的 fresh review 中,发现两个可以考虑用独立小补丁处理的 P2:

  1. 同一 target 的重连目前会清除 Client-owned Session 和 composer 状态。reconnecting → ready 没有改变 epoch,但 renderer 对两个事件都执行完整 reset,因此普通网络中断可能清除当前 Session、未发送附件和引用。建议只在 epoch/target generation 真正改变时完整 reset;同 epoch reconnect 只暂停和刷新 Host projection。

  2. 部分 remote surface 仍显示 Main 必然拒绝的 Client-local path 操作。Project picker 和 Projects Settings 已消费 capability snapshot,但标题栏/命令面板的 Open Project Folder,以及 Skills 中打开本地文件、文件夹和导入本地来源的操作仍然可见。建议让这些 surface 消费同一个 active-target local-path capability。

这两个问题不改变 Runtime Host 的核心 authority、exact-target credential binding 或此前的合并判断,适合作为一个 focused follow-up,而不是重新调整 profile 架构。

如果 reconnect 本来就被定义为完整 Client workspace reset,或者这些本地路径操作还有其他 capability 来源,也欢迎直接 push back。

本次由 Codex 完成合并后代码检查和多路对抗性复审;Astro-Han 复核综合证据、将问题重新校准为 P2,并决定纳入哪些 follow-up。具体复现路径仍属于 AI-assisted analysis,建议在实现 follow-up 时再次验证。

@liugddx

Copy link
Copy Markdown
Member

There is one reconnect boundary worth handling in a focused follow-up PR.

runtime-host-profiles:changed carries a target epoch, but the renderer handler currently clears state and starts fire-and-forget refreshes without retaining or checking that epoch. The individual refresh functions also commit unconditionally after await. Because a same-target reconnect keeps the same target epoch, a read that started on candidate A can complete after candidate B is ready and overwrite B's newer projection.

A minimal fix would give each candidate replacement its own generation and fence Host-scoped refresh commits against it, either at the IPC result boundary or in the renderer. The regression should hold an A read pending, reconnect and complete B's refresh, then release A and verify that A cannot replace B's state.

This does not require reverting this PR or reopening the broader profile design. A small follow-up PR covering the generation fence and one behavioral test should be enough.

简体中文

建议用一个聚焦的后续 PR 处理 remaining reconnect 边界。

runtime-host-profiles:changed 虽然携带 target epoch,但 renderer handler 当前只是清空状态并触发一组 fire-and-forget refresh,没有保存或校验该 epoch。各 refresh 在 await 后也会无条件提交。由于 same-target reconnect 复用同一个 target epoch,candidate A 上已经开始的读取可能在 candidate B ready 并完成刷新之后才返回,从而用 A 的旧投影覆盖 B。

最小修复是为每次 candidate replacement 增加独立 generation,并在 IPC result 边界或 renderer commit 前校验 generation。回归测试应挂起 A 的读取,让 B 重连并完成刷新,再释放 A,确认 A 不能覆盖 B 的状态。

这不需要回滚本 PR,也不需要重新展开整个 profile 设计。单独开一个小 PR,完成 generation fence 和一条行为测试即可。

Sign up for freeto 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.

3 participants

@M4n5ter@Astro-Han@liugddx
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat(runtime-host): add client-selectable host profiles - #2834

Merged
M4n5ter merged 7 commits into
mainfrom
feat/runtime-host-profiles
Aug 12, 2026
Merged

feat(runtime-host): add client-selectable host profiles#2834
M4n5ter merged 7 commits into
mainfrom
feat/runtime-host-profiles

Conversation

@M4n5ter

@M4n5terM4n5ter commented Aug 11, 2026

Copy link
Copy Markdown
Member
English

Summary

  • add a shared Runtime Host profile catalog for Desktop, CLI, and TUI
  • let Desktop switch between Local and remote Runtime Hosts without restarting
  • route remote work through Host-owned Project identities instead of Client-local paths
  • bind each access credential to an exact profile target and keep it outside profile documents
  • reset Host-owned Client projections at the target-generation boundary

Why

Runtime Host is now the execution authority, but Clients could still only select the implicit local Host. This change introduces the smallest shared target model needed for future remote Hosts while preserving a strict boundary: a remote Host path is never treated as a local Client path.

Each Desktop target generation owns its reconnect lifecycle, Session observations, Project policy, and local-path permissions. Switching reports the Host that is actually active even if selection persistence and rollback both fail. CLI and TUI omit Client-side cwd relocation for remote Sessions.

User impact

Desktop users can manage and switch Runtime Host profiles from Workspace settings. CLI and TUI users can select the same profiles with --host and select an existing remote Project with --project. Credentials are accepted through secure storage or environment input, never command-line arguments or profile JSON.

Validation

  • Runtime Host, Storage, CLI, and Desktop typechecks
  • focused Desktop, CLI/TUI, Runtime Host, and Storage regressions: 130 passed
  • complete production build
  • Biome and git diff --check

Refs #2522

简体中文

概要

  • 为 Desktop、CLI 和 TUI 增加共享的 Runtime Host profile catalog
  • Desktop 无需重启即可在 Local 与远程 Runtime Host 之间切换
  • 远程工作通过 Host 持有的 Project identity 路由,不再依赖 Client 本地路径
  • access credential 与精确的 profile target 绑定,并与 profile 文档分离保存
  • 在 target generation 边界清空并重载 Host-owned Client projections

原因

Runtime Host 已成为执行权威,但 Client 仍只能使用隐式的本地 Host。本次变更增加未来远程 Host 所需的最小共享目标模型,同时保留严格边界:远程 Host 路径绝不会被当作 Client 本地路径使用。

Desktop 的每个 target generation 独立持有 reconnect lifecycle、Session observations、Project policy 与本地路径权限。即使 selection 持久化与回滚同时失败,切换结果仍会报告实际活跃的 Host。CLI 与 TUI 不会对远程 Session 执行 Client 侧 cwd relocation。

用户影响

Desktop 用户可以在工作区设置中管理和切换 Runtime Host profiles。CLI 和 TUI 用户可以通过 --host 选择同一套 profiles,并通过 --project 选择已有的远程 Project。Credential 仅通过安全存储或环境变量输入,不会进入命令行参数或 profile JSON。

验证

  • Runtime Host、Storage、CLI 与 Desktop typecheck
  • Desktop、CLI/TUI、Runtime Host 与 Storage 定向回归:130 项通过
  • 完整 production build
  • Biome 与 git diff --check

关联 #2522

@M4n5ter
M4n5ter marked this pull request as ready for review August 12, 2026 00:35
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from 50f0923 to c71fa13CompareAugust 12, 2026 00:45

@Astro-HanAstro-Han 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.

The profile catalog itself looks sound. It keeps connection configuration on the Client, preserves the Runtime Host as the execution authority, and does not add another discovery or spawn path.

I think the remaining problems come from three missing boundaries rather than a collection of unrelated bugs.

  1. A target switch has no identity that follows its requests and results. Reconnecting to Host A and switching from A to B both look like a temporary handler gap. Commands can wait for B's handler, while reads started on A can complete after the switch and overwrite B's renderer state.

  2. Host-owned and Client-owned locations share the same representation. A plain cwd may be treated as a Host execution path, a Client filesystem path, a display value, or an autocomplete root. This breaks remote resume and also lets a remote capability frame supply a Client-local cwd despite advertising hostPathAccess: "none". Bare Project IDs have the same ownership problem when a global default is reused across Host roots.

  3. Desired profile selection, active Host readiness, and persisted credential state are mixed together. A profile or credential read failure currently rewrites the selection to Local, so a temporary storage failure can change execution authority. Profile and credential mutations also have commit points that can leave one side updated without enough metadata to recover the other.

I would keep the catalog, root pinning, credential binding, and per-target lifecycle. The smallest coherent revision is narrower:

  • Give each target generation an epoch. Commands must fail when their epoch is gone. Reconnectable reads may wait or retry only within the same epoch, and renderer reads must check the epoch before committing.
  • Split Client-local cwd from Host-owned execution location. Remote paths must not reach Client realpath, autocomplete, foreign-session scanning, or native capabilities unless the registered offer explicitly permits Host path access.
  • Keep a failed remote selection in an unavailable state. Persist Local only after an explicit user choice.
  • Keep remote Project policy root-scoped. Do not reuse the Client-global default, and hide workspace actions that remote main will reject.
  • Make profile and credential updates recoverable. Do not discard target metadata after a cleanup failure, and finish fallible preparation before committing profile metadata.

The cleaner end state is one Desktop owner publishing {epoch, target, readiness, candidate}. Boot, profile service, IPC, runtime policy, and renderer should consume that snapshot instead of storing separate versions of the active target. Execution locations should also be a discriminated type, so only a client-path can reach Client filesystem APIs.

I left inline comments at the smallest code sites that expose these boundaries.

The current red typecheck and workspace checks appear to reproduce on the base and are not attributed to this diff. The branch is still not green, but those failures are separate from the points above.

Disclosure: Codex performed the code inspection and adversarial analysis using several focused review passes. I (Astro-Han) reviewed and challenged the analysis over multiple rounds, selected the issues included here, and own the final review. I have not independently rerun every reproduction.

Comment threadapps/desktop/src/main/runtime-host-desktop-owner.ts Outdated
Comment threadapps/desktop/src/renderer/app-shell-effects.ts
Comment threadapps/desktop/src/main/runtime-host-native-capabilities.ts Outdated
Comment threadpackages/cli/src/runtime-host-session-driver.ts
Comment threadapps/desktop/src/main/runtime-host-profile-service.ts
Comment threadpackages/runtime-host/src/client/host-profile.ts Outdated

@liugddxliugddx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Follow-up adversarial review with findings anchored to the smallest changed lines. These complement the consolidated review and avoid duplicating existing inline comments.

简体中文

补充对抗性审查:以下问题锚定到最小变更行,作为汇总评论的可逐项 resolve 版本,并避免重复已有行级评论。

Comment threadpackages/cli/src/runtime-host-tui-context.ts
Comment threadapps/desktop/src/main/runtime-host-profile-service.ts Outdated
Comment threadapps/desktop/src/main/runtime-host-profile-service.ts Outdated
Comment threadpackages/cli/src/__tests__/runtime-host-run-command.test.ts Outdated
Comment threadapps/desktop/src/main/__tests__/runtime-host-desktop-owner.test.ts Outdated
Comment threadpackages/runtime-host/src/__tests__/host-profile.test.ts Outdated
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from e6857cb to 51be16bCompareAugust 12, 2026 03:12
@M4n5ter

Copy link
Copy Markdown
MemberAuthor

Addressed both review passes at 51be16b4d and rebased onto the latest main.

The revision now:

  • fences Desktop requests, results, and observations by target generation;
  • separates desired selection from the actually active Host, including the no-active-Host state;
  • keeps remote paths Host-owned across Desktop, CLI, TUI, and Client Capability calls;
  • makes profile targets immutable and credentials exact-target bound with recoverable mutation ordering;
  • removes the low-value remote-resume and object-identity tests called out in review.

Local validation passed: Runtime Host coverage (the only stale-build failure passed after rebuilding its updated dependency), CLI 233/233, Desktop 758/758, all three workspace typechecks, Biome, and git diff --check.

Could you please re-review the current head?

@Astro-HanAstro-Han 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.

The previous generation, path-authority, active-state, and credential-isolation findings are materially addressed in 51be16b4d.

Requests and results are now fenced by target epoch; remote Host paths no longer reach Client filesystem or capability APIs; rollback can represent no active Host; preference-write failure keeps the healthy Host active; and credentials are bound and tested against the exact immutable target. The focused tests now exercise the production boundaries rather than object identity or injected fakes.

I think the two remaining findings share one root cause rather than being isolated guards: the Runtime Host authority model has been updated, but two Client-facing boundaries still encode the former assumption that a valid local state always exists.

At startup, an unreadable selection is represented as Local because the resolution type cannot express “the authority is unknown.” In the project UI, the absence of a Client-visible path is interpreted as an unavailable Project, while Client-local actions remain visible, because the renderer infers capabilities from the old local Project representation instead of consuming capabilities published by the active target.

This creates the same failure in two forms: an unavailable or intentionally absent Client-local value is converted into Local authority instead of remaining an explicit unavailable or Host-owned state. The result is either a silent authority change or UI actions that the active Host cannot support.

From first principles, an unknown selection must not become an execution target, and the renderer should only expose operations granted by the active authority. I would therefore fix these at their natural boundaries rather than adding individual if (remote) checks:

  • Make startup resolution a closed ready | unavailable result. Only ready may contain a target; choosing Local should be a separate explicit transition.
  • Publish a small project-capability projection for the active target, covering directory actions, no-project allowance, local defaults, and path visibility. The renderer should consume that projection rather than infer authority from profile.kind, preferredPath, or failed calls.

I found two concrete consequences of the incomplete boundary:

  1. A failure to read the selection file still synthesizes Local as the selected target, and the startup recovery dialog skips that Local-shaped failure. An existing remote selection can therefore become Local execution authority without an explicit user choice.
  2. The remote Desktop project projection still exposes Client-local actions and permits “No project,” although remote Session creation requires a Host Project. The Settings view also interprets intentionally hidden Host paths as unavailable Projects.

These look like contained follow-ups to the Client state model, not reasons to revisit the profile catalog or Runtime Host architecture. Please feel free to push back if there is an existing authority or product contract that makes either state intentional.

Current CI is green. I am leaving this as a comment rather than Request Changes.

Disclosure: Codex performed the code inspection and adversarial analysis through four independent focused review passes. I (Astro-Han) reviewed the evidence, selected the findings included here, and made the final decision to submit this review. I did not independently rerun every reproduction.

Comment threadapps/desktop/src/main/runtime-host-profile-service.ts
Comment threadapps/desktop/src/main/project-management-service.ts
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from 51be16b to 3f7c664CompareAugust 12, 2026 04:24
@M4n5ter

Copy link
Copy Markdown
MemberAuthor

Addressed the latest authority-boundary review in 3f7c66418 and rebased onto current main.

Startup selection now remains explicitly unavailable on read/decode failure until the user chooses Local. The active target also publishes one Project capability snapshot consumed by Main and renderer surfaces, so remote Hosts no longer expose unsupported Client-directory, no-Project, local-default, or path-based behavior.

Local validation passed: UI 113/113, Desktop 760/760, focused regressions 15/15, Desktop/UI typechecks, Biome, and diff checks. All addressed review threads are resolved.

@Astro-Han@liugddx, could you please re-review the current head?

@M4n5ter
M4n5ter requested a review from Astro-HanAugust 12, 2026 04:25

@Astro-HanAstro-Han 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.

The two findings from my previous round are materially fixed in 3f7c66418. Startup resolution is now a closed ready | unavailable result, and Project capabilities are published by the active target and consumed by the main picker and Settings surfaces. CI is green and the PR is mergeable.

I also asked six fresh reviewers to inspect the entire PR independently, without relying on the previous findings. The core architecture held up well:

  • Runtime Host remains the sole execution authority.
  • Desired profile selection and the actually active target are separate.
  • Requests, results, observations, and Client Capability calls are fenced by target epoch.
  • Remote Host paths no longer enter Client filesystem authority.
  • Credentials are bound to an exact immutable target and remain outside profile documents.
  • Desktop, CLI, and TUI share one profile catalog rather than introducing another discovery or execution path.

The remaining observations mostly reduce to one architectural seam rather than a list of unrelated defects:

Target generation is now the authority for IPC, but it is not yet the single state model consumed by reconnect recovery, renderer projections, execution-location UI, and Project resolution.

For example, the owner knows when a target is connecting, ready, reconnecting, or unavailable, while consumers generally receive only a successful-activation invalidation. This leaves old projections visible during a switch, does not refresh all global projections after a same-target reconnect, and lets some renderer surfaces continue exposing local-path actions. Project identity is similarly canonical on the Host, but Desktop, CLI, and TUI still implement separate ID/alias and cwd rules.

I think the clean end state is one target-state projection owned by the Desktop target owner:

typeClientTargetState=|{readiness: "connecting";epoch: string;desiredProfileId: string;activeTarget?: ResolvedTarget;}|{readiness: "ready";epoch: string;desiredProfileId: string;activeTarget: ResolvedTarget;projectCapabilities: ProjectCapabilities;location: ExecutionLocation;}|{readiness: "unavailable";desiredProfileId: string;error: Error;};

Renderer invalidation, reconnect resynchronization, profile status, local-path actions, and the visible Host/execution location can then consume the same fact instead of inferring it from cached data, profile.kind, or a bare cwd.

The other independent concern is persistence. Profile metadata, selection, credential storage, and the directory lock currently have multiple durable commit points without automatic crash recovery. I would define one visible commit point, keep reads independent of the mutation lock where safe, bound all renderer-to-main input, and make credential replacement an explicit operation on an immutable target.

This is a large PR—82 files and roughly 5.3k changed lines—but I would not split it by Runtime Host, Desktop, CLI, and TUI. Those pieces form one authority change, and independently mergeable layers would create temporary incomplete states. I would instead shrink it through consolidation:

  • publish one target-state projection rather than adding more renderer-specific invalidations;
  • share one Project ID/alias resolver across Desktop, CLI run, and TUI;
  • represent execution location as client_path | host_project, allowing only the first to reach Client filesystem APIs;
  • remove the second location inference from the raw hostProfileId after the profile has already been resolved;
  • remove the write lock from safe read-only resolution and remove the redundant post-rename chmod.

I left three inline comments at the natural owner boundaries rather than filing every downstream symptom separately. All are non-blocking P2 observations. I’m approving the current revision; please feel free to push back if an existing invariant or recovery guarantee changes this analysis.

AI-assisted review disclosure

Codex performed the code inspection and six independent adversarial review passes. I, Astro-Han, reviewed and challenged the evidence, grouped the findings by their underlying invariants, selected the comments included here, and made the final decision to approve. I did not independently rerun every failure sequence described below.

中文对照

上一轮提出的两个问题已经在 3f7c66418 中得到实质修复:启动解析现在是闭合的 ready | unavailable 状态;Project capability 由 active target 发布,并被主项目选择器和设置页面消费。CI 全绿,PR 当前可以合并。

我又安排了六路互相独立的 fresh-eye 对抗性复审。整体架构经受住了审查:Runtime Host 仍是唯一执行权威;desired profile 与 active target 已分离;request、result、observation 和 Client Capability 都有 epoch fencing;远程 Host path 不再进入 Client 文件系统权限;credential 与精确且不可变的 target 绑定;Desktop、CLI、TUI 共用同一个 profile catalog。

剩余问题大多来自同一个架构边界:

target generation 已经是 IPC 的 authority,但还没有成为 reconnect、renderer projection、execution-location UI 和 Project resolution 共同消费的唯一状态模型。

最干净的终态是让 Desktop target owner 发布一个包含 epoch、desired profile、active target、readiness、Project capability 和 execution location 的闭合状态。Renderer 清理与刷新、重连恢复、profile 状态、本地路径操作和 Host/location 展示都消费这个事实,不再从旧缓存、profile.kind 或裸 cwd 推断。

另一个相对独立的问题是 persistence:profile metadata、selection、credential store 和目录锁目前存在多个 durable commit point,但没有自动的崩溃恢复协议。建议明确唯一可见提交点,让安全的只读解析不受 mutation lock 阻塞,统一限制 renderer-to-main 输入大小,并把 credential replacement 建模为 immutable target 上的独立操作。

这个 PR 的确很大,但不建议按 Runtime Host、Desktop、CLI、TUI 拆开,因为它们共同组成一个 authority change,分开合并会制造不安全的中间状态。更合适的是通过合并重复规则和删除平行推断来缩小复杂度。

下面只在三个自然 owner 上留下 inline comments,不把同一根因展开成十个局部问题。它们都是非阻塞的 P2。我会批准当前版本;如果已有不变量或恢复保证能够推翻这些分析,也欢迎作者直接说明或讨论。

AI 辅助审查披露

Codex 负责代码检查和六路独立的对抗性复审。Astro-Han 复核并质疑了相关证据,按共同不变量归并 findings,选择了最终评论,并作出批准决定。Astro-Han 没有独立重新执行下面描述的每一个失败序列。

Comment threadapps/desktop/src/main/runtime-host-desktop-owner.ts
Comment threadpackages/cli/src/runtime-host-run-command.ts Outdated
Comment threadpackages/runtime-host/src/client/host-profile.ts Outdated
Introduce a shared profile catalog for Desktop, CLI, and TUI, with credentials bound to an exact Host target. Desktop can switch targets live while keeping reconnect and session observation ownership isolated per target generation.
Remote targets operate through Project identities and do not inherit Client-local filesystem authority.
Keep Desktop projections, project policy, and rollback state aligned with the Runtime Host that is actually active. Remote clients no longer interpret Host-owned paths as local filesystem authority, including TUI and non-interactive resume flows.
Keep regression coverage at the public catalog and CLI boundaries while removing assertions that freeze internal credential key encoding and generic parser plumbing. Replace the stateful CLI fake with a capture-only dependency so the test reflects command ownership.
Represent remote connectivity as a discriminated TLS transport and bind credentials to that transport identity. This preserves a clear extension point for SSH and explicitly acknowledged insecure transports without ambiguous URL or boolean combinations.
Fence Desktop requests and projections by target generation, and represent desired selection independently from the active Host. Keep remote paths Host-owned across Desktop, CLI, TUI, and Client Capability calls.\n\nMake profile targets immutable and credentials exact-target bound so partial mutations remain explicit and recoverable without cleanup heuristics.
Keep unreadable Runtime Host selections unavailable until the user explicitly chooses Local. Publish target-scoped Project capabilities so renderer surfaces expose only actions supported by the active Host.
Publish target lifecycle transitions so Desktop clears stale projections during switches and reconnects. Canonicalize Project aliases across clients, and make the atomic profile document the visible persistence commit while keeping safe reads independent of writer serialization.
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from 3f7c664 to 112cef2CompareAugust 12, 2026 05:28
@M4n5ter

M4n5ter commented Aug 12, 2026

Copy link
Copy Markdown
MemberAuthor

Addressed the three follow-up P2 threads in 112cef2f7 and rebased onto current main.

  • Desktop now publishes the complete target lifecycle, clears stale Host projections before readiness, and refreshes them after activation or same-target reconnect
  • Project ID/alias resolution is shared across Host, Desktop, CLI run, and TUI, with Clients forwarding the canonical Project ID
  • Profile reads no longer wait on mutation locks; the atomic profile document is the visible commit point; credential input is bounded; and the redundant post-rename chmod is removed

I kept the existing fail-loud writer-lock policy instead of auto-stealing stale directories. The inline response records the TOCTOU reason and the resulting recovery contract.

Local validation after rebasing: all four affected workspace typechecks passed, the 52 focused boundary tests passed, Biome and diff checks passed. Before the rebase, the complete Runtime Host, CLI, and Desktop test suites also passed. CI is green on the rebased head.

@M4n5ter
M4n5ter merged commit 5df53c1 into mainAug 12, 2026
11 checks passed
@M4n5ter
M4n5ter deleted the feat/runtime-host-profiles branch August 12, 2026 05:45
@Astro-Han

Copy link
Copy Markdown
Contributor

A small post-merge follow-up from a fresh review of the final head: I found two P2 issues that may be worth addressing in a focused patch.

  1. Same-target reconnect currently clears Client-owned session and composer state.

    The owner publishes reconnecting → ready within the same target epoch, but the renderer performs a full Runtime Host reset for both events. That clears the active Session, messages, pending attachments, and pending quotes. A normal remote-network interruption can therefore return the user to the new-session screen and discard unsent composer state even though the execution authority did not change.

    The narrow fix is to reserve the full reset for an actual epoch/target-generation change. A same-epoch reconnect should pause and refresh Host-owned projections while preserving the active Session intent and Client-owned drafts.

  2. Some remote surfaces still expose Client-local path actions that Main will reject.

    The Project picker and Projects Settings now consume the target capability snapshot, but the title-bar/command-palette “Open Project Folder” actions and the Skills actions for opening local files/folders or importing a local source remain visible on a remote target. These are normally reachable controls that deterministically fail at the Main boundary.

    It would be cleaner for these surfaces to consume the same active-target local-path capability rather than adding independent remote checks.

Neither issue changes the core Runtime Host authority model, exact-target credential binding, or the merge decision. They look suitable for one focused follow-up rather than reopening the profile architecture.

Please feel free to push back if reconnect is intentionally defined as a full Client workspace reset, or if these local-path actions have another capability source I missed.

AI-assisted review disclosure

Codex performed the post-merge code inspection and delegated adversarial review. Astro-Han reviewed the synthesized evidence, recalibrated the findings to P2, and decided which follow-ups to include. The reproduction paths remain AI-assisted and should be verified while implementing the follow-up.

中文对照(默认折叠)

在最终 head 合并后的 fresh review 中,发现两个可以考虑用独立小补丁处理的 P2:

  1. 同一 target 的重连目前会清除 Client-owned Session 和 composer 状态。reconnecting → ready 没有改变 epoch,但 renderer 对两个事件都执行完整 reset,因此普通网络中断可能清除当前 Session、未发送附件和引用。建议只在 epoch/target generation 真正改变时完整 reset;同 epoch reconnect 只暂停和刷新 Host projection。

  2. 部分 remote surface 仍显示 Main 必然拒绝的 Client-local path 操作。Project picker 和 Projects Settings 已消费 capability snapshot,但标题栏/命令面板的 Open Project Folder,以及 Skills 中打开本地文件、文件夹和导入本地来源的操作仍然可见。建议让这些 surface 消费同一个 active-target local-path capability。

这两个问题不改变 Runtime Host 的核心 authority、exact-target credential binding 或此前的合并判断,适合作为一个 focused follow-up,而不是重新调整 profile 架构。

如果 reconnect 本来就被定义为完整 Client workspace reset,或者这些本地路径操作还有其他 capability 来源,也欢迎直接 push back。

本次由 Codex 完成合并后代码检查和多路对抗性复审;Astro-Han 复核综合证据、将问题重新校准为 P2,并决定纳入哪些 follow-up。具体复现路径仍属于 AI-assisted analysis,建议在实现 follow-up 时再次验证。

@liugddx

Copy link
Copy Markdown
Member

There is one reconnect boundary worth handling in a focused follow-up PR.

runtime-host-profiles:changed carries a target epoch, but the renderer handler currently clears state and starts fire-and-forget refreshes without retaining or checking that epoch. The individual refresh functions also commit unconditionally after await. Because a same-target reconnect keeps the same target epoch, a read that started on candidate A can complete after candidate B is ready and overwrite B's newer projection.

A minimal fix would give each candidate replacement its own generation and fence Host-scoped refresh commits against it, either at the IPC result boundary or in the renderer. The regression should hold an A read pending, reconnect and complete B's refresh, then release A and verify that A cannot replace B's state.

This does not require reverting this PR or reopening the broader profile design. A small follow-up PR covering the generation fence and one behavioral test should be enough.

简体中文

建议用一个聚焦的后续 PR 处理 remaining reconnect 边界。

runtime-host-profiles:changed 虽然携带 target epoch,但 renderer handler 当前只是清空状态并触发一组 fire-and-forget refresh,没有保存或校验该 epoch。各 refresh 在 await 后也会无条件提交。由于 same-target reconnect 复用同一个 target epoch,candidate A 上已经开始的读取可能在 candidate B ready 并完成刷新之后才返回,从而用 A 的旧投影覆盖 B。

最小修复是为每次 candidate replacement 增加独立 generation,并在 IPC result 边界或 renderer commit 前校验 generation。回归测试应挂起 A 的读取,让 B 重连并完成刷新,再释放 A,确认 A 不能覆盖 B 的状态。

这不需要回滚本 PR,也不需要重新展开整个 profile 设计。单独开一个小 PR,完成 generation fence 和一条行为测试即可。

Sign up for freeto 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.

3 participants

@M4n5ter@Astro-Han@liugddx
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(runtime-host): add client-selectable host profiles - #2834

Merged
M4n5ter merged 7 commits into
mainfrom
feat/runtime-host-profiles
Aug 12, 2026
Merged

feat(runtime-host): add client-selectable host profiles#2834
M4n5ter merged 7 commits into
mainfrom
feat/runtime-host-profiles

Conversation

@M4n5ter

@M4n5terM4n5ter commented Aug 11, 2026

Copy link
Copy Markdown
Member
English

Summary

  • add a shared Runtime Host profile catalog for Desktop, CLI, and TUI
  • let Desktop switch between Local and remote Runtime Hosts without restarting
  • route remote work through Host-owned Project identities instead of Client-local paths
  • bind each access credential to an exact profile target and keep it outside profile documents
  • reset Host-owned Client projections at the target-generation boundary

Why

Runtime Host is now the execution authority, but Clients could still only select the implicit local Host. This change introduces the smallest shared target model needed for future remote Hosts while preserving a strict boundary: a remote Host path is never treated as a local Client path.

Each Desktop target generation owns its reconnect lifecycle, Session observations, Project policy, and local-path permissions. Switching reports the Host that is actually active even if selection persistence and rollback both fail. CLI and TUI omit Client-side cwd relocation for remote Sessions.

User impact

Desktop users can manage and switch Runtime Host profiles from Workspace settings. CLI and TUI users can select the same profiles with --host and select an existing remote Project with --project. Credentials are accepted through secure storage or environment input, never command-line arguments or profile JSON.

Validation

  • Runtime Host, Storage, CLI, and Desktop typechecks
  • focused Desktop, CLI/TUI, Runtime Host, and Storage regressions: 130 passed
  • complete production build
  • Biome and git diff --check

Refs #2522

简体中文

概要

  • 为 Desktop、CLI 和 TUI 增加共享的 Runtime Host profile catalog
  • Desktop 无需重启即可在 Local 与远程 Runtime Host 之间切换
  • 远程工作通过 Host 持有的 Project identity 路由,不再依赖 Client 本地路径
  • access credential 与精确的 profile target 绑定,并与 profile 文档分离保存
  • 在 target generation 边界清空并重载 Host-owned Client projections

原因

Runtime Host 已成为执行权威,但 Client 仍只能使用隐式的本地 Host。本次变更增加未来远程 Host 所需的最小共享目标模型,同时保留严格边界:远程 Host 路径绝不会被当作 Client 本地路径使用。

Desktop 的每个 target generation 独立持有 reconnect lifecycle、Session observations、Project policy 与本地路径权限。即使 selection 持久化与回滚同时失败,切换结果仍会报告实际活跃的 Host。CLI 与 TUI 不会对远程 Session 执行 Client 侧 cwd relocation。

用户影响

Desktop 用户可以在工作区设置中管理和切换 Runtime Host profiles。CLI 和 TUI 用户可以通过 --host 选择同一套 profiles,并通过 --project 选择已有的远程 Project。Credential 仅通过安全存储或环境变量输入,不会进入命令行参数或 profile JSON。

验证

  • Runtime Host、Storage、CLI 与 Desktop typecheck
  • Desktop、CLI/TUI、Runtime Host 与 Storage 定向回归:130 项通过
  • 完整 production build
  • Biome 与 git diff --check

关联 #2522

@M4n5ter
M4n5ter marked this pull request as ready for review August 12, 2026 00:35
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from 50f0923 to c71fa13CompareAugust 12, 2026 00:45

@Astro-HanAstro-Han 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.

The profile catalog itself looks sound. It keeps connection configuration on the Client, preserves the Runtime Host as the execution authority, and does not add another discovery or spawn path.

I think the remaining problems come from three missing boundaries rather than a collection of unrelated bugs.

  1. A target switch has no identity that follows its requests and results. Reconnecting to Host A and switching from A to B both look like a temporary handler gap. Commands can wait for B's handler, while reads started on A can complete after the switch and overwrite B's renderer state.

  2. Host-owned and Client-owned locations share the same representation. A plain cwd may be treated as a Host execution path, a Client filesystem path, a display value, or an autocomplete root. This breaks remote resume and also lets a remote capability frame supply a Client-local cwd despite advertising hostPathAccess: "none". Bare Project IDs have the same ownership problem when a global default is reused across Host roots.

  3. Desired profile selection, active Host readiness, and persisted credential state are mixed together. A profile or credential read failure currently rewrites the selection to Local, so a temporary storage failure can change execution authority. Profile and credential mutations also have commit points that can leave one side updated without enough metadata to recover the other.

I would keep the catalog, root pinning, credential binding, and per-target lifecycle. The smallest coherent revision is narrower:

  • Give each target generation an epoch. Commands must fail when their epoch is gone. Reconnectable reads may wait or retry only within the same epoch, and renderer reads must check the epoch before committing.
  • Split Client-local cwd from Host-owned execution location. Remote paths must not reach Client realpath, autocomplete, foreign-session scanning, or native capabilities unless the registered offer explicitly permits Host path access.
  • Keep a failed remote selection in an unavailable state. Persist Local only after an explicit user choice.
  • Keep remote Project policy root-scoped. Do not reuse the Client-global default, and hide workspace actions that remote main will reject.
  • Make profile and credential updates recoverable. Do not discard target metadata after a cleanup failure, and finish fallible preparation before committing profile metadata.

The cleaner end state is one Desktop owner publishing {epoch, target, readiness, candidate}. Boot, profile service, IPC, runtime policy, and renderer should consume that snapshot instead of storing separate versions of the active target. Execution locations should also be a discriminated type, so only a client-path can reach Client filesystem APIs.

I left inline comments at the smallest code sites that expose these boundaries.

The current red typecheck and workspace checks appear to reproduce on the base and are not attributed to this diff. The branch is still not green, but those failures are separate from the points above.

Disclosure: Codex performed the code inspection and adversarial analysis using several focused review passes. I (Astro-Han) reviewed and challenged the analysis over multiple rounds, selected the issues included here, and own the final review. I have not independently rerun every reproduction.

Comment threadapps/desktop/src/main/runtime-host-desktop-owner.ts Outdated
Comment threadapps/desktop/src/renderer/app-shell-effects.ts
Comment threadapps/desktop/src/main/runtime-host-native-capabilities.ts Outdated
Comment threadpackages/cli/src/runtime-host-session-driver.ts
Comment threadapps/desktop/src/main/runtime-host-profile-service.ts
Comment threadpackages/runtime-host/src/client/host-profile.ts Outdated

@liugddxliugddx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Follow-up adversarial review with findings anchored to the smallest changed lines. These complement the consolidated review and avoid duplicating existing inline comments.

简体中文

补充对抗性审查:以下问题锚定到最小变更行,作为汇总评论的可逐项 resolve 版本,并避免重复已有行级评论。

Comment threadpackages/cli/src/runtime-host-tui-context.ts
Comment threadapps/desktop/src/main/runtime-host-profile-service.ts Outdated
Comment threadapps/desktop/src/main/runtime-host-profile-service.ts Outdated
Comment threadpackages/cli/src/__tests__/runtime-host-run-command.test.ts Outdated
Comment threadapps/desktop/src/main/__tests__/runtime-host-desktop-owner.test.ts Outdated
Comment threadpackages/runtime-host/src/__tests__/host-profile.test.ts Outdated
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from e6857cb to 51be16bCompareAugust 12, 2026 03:12
@M4n5ter

Copy link
Copy Markdown
MemberAuthor

Addressed both review passes at 51be16b4d and rebased onto the latest main.

The revision now:

  • fences Desktop requests, results, and observations by target generation;
  • separates desired selection from the actually active Host, including the no-active-Host state;
  • keeps remote paths Host-owned across Desktop, CLI, TUI, and Client Capability calls;
  • makes profile targets immutable and credentials exact-target bound with recoverable mutation ordering;
  • removes the low-value remote-resume and object-identity tests called out in review.

Local validation passed: Runtime Host coverage (the only stale-build failure passed after rebuilding its updated dependency), CLI 233/233, Desktop 758/758, all three workspace typechecks, Biome, and git diff --check.

Could you please re-review the current head?

@Astro-HanAstro-Han 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.

The previous generation, path-authority, active-state, and credential-isolation findings are materially addressed in 51be16b4d.

Requests and results are now fenced by target epoch; remote Host paths no longer reach Client filesystem or capability APIs; rollback can represent no active Host; preference-write failure keeps the healthy Host active; and credentials are bound and tested against the exact immutable target. The focused tests now exercise the production boundaries rather than object identity or injected fakes.

I think the two remaining findings share one root cause rather than being isolated guards: the Runtime Host authority model has been updated, but two Client-facing boundaries still encode the former assumption that a valid local state always exists.

At startup, an unreadable selection is represented as Local because the resolution type cannot express “the authority is unknown.” In the project UI, the absence of a Client-visible path is interpreted as an unavailable Project, while Client-local actions remain visible, because the renderer infers capabilities from the old local Project representation instead of consuming capabilities published by the active target.

This creates the same failure in two forms: an unavailable or intentionally absent Client-local value is converted into Local authority instead of remaining an explicit unavailable or Host-owned state. The result is either a silent authority change or UI actions that the active Host cannot support.

From first principles, an unknown selection must not become an execution target, and the renderer should only expose operations granted by the active authority. I would therefore fix these at their natural boundaries rather than adding individual if (remote) checks:

  • Make startup resolution a closed ready | unavailable result. Only ready may contain a target; choosing Local should be a separate explicit transition.
  • Publish a small project-capability projection for the active target, covering directory actions, no-project allowance, local defaults, and path visibility. The renderer should consume that projection rather than infer authority from profile.kind, preferredPath, or failed calls.

I found two concrete consequences of the incomplete boundary:

  1. A failure to read the selection file still synthesizes Local as the selected target, and the startup recovery dialog skips that Local-shaped failure. An existing remote selection can therefore become Local execution authority without an explicit user choice.
  2. The remote Desktop project projection still exposes Client-local actions and permits “No project,” although remote Session creation requires a Host Project. The Settings view also interprets intentionally hidden Host paths as unavailable Projects.

These look like contained follow-ups to the Client state model, not reasons to revisit the profile catalog or Runtime Host architecture. Please feel free to push back if there is an existing authority or product contract that makes either state intentional.

Current CI is green. I am leaving this as a comment rather than Request Changes.

Disclosure: Codex performed the code inspection and adversarial analysis through four independent focused review passes. I (Astro-Han) reviewed the evidence, selected the findings included here, and made the final decision to submit this review. I did not independently rerun every reproduction.

Comment threadapps/desktop/src/main/runtime-host-profile-service.ts
Comment threadapps/desktop/src/main/project-management-service.ts
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from 51be16b to 3f7c664CompareAugust 12, 2026 04:24
@M4n5ter

Copy link
Copy Markdown
MemberAuthor

Addressed the latest authority-boundary review in 3f7c66418 and rebased onto current main.

Startup selection now remains explicitly unavailable on read/decode failure until the user chooses Local. The active target also publishes one Project capability snapshot consumed by Main and renderer surfaces, so remote Hosts no longer expose unsupported Client-directory, no-Project, local-default, or path-based behavior.

Local validation passed: UI 113/113, Desktop 760/760, focused regressions 15/15, Desktop/UI typechecks, Biome, and diff checks. All addressed review threads are resolved.

@Astro-Han@liugddx, could you please re-review the current head?

@M4n5ter
M4n5ter requested a review from Astro-HanAugust 12, 2026 04:25

@Astro-HanAstro-Han 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.

The two findings from my previous round are materially fixed in 3f7c66418. Startup resolution is now a closed ready | unavailable result, and Project capabilities are published by the active target and consumed by the main picker and Settings surfaces. CI is green and the PR is mergeable.

I also asked six fresh reviewers to inspect the entire PR independently, without relying on the previous findings. The core architecture held up well:

  • Runtime Host remains the sole execution authority.
  • Desired profile selection and the actually active target are separate.
  • Requests, results, observations, and Client Capability calls are fenced by target epoch.
  • Remote Host paths no longer enter Client filesystem authority.
  • Credentials are bound to an exact immutable target and remain outside profile documents.
  • Desktop, CLI, and TUI share one profile catalog rather than introducing another discovery or execution path.

The remaining observations mostly reduce to one architectural seam rather than a list of unrelated defects:

Target generation is now the authority for IPC, but it is not yet the single state model consumed by reconnect recovery, renderer projections, execution-location UI, and Project resolution.

For example, the owner knows when a target is connecting, ready, reconnecting, or unavailable, while consumers generally receive only a successful-activation invalidation. This leaves old projections visible during a switch, does not refresh all global projections after a same-target reconnect, and lets some renderer surfaces continue exposing local-path actions. Project identity is similarly canonical on the Host, but Desktop, CLI, and TUI still implement separate ID/alias and cwd rules.

I think the clean end state is one target-state projection owned by the Desktop target owner:

typeClientTargetState=|{readiness: "connecting";epoch: string;desiredProfileId: string;activeTarget?: ResolvedTarget;}|{readiness: "ready";epoch: string;desiredProfileId: string;activeTarget: ResolvedTarget;projectCapabilities: ProjectCapabilities;location: ExecutionLocation;}|{readiness: "unavailable";desiredProfileId: string;error: Error;};

Renderer invalidation, reconnect resynchronization, profile status, local-path actions, and the visible Host/execution location can then consume the same fact instead of inferring it from cached data, profile.kind, or a bare cwd.

The other independent concern is persistence. Profile metadata, selection, credential storage, and the directory lock currently have multiple durable commit points without automatic crash recovery. I would define one visible commit point, keep reads independent of the mutation lock where safe, bound all renderer-to-main input, and make credential replacement an explicit operation on an immutable target.

This is a large PR—82 files and roughly 5.3k changed lines—but I would not split it by Runtime Host, Desktop, CLI, and TUI. Those pieces form one authority change, and independently mergeable layers would create temporary incomplete states. I would instead shrink it through consolidation:

  • publish one target-state projection rather than adding more renderer-specific invalidations;
  • share one Project ID/alias resolver across Desktop, CLI run, and TUI;
  • represent execution location as client_path | host_project, allowing only the first to reach Client filesystem APIs;
  • remove the second location inference from the raw hostProfileId after the profile has already been resolved;
  • remove the write lock from safe read-only resolution and remove the redundant post-rename chmod.

I left three inline comments at the natural owner boundaries rather than filing every downstream symptom separately. All are non-blocking P2 observations. I’m approving the current revision; please feel free to push back if an existing invariant or recovery guarantee changes this analysis.

AI-assisted review disclosure

Codex performed the code inspection and six independent adversarial review passes. I, Astro-Han, reviewed and challenged the evidence, grouped the findings by their underlying invariants, selected the comments included here, and made the final decision to approve. I did not independently rerun every failure sequence described below.

中文对照

上一轮提出的两个问题已经在 3f7c66418 中得到实质修复:启动解析现在是闭合的 ready | unavailable 状态;Project capability 由 active target 发布,并被主项目选择器和设置页面消费。CI 全绿,PR 当前可以合并。

我又安排了六路互相独立的 fresh-eye 对抗性复审。整体架构经受住了审查:Runtime Host 仍是唯一执行权威;desired profile 与 active target 已分离;request、result、observation 和 Client Capability 都有 epoch fencing;远程 Host path 不再进入 Client 文件系统权限;credential 与精确且不可变的 target 绑定;Desktop、CLI、TUI 共用同一个 profile catalog。

剩余问题大多来自同一个架构边界:

target generation 已经是 IPC 的 authority,但还没有成为 reconnect、renderer projection、execution-location UI 和 Project resolution 共同消费的唯一状态模型。

最干净的终态是让 Desktop target owner 发布一个包含 epoch、desired profile、active target、readiness、Project capability 和 execution location 的闭合状态。Renderer 清理与刷新、重连恢复、profile 状态、本地路径操作和 Host/location 展示都消费这个事实,不再从旧缓存、profile.kind 或裸 cwd 推断。

另一个相对独立的问题是 persistence:profile metadata、selection、credential store 和目录锁目前存在多个 durable commit point,但没有自动的崩溃恢复协议。建议明确唯一可见提交点,让安全的只读解析不受 mutation lock 阻塞,统一限制 renderer-to-main 输入大小,并把 credential replacement 建模为 immutable target 上的独立操作。

这个 PR 的确很大,但不建议按 Runtime Host、Desktop、CLI、TUI 拆开,因为它们共同组成一个 authority change,分开合并会制造不安全的中间状态。更合适的是通过合并重复规则和删除平行推断来缩小复杂度。

下面只在三个自然 owner 上留下 inline comments,不把同一根因展开成十个局部问题。它们都是非阻塞的 P2。我会批准当前版本;如果已有不变量或恢复保证能够推翻这些分析,也欢迎作者直接说明或讨论。

AI 辅助审查披露

Codex 负责代码检查和六路独立的对抗性复审。Astro-Han 复核并质疑了相关证据,按共同不变量归并 findings,选择了最终评论,并作出批准决定。Astro-Han 没有独立重新执行下面描述的每一个失败序列。

Comment threadapps/desktop/src/main/runtime-host-desktop-owner.ts
Comment threadpackages/cli/src/runtime-host-run-command.ts Outdated
Comment threadpackages/runtime-host/src/client/host-profile.ts Outdated
Introduce a shared profile catalog for Desktop, CLI, and TUI, with credentials bound to an exact Host target. Desktop can switch targets live while keeping reconnect and session observation ownership isolated per target generation.
Remote targets operate through Project identities and do not inherit Client-local filesystem authority.
Keep Desktop projections, project policy, and rollback state aligned with the Runtime Host that is actually active. Remote clients no longer interpret Host-owned paths as local filesystem authority, including TUI and non-interactive resume flows.
Keep regression coverage at the public catalog and CLI boundaries while removing assertions that freeze internal credential key encoding and generic parser plumbing. Replace the stateful CLI fake with a capture-only dependency so the test reflects command ownership.
Represent remote connectivity as a discriminated TLS transport and bind credentials to that transport identity. This preserves a clear extension point for SSH and explicitly acknowledged insecure transports without ambiguous URL or boolean combinations.
Fence Desktop requests and projections by target generation, and represent desired selection independently from the active Host. Keep remote paths Host-owned across Desktop, CLI, TUI, and Client Capability calls.\n\nMake profile targets immutable and credentials exact-target bound so partial mutations remain explicit and recoverable without cleanup heuristics.
Keep unreadable Runtime Host selections unavailable until the user explicitly chooses Local. Publish target-scoped Project capabilities so renderer surfaces expose only actions supported by the active Host.
Publish target lifecycle transitions so Desktop clears stale projections during switches and reconnects. Canonicalize Project aliases across clients, and make the atomic profile document the visible persistence commit while keeping safe reads independent of writer serialization.
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from 3f7c664 to 112cef2CompareAugust 12, 2026 05:28
@M4n5ter

M4n5ter commented Aug 12, 2026

Copy link
Copy Markdown
MemberAuthor

Addressed the three follow-up P2 threads in 112cef2f7 and rebased onto current main.

  • Desktop now publishes the complete target lifecycle, clears stale Host projections before readiness, and refreshes them after activation or same-target reconnect
  • Project ID/alias resolution is shared across Host, Desktop, CLI run, and TUI, with Clients forwarding the canonical Project ID
  • Profile reads no longer wait on mutation locks; the atomic profile document is the visible commit point; credential input is bounded; and the redundant post-rename chmod is removed

I kept the existing fail-loud writer-lock policy instead of auto-stealing stale directories. The inline response records the TOCTOU reason and the resulting recovery contract.

Local validation after rebasing: all four affected workspace typechecks passed, the 52 focused boundary tests passed, Biome and diff checks passed. Before the rebase, the complete Runtime Host, CLI, and Desktop test suites also passed. CI is green on the rebased head.

@M4n5ter
M4n5ter merged commit 5df53c1 into mainAug 12, 2026
11 checks passed
@M4n5ter
M4n5ter deleted the feat/runtime-host-profiles branch August 12, 2026 05:45
@Astro-Han

Copy link
Copy Markdown
Contributor

A small post-merge follow-up from a fresh review of the final head: I found two P2 issues that may be worth addressing in a focused patch.

  1. Same-target reconnect currently clears Client-owned session and composer state.

    The owner publishes reconnecting → ready within the same target epoch, but the renderer performs a full Runtime Host reset for both events. That clears the active Session, messages, pending attachments, and pending quotes. A normal remote-network interruption can therefore return the user to the new-session screen and discard unsent composer state even though the execution authority did not change.

    The narrow fix is to reserve the full reset for an actual epoch/target-generation change. A same-epoch reconnect should pause and refresh Host-owned projections while preserving the active Session intent and Client-owned drafts.

  2. Some remote surfaces still expose Client-local path actions that Main will reject.

    The Project picker and Projects Settings now consume the target capability snapshot, but the title-bar/command-palette “Open Project Folder” actions and the Skills actions for opening local files/folders or importing a local source remain visible on a remote target. These are normally reachable controls that deterministically fail at the Main boundary.

    It would be cleaner for these surfaces to consume the same active-target local-path capability rather than adding independent remote checks.

Neither issue changes the core Runtime Host authority model, exact-target credential binding, or the merge decision. They look suitable for one focused follow-up rather than reopening the profile architecture.

Please feel free to push back if reconnect is intentionally defined as a full Client workspace reset, or if these local-path actions have another capability source I missed.

AI-assisted review disclosure

Codex performed the post-merge code inspection and delegated adversarial review. Astro-Han reviewed the synthesized evidence, recalibrated the findings to P2, and decided which follow-ups to include. The reproduction paths remain AI-assisted and should be verified while implementing the follow-up.

中文对照(默认折叠)

在最终 head 合并后的 fresh review 中,发现两个可以考虑用独立小补丁处理的 P2:

  1. 同一 target 的重连目前会清除 Client-owned Session 和 composer 状态。reconnecting → ready 没有改变 epoch,但 renderer 对两个事件都执行完整 reset,因此普通网络中断可能清除当前 Session、未发送附件和引用。建议只在 epoch/target generation 真正改变时完整 reset;同 epoch reconnect 只暂停和刷新 Host projection。

  2. 部分 remote surface 仍显示 Main 必然拒绝的 Client-local path 操作。Project picker 和 Projects Settings 已消费 capability snapshot,但标题栏/命令面板的 Open Project Folder,以及 Skills 中打开本地文件、文件夹和导入本地来源的操作仍然可见。建议让这些 surface 消费同一个 active-target local-path capability。

这两个问题不改变 Runtime Host 的核心 authority、exact-target credential binding 或此前的合并判断,适合作为一个 focused follow-up,而不是重新调整 profile 架构。

如果 reconnect 本来就被定义为完整 Client workspace reset,或者这些本地路径操作还有其他 capability 来源,也欢迎直接 push back。

本次由 Codex 完成合并后代码检查和多路对抗性复审;Astro-Han 复核综合证据、将问题重新校准为 P2,并决定纳入哪些 follow-up。具体复现路径仍属于 AI-assisted analysis,建议在实现 follow-up 时再次验证。

@liugddx

Copy link
Copy Markdown
Member

There is one reconnect boundary worth handling in a focused follow-up PR.

runtime-host-profiles:changed carries a target epoch, but the renderer handler currently clears state and starts fire-and-forget refreshes without retaining or checking that epoch. The individual refresh functions also commit unconditionally after await. Because a same-target reconnect keeps the same target epoch, a read that started on candidate A can complete after candidate B is ready and overwrite B's newer projection.

A minimal fix would give each candidate replacement its own generation and fence Host-scoped refresh commits against it, either at the IPC result boundary or in the renderer. The regression should hold an A read pending, reconnect and complete B's refresh, then release A and verify that A cannot replace B's state.

This does not require reverting this PR or reopening the broader profile design. A small follow-up PR covering the generation fence and one behavioral test should be enough.

简体中文

建议用一个聚焦的后续 PR 处理 remaining reconnect 边界。

runtime-host-profiles:changed 虽然携带 target epoch,但 renderer handler 当前只是清空状态并触发一组 fire-and-forget refresh,没有保存或校验该 epoch。各 refresh 在 await 后也会无条件提交。由于 same-target reconnect 复用同一个 target epoch,candidate A 上已经开始的读取可能在 candidate B ready 并完成刷新之后才返回,从而用 A 的旧投影覆盖 B。

最小修复是为每次 candidate replacement 增加独立 generation,并在 IPC result 边界或 renderer commit 前校验 generation。回归测试应挂起 A 的读取,让 B 重连并完成刷新,再释放 A,确认 A 不能覆盖 B 的状态。

这不需要回滚本 PR,也不需要重新展开整个 profile 设计。单独开一个小 PR,完成 generation fence 和一条行为测试即可。

Sign up for freeto 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.

3 participants

@M4n5ter@Astro-Han@liugddx
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(runtime-host): add client-selectable host profiles - #2834

Merged
M4n5ter merged 7 commits into
mainfrom
feat/runtime-host-profiles
Aug 12, 2026
Merged

feat(runtime-host): add client-selectable host profiles#2834
M4n5ter merged 7 commits into
mainfrom
feat/runtime-host-profiles

Conversation

@M4n5ter

@M4n5terM4n5ter commented Aug 11, 2026

Copy link
Copy Markdown
Member
English

Summary

  • add a shared Runtime Host profile catalog for Desktop, CLI, and TUI
  • let Desktop switch between Local and remote Runtime Hosts without restarting
  • route remote work through Host-owned Project identities instead of Client-local paths
  • bind each access credential to an exact profile target and keep it outside profile documents
  • reset Host-owned Client projections at the target-generation boundary

Why

Runtime Host is now the execution authority, but Clients could still only select the implicit local Host. This change introduces the smallest shared target model needed for future remote Hosts while preserving a strict boundary: a remote Host path is never treated as a local Client path.

Each Desktop target generation owns its reconnect lifecycle, Session observations, Project policy, and local-path permissions. Switching reports the Host that is actually active even if selection persistence and rollback both fail. CLI and TUI omit Client-side cwd relocation for remote Sessions.

User impact

Desktop users can manage and switch Runtime Host profiles from Workspace settings. CLI and TUI users can select the same profiles with --host and select an existing remote Project with --project. Credentials are accepted through secure storage or environment input, never command-line arguments or profile JSON.

Validation

  • Runtime Host, Storage, CLI, and Desktop typechecks
  • focused Desktop, CLI/TUI, Runtime Host, and Storage regressions: 130 passed
  • complete production build
  • Biome and git diff --check

Refs #2522

简体中文

概要

  • 为 Desktop、CLI 和 TUI 增加共享的 Runtime Host profile catalog
  • Desktop 无需重启即可在 Local 与远程 Runtime Host 之间切换
  • 远程工作通过 Host 持有的 Project identity 路由,不再依赖 Client 本地路径
  • access credential 与精确的 profile target 绑定,并与 profile 文档分离保存
  • 在 target generation 边界清空并重载 Host-owned Client projections

原因

Runtime Host 已成为执行权威,但 Client 仍只能使用隐式的本地 Host。本次变更增加未来远程 Host 所需的最小共享目标模型,同时保留严格边界:远程 Host 路径绝不会被当作 Client 本地路径使用。

Desktop 的每个 target generation 独立持有 reconnect lifecycle、Session observations、Project policy 与本地路径权限。即使 selection 持久化与回滚同时失败,切换结果仍会报告实际活跃的 Host。CLI 与 TUI 不会对远程 Session 执行 Client 侧 cwd relocation。

用户影响

Desktop 用户可以在工作区设置中管理和切换 Runtime Host profiles。CLI 和 TUI 用户可以通过 --host 选择同一套 profiles,并通过 --project 选择已有的远程 Project。Credential 仅通过安全存储或环境变量输入,不会进入命令行参数或 profile JSON。

验证

  • Runtime Host、Storage、CLI 与 Desktop typecheck
  • Desktop、CLI/TUI、Runtime Host 与 Storage 定向回归:130 项通过
  • 完整 production build
  • Biome 与 git diff --check

关联 #2522

@M4n5ter
M4n5ter marked this pull request as ready for review August 12, 2026 00:35
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from 50f0923 to c71fa13CompareAugust 12, 2026 00:45

@Astro-HanAstro-Han 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.

The profile catalog itself looks sound. It keeps connection configuration on the Client, preserves the Runtime Host as the execution authority, and does not add another discovery or spawn path.

I think the remaining problems come from three missing boundaries rather than a collection of unrelated bugs.

  1. A target switch has no identity that follows its requests and results. Reconnecting to Host A and switching from A to B both look like a temporary handler gap. Commands can wait for B's handler, while reads started on A can complete after the switch and overwrite B's renderer state.

  2. Host-owned and Client-owned locations share the same representation. A plain cwd may be treated as a Host execution path, a Client filesystem path, a display value, or an autocomplete root. This breaks remote resume and also lets a remote capability frame supply a Client-local cwd despite advertising hostPathAccess: "none". Bare Project IDs have the same ownership problem when a global default is reused across Host roots.

  3. Desired profile selection, active Host readiness, and persisted credential state are mixed together. A profile or credential read failure currently rewrites the selection to Local, so a temporary storage failure can change execution authority. Profile and credential mutations also have commit points that can leave one side updated without enough metadata to recover the other.

I would keep the catalog, root pinning, credential binding, and per-target lifecycle. The smallest coherent revision is narrower:

  • Give each target generation an epoch. Commands must fail when their epoch is gone. Reconnectable reads may wait or retry only within the same epoch, and renderer reads must check the epoch before committing.
  • Split Client-local cwd from Host-owned execution location. Remote paths must not reach Client realpath, autocomplete, foreign-session scanning, or native capabilities unless the registered offer explicitly permits Host path access.
  • Keep a failed remote selection in an unavailable state. Persist Local only after an explicit user choice.
  • Keep remote Project policy root-scoped. Do not reuse the Client-global default, and hide workspace actions that remote main will reject.
  • Make profile and credential updates recoverable. Do not discard target metadata after a cleanup failure, and finish fallible preparation before committing profile metadata.

The cleaner end state is one Desktop owner publishing {epoch, target, readiness, candidate}. Boot, profile service, IPC, runtime policy, and renderer should consume that snapshot instead of storing separate versions of the active target. Execution locations should also be a discriminated type, so only a client-path can reach Client filesystem APIs.

I left inline comments at the smallest code sites that expose these boundaries.

The current red typecheck and workspace checks appear to reproduce on the base and are not attributed to this diff. The branch is still not green, but those failures are separate from the points above.

Disclosure: Codex performed the code inspection and adversarial analysis using several focused review passes. I (Astro-Han) reviewed and challenged the analysis over multiple rounds, selected the issues included here, and own the final review. I have not independently rerun every reproduction.

Comment threadapps/desktop/src/main/runtime-host-desktop-owner.ts Outdated
Comment threadapps/desktop/src/renderer/app-shell-effects.ts
Comment threadapps/desktop/src/main/runtime-host-native-capabilities.ts Outdated
Comment threadpackages/cli/src/runtime-host-session-driver.ts
Comment threadapps/desktop/src/main/runtime-host-profile-service.ts
Comment threadpackages/runtime-host/src/client/host-profile.ts Outdated

@liugddxliugddx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Follow-up adversarial review with findings anchored to the smallest changed lines. These complement the consolidated review and avoid duplicating existing inline comments.

简体中文

补充对抗性审查:以下问题锚定到最小变更行,作为汇总评论的可逐项 resolve 版本,并避免重复已有行级评论。

Comment threadpackages/cli/src/runtime-host-tui-context.ts
Comment threadapps/desktop/src/main/runtime-host-profile-service.ts Outdated
Comment threadapps/desktop/src/main/runtime-host-profile-service.ts Outdated
Comment threadpackages/cli/src/__tests__/runtime-host-run-command.test.ts Outdated
Comment threadapps/desktop/src/main/__tests__/runtime-host-desktop-owner.test.ts Outdated
Comment threadpackages/runtime-host/src/__tests__/host-profile.test.ts Outdated
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from e6857cb to 51be16bCompareAugust 12, 2026 03:12
@M4n5ter

Copy link
Copy Markdown
MemberAuthor

Addressed both review passes at 51be16b4d and rebased onto the latest main.

The revision now:

  • fences Desktop requests, results, and observations by target generation;
  • separates desired selection from the actually active Host, including the no-active-Host state;
  • keeps remote paths Host-owned across Desktop, CLI, TUI, and Client Capability calls;
  • makes profile targets immutable and credentials exact-target bound with recoverable mutation ordering;
  • removes the low-value remote-resume and object-identity tests called out in review.

Local validation passed: Runtime Host coverage (the only stale-build failure passed after rebuilding its updated dependency), CLI 233/233, Desktop 758/758, all three workspace typechecks, Biome, and git diff --check.

Could you please re-review the current head?

@Astro-HanAstro-Han 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.

The previous generation, path-authority, active-state, and credential-isolation findings are materially addressed in 51be16b4d.

Requests and results are now fenced by target epoch; remote Host paths no longer reach Client filesystem or capability APIs; rollback can represent no active Host; preference-write failure keeps the healthy Host active; and credentials are bound and tested against the exact immutable target. The focused tests now exercise the production boundaries rather than object identity or injected fakes.

I think the two remaining findings share one root cause rather than being isolated guards: the Runtime Host authority model has been updated, but two Client-facing boundaries still encode the former assumption that a valid local state always exists.

At startup, an unreadable selection is represented as Local because the resolution type cannot express “the authority is unknown.” In the project UI, the absence of a Client-visible path is interpreted as an unavailable Project, while Client-local actions remain visible, because the renderer infers capabilities from the old local Project representation instead of consuming capabilities published by the active target.

This creates the same failure in two forms: an unavailable or intentionally absent Client-local value is converted into Local authority instead of remaining an explicit unavailable or Host-owned state. The result is either a silent authority change or UI actions that the active Host cannot support.

From first principles, an unknown selection must not become an execution target, and the renderer should only expose operations granted by the active authority. I would therefore fix these at their natural boundaries rather than adding individual if (remote) checks:

  • Make startup resolution a closed ready | unavailable result. Only ready may contain a target; choosing Local should be a separate explicit transition.
  • Publish a small project-capability projection for the active target, covering directory actions, no-project allowance, local defaults, and path visibility. The renderer should consume that projection rather than infer authority from profile.kind, preferredPath, or failed calls.

I found two concrete consequences of the incomplete boundary:

  1. A failure to read the selection file still synthesizes Local as the selected target, and the startup recovery dialog skips that Local-shaped failure. An existing remote selection can therefore become Local execution authority without an explicit user choice.
  2. The remote Desktop project projection still exposes Client-local actions and permits “No project,” although remote Session creation requires a Host Project. The Settings view also interprets intentionally hidden Host paths as unavailable Projects.

These look like contained follow-ups to the Client state model, not reasons to revisit the profile catalog or Runtime Host architecture. Please feel free to push back if there is an existing authority or product contract that makes either state intentional.

Current CI is green. I am leaving this as a comment rather than Request Changes.

Disclosure: Codex performed the code inspection and adversarial analysis through four independent focused review passes. I (Astro-Han) reviewed the evidence, selected the findings included here, and made the final decision to submit this review. I did not independently rerun every reproduction.

Comment threadapps/desktop/src/main/runtime-host-profile-service.ts
Comment threadapps/desktop/src/main/project-management-service.ts
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from 51be16b to 3f7c664CompareAugust 12, 2026 04:24
@M4n5ter

Copy link
Copy Markdown
MemberAuthor

Addressed the latest authority-boundary review in 3f7c66418 and rebased onto current main.

Startup selection now remains explicitly unavailable on read/decode failure until the user chooses Local. The active target also publishes one Project capability snapshot consumed by Main and renderer surfaces, so remote Hosts no longer expose unsupported Client-directory, no-Project, local-default, or path-based behavior.

Local validation passed: UI 113/113, Desktop 760/760, focused regressions 15/15, Desktop/UI typechecks, Biome, and diff checks. All addressed review threads are resolved.

@Astro-Han@liugddx, could you please re-review the current head?

@M4n5ter
M4n5ter requested a review from Astro-HanAugust 12, 2026 04:25

@Astro-HanAstro-Han 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.

The two findings from my previous round are materially fixed in 3f7c66418. Startup resolution is now a closed ready | unavailable result, and Project capabilities are published by the active target and consumed by the main picker and Settings surfaces. CI is green and the PR is mergeable.

I also asked six fresh reviewers to inspect the entire PR independently, without relying on the previous findings. The core architecture held up well:

  • Runtime Host remains the sole execution authority.
  • Desired profile selection and the actually active target are separate.
  • Requests, results, observations, and Client Capability calls are fenced by target epoch.
  • Remote Host paths no longer enter Client filesystem authority.
  • Credentials are bound to an exact immutable target and remain outside profile documents.
  • Desktop, CLI, and TUI share one profile catalog rather than introducing another discovery or execution path.

The remaining observations mostly reduce to one architectural seam rather than a list of unrelated defects:

Target generation is now the authority for IPC, but it is not yet the single state model consumed by reconnect recovery, renderer projections, execution-location UI, and Project resolution.

For example, the owner knows when a target is connecting, ready, reconnecting, or unavailable, while consumers generally receive only a successful-activation invalidation. This leaves old projections visible during a switch, does not refresh all global projections after a same-target reconnect, and lets some renderer surfaces continue exposing local-path actions. Project identity is similarly canonical on the Host, but Desktop, CLI, and TUI still implement separate ID/alias and cwd rules.

I think the clean end state is one target-state projection owned by the Desktop target owner:

typeClientTargetState=|{readiness: "connecting";epoch: string;desiredProfileId: string;activeTarget?: ResolvedTarget;}|{readiness: "ready";epoch: string;desiredProfileId: string;activeTarget: ResolvedTarget;projectCapabilities: ProjectCapabilities;location: ExecutionLocation;}|{readiness: "unavailable";desiredProfileId: string;error: Error;};

Renderer invalidation, reconnect resynchronization, profile status, local-path actions, and the visible Host/execution location can then consume the same fact instead of inferring it from cached data, profile.kind, or a bare cwd.

The other independent concern is persistence. Profile metadata, selection, credential storage, and the directory lock currently have multiple durable commit points without automatic crash recovery. I would define one visible commit point, keep reads independent of the mutation lock where safe, bound all renderer-to-main input, and make credential replacement an explicit operation on an immutable target.

This is a large PR—82 files and roughly 5.3k changed lines—but I would not split it by Runtime Host, Desktop, CLI, and TUI. Those pieces form one authority change, and independently mergeable layers would create temporary incomplete states. I would instead shrink it through consolidation:

  • publish one target-state projection rather than adding more renderer-specific invalidations;
  • share one Project ID/alias resolver across Desktop, CLI run, and TUI;
  • represent execution location as client_path | host_project, allowing only the first to reach Client filesystem APIs;
  • remove the second location inference from the raw hostProfileId after the profile has already been resolved;
  • remove the write lock from safe read-only resolution and remove the redundant post-rename chmod.

I left three inline comments at the natural owner boundaries rather than filing every downstream symptom separately. All are non-blocking P2 observations. I’m approving the current revision; please feel free to push back if an existing invariant or recovery guarantee changes this analysis.

AI-assisted review disclosure

Codex performed the code inspection and six independent adversarial review passes. I, Astro-Han, reviewed and challenged the evidence, grouped the findings by their underlying invariants, selected the comments included here, and made the final decision to approve. I did not independently rerun every failure sequence described below.

中文对照

上一轮提出的两个问题已经在 3f7c66418 中得到实质修复:启动解析现在是闭合的 ready | unavailable 状态;Project capability 由 active target 发布,并被主项目选择器和设置页面消费。CI 全绿,PR 当前可以合并。

我又安排了六路互相独立的 fresh-eye 对抗性复审。整体架构经受住了审查:Runtime Host 仍是唯一执行权威;desired profile 与 active target 已分离;request、result、observation 和 Client Capability 都有 epoch fencing;远程 Host path 不再进入 Client 文件系统权限;credential 与精确且不可变的 target 绑定;Desktop、CLI、TUI 共用同一个 profile catalog。

剩余问题大多来自同一个架构边界:

target generation 已经是 IPC 的 authority,但还没有成为 reconnect、renderer projection、execution-location UI 和 Project resolution 共同消费的唯一状态模型。

最干净的终态是让 Desktop target owner 发布一个包含 epoch、desired profile、active target、readiness、Project capability 和 execution location 的闭合状态。Renderer 清理与刷新、重连恢复、profile 状态、本地路径操作和 Host/location 展示都消费这个事实,不再从旧缓存、profile.kind 或裸 cwd 推断。

另一个相对独立的问题是 persistence:profile metadata、selection、credential store 和目录锁目前存在多个 durable commit point,但没有自动的崩溃恢复协议。建议明确唯一可见提交点,让安全的只读解析不受 mutation lock 阻塞,统一限制 renderer-to-main 输入大小,并把 credential replacement 建模为 immutable target 上的独立操作。

这个 PR 的确很大,但不建议按 Runtime Host、Desktop、CLI、TUI 拆开,因为它们共同组成一个 authority change,分开合并会制造不安全的中间状态。更合适的是通过合并重复规则和删除平行推断来缩小复杂度。

下面只在三个自然 owner 上留下 inline comments,不把同一根因展开成十个局部问题。它们都是非阻塞的 P2。我会批准当前版本;如果已有不变量或恢复保证能够推翻这些分析,也欢迎作者直接说明或讨论。

AI 辅助审查披露

Codex 负责代码检查和六路独立的对抗性复审。Astro-Han 复核并质疑了相关证据,按共同不变量归并 findings,选择了最终评论,并作出批准决定。Astro-Han 没有独立重新执行下面描述的每一个失败序列。

Comment threadapps/desktop/src/main/runtime-host-desktop-owner.ts
Comment threadpackages/cli/src/runtime-host-run-command.ts Outdated
Comment threadpackages/runtime-host/src/client/host-profile.ts Outdated
Introduce a shared profile catalog for Desktop, CLI, and TUI, with credentials bound to an exact Host target. Desktop can switch targets live while keeping reconnect and session observation ownership isolated per target generation.
Remote targets operate through Project identities and do not inherit Client-local filesystem authority.
Keep Desktop projections, project policy, and rollback state aligned with the Runtime Host that is actually active. Remote clients no longer interpret Host-owned paths as local filesystem authority, including TUI and non-interactive resume flows.
Keep regression coverage at the public catalog and CLI boundaries while removing assertions that freeze internal credential key encoding and generic parser plumbing. Replace the stateful CLI fake with a capture-only dependency so the test reflects command ownership.
Represent remote connectivity as a discriminated TLS transport and bind credentials to that transport identity. This preserves a clear extension point for SSH and explicitly acknowledged insecure transports without ambiguous URL or boolean combinations.
Fence Desktop requests and projections by target generation, and represent desired selection independently from the active Host. Keep remote paths Host-owned across Desktop, CLI, TUI, and Client Capability calls.\n\nMake profile targets immutable and credentials exact-target bound so partial mutations remain explicit and recoverable without cleanup heuristics.
Keep unreadable Runtime Host selections unavailable until the user explicitly chooses Local. Publish target-scoped Project capabilities so renderer surfaces expose only actions supported by the active Host.
Publish target lifecycle transitions so Desktop clears stale projections during switches and reconnects. Canonicalize Project aliases across clients, and make the atomic profile document the visible persistence commit while keeping safe reads independent of writer serialization.
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from 3f7c664 to 112cef2CompareAugust 12, 2026 05:28
@M4n5ter

M4n5ter commented Aug 12, 2026

Copy link
Copy Markdown
MemberAuthor

Addressed the three follow-up P2 threads in 112cef2f7 and rebased onto current main.

  • Desktop now publishes the complete target lifecycle, clears stale Host projections before readiness, and refreshes them after activation or same-target reconnect
  • Project ID/alias resolution is shared across Host, Desktop, CLI run, and TUI, with Clients forwarding the canonical Project ID
  • Profile reads no longer wait on mutation locks; the atomic profile document is the visible commit point; credential input is bounded; and the redundant post-rename chmod is removed

I kept the existing fail-loud writer-lock policy instead of auto-stealing stale directories. The inline response records the TOCTOU reason and the resulting recovery contract.

Local validation after rebasing: all four affected workspace typechecks passed, the 52 focused boundary tests passed, Biome and diff checks passed. Before the rebase, the complete Runtime Host, CLI, and Desktop test suites also passed. CI is green on the rebased head.

@M4n5ter
M4n5ter merged commit 5df53c1 into mainAug 12, 2026
11 checks passed
@M4n5ter
M4n5ter deleted the feat/runtime-host-profiles branch August 12, 2026 05:45
@Astro-Han

Copy link
Copy Markdown
Contributor

A small post-merge follow-up from a fresh review of the final head: I found two P2 issues that may be worth addressing in a focused patch.

  1. Same-target reconnect currently clears Client-owned session and composer state.

    The owner publishes reconnecting → ready within the same target epoch, but the renderer performs a full Runtime Host reset for both events. That clears the active Session, messages, pending attachments, and pending quotes. A normal remote-network interruption can therefore return the user to the new-session screen and discard unsent composer state even though the execution authority did not change.

    The narrow fix is to reserve the full reset for an actual epoch/target-generation change. A same-epoch reconnect should pause and refresh Host-owned projections while preserving the active Session intent and Client-owned drafts.

  2. Some remote surfaces still expose Client-local path actions that Main will reject.

    The Project picker and Projects Settings now consume the target capability snapshot, but the title-bar/command-palette “Open Project Folder” actions and the Skills actions for opening local files/folders or importing a local source remain visible on a remote target. These are normally reachable controls that deterministically fail at the Main boundary.

    It would be cleaner for these surfaces to consume the same active-target local-path capability rather than adding independent remote checks.

Neither issue changes the core Runtime Host authority model, exact-target credential binding, or the merge decision. They look suitable for one focused follow-up rather than reopening the profile architecture.

Please feel free to push back if reconnect is intentionally defined as a full Client workspace reset, or if these local-path actions have another capability source I missed.

AI-assisted review disclosure

Codex performed the post-merge code inspection and delegated adversarial review. Astro-Han reviewed the synthesized evidence, recalibrated the findings to P2, and decided which follow-ups to include. The reproduction paths remain AI-assisted and should be verified while implementing the follow-up.

中文对照(默认折叠)

在最终 head 合并后的 fresh review 中,发现两个可以考虑用独立小补丁处理的 P2:

  1. 同一 target 的重连目前会清除 Client-owned Session 和 composer 状态。reconnecting → ready 没有改变 epoch,但 renderer 对两个事件都执行完整 reset,因此普通网络中断可能清除当前 Session、未发送附件和引用。建议只在 epoch/target generation 真正改变时完整 reset;同 epoch reconnect 只暂停和刷新 Host projection。

  2. 部分 remote surface 仍显示 Main 必然拒绝的 Client-local path 操作。Project picker 和 Projects Settings 已消费 capability snapshot,但标题栏/命令面板的 Open Project Folder,以及 Skills 中打开本地文件、文件夹和导入本地来源的操作仍然可见。建议让这些 surface 消费同一个 active-target local-path capability。

这两个问题不改变 Runtime Host 的核心 authority、exact-target credential binding 或此前的合并判断,适合作为一个 focused follow-up,而不是重新调整 profile 架构。

如果 reconnect 本来就被定义为完整 Client workspace reset,或者这些本地路径操作还有其他 capability 来源,也欢迎直接 push back。

本次由 Codex 完成合并后代码检查和多路对抗性复审;Astro-Han 复核综合证据、将问题重新校准为 P2,并决定纳入哪些 follow-up。具体复现路径仍属于 AI-assisted analysis,建议在实现 follow-up 时再次验证。

@liugddx

Copy link
Copy Markdown
Member

There is one reconnect boundary worth handling in a focused follow-up PR.

runtime-host-profiles:changed carries a target epoch, but the renderer handler currently clears state and starts fire-and-forget refreshes without retaining or checking that epoch. The individual refresh functions also commit unconditionally after await. Because a same-target reconnect keeps the same target epoch, a read that started on candidate A can complete after candidate B is ready and overwrite B's newer projection.

A minimal fix would give each candidate replacement its own generation and fence Host-scoped refresh commits against it, either at the IPC result boundary or in the renderer. The regression should hold an A read pending, reconnect and complete B's refresh, then release A and verify that A cannot replace B's state.

This does not require reverting this PR or reopening the broader profile design. A small follow-up PR covering the generation fence and one behavioral test should be enough.

简体中文

建议用一个聚焦的后续 PR 处理 remaining reconnect 边界。

runtime-host-profiles:changed 虽然携带 target epoch,但 renderer handler 当前只是清空状态并触发一组 fire-and-forget refresh,没有保存或校验该 epoch。各 refresh 在 await 后也会无条件提交。由于 same-target reconnect 复用同一个 target epoch,candidate A 上已经开始的读取可能在 candidate B ready 并完成刷新之后才返回,从而用 A 的旧投影覆盖 B。

最小修复是为每次 candidate replacement 增加独立 generation,并在 IPC result 边界或 renderer commit 前校验 generation。回归测试应挂起 A 的读取,让 B 重连并完成刷新,再释放 A,确认 A 不能覆盖 B 的状态。

这不需要回滚本 PR,也不需要重新展开整个 profile 设计。单独开一个小 PR,完成 generation fence 和一条行为测试即可。

Sign up for freeto 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.

3 participants

@M4n5ter@Astro-Han@liugddx
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat(runtime-host): add client-selectable host profiles - #2834

Merged
M4n5ter merged 7 commits into
mainfrom
feat/runtime-host-profiles
Aug 12, 2026
Merged

feat(runtime-host): add client-selectable host profiles#2834
M4n5ter merged 7 commits into
mainfrom
feat/runtime-host-profiles

Conversation

@M4n5ter

@M4n5terM4n5ter commented Aug 11, 2026

Copy link
Copy Markdown
Member
English

Summary

  • add a shared Runtime Host profile catalog for Desktop, CLI, and TUI
  • let Desktop switch between Local and remote Runtime Hosts without restarting
  • route remote work through Host-owned Project identities instead of Client-local paths
  • bind each access credential to an exact profile target and keep it outside profile documents
  • reset Host-owned Client projections at the target-generation boundary

Why

Runtime Host is now the execution authority, but Clients could still only select the implicit local Host. This change introduces the smallest shared target model needed for future remote Hosts while preserving a strict boundary: a remote Host path is never treated as a local Client path.

Each Desktop target generation owns its reconnect lifecycle, Session observations, Project policy, and local-path permissions. Switching reports the Host that is actually active even if selection persistence and rollback both fail. CLI and TUI omit Client-side cwd relocation for remote Sessions.

User impact

Desktop users can manage and switch Runtime Host profiles from Workspace settings. CLI and TUI users can select the same profiles with --host and select an existing remote Project with --project. Credentials are accepted through secure storage or environment input, never command-line arguments or profile JSON.

Validation

  • Runtime Host, Storage, CLI, and Desktop typechecks
  • focused Desktop, CLI/TUI, Runtime Host, and Storage regressions: 130 passed
  • complete production build
  • Biome and git diff --check

Refs #2522

简体中文

概要

  • 为 Desktop、CLI 和 TUI 增加共享的 Runtime Host profile catalog
  • Desktop 无需重启即可在 Local 与远程 Runtime Host 之间切换
  • 远程工作通过 Host 持有的 Project identity 路由,不再依赖 Client 本地路径
  • access credential 与精确的 profile target 绑定,并与 profile 文档分离保存
  • 在 target generation 边界清空并重载 Host-owned Client projections

原因

Runtime Host 已成为执行权威,但 Client 仍只能使用隐式的本地 Host。本次变更增加未来远程 Host 所需的最小共享目标模型,同时保留严格边界:远程 Host 路径绝不会被当作 Client 本地路径使用。

Desktop 的每个 target generation 独立持有 reconnect lifecycle、Session observations、Project policy 与本地路径权限。即使 selection 持久化与回滚同时失败,切换结果仍会报告实际活跃的 Host。CLI 与 TUI 不会对远程 Session 执行 Client 侧 cwd relocation。

用户影响

Desktop 用户可以在工作区设置中管理和切换 Runtime Host profiles。CLI 和 TUI 用户可以通过 --host 选择同一套 profiles,并通过 --project 选择已有的远程 Project。Credential 仅通过安全存储或环境变量输入,不会进入命令行参数或 profile JSON。

验证

  • Runtime Host、Storage、CLI 与 Desktop typecheck
  • Desktop、CLI/TUI、Runtime Host 与 Storage 定向回归:130 项通过
  • 完整 production build
  • Biome 与 git diff --check

关联 #2522

@M4n5ter
M4n5ter marked this pull request as ready for review August 12, 2026 00:35
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from 50f0923 to c71fa13CompareAugust 12, 2026 00:45

@Astro-HanAstro-Han 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.

The profile catalog itself looks sound. It keeps connection configuration on the Client, preserves the Runtime Host as the execution authority, and does not add another discovery or spawn path.

I think the remaining problems come from three missing boundaries rather than a collection of unrelated bugs.

  1. A target switch has no identity that follows its requests and results. Reconnecting to Host A and switching from A to B both look like a temporary handler gap. Commands can wait for B's handler, while reads started on A can complete after the switch and overwrite B's renderer state.

  2. Host-owned and Client-owned locations share the same representation. A plain cwd may be treated as a Host execution path, a Client filesystem path, a display value, or an autocomplete root. This breaks remote resume and also lets a remote capability frame supply a Client-local cwd despite advertising hostPathAccess: "none". Bare Project IDs have the same ownership problem when a global default is reused across Host roots.

  3. Desired profile selection, active Host readiness, and persisted credential state are mixed together. A profile or credential read failure currently rewrites the selection to Local, so a temporary storage failure can change execution authority. Profile and credential mutations also have commit points that can leave one side updated without enough metadata to recover the other.

I would keep the catalog, root pinning, credential binding, and per-target lifecycle. The smallest coherent revision is narrower:

  • Give each target generation an epoch. Commands must fail when their epoch is gone. Reconnectable reads may wait or retry only within the same epoch, and renderer reads must check the epoch before committing.
  • Split Client-local cwd from Host-owned execution location. Remote paths must not reach Client realpath, autocomplete, foreign-session scanning, or native capabilities unless the registered offer explicitly permits Host path access.
  • Keep a failed remote selection in an unavailable state. Persist Local only after an explicit user choice.
  • Keep remote Project policy root-scoped. Do not reuse the Client-global default, and hide workspace actions that remote main will reject.
  • Make profile and credential updates recoverable. Do not discard target metadata after a cleanup failure, and finish fallible preparation before committing profile metadata.

The cleaner end state is one Desktop owner publishing {epoch, target, readiness, candidate}. Boot, profile service, IPC, runtime policy, and renderer should consume that snapshot instead of storing separate versions of the active target. Execution locations should also be a discriminated type, so only a client-path can reach Client filesystem APIs.

I left inline comments at the smallest code sites that expose these boundaries.

The current red typecheck and workspace checks appear to reproduce on the base and are not attributed to this diff. The branch is still not green, but those failures are separate from the points above.

Disclosure: Codex performed the code inspection and adversarial analysis using several focused review passes. I (Astro-Han) reviewed and challenged the analysis over multiple rounds, selected the issues included here, and own the final review. I have not independently rerun every reproduction.

Comment threadapps/desktop/src/main/runtime-host-desktop-owner.ts Outdated
Comment threadapps/desktop/src/renderer/app-shell-effects.ts
Comment threadapps/desktop/src/main/runtime-host-native-capabilities.ts Outdated
Comment threadpackages/cli/src/runtime-host-session-driver.ts
Comment threadapps/desktop/src/main/runtime-host-profile-service.ts
Comment threadpackages/runtime-host/src/client/host-profile.ts Outdated

@liugddxliugddx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Follow-up adversarial review with findings anchored to the smallest changed lines. These complement the consolidated review and avoid duplicating existing inline comments.

简体中文

补充对抗性审查:以下问题锚定到最小变更行,作为汇总评论的可逐项 resolve 版本,并避免重复已有行级评论。

Comment threadpackages/cli/src/runtime-host-tui-context.ts
Comment threadapps/desktop/src/main/runtime-host-profile-service.ts Outdated
Comment threadapps/desktop/src/main/runtime-host-profile-service.ts Outdated
Comment threadpackages/cli/src/__tests__/runtime-host-run-command.test.ts Outdated
Comment threadapps/desktop/src/main/__tests__/runtime-host-desktop-owner.test.ts Outdated
Comment threadpackages/runtime-host/src/__tests__/host-profile.test.ts Outdated
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from e6857cb to 51be16bCompareAugust 12, 2026 03:12
@M4n5ter

Copy link
Copy Markdown
MemberAuthor

Addressed both review passes at 51be16b4d and rebased onto the latest main.

The revision now:

  • fences Desktop requests, results, and observations by target generation;
  • separates desired selection from the actually active Host, including the no-active-Host state;
  • keeps remote paths Host-owned across Desktop, CLI, TUI, and Client Capability calls;
  • makes profile targets immutable and credentials exact-target bound with recoverable mutation ordering;
  • removes the low-value remote-resume and object-identity tests called out in review.

Local validation passed: Runtime Host coverage (the only stale-build failure passed after rebuilding its updated dependency), CLI 233/233, Desktop 758/758, all three workspace typechecks, Biome, and git diff --check.

Could you please re-review the current head?

@Astro-HanAstro-Han 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.

The previous generation, path-authority, active-state, and credential-isolation findings are materially addressed in 51be16b4d.

Requests and results are now fenced by target epoch; remote Host paths no longer reach Client filesystem or capability APIs; rollback can represent no active Host; preference-write failure keeps the healthy Host active; and credentials are bound and tested against the exact immutable target. The focused tests now exercise the production boundaries rather than object identity or injected fakes.

I think the two remaining findings share one root cause rather than being isolated guards: the Runtime Host authority model has been updated, but two Client-facing boundaries still encode the former assumption that a valid local state always exists.

At startup, an unreadable selection is represented as Local because the resolution type cannot express “the authority is unknown.” In the project UI, the absence of a Client-visible path is interpreted as an unavailable Project, while Client-local actions remain visible, because the renderer infers capabilities from the old local Project representation instead of consuming capabilities published by the active target.

This creates the same failure in two forms: an unavailable or intentionally absent Client-local value is converted into Local authority instead of remaining an explicit unavailable or Host-owned state. The result is either a silent authority change or UI actions that the active Host cannot support.

From first principles, an unknown selection must not become an execution target, and the renderer should only expose operations granted by the active authority. I would therefore fix these at their natural boundaries rather than adding individual if (remote) checks:

  • Make startup resolution a closed ready | unavailable result. Only ready may contain a target; choosing Local should be a separate explicit transition.
  • Publish a small project-capability projection for the active target, covering directory actions, no-project allowance, local defaults, and path visibility. The renderer should consume that projection rather than infer authority from profile.kind, preferredPath, or failed calls.

I found two concrete consequences of the incomplete boundary:

  1. A failure to read the selection file still synthesizes Local as the selected target, and the startup recovery dialog skips that Local-shaped failure. An existing remote selection can therefore become Local execution authority without an explicit user choice.
  2. The remote Desktop project projection still exposes Client-local actions and permits “No project,” although remote Session creation requires a Host Project. The Settings view also interprets intentionally hidden Host paths as unavailable Projects.

These look like contained follow-ups to the Client state model, not reasons to revisit the profile catalog or Runtime Host architecture. Please feel free to push back if there is an existing authority or product contract that makes either state intentional.

Current CI is green. I am leaving this as a comment rather than Request Changes.

Disclosure: Codex performed the code inspection and adversarial analysis through four independent focused review passes. I (Astro-Han) reviewed the evidence, selected the findings included here, and made the final decision to submit this review. I did not independently rerun every reproduction.

Comment threadapps/desktop/src/main/runtime-host-profile-service.ts
Comment threadapps/desktop/src/main/project-management-service.ts
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from 51be16b to 3f7c664CompareAugust 12, 2026 04:24
@M4n5ter

Copy link
Copy Markdown
MemberAuthor

Addressed the latest authority-boundary review in 3f7c66418 and rebased onto current main.

Startup selection now remains explicitly unavailable on read/decode failure until the user chooses Local. The active target also publishes one Project capability snapshot consumed by Main and renderer surfaces, so remote Hosts no longer expose unsupported Client-directory, no-Project, local-default, or path-based behavior.

Local validation passed: UI 113/113, Desktop 760/760, focused regressions 15/15, Desktop/UI typechecks, Biome, and diff checks. All addressed review threads are resolved.

@Astro-Han@liugddx, could you please re-review the current head?

@M4n5ter
M4n5ter requested a review from Astro-HanAugust 12, 2026 04:25

@Astro-HanAstro-Han 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.

The two findings from my previous round are materially fixed in 3f7c66418. Startup resolution is now a closed ready | unavailable result, and Project capabilities are published by the active target and consumed by the main picker and Settings surfaces. CI is green and the PR is mergeable.

I also asked six fresh reviewers to inspect the entire PR independently, without relying on the previous findings. The core architecture held up well:

  • Runtime Host remains the sole execution authority.
  • Desired profile selection and the actually active target are separate.
  • Requests, results, observations, and Client Capability calls are fenced by target epoch.
  • Remote Host paths no longer enter Client filesystem authority.
  • Credentials are bound to an exact immutable target and remain outside profile documents.
  • Desktop, CLI, and TUI share one profile catalog rather than introducing another discovery or execution path.

The remaining observations mostly reduce to one architectural seam rather than a list of unrelated defects:

Target generation is now the authority for IPC, but it is not yet the single state model consumed by reconnect recovery, renderer projections, execution-location UI, and Project resolution.

For example, the owner knows when a target is connecting, ready, reconnecting, or unavailable, while consumers generally receive only a successful-activation invalidation. This leaves old projections visible during a switch, does not refresh all global projections after a same-target reconnect, and lets some renderer surfaces continue exposing local-path actions. Project identity is similarly canonical on the Host, but Desktop, CLI, and TUI still implement separate ID/alias and cwd rules.

I think the clean end state is one target-state projection owned by the Desktop target owner:

typeClientTargetState=|{readiness: "connecting";epoch: string;desiredProfileId: string;activeTarget?: ResolvedTarget;}|{readiness: "ready";epoch: string;desiredProfileId: string;activeTarget: ResolvedTarget;projectCapabilities: ProjectCapabilities;location: ExecutionLocation;}|{readiness: "unavailable";desiredProfileId: string;error: Error;};

Renderer invalidation, reconnect resynchronization, profile status, local-path actions, and the visible Host/execution location can then consume the same fact instead of inferring it from cached data, profile.kind, or a bare cwd.

The other independent concern is persistence. Profile metadata, selection, credential storage, and the directory lock currently have multiple durable commit points without automatic crash recovery. I would define one visible commit point, keep reads independent of the mutation lock where safe, bound all renderer-to-main input, and make credential replacement an explicit operation on an immutable target.

This is a large PR—82 files and roughly 5.3k changed lines—but I would not split it by Runtime Host, Desktop, CLI, and TUI. Those pieces form one authority change, and independently mergeable layers would create temporary incomplete states. I would instead shrink it through consolidation:

  • publish one target-state projection rather than adding more renderer-specific invalidations;
  • share one Project ID/alias resolver across Desktop, CLI run, and TUI;
  • represent execution location as client_path | host_project, allowing only the first to reach Client filesystem APIs;
  • remove the second location inference from the raw hostProfileId after the profile has already been resolved;
  • remove the write lock from safe read-only resolution and remove the redundant post-rename chmod.

I left three inline comments at the natural owner boundaries rather than filing every downstream symptom separately. All are non-blocking P2 observations. I’m approving the current revision; please feel free to push back if an existing invariant or recovery guarantee changes this analysis.

AI-assisted review disclosure

Codex performed the code inspection and six independent adversarial review passes. I, Astro-Han, reviewed and challenged the evidence, grouped the findings by their underlying invariants, selected the comments included here, and made the final decision to approve. I did not independently rerun every failure sequence described below.

中文对照

上一轮提出的两个问题已经在 3f7c66418 中得到实质修复:启动解析现在是闭合的 ready | unavailable 状态;Project capability 由 active target 发布,并被主项目选择器和设置页面消费。CI 全绿,PR 当前可以合并。

我又安排了六路互相独立的 fresh-eye 对抗性复审。整体架构经受住了审查:Runtime Host 仍是唯一执行权威;desired profile 与 active target 已分离;request、result、observation 和 Client Capability 都有 epoch fencing;远程 Host path 不再进入 Client 文件系统权限;credential 与精确且不可变的 target 绑定;Desktop、CLI、TUI 共用同一个 profile catalog。

剩余问题大多来自同一个架构边界:

target generation 已经是 IPC 的 authority,但还没有成为 reconnect、renderer projection、execution-location UI 和 Project resolution 共同消费的唯一状态模型。

最干净的终态是让 Desktop target owner 发布一个包含 epoch、desired profile、active target、readiness、Project capability 和 execution location 的闭合状态。Renderer 清理与刷新、重连恢复、profile 状态、本地路径操作和 Host/location 展示都消费这个事实,不再从旧缓存、profile.kind 或裸 cwd 推断。

另一个相对独立的问题是 persistence:profile metadata、selection、credential store 和目录锁目前存在多个 durable commit point,但没有自动的崩溃恢复协议。建议明确唯一可见提交点,让安全的只读解析不受 mutation lock 阻塞,统一限制 renderer-to-main 输入大小,并把 credential replacement 建模为 immutable target 上的独立操作。

这个 PR 的确很大,但不建议按 Runtime Host、Desktop、CLI、TUI 拆开,因为它们共同组成一个 authority change,分开合并会制造不安全的中间状态。更合适的是通过合并重复规则和删除平行推断来缩小复杂度。

下面只在三个自然 owner 上留下 inline comments,不把同一根因展开成十个局部问题。它们都是非阻塞的 P2。我会批准当前版本;如果已有不变量或恢复保证能够推翻这些分析,也欢迎作者直接说明或讨论。

AI 辅助审查披露

Codex 负责代码检查和六路独立的对抗性复审。Astro-Han 复核并质疑了相关证据,按共同不变量归并 findings,选择了最终评论,并作出批准决定。Astro-Han 没有独立重新执行下面描述的每一个失败序列。

Comment threadapps/desktop/src/main/runtime-host-desktop-owner.ts
Comment threadpackages/cli/src/runtime-host-run-command.ts Outdated
Comment threadpackages/runtime-host/src/client/host-profile.ts Outdated
Introduce a shared profile catalog for Desktop, CLI, and TUI, with credentials bound to an exact Host target. Desktop can switch targets live while keeping reconnect and session observation ownership isolated per target generation.
Remote targets operate through Project identities and do not inherit Client-local filesystem authority.
Keep Desktop projections, project policy, and rollback state aligned with the Runtime Host that is actually active. Remote clients no longer interpret Host-owned paths as local filesystem authority, including TUI and non-interactive resume flows.
Keep regression coverage at the public catalog and CLI boundaries while removing assertions that freeze internal credential key encoding and generic parser plumbing. Replace the stateful CLI fake with a capture-only dependency so the test reflects command ownership.
Represent remote connectivity as a discriminated TLS transport and bind credentials to that transport identity. This preserves a clear extension point for SSH and explicitly acknowledged insecure transports without ambiguous URL or boolean combinations.
Fence Desktop requests and projections by target generation, and represent desired selection independently from the active Host. Keep remote paths Host-owned across Desktop, CLI, TUI, and Client Capability calls.\n\nMake profile targets immutable and credentials exact-target bound so partial mutations remain explicit and recoverable without cleanup heuristics.
Keep unreadable Runtime Host selections unavailable until the user explicitly chooses Local. Publish target-scoped Project capabilities so renderer surfaces expose only actions supported by the active Host.
Publish target lifecycle transitions so Desktop clears stale projections during switches and reconnects. Canonicalize Project aliases across clients, and make the atomic profile document the visible persistence commit while keeping safe reads independent of writer serialization.
@M4n5ter
M4n5terforce-pushed the feat/runtime-host-profiles branch from 3f7c664 to 112cef2CompareAugust 12, 2026 05:28
@M4n5ter

M4n5ter commented Aug 12, 2026

Copy link
Copy Markdown
MemberAuthor

Addressed the three follow-up P2 threads in 112cef2f7 and rebased onto current main.

  • Desktop now publishes the complete target lifecycle, clears stale Host projections before readiness, and refreshes them after activation or same-target reconnect
  • Project ID/alias resolution is shared across Host, Desktop, CLI run, and TUI, with Clients forwarding the canonical Project ID
  • Profile reads no longer wait on mutation locks; the atomic profile document is the visible commit point; credential input is bounded; and the redundant post-rename chmod is removed

I kept the existing fail-loud writer-lock policy instead of auto-stealing stale directories. The inline response records the TOCTOU reason and the resulting recovery contract.

Local validation after rebasing: all four affected workspace typechecks passed, the 52 focused boundary tests passed, Biome and diff checks passed. Before the rebase, the complete Runtime Host, CLI, and Desktop test suites also passed. CI is green on the rebased head.

@M4n5ter
M4n5ter merged commit 5df53c1 into mainAug 12, 2026
11 checks passed
@M4n5ter
M4n5ter deleted the feat/runtime-host-profiles branch August 12, 2026 05:45
@Astro-Han

Copy link
Copy Markdown
Contributor

A small post-merge follow-up from a fresh review of the final head: I found two P2 issues that may be worth addressing in a focused patch.

  1. Same-target reconnect currently clears Client-owned session and composer state.

    The owner publishes reconnecting → ready within the same target epoch, but the renderer performs a full Runtime Host reset for both events. That clears the active Session, messages, pending attachments, and pending quotes. A normal remote-network interruption can therefore return the user to the new-session screen and discard unsent composer state even though the execution authority did not change.

    The narrow fix is to reserve the full reset for an actual epoch/target-generation change. A same-epoch reconnect should pause and refresh Host-owned projections while preserving the active Session intent and Client-owned drafts.

  2. Some remote surfaces still expose Client-local path actions that Main will reject.

    The Project picker and Projects Settings now consume the target capability snapshot, but the title-bar/command-palette “Open Project Folder” actions and the Skills actions for opening local files/folders or importing a local source remain visible on a remote target. These are normally reachable controls that deterministically fail at the Main boundary.

    It would be cleaner for these surfaces to consume the same active-target local-path capability rather than adding independent remote checks.

Neither issue changes the core Runtime Host authority model, exact-target credential binding, or the merge decision. They look suitable for one focused follow-up rather than reopening the profile architecture.

Please feel free to push back if reconnect is intentionally defined as a full Client workspace reset, or if these local-path actions have another capability source I missed.

AI-assisted review disclosure

Codex performed the post-merge code inspection and delegated adversarial review. Astro-Han reviewed the synthesized evidence, recalibrated the findings to P2, and decided which follow-ups to include. The reproduction paths remain AI-assisted and should be verified while implementing the follow-up.

中文对照(默认折叠)

在最终 head 合并后的 fresh review 中,发现两个可以考虑用独立小补丁处理的 P2:

  1. 同一 target 的重连目前会清除 Client-owned Session 和 composer 状态。reconnecting → ready 没有改变 epoch,但 renderer 对两个事件都执行完整 reset,因此普通网络中断可能清除当前 Session、未发送附件和引用。建议只在 epoch/target generation 真正改变时完整 reset;同 epoch reconnect 只暂停和刷新 Host projection。

  2. 部分 remote surface 仍显示 Main 必然拒绝的 Client-local path 操作。Project picker 和 Projects Settings 已消费 capability snapshot,但标题栏/命令面板的 Open Project Folder,以及 Skills 中打开本地文件、文件夹和导入本地来源的操作仍然可见。建议让这些 surface 消费同一个 active-target local-path capability。

这两个问题不改变 Runtime Host 的核心 authority、exact-target credential binding 或此前的合并判断,适合作为一个 focused follow-up,而不是重新调整 profile 架构。

如果 reconnect 本来就被定义为完整 Client workspace reset,或者这些本地路径操作还有其他 capability 来源,也欢迎直接 push back。

本次由 Codex 完成合并后代码检查和多路对抗性复审;Astro-Han 复核综合证据、将问题重新校准为 P2,并决定纳入哪些 follow-up。具体复现路径仍属于 AI-assisted analysis,建议在实现 follow-up 时再次验证。

@liugddx

Copy link
Copy Markdown
Member

There is one reconnect boundary worth handling in a focused follow-up PR.

runtime-host-profiles:changed carries a target epoch, but the renderer handler currently clears state and starts fire-and-forget refreshes without retaining or checking that epoch. The individual refresh functions also commit unconditionally after await. Because a same-target reconnect keeps the same target epoch, a read that started on candidate A can complete after candidate B is ready and overwrite B's newer projection.

A minimal fix would give each candidate replacement its own generation and fence Host-scoped refresh commits against it, either at the IPC result boundary or in the renderer. The regression should hold an A read pending, reconnect and complete B's refresh, then release A and verify that A cannot replace B's state.

This does not require reverting this PR or reopening the broader profile design. A small follow-up PR covering the generation fence and one behavioral test should be enough.

简体中文

建议用一个聚焦的后续 PR 处理 remaining reconnect 边界。

runtime-host-profiles:changed 虽然携带 target epoch,但 renderer handler 当前只是清空状态并触发一组 fire-and-forget refresh,没有保存或校验该 epoch。各 refresh 在 await 后也会无条件提交。由于 same-target reconnect 复用同一个 target epoch,candidate A 上已经开始的读取可能在 candidate B ready 并完成刷新之后才返回,从而用 A 的旧投影覆盖 B。

最小修复是为每次 candidate replacement 增加独立 generation,并在 IPC result 边界或 renderer commit 前校验 generation。回归测试应挂起 A 的读取,让 B 重连并完成刷新,再释放 A,确认 A 不能覆盖 B 的状态。

这不需要回滚本 PR,也不需要重新展开整个 profile 设计。单独开一个小 PR,完成 generation fence 和一条行为测试即可。

Sign up for freeto 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.

3 participants

@M4n5ter@Astro-Han@liugddx