You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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>
Copy file name to clipboardExpand all lines: docs/planning/clone-auth-header-config-editing.md
+23-22Lines changed: 23 additions & 22 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,7 +1,7 @@
1
1
# Clone Auth Header Config Editing
2
2
3
3
**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.
5
5
**Depends on:** Clone lineage (`cloned_from`, migration 021) and `manual_entry` install source — both already shipped
6
6
**Unblocks:** Editing/fixing auth headers on any `manual_entry` clone through the UI instead of raw `sqlite3`
7
7
@@ -21,7 +21,7 @@ Traced live (DB inspection + code reading, not guessed):
21
21
- 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.
22
22
- 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.
23
23
- 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).
25
25
26
26
---
27
27
@@ -33,7 +33,7 @@ Traced live (DB inspection + code reading, not guessed):
33
33
| 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 |
34
34
| 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 |
35
35
| 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 |
37
37
| 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 |
38
38
39
39
---
@@ -48,11 +48,10 @@ Traced live (DB inspection + code reading, not guessed):
48
48
- Warning banner + toast when a clone with auth-requiring parent/definition has empty `extra_headers`
-`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)
-`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
56
55
57
56
**Out:**
58
57
@@ -153,8 +152,8 @@ Exposed as a new Tauri command (desktop) and admin bridge command (web admin), p
153
152
|[`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 |
154
153
|[`apps/desktop/src/lib/api/spaces.ts`](../../apps/desktop/src/lib/api/spaces.ts)| New API shim for the DB-backed definition update command |
155
154
|[`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 |
|[`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 |
159
158
160
159
---
@@ -185,17 +184,19 @@ See [`pool-invalidation-and-session-survival.md`](./pool-invalidation-and-sessio
185
184
186
185
**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.
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:
191
190
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`
-`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
197
198
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.
199
200
200
201
---
201
202
@@ -212,8 +213,8 @@ Every underlying method already exists on `ServerAppService` and is already exer
|[`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 |
|[`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 |
218
219
|[`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 |
219
220
|[`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
226
227
## Related Documentation
227
228
228
229
-[`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
231
232
-[`user-config-sync-collision-fix.md`](./user-config-sync-collision-fix.md) — separate `UserSpaceSyncService` bug fixed same layer; `ServerConfigUpdated` emission already shipped
232
233
-[`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