Skip to content

Commit ae445a1

Browse files
committed
docs(planning): mark web admin clone parity as shipped
Phase 5 and leftovers item 4 landed in f024d9e; keep the planning docs from describing those stubs as still open. Signed-off-by: crimsonsunset <jsangio1@gmail.com>
1 parent f024d9e commit ae445a1

2 files changed

Lines changed: 28 additions & 26 deletions

File tree

docs/planning/clone-auth-header-config-editing.md

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Clone Auth Header Config Editing
22

33
**Last Updated:** Aug 20, 2026
4-
**Status:** Desktop shipped. Web admin is in scope and not yet done — clone auth should work on web, not just desktop. Clone-time seeding (`clone_server` rewrites `source`, seeds `extra_headers`/`input_values`), the DB-backed `update_definition()` save path, the Definition-editor save dispatch, and the empty-header warning banner are all in code, but only reachable through desktop Tauri. Decision 4 pool half **shipped** (`dcc2977`, [`pool-invalidation-and-session-survival.md`](./pool-invalidation-and-session-survival.md)); `retry_connection` uses `reconnect_fresh` on both runtimes. Phase 5 below is the remaining work: `clone_server`, `is_clone_id_available`, `suggest_clone_suffix`, `list_clone_dependents`, and `set_server_display_name` all return `"...not yet available"` in the web admin bridge (`command_bridge/write.rs` L1036/L1054, `command_bridge/read.rs` L805-831) even though every underlying `ServerAppService` method they need already exists and is called the same way by desktop Tauri.
4+
**Status:** Shipped — desktop and web admin. Phase 5 (`f024d9e`) un-stubbed `clone_server`, `set_server_display_name`, `is_clone_id_available`, `suggest_clone_suffix`, and `list_clone_dependents` in the admin bridge, closing the last desktop-only gap. Clone auth now works identically from web admin and desktop. Decision 4 pool half **shipped** (`dcc2977`, [`pool-invalidation-and-session-survival.md`](./pool-invalidation-and-session-survival.md)); `retry_connection` uses `reconnect_fresh` on both runtimes. Bonus fix landed in the same commit: `save_server_inputs` was silently dropping `display_name_override` on both runtimes (desktop's Tauri command never declared the param; `update_config()` has no such param either) and dropping `update_policy`/`pinned_version` on web admin specifically (hardcoded to `None, None`) — all three now forward correctly on both runtimes.
55
**Depends on:** Clone lineage (`cloned_from`, migration 021) and `manual_entry` install source — both already shipped
66
**Unblocks:** Editing/fixing auth headers on any `manual_entry` clone through the UI instead of raw `sqlite3`
77

@@ -21,7 +21,7 @@ Traced live (DB inspection + code reading, not guessed):
2121
- The Definition editor gates edit vs. read-only purely on that inherited tag — `isEditable = server.source.type === 'UserSpace'` (`ServerDefinitionModal.tsx:80`), `canEditDefinition={server.source.type === 'UserSpace'}` (`ServersPage.tsx:1967`) — never checking `installation_source`. So the UI shows an "editable" definition, but Save calls `updateServerInConfig()``update_server_in_config` (`apps/desktop/src-tauri/src/commands/space.rs:322`, mirrored in `crates/mcpmux-gateway/src/admin/command_bridge/space.rs:182`), which only reads/writes `spaces/*.json`. The clone has no key there → `"not found in config"`. Every `manual_entry` clone hits this, not just this one server.
2222
- Separately, `clone_server()` copies definition/lineage but **not** `extra_headers`, `input_values`, `env_overrides`, `args_append`, or credentials (`server.rs:413-420`) — the new row starts with `extra_headers: {}`. There is **no runtime fallback to the parent's `extra_headers`**`build_transport_config()` (`crates/mcpmux-gateway/src/pool/transport/resolution.rs:47-127`) only reads the clone's own row and merges `installed.extra_headers` last (line 127). The wrong-project behavior came from the clone's copied `cached_definition` carrying the parent's baked-in header/template values forward while the clone's own override column stayed empty — not from any live parent lookup. Working sibling `posthog-personal-gait` has both `Authorization` and `x-posthog-project-id` set in its **own** `extra_headers`, proving the override mechanism works fine once populated.
2323
- The Configure modal (`ServersPage.tsx`, `save_server_inputs``update_config()` at `server.rs:217-269`) already has key/value editors for `extra_headers` (HTTP transports) and does correctly persist per-clone overrides — the gap is that nothing seeds or prompts for them at clone time, and nothing warns when they're left empty.
24-
- After patching `cached_definition` directly via SQL, a plain client-side reconnect/retry returned "Connection closed"; only killing and relaunching `McpMux.app` picked up the change. Root cause at investigation: `PoolService::connect_server` returns early whenever `is_healthy()`, and `ServerConfigUpdated` was UI-toast-only. **Shipped since:** `ServerConfigUpdatedHandler` evicts + `reconnect_fresh`; `UserSpaceSync` emits the event; `LiveGatewayWriteRuntime::retry_connection` is implemented; Definition editor edit/save and the Configure-side seeding/warning UI are also implemented. Only the web-admin `clone_server`/`set_server_display_name` stubs remain (see Status above).
24+
- After patching `cached_definition` directly via SQL, a plain client-side reconnect/retry returned "Connection closed"; only killing and relaunching `McpMux.app` picked up the change. Root cause at investigation: `PoolService::connect_server` returns early whenever `is_healthy()`, and `ServerConfigUpdated` was UI-toast-only. **Shipped since:** `ServerConfigUpdatedHandler` evicts + `reconnect_fresh`; `UserSpaceSync` emits the event; `LiveGatewayWriteRuntime::retry_connection` is implemented; Definition editor edit/save and the Configure-side seeding/warning UI are also implemented; web-admin `clone_server`/`set_server_display_name` and the other clone bridge stubs are also implemented (see Status above).
2525

2626
---
2727

@@ -33,7 +33,7 @@ Traced live (DB inspection + code reading, not guessed):
3333
| 2 | Clone creation: auth seeding | **Copy parent's `extra_headers` / `input_values`** into the new clone as editable starting values | Directly prevents the exact bug — a header-auth'd clone no longer starts silently blank; user still must swap the project-specific value but isn't starting from nothing |
3434
| 3 | Empty/inherited-auth footgun | **Warn**, don't block | A banner/toast when a clone with empty `extra_headers` is enabled while its parent/definition required auth headers. Fail-closed was rejected — some clones legitimately don't need headers (e.g. stdio env-only auth), and a hard block would break those |
3535
| 4 | Pool invalidation on save | **Auto-evict + reconnect on `ServerConfigUpdated`** | **Shipped** (`dcc2977`). Handler `reconnect_fresh` + `UserSpaceSync` emit. Clone definition-save still needs to emit the same event when that path lands |
36-
| 5 | Scope | **Desktop + web admin parity, both required** | Clone auth is meant to work identically from the web admin UI, not just desktop. `retry_connection` already has parity (`reconnect_fresh` on both). Clone create/list/display-name in `command_bridge/{read,write}.rs` do not — Phase 5 closes that gap. Not tracked in `dev-to-main-port.md`; its Phase 6 is desktop-only cloning UI and never touches these bridge functions |
36+
| 5 | Scope | **Desktop + web admin parity, both required** | Clone auth is meant to work identically from the web admin UI, not just desktop. `retry_connection` already had parity (`reconnect_fresh` on both). Clone create/list/display-name in `command_bridge/{read,write}.rs` did not — **shipped** (`f024d9e`), closing that gap. Not tracked in `dev-to-main-port.md`; its Phase 6 is desktop-only cloning UI and never touches these bridge functions |
3737
| 6 | Clone-time `source` rewrite | **Yes**`clone_server()` rewrites the copied definition's embedded `source` to reflect the clone's own storage (not `UserSpace`) | Removes the root cause of the false editability signal at its origin, so the UI gate (`isEditable`/`canEditDefinition`) stays correct without needing to special-case `installation_source` everywhere it's checked |
3838

3939
---
@@ -48,11 +48,10 @@ Traced live (DB inspection + code reading, not guessed):
4848
- Warning banner + toast when a clone with auth-requiring parent/definition has empty `extra_headers`
4949
- `ServerConfigUpdated` gateway subscriber + `reconnect_fresh`**shipped**
5050
- `UserSpaceSync` emit + `LiveGatewayWriteRuntime::retry_connection`**shipped**
51-
52-
**In (not shipped — Phase 5):**
53-
- `command_bridge/write.rs`: un-stub `clone_server` and `set_server_display_name` — call `ctx.services.server().clone_server(...)` / `.set_display_name_override(...)`, same calls desktop Tauri already makes
54-
- `command_bridge/read.rs`: un-stub `is_clone_id_available`, `suggest_clone_suffix`, `list_clone_dependents` the same way
55-
- Web admin UI: clone wizard + display-name edit surfaces, wired to the now-working bridge commands (confirm these exist on the web admin frontend or need building)
51+
- `command_bridge/write.rs`: `clone_server` and `set_server_display_name` un-stubbed, calling `ctx.services.server().clone_server(...)` / `.set_display_name_override(...)`**shipped** (`f024d9e`)
52+
- `command_bridge/read.rs`: `is_clone_id_available`, `suggest_clone_suffix`, `list_clone_dependents` un-stubbed the same way — **shipped** (`f024d9e`)
53+
- Web admin UI: clone wizard (`CloneAccountModal.tsx`) and display-name field (Configure modal) already existed in the shared SPA with no `isTauri()` gating and were already wired to the bridge routes — no new frontend work needed, just backend un-stubbing
54+
- Bonus fix in the same commit: `save_server_inputs` now forwards `display_name_override` (via a follow-up `set_display_name_override` call) and `update_policy`/`pinned_version` (previously hardcoded to `None, None` on web admin) on both desktop and web admin
5655

5756
**Out:**
5857

@@ -153,8 +152,8 @@ Exposed as a new Tauri command (desktop) and admin bridge command (web admin), p
153152
| [`apps/desktop/src/features/servers/CloneAccountModal.tsx`](../../apps/desktop/src/features/servers/CloneAccountModal.tsx) | Add header/input seeding step or pre-fill Configure with parent's values post-clone |
154153
| [`apps/desktop/src/lib/api/spaces.ts`](../../apps/desktop/src/lib/api/spaces.ts) | New API shim for the DB-backed definition update command |
155154
| [`apps/desktop/src/lib/backend/events/useDomainEvents.ts`](../../apps/desktop/src/lib/backend/events/useDomainEvents.ts) | Confirm `config_updated` payload shape is sufficient for the new reconnect-on-save handler |
156-
| [`crates/mcpmux-gateway/src/admin/command_bridge/write.rs`](../../crates/mcpmux-gateway/src/admin/command_bridge/write.rs) | Phase 5: un-stub `clone_server` (L1052-1055), `set_server_display_name` (L1030-1037) |
157-
| [`crates/mcpmux-gateway/src/admin/command_bridge/read.rs`](../../crates/mcpmux-gateway/src/admin/command_bridge/read.rs) | Phase 5: un-stub `is_clone_id_available`, `suggest_clone_suffix`, `list_clone_dependents` (L805-831) |
155+
| [`crates/mcpmux-gateway/src/admin/command_bridge/write.rs`](../../crates/mcpmux-gateway/src/admin/command_bridge/write.rs) | Phase 5 **shipped** (`f024d9e`): un-stubbed `clone_server`, `set_server_display_name`; fixed `save_server_inputs` to forward `display_name_override`/`update_policy`/`pinned_version` |
156+
| [`crates/mcpmux-gateway/src/admin/command_bridge/read.rs`](../../crates/mcpmux-gateway/src/admin/command_bridge/read.rs) | Phase 5 **shipped** (`f024d9e`): un-stubbed `is_clone_id_available`, `suggest_clone_suffix`, `list_clone_dependents` |
158157
| [`apps/desktop/src-tauri/src/commands/server_clone.rs`](../../apps/desktop/src-tauri/src/commands/server_clone.rs) | Phase 5 reference implementation — the admin bridge calls the same `ServerAppService` methods this file already calls |
159158

160159
---
@@ -185,17 +184,19 @@ See [`pool-invalidation-and-session-survival.md`](./pool-invalidation-and-sessio
185184

186185
**Outcome (landed):** A clone left with genuinely empty required headers surfaces a visible warning instead of connecting silently against the wrong (or missing) credentials. No frontend test coverage for this warning logic yet.
187186

188-
### Phase 5 — Web admin clone parity (~half day)
187+
### Phase 5 — Web admin clone parity **shipped** (`f024d9e`)
189188

190-
Every underlying method already exists on `ServerAppService` and is already exercised by desktop Tauri (`apps/desktop/src-tauri/src/commands/server_clone.rs`) — this is a mechanical port to the admin bridge, not new logic:
189+
Mechanical port of the desktop Tauri clone commands onto the admin bridge, exactly as scoped — no new logic:
191190

192-
- `command_bridge/write.rs`: `clone_server(ctx, body)` calls `ctx.services.server().clone_server(space_uuid, &body.source_server_id, &body.suffix, body.alias.as_deref(), body.display_name.as_deref())`; `set_server_display_name(ctx, id, body)` calls `.set_display_name_override(&server_id, ...)` — same signatures the Tauri commands already use, just swap `State<ServerAppService>` for `ctx.services.server()`
193-
- `command_bridge/read.rs`: `is_clone_id_available`, `suggest_clone_suffix`, `list_clone_dependents` — same pattern, delegate to the existing service methods instead of the `Err(anyhow!("...not yet available"))` stub
194-
- Remove the `// ponytail: clone_server lands in Phase 6` comments on all five stubs — this is that phase, and it is Phase 5 in this doc, not `dev-to-main-port.md`'s Phase 6 (see Decision 5)
195-
- Confirm the web admin frontend has (or needs) clone wizard / display-name-edit UI wired to these commands — if the UI doesn't exist yet, that's an additional sub-task, not just a backend un-stub
196-
- `pnpm test:rust` targeted on `command_bridge`; `pnpm lint`
191+
- `command_bridge/write.rs`: `clone_server(ctx, body)` calls `ctx.services.server().clone_server(space_uuid, &body.source_server_id, &body.suffix, body.alias.as_deref(), body.display_name.as_deref())`; `set_server_display_name(ctx, id, body)` calls `.set_display_name_override(...)`
192+
- `command_bridge/read.rs`: `is_clone_id_available`, `suggest_clone_suffix`, `list_clone_dependents` delegate to the existing service methods
193+
- Ponytail "Phase 6" stub comments removed on all five
194+
- Web admin frontend already had the clone wizard (`CloneAccountModal.tsx`) and display-name field (Configure modal) with no `isTauri()` gating, already wired to these bridge routes — confirmed no frontend work was needed
195+
- Found in the process: `save_server_inputs` was silently dropping `display_name_override` on both runtimes (desktop's Tauri command never declared the param) and `update_policy`/`pinned_version` on web admin specifically (hardcoded to `None, None`) — fixed alongside the un-stubbing since it's the same code path
196+
- Added 4 unit tests at the `ServerAppService` layer (`is_clone_id_available`, `suggest_clone_suffix`, `list_clone_dependents`, `set_display_name_override`) — no `command_bridge`-level test harness exists yet, so tests target the actual logic layer the bridge delegates to
197+
- `cargo test`, `cargo clippy -D warnings`, `cargo fmt --check` all clean on `mcpmux-core`/`mcpmux-gateway`/desktop Tauri crate; `tests/ts/admin-transport.test.ts` (118 tests) unaffected
197198

198-
**Outcome:** Cloning a server, checking clone-id availability, listing clone dependents, and overriding a display name all work identically from the web admin UI and from desktop. No `command_bridge` function returns "not yet available" for clone operations.
199+
**Outcome (landed):** Cloning a server, checking clone-id availability, listing clone dependents, and overriding a display name all work identically from the web admin UI and from desktop. No `command_bridge` function returns "not yet available" for clone operations.
199200

200201
---
201202

@@ -212,8 +213,8 @@ Every underlying method already exists on `ServerAppService` and is already exer
212213
| [`crates/mcpmux-gateway/src/pool/service.rs`](../../crates/mcpmux-gateway/src/pool/service.rs) | `connect_server()` L267-297 — healthy-instance reuse (`reused: true` L291) skips config reload unless evicted first; `remove_instance()` L344 |
213214
| [`apps/desktop/src-tauri/src/commands/server_manager.rs`](../../apps/desktop/src-tauri/src/commands/server_manager.rs) | `retry_connection()` L512-545 — existing evict-then-reconnect pattern this fix generalizes via the event subscriber |
214215
| [`crates/mcpmux-gateway/src/admin/write_runtime.rs`](../../crates/mcpmux-gateway/src/admin/write_runtime.rs) | `retry_connection` implemented (`reconnect_fresh`) |
215-
| [`crates/mcpmux-gateway/src/admin/command_bridge/write.rs`](../../crates/mcpmux-gateway/src/admin/command_bridge/write.rs) | `clone_server` / `set_server_display_name` still stubbed (`"...not yet available"`) — Phase 5 target |
216-
| [`crates/mcpmux-gateway/src/admin/command_bridge/read.rs`](../../crates/mcpmux-gateway/src/admin/command_bridge/read.rs) | `is_clone_id_available` / `suggest_clone_suffix` / `list_clone_dependents` still stubbed — Phase 5 target |
216+
| [`crates/mcpmux-gateway/src/admin/command_bridge/write.rs`](../../crates/mcpmux-gateway/src/admin/command_bridge/write.rs) | `clone_server` / `set_server_display_name` un-stubbed — Phase 5 shipped (`f024d9e`) |
217+
| [`crates/mcpmux-gateway/src/admin/command_bridge/read.rs`](../../crates/mcpmux-gateway/src/admin/command_bridge/read.rs) | `is_clone_id_available` / `suggest_clone_suffix` / `list_clone_dependents` un-stubbed — Phase 5 shipped (`f024d9e`) |
217218
| [`apps/desktop/src-tauri/src/commands/space.rs`](../../apps/desktop/src-tauri/src/commands/space.rs) | `update_server_in_config` error site L322 — the exact "not found in config" message users hit today |
218219
| [`crates/mcpmux-gateway/src/admin/command_bridge/space.rs`](../../crates/mcpmux-gateway/src/admin/command_bridge/space.rs) | Same error, web-admin bridge copy, L182 |
219220
| [`apps/desktop/src/components/ServerDefinitionModal.tsx`](../../apps/desktop/src/components/ServerDefinitionModal.tsx) | `isEditable` gate L80 — root of the false-affordance bug |
@@ -226,7 +227,7 @@ Every underlying method already exists on `ServerAppService` and is already exer
226227
## Related Documentation
227228

228229
- [`pool-invalidation-and-session-survival.md`](./pool-invalidation-and-session-survival.md) — Decision 4 pool reconnect, shipped
229-
- [`dev-to-main-port.md`](./dev-to-main-port.md) — original clone lineage work (migration 021, `cloned_from`), Phase 6 desktop cloning UI. Its Phase 6 does not cover the web-admin `command_bridge/{read,write}.rs` stubs — those are Phase 5 in this doc
230-
- [`resilience-routing-leftovers.md`](./resilience-routing-leftovers.md)this doc's web-admin clone gap is now Phase 5 here, not parked; that doc should be updated to stop calling it frozen
230+
- [`dev-to-main-port.md`](./dev-to-main-port.md) — original clone lineage work (migration 021, `cloned_from`), Phase 6 desktop cloning UI. Its Phase 6 does not cover the web-admin `command_bridge/{read,write}.rs` stubs — those were Phase 5 in this doc, now shipped
231+
- [`resilience-routing-leftovers.md`](./resilience-routing-leftovers.md)item 4 there tracked this same Phase 5 work, now shipped in both docs
231232
- [`user-config-sync-collision-fix.md`](./user-config-sync-collision-fix.md) — separate `UserSpaceSyncService` bug fixed same layer; `ServerConfigUpdated` emission already shipped
232233
- [`dev-rebased-post-port-completion.md`](./dev-rebased-post-port-completion.md) — QA checklist item "Clone account — independent config" this fix is meant to finally satisfy

0 commit comments

Comments
 (0)