page:docs-specs-platform-gaps-research-delta-map
Research dossier: Interface delta map — fabric adapters/kradle/genty-shells vs t3code reference
Date: 2026-09-27 · Author: platform-gaps spec research run · Status: input to 10-t3code-migration.md (and Gate A adoption-model proposal)
Research dossier: Interface delta map — fabric adapters/kradle/genty-shells vs t3code
Date: 2026-09-27 · Author: platform-gaps spec research run · Status: input to 10-t3code-migration.md (and Gate A adoption-model proposal)
Builds on and does not repeat: research/fabric-current-state.md (R1, fabric HEAD 5e46b998) and research/t3code-architecture.md (R2, snapshot 2026-09-27). Citations: fabric: paths are relative to C:/work/fabric; t3: paths are relative to the t3code snapshot at C:/Users/tmusk/AppData/Local/Temp/claude/C--work-company/368e0399-fd11-4960-8b4d-b4dcf1d79c43/scratchpad/t3code. All new path:line cites below were read at those revisions for this dossier; claims imported from R1/R2 carry their original cites.
**Overlap % method (read this before quoting the numbers):** for each row I enumerated the functional sub-capabilities of both sides (the checklists in each section), classified each as SHARED (both implement a recognizable version), FABRIC-ONLY, or T3-ONLY, and computed overlap = SHARED / (SHARED + FABRIC-ONLY + T3-ONLY), rounded to 5%. It is a *capability-count* figure, not LOC — by LOC every overlap would round to ~0 because t3code's surfaces are 5–20× larger (R2 §2). The checklists are printed so the spec author can re-weight.
---
0. Master delta table
| Row | Fabric side | t3code side | Overlap | Must-survive (fabric-unique) | Gained (t3-unique) | Primary ACP/interface seam |
|---|---|---|---|---|---|---|
| **A** | adapters harness layer: 22 drive-side codec adapters + 12 observe-side hook adapters + canonical 49-type AgentEvent union | packages/contracts (providerRuntime.ts) + packages/effect-acp + apps/server/src/provider/* (6 drivers, generic ACP runtime) | **~45%** | Hook/observe lane for foreign-driven sessions; journal/run binding; trust-signed policy gates; cost records; atlas graph coupling | ACP client+agent protocol impl; Codex app-server typed protocol; drive-side approvals (CanUseTool); provider installers/updaters; usage/quota ingestion | Fabric ProviderAdapter-equivalent contract: ACP lane (Cursor/Grok/Antigravity) + 3 bespoke lanes (Claude SDK, Codex app-server, OpenCode HTTP); AgentEvent becomes a projection of ACP/ProviderRuntimeEvent (D6) |
| **B** | kradle control plane: 69 core controllers, 81 Next.js API route files under orgs/[org], K8s CR resource model, plus adapters/gateway 17-frame WS protocol | apps/server (event-sourced decider/projector, SQLite) + packages/contracts RPC (144+8 WS methods) + environment auth | **~30%** | Multi-tenant org control plane (K8s namespaces, Helm/GKE); 7-action approval gate taxonomy; secrets/policy/exception-request/report surfaces; gitops; inference/virtual-model routing; journal-derived kanban | Event-sourced command/decider/projector/reactor core with replay-forever; typed single wire boundary (Effect Schema); capability-flag versioning; checkpoints; PR domain; DPoP auth; terminal; device control | t3code environment server becomes the **single-node environment** under kradle's multi-tenant plane; contracts environment descriptor + capability flags = the kradle↔environment boundary; approvals bridged to babysitter signed breakpoints |
| **C** | genty client shells (web-app + Electron scaffold + 7 L0 device shells) + genty/ui GatewayClient + kradle/web REST/SSE data layer | packages/client-runtime (connection supervisor, ~30 atom state services) + apps/web (318k LOC) + apps/desktop (backend-bundling Electron) | **~20%** | Multi-org UI semantics; approval/policy/observability UI surfaces; org-admin REST/SSE pages; react-native WS transport; journal kanban views | Multi-environment connection catalog + supervisor; reactive atom state layer; composer/diff/terminal-wasm/checkpoint UI; desktop that bundles its own server | client-runtime platform/ service interfaces = shell plug-point; connection registry treats each kradle-managed environment as a t3code environment; kradle-web keeps org-admin, thread/run UX moves to adapted apps/web |
| **D** | Remote access: **absent** except gateway token auth + LAN short-code pairing frames | packages/ssh, packages/tailscale, apps/server/src/cloud + infra/relay (T3 Connect broker) | **~5%** | Trust-core key/chain machinery must become the signing substrate (not duplicated); org-awareness of any hosted relay | All four remote routes: DPoP direct pairing, tailscale endpoint, ssh launch+forward, hosted relay (broker-not-proxy, cloudflared) | Gateway auth/ + pairing/ replaced by t3code environment auth (DPoP + pairing grants); ssh/tailscale packages extractable as-is (dep: effect only) |
---
Row A — fabric `adapters` harness layer vs t3code `contracts` + `effect-acp` + provider adapters
A.1 What fabric actually has (corrects a simplification in R1)
R1 §3.1 describes the adapters family primarily through the hooks lane. The drive lane is real and larger than R1 implies:
- **Drive-side adapter contract.**
fabric:packages/adapters/core/src/adapter-types.ts:24definesBaseAgentAdapterInterfacewith identity,capabilities(:35),detectAuth()(:42), session-file parsing (sessionDir/parseSessionFile/listSessionFiles), config read/write, model discovery, and host-detection env signals; three adapter kinds:SubprocessAdapter(:79),RemoteAdapterwithconnect/startServer/healthCheck(:93-104),ProgrammaticAdapter(:168-169). - **RunHandle.**
adapter.run()returns aRunHandlethat is simultaneouslyAsyncIterable<AgentEvent>, a typed event emitter, and a thenable resolvingRunResult(fabric:packages/adapters/core/src/run-handle.ts:1-8, interface at:150), including deferred-prompt steering (DeferredPromptTarget = 'next-turn' | 'after-tool' | 'after-response',run-handle.ts:44-51). - **Canonical event model.** 49 event interfaces extending
BaseEvent(measured:grep -c "extends BaseEvent"= 49) infabric:packages/adapters/core/src/events.ts— session lifecycle (:21-46), turns/steps (:57-75), message/thinking deltas (:85-115), tool calls (:124-153), file ops (:164-186), shell (:196-212), MCP (:222-238), subagents (:250-265) — unioned asAgentEventatevents.ts:411. This is the "current adapters event model" that D6 demotes to an internal projection. - **22 concrete drive adapters** in
fabric:packages/adapters/codecs/src/(measured:ls *-adapter.ts | wc -l= 22): incl.claude-agent-sdk-adapter.ts,codex-sdk-adapter.ts,codex-websocket-adapter.ts,opencode-http-adapter.ts,cursor-adapter.ts, plus subprocess adapters for gemini/copilot/droid/amp/omp/etc., self-registering via a global factory registry (fabric:packages/adapters/codecs/src/base-adapter.ts:61, base class:75). - **12 observe-side hook adapters** (
fabric:packages/adapters/hooks/adapter-{antigravity,claude,codex,copilot,cursor,gemini,genty,hermes,oh-my-pi,openclaw,opencode,pi}, directory listing) that instrument a harness the *user* is driving and feed events/hooks back into runs — R1 §3.1. - **No ACP anywhere** (R1 §3.1: zero hits for ACP under
packages/adapters— re-confirmed premise for D6 greenfield).
A.2 t3code side (from R2, key cites)
ProviderAdapter contract (t3:apps/server/src/provider/Services/ProviderAdapter.ts:1-60) with typed capabilities — sessionModelSwitch, promptlessTurnContinuation, supportsConversationRollback (:46-56) — and a compaction contract (native start-call vs slash-command, :36-44); 6 built-in drivers (t3:apps/server/src/provider/builtInDrivers.ts:49-56); one generic ACP runtime + per-provider extension modules (R2 §4); packages/effect-acp implementing ACP **both directions** — client for driving agents, agent for being one (R2 §6); packages/effect-codex-app-server as the typed Codex lane (R2 §7); canonical ProviderRuntimeEvent surface in t3:packages/contracts/src/providerRuntime.ts (1,234 LOC, R2 §5).
A.3 Sub-capability checklist
| Sub-capability | Fabric | t3code | Class |
|---|---|---|---|
| Per-harness adapter registry + factory | base-adapter.ts:61 | builtInDrivers.ts:49-56 + ProviderInstanceRegistry | SHARED |
| Canonical normalized event stream | events.ts:411 (49 types) | providerRuntime.ts + Normalizer.ts | SHARED |
| Capability declaration per harness | adapter-types.ts:35 | ProviderAdapter.ts:46-56 | SHARED |
| Auth detection / guidance | adapter-types.ts:42-43 | ProviderAuthService + per-provider flows (R2 §4) | SHARED |
| Harness install/update | codecs/src/adapter-install.ts (import at base-adapter.ts:44) | installers incl. Antigravity version-leased downloads (AntigravityInstallation.ts) | SHARED (t3 deeper) |
| Session-file parsing of harness-native session dirs | adapter-types.ts:46-49 | agentSessions.ts import (claudeAgent/codex only, R2 §5) | SHARED (fabric broader) |
| Run steering (deferred prompts) | run-handle.ts:44-51 | turn dispatch + async question protocol (R2 §4 Codex row) | SHARED |
| Cost/token accounting on the event stream | run-handle.ts:21-27 (TokenUsageSummary), CostRecord (run-handle.ts:9) | turn token usage in providerRuntime.ts; usage/quota ingestion subsystem (R2 §3) | SHARED (different shape) |
| **ACP protocol implementation (client+agent)** | — | effect-acp (R2 §6) | T3-ONLY |
| **Typed Codex app-server protocol** | — (fabric codex lane is CLI/hooks/WebSocket: codecs/src/codex-websocket-adapter.ts) | effect-codex-app-server (R2 §7) | T3-ONLY |
| **Drive-side permission callbacks** (approve tool use mid-turn) | — in the drive lane (approvals live in hooks/policy/breakpoints, Row B) | Claude CanUseTool, ACP permission modes (R2 §4) | T3-ONLY |
| **Workspace checkpoint coordination with conversation rollback** | — | checkpointing/CheckpointStore.ts:1-15 + revert rules (R2 §3.4) | T3-ONLY |
| **MCP endpoint injected into every provider session** | — (fabric has MCP plugins, codecs/src/mcp-plugins.ts, not an injected control endpoint) | mcp/McpProviderSession.ts:1-12 | T3-ONLY |
| **Observe-side hook adapters for foreign-driven sessions** | 12 hook adapters (dir listing above); R1 §3.1 | — (t3code only drives sessions it started) | FABRIC-ONLY |
| **Journal/run binding of harness events** | stop-hook → session → run binding (babysitter-sdk/src/harness/hooks/stopHookHandler.ts:209-225, R1 H7) | — (events bind to t3code's own SQLite threads, not an external ledger) | FABRIC-ONLY |
| **Trust-signed policy gating of tools/MCP** | adapters/policy/src/proven-bridge.ts:92-119 (human-root chain), enforcement wiring R1 C3 | — (approvals are session-scoped UX, not signed) | FABRIC-ONLY |
| **Model-proxy transport with attestation seam** | adapters/transport/src/server.ts:1596-1607 (R1 H10) | — | FABRIC-ONLY |
| **Graph-derived harness metadata (atlas)** | atlas dep in every hooks adapter (R1 §3.6) | — (model-manifest.json is static, R2 §4) | FABRIC-ONLY |
Count: 8 SHARED, 5 FABRIC-ONLY, 5 T3-ONLY → **overlap ≈ 45%**.
A.4 Must-survive (fabric-unique, binding)
1. **Journal/run binding**: every harness event lane must still terminate in the babysitter journal/run model (journal write path fabric:packages/babysitter-sdk/src/storage/journal.ts:72-82; hook→session→run resolution stopHookHandler.ts:209-225). t3code has no external ledger concept; the ACP re-platforming must not silently replace the journal with t3code's SQLite event store as source of truth for *runs*. 2. **Observe lane**: the 12 hook adapters cover harnesses t3code does not (gemini, copilot, pi, oh-my-pi, openclaw, hermes, genty) and a mode t3code lacks entirely (instrumenting a user-driven session). They stay, feeding the same canonical projection. 3. **Trust-signed gates**: proven-bridge.ts:92-119 human-root verification and the policy enforcement wiring (R1 C3) must gate the new drive lane's permission callbacks, not be bypassed by them. 4. **Cost records + attestation seam** (run-handle.ts cost types; transport/src/server.ts:1596-1607) survive as projection consumers.
A.5 Gained from t3code
ACP client+agent runtime (5.2k handwritten LOC, dep = effect only, R2 §18.2/§18.9); typed Codex app-server lane (3.8k handwritten); generic-runtime-plus-extension-modules pattern for per-harness ACP quirks (t3:apps/server/src/provider/acp/{AcpSessionRuntime,CursorAcpExtension,XAiAcpExtension}.ts, R2 §4); provider installer/updater machinery with ownership proofs (providerMaintenance.ts); usage/quota ingestion; ACP mock agent for protocol tests (t3:apps/server/scripts/acp-mock-agent.ts, R2 §18.11).
A.6 The seam, precisely (D6)
D6 says ACP is canonical and the current adapters event model becomes a projection. The t3code evidence fixes the shape:
1. **One adapter contract, four lanes.** Fabric adapters gets a ProviderAdapter-equivalent contract (successor of adapter-types.ts:24 + run-handle.ts:150) with: an **ACP lane** (effect-acp client; covers Cursor cursor-agent … acp, Grok grok … agent stdio, Antigravity downloaded ACP release — R2 §18.1) and **three bespoke lanes behind the same contract**: Claude via @anthropic-ai/claude-agent-sdk in-process, Codex via app-server JSON-RPC stdio, OpenCode via HTTP server-per-thread (R2 §18.1). This is exactly t3code's shape; fabric adds its remaining harnesses (gemini, copilot, pi, …) as either new ACP lanes or retained subprocess codecs behind the same contract. 2. **Projection direction.** ACP session/update notifications and bespoke-lane events → normalizer → **fabric AgentEvent (events.ts:411) as derived view** → existing consumers (RunHandle iterators, gateway run.event frames, journal binding, cost/attestation). Consumers keep compiling; only the producer side is re-platformed. 3. **Fabric as ACP agent.** effect-acp's agent.ts service (R2 §6) lets fabric expose babysitter runs/genty sessions *as* ACP sessions — the reciprocal seam that makes fabric drivable by other ACP clients. No fabric equivalent exists today. 4. **Effect boundary (D5 input).** effect-acp and effect-codex-app-server depend only on effect (R2 §18.2) — they can be quarantined behind the lane boundary: the adapter contract surface stays plain-TS (Promise/AsyncIterable, as run-handle.ts already is), Effect lives inside the lane implementations.
---
Row B — `kradle` control-surface APIs/UIs vs t3code `apps/server` (+ contracts RPC) / `apps/web`
B.1 Fabric side
Three distinct control surfaces exist (a fact the spec must not blur):
- **kradle/core**: 69 controller modules (dir listing; R1 §3.2) on a K8s CR resource model — dispatch, approvals, memory, persona, workspace, writeback, secrets, transport binding, triggers, subagents, gitops (
argocd-gitops.js), gitea backend, event bus, control/data-plane split (control-plane.js,data-plane.js), org scoping via K8s namespaces (fabric:packages/kradle/core/src/org-scoping.js:5re-exportingorgNamespaceName/normalizeOrgSlug/resolveResourceOrg/withOrgScopefromkubernetes-controller.js). - **kradle/web API**: **81 route files** under
app/api(measured:find app/api -name "route.js" -o -name "route.ts" | wc -l= 81), org-scoped underorgs/[org]/{agents,artifacts,assistant,external,hooks,inference,jitsi,pipelines,policies,policy-exception-requests,policy-reports,profile,repositories,resources,search,secrets,snapshot,views,webhooks,workspaces}(dir listing) plusauth/*,atlas/search,git-proxy, and an SSE watch route streaming controller watch events (fabric:packages/kradle/web/app/api/watch/[[...resource]]/route.js:1-20,createKradleApiController()at:1). Routes are untyped.js— no schema layer. - **adapters/gateway**: the run-facing WS control surface — HTTP + WebSocketServer (
fabric:packages/adapters/gateway/src/server.ts:4,177,181,572), a zod-validated **17-frame protocol v1** (fabric:packages/adapters/gateway/src/protocol/v1.ts, 151 lines:auth:6,hello:11,subscribe:26,session.start:47,session.message:61,run.event:79,hook.request:87,hook.decision:96,hook.resolved:103,pairing.register:110,pairing.consume:117, union:151), a run manager holding liveRunHandles with per-run event log + hook broker + webhook fan-out (fabric:packages/adapters/gateway/src/runs/manager.ts:103-224), token auth (auth/tokens.ts,auth/middleware.ts), and a kanban read-model built directly over.a5c/runs/journal directories (fabric:packages/adapters/gateway/src/kanban/lib/source-discovery.ts:12-34, routes registeredserver.ts:24). - **Approvals**: kradle's
AgentApprovalresource with **seven first-class gate actions** —review, ai-review, tool-use, secret-access, write-back, release, escalation(fabric:packages/kradle/core/src/agent-approval-controller.js:15, boundary decl:3-9), and babysitter's signed-breakpoint enforcement chained to human trust roots (fabric:packages/adapters/policy/src/proven-bridge.ts:92-119, R1 H9).
B.2 t3code side (R2 cites)
Single-user environment server: event-sourced engine (commands → pure decider → events → projector → reactors, one SQLite transaction — R2 §3.1, t3:docs/internals/overview.md:56-72); **144 Rpc.make WS methods + 8 orchestration methods** as the sole typed wire boundary (t3:packages/contracts/src/rpc.ts:277,1394; orchestration.ts:35-44); environment auth with DPoP + pairing grants + per-method RPC authorization (R2 §3.3); checkpoints as hidden git refs (R2 §3.4); PR domain across 4 providers (R2 §3); terminal, device control, resource telemetry, usage ingestion; approval policies + pending-approvals projection + sandbox modes in contracts/orchestration.ts (R2 §5).
B.3 Sub-capability checklist
| Sub-capability | Fabric | t3code | Class |
|---|---|---|---|
| Create/dispatch agent run/thread | agent-dispatch-controller.js; gateway session.start frame (v1.ts:47) | dispatchCommand (orchestration.ts:35-44) | SHARED |
| Live event streaming to clients | gateway run.event fan-out (manager.ts:103-110) | subscribeThread/subscribeShell + ThreadLiveEventCoalescer (R2 §3.1) | SHARED |
| Human approval gates | 7-action AgentApproval (agent-approval-controller.js:15) + gateway hook.request/decision (v1.ts:87,96) + signed breakpoints (proven-bridge.ts:92-119) | approval policies + pending-approvals projection + ProviderApprovalDecision (R2 §5, ProviderAdapter.ts imports) | SHARED (fabric taxonomy richer; t3 UX deeper) |
| Project/repo registry + source control | agent-project-controller.js, repository-reconciler.js, gitea backend | project/ + sourceControl/ + vcs/ (R2 §3) | SHARED |
| Session transcript access | agent-session-transcript-controller.js | projections (threads/messages/turns) (R2 §3.2) | SHARED |
| Secrets handling | agent-secret-config-grant-controller.js + orgs/[org]/secrets routes | ServerSecretStore + keyring (R2 §3.3) | SHARED (different scope: org grants vs host keyring) |
| Server config/probe/update surface | agent-gateway-config-controller.js, api-controller.js | contracts/server.ts (891 LOC) + selfUpdate (R2 §5, §3.6) | SHARED |
| Auth for the control surface | gateway bearer tokens (auth/tokens.ts); kradle web auth/[provider] OAuth routes | DPoP + pairing grants + scoped sessions (R2 §3.3) | SHARED (t3 stronger for device pairing; fabric has org SSO) |
| **Multi-tenant org control plane** (K8s namespaces, CR reconcilers, Helm/GKE) | org-scoping.js:5; 81 org-scoped routes; charts + GKE deploy (R1 §3.2) | — (one environment = one user's machine) | FABRIC-ONLY |
| **Policy engine surfaces** (policies, exception requests, reports) | orgs/[org]/{policies,policy-exception-requests,policy-reports} routes | — | FABRIC-ONLY |
| **Inference/virtual-model routing** | kradle-inference-service-controller.js, virtual-model-controller.js, infer route (R1 CAP5) | — | FABRIC-ONLY |
| **GitOps/CI runners/jitsi realtime** | argocd-gitops.js, runners-ci.js, jitsi-*.js | — | FABRIC-ONLY |
| **Journal-derived kanban read model** | kanban/lib/source-discovery.ts:12-34 | — | FABRIC-ONLY |
| **Event-sourced core with replay-forever discipline** | — (kradle state = K8s CRs + gateway SQLite event log without decider/projector split) | R2 §3.1, overview.md:70-72 | T3-ONLY |
| **Typed contract package as sole wire boundary** | — (81 untyped .js routes; gateway zod frames are the only typed slice) | contracts 48 modules, Effect Schema (R2 §5) | T3-ONLY |
| **Capability-flag client/environment version negotiation** | — | environment.ts:1-3 + doctrine (overview.md:24-44) | T3-ONLY |
| **Workspace checkpoints / turn diff / restore** | — | getTurnDiff/getFullThreadDiff (orchestration.ts:35-44), CheckpointStore (R2 §3.4) | T3-ONLY |
| **PR lifecycle domain (4 providers), terminal, device control, resource telemetry** | — (kradle has repos, not PR stacks; no PTY surface; no device control) | R2 §3, §5 | T3-ONLY |
Count: 8 SHARED, 5 FABRIC-ONLY, 5 T3-ONLY → **≈ 45%** by raw count, **but** the five t3-only entries bundle whole subsystems (PR/terminal/device/telemetry counted once). Weighting those as four distinct capabilities gives 8/(8+5+8) → **~30%**, the honest figure used in the master table.
B.4 Must-survive (fabric-unique, binding)
1. **Multi-tenant control plane**: org scoping (org-scoping.js:5), the 81 org-scoped API routes, K8s CR reconcilers and Helm/GKE deployment (R1 §3.2) are the product's SaaS spine; t3code's single-user environment model must slot *under* it, never replace it. 2. **Approval taxonomy + signed breakpoints**: the 7 gate actions (agent-approval-controller.js:15) and human-root-chained breakpoint proofs (proven-bridge.ts:92-119) are a strict superset of t3code approvals; the migrated surface must map t3code's pending-approvals projection *onto* AgentApproval, preserving secret-access/write-back/release/escalation semantics t3code has no notion of. 3. **Journal as run source-of-truth**: gateway kanban and run manager derive from .a5c/runs journals (source-discovery.ts:12-34, manager.ts:110); t3code's SQLite store may own *its* thread/turn projections but run identity/provenance stays with the babysitter journal. 4. **Process orchestration**: babysitter defineTask/breakpoints exports (fabric:packages/babysitter-sdk/src/index.ts:16) and the run lifecycle (R1 §3.4) are the orchestration substrate; t3code's decider orchestrates chat turns, not long-running processes with quality gates. 5. **Trust signing substrate**: trust-core sign/verify families (fabric:packages/trust-core/src/index.ts:1-7: payload, model-response, model-decision, agent-request/prompt, tool-result/permission-evidence, chain).
B.5 Gained from t3code
Event-sourced orchestration core with single-transaction commit and replay-forever discipline; a typed contract package with 144+8 methods and the capability-flag/schema-transform compat doctrine (R2 §18.5) — the direct cure for kradle's 81 untyped routes; checkpoint/diff/restore; PR stacks; terminal; DPoP/pairing auth; receipts + drainable-worker test pattern (R2 §18.11).
B.6 The seam, precisely
- **Topology**: kradle (multi-tenant, K8s) ⟷ *N* t3code-style **environments** (one per workspace/runner). The boundary contract is t3code's environment descriptor + capability flags +
environmentHttp.tsHttpApi groups (R2 §5). Kradle'sagent-dispatch-controller/agent-workspace-controllerbecome environment *clients* dispatchingOrchestrationCommands instead of driving adapters directly. - **Gateway fate**: the 17-frame gateway protocol (
v1.ts) is the projection ancestor of the contracts RPC surface —session.start/message↔dispatchCommand,run.event↔subscribeThread,hook.request/decision↔ pending-approval flow. The spec must choose (D1, per-component): extendcontractswith fabric's run/hook/pairing groups (tracked fork per D9) vs keep the gateway as a thin ACP-facing shim. Evidence favors the former: the gateway's zod frames are the only typed slice fabric has, and they are 151 lines vs contracts' 26,979. - **ACP touchpoint**: the environment server hosts Row A's four-lane provider layer; kradle never speaks ACP directly — ACP terminates inside the environment (mirrors t3code, where web/desktop never see ACP, only contracts RPC).
---
Row C — `genty` client shells + `kradle-web` data layer vs t3code `apps/*` + `client-runtime`
C.1 Fabric side
- **genty/ui client layer**:
GatewayClient+ provider/hooks/store exports (fabric:packages/genty/ui/src/gateway.ts:1-8:useGateway/useAgents/useRun/useHookRequests/useConnection), WS transports for browser/node/react-native (fabric:packages/genty/ui/src/client/transports/{ws-browser,ws-node,ws-react-native}.ts), backoff logic (client/backoff.ts), and a **mirror copy of the gateway protocol** (fabric:packages/genty/ui/src/protocol/v1.ts:1-15, 151 lines, 18 exported interfaces — duplicated type-for-type from the gateway package rather than imported from a shared contracts unit). - **Shells**:
genty/web-app= thin Vite shell (src/{App.tsx,main.tsx,layout,pages}, dir listing) — the only Playwright-gated shell (R1 §3.3);genty/desktop-app= 10-file Electron scaffold (fabric:packages/genty/desktop-app/package.json:9-13:electron ./electron-builder); 7 device shells at L0 (R1 §3.3, Spec input 7). - **kradle/web data layer**: Next.js server components + REST fetch to the 81 routes + **SSE** live updates —
EventSourceon/api/orgs/${org}/agents/events/streamwith manual retry/refresh debounce (fabric:packages/kradle/web/app/components/agent/live-updates.jsx:30, retry state:8-14) and the[[...resource]]SSE watch route (route.js:1-20). No shared client runtime: kradle-web and genty-ui have zero common data-layer code (separate packages, different protocols — REST/SSE vs gateway WS).
C.2 t3code side (R2 cites)
client-runtime (256 files / 65.8k LOC): single connection supervisor owning transport retry, per-environment connection registry, compatibility negotiation, ~30 atom-based state services, credential refresh decoupled from socket lifetime, platform/ service interfaces that shells implement (R2 §8, t3:docs/internals/connection-runtime.md:1-27). apps/web 318k LOC (React 19, TanStack Router, composer, ghostty-wasm terminal, hosted app as pure client with browser-held catalog — R2 §11). apps/desktop 81k LOC Electron 44 that **bundles and spawns the server** (DesktopBackendManager/Pool, R2 §11). Mobile deferred (D13) but consumes the same stack (R2 §11).
C.3 Sub-capability checklist
| Sub-capability | Fabric | t3code | Class |
|---|---|---|---|
| WS client with typed frames + backoff | genty/ui/src/client/{GatewayClient,backoff}.ts | client-runtime/connection/supervisor.ts | SHARED |
| React hooks/state over live runs | gateway.ts:1-8 hooks + store | state/ atom services (R2 §8) | SHARED |
| Web shell for run interaction | genty/web-app (Playwright-gated) | apps/web chat/threads (R2 §11) | SHARED |
| Desktop shell | genty/desktop-app Electron scaffold (package.json:9-13) | apps/desktop (81k LOC, bundles server) | SHARED (fabric = scaffold only) |
| React-native transport | transports/ws-react-native.ts | Expo mobile app (deferred D13) | SHARED (both exist; both out-of-depth here) |
| **Org-admin UI over the control plane** (policies, secrets, webhooks, inference, pipelines) | kradle/web pages over 81 routes | — | FABRIC-ONLY |
| **Observability dashboards** (activity feed, health monitor) | kradle/web/app/components/observability/{activity-feed,health-monitor}.jsx; genty-ui ActivityFeed.tsx | agentActivity exists relay-side only (R2 §12) | FABRIC-ONLY |
| **Journal/kanban + run-history views** | gateway kanban UI routes (kanban/routes.ts); observer-dashboard (R1 §3.8) | — | FABRIC-ONLY |
| **Multi-environment connection catalog + supervisor** | — (genty-ui connects to one gateway) | connection/registry.ts + browser-held catalog (R2 §8, §11) | T3-ONLY |
| **Composer/diff/terminal/checkpoint UX** | — | tiptap, @pierre/diffs, ghostty wasm, checkpointDiff state (R2 §8, §11) | T3-ONLY |
| **Credential refresh decoupled from socket; compat negotiation** | — | authorization/service.ts, catalog compat (R2 §8) | T3-ONLY |
| **Desktop-as-host-environment** (spawns bundled server) | — (fabric desktop scaffold renders only) | DesktopBackendManager.ts (R2 §11) | T3-ONLY |
Count: 5 SHARED, 3 FABRIC-ONLY, 4 T3-ONLY → ~40% by count, but three of the five SHARED rows are fabric scaffolds vs t3code production systems (desktop 10 files vs 258; web-app thin shell vs 1,258 files; hooks/store vs 30 services). Discounting the scaffold rows to half-credit: **~20-25%** — the master table says ~20%.
C.4 Must-survive (fabric-unique, binding)
1. **Org-admin surfaces**: policies/exception-requests/secrets/webhooks/inference/pipelines pages have no t3code analogue and stay on kradle-web (REST/SSE is adequate for admin CRUD). 2. **Observability/journal views**: activity feeds, health monitor, kanban-over-journal, observer-dashboard — these render fabric-unique data (journal runs, org events) and must be re-pointed at whatever client runtime wins, not dropped. 3. **The genty-ui hook API** (useRun/useHookRequests, gateway.ts:1-8) is the only fabric client code with real consumers *and* tests (genty-ui 18 test files, R1 §4.4); its semantics (hook-request surfacing = approvals UX) must be reproduced in any client-runtime adoption. 4. Per D2c the 7 device shells + desktop scaffold are **retired**, destroying ~0 tested functionality (R1 Spec input 7) — nothing to survive there beyond the react-native transport learning.
C.5 Gained from t3code
A real client runtime: one supervisor for reconnects across all surfaces (the exact bug-class genty-ui's per-client backoff and kradle-web's hand-rolled EventSource retry (live-updates.jsx:8-30) both re-implement, divergently — R2's design rationale at connection-runtime.md:1-27 names this failure mode); multi-environment catalog; production web app with composer/diff/terminal; a desktop app that *is* a host environment (directly serves D3 single-node UX).
C.6 The seam, precisely
- **Plug point** =
client-runtime'splatform/service interfaces (R2 §8): fabric shells (adapted web app; kradle-web where it embeds run views) implement platform services; everything above them is shared. - **Connection model**: each kradle-managed environment (Row B seam) appears as one entry in the client-runtime connection registry — the hosted-web pattern (browser-held catalog, direct connection to each environment,
t3:docs/internals/remote.md:28-38) is exactly the multi-tenant fabric topology, with kradle acting as catalog *source*. - **Protocol duplication ends**: genty-ui's copied
protocol/v1.ts(151 lines duplicated from gateway) is the anti-pattern the contracts package eliminates — one schema package, imported by both sides (R2 §5 doctrine). - **ACP touchpoint**: none direct — clients speak contracts RPC; ACP stays inside the environment (Row A). The only client-visible ACP artifact is normalized activity/permission events, already typed in
providerRuntime.ts.
---
Row D — fabric remote story (absent) vs t3code ssh / tailscale / T3 Connect relay
D.1 Fabric side
R1 §3.10 confirmed: zero tailscale/relay/ssh-tunnel code in fabric. What *does* exist, and counts as the 5% seed:
- Gateway bearer-token auth (
fabric:packages/adapters/gateway/src/auth/{tokens,middleware,bootstrap,hashing}.ts, dir listing) — same-host/LAN trust only, no proof-of-possession. - LAN pairing:
pairing.register/consume/consumedframes (v1.ts:110-122) backed by an in-memory 8-digit short-code store with 5-minute TTL (fabric:packages/adapters/gateway/src/pairing/short-code.ts:1-20). - kradle is *cloud-deployed* (GKE, R1 §3.2) — but that is hosted control plane, not remote access to a user's local environment; the D12 gap (reach my machine's agents from anywhere) is untouched.
D.2 t3code side (R2 §10, §12, §18.6)
Four routes: (1) direct/LAN pairing hardened with DPoP proofs + pairing grants + scoped sessions; (2) Tailscale as "an endpoint for ordinary pairing" (t3:packages/tailscale, 763 LOC, wraps user's tailscale CLI with secret-scrubbing errors); (3) SSH bootstrap + port-forward with desktop-owned lifecycle (t3:packages/ssh, 4,143 LOC, downloads server via cliRelease); (4) T3 Connect hosted relay — Cloudflare Worker + Postgres + Clerk, broker-not-proxy (relay never sees session tokens; one-time bootstrap credential bound to client DPoP key; cloudflared managed tunnels; t3:docs/internals/t3-connect.md:3-33).
D.3 Delta
| Sub-capability | Fabric | t3code | Class |
|---|---|---|---|
| Short-lived pairing codes | short-code.ts:1-20 (random digits, no key binding) | pairing grants bound to DPoP keys (R2 §3.3) | SHARED (fabric version is a toy by comparison) |
| Everything else — DPoP, tailscale, ssh, relay, tunnel lifecycle, self-update handoff | — | R2 §10, §12, §3.6 | T3-ONLY (4+ capabilities) |
**Overlap ≈ 5%.**
D.4 Must-survive / must-integrate (fabric-unique)
1. **trust-core as signing substrate**: the relay protocol uses @noble/curves signing both directions (R2 §12); fabric already owns key-pair/chain machinery with human-root semantics (trust-core/src/index.ts:1-7, chain.ts). The spec must decide integration (relay envelopes verified against fabric trust chains) rather than shipping two parallel PKIs. 2. **Org-aware relay**: T3 Connect identity is per-user Clerk (R2 §12); a fabric relay must scope environments to kradle orgs (org-scoping.js:5) — the multi-tenant control plane is the natural place to anchor environment↔org linking, replacing Clerk. 3. Gateway pairing frames (v1.ts:110-122) are the migration shim: same UX slot, upgraded to DPoP-bound grants.
D.5 Gained
The entire D12 mandate, pre-built: extractable ssh/tailscale packages (deps: effect only, R2 §18.2), the environment-side cloud/ module (managed cloudflared tunnels, CLI tokens, self-update), and a reference relay whose trust model (broker-not-proxy, t3-connect.md:3-6) is the design fabric should adopt verbatim. Operational cost: Cloudflare Worker + Postgres + Clerk-or-replacement stack (R2 §16).
D.6 The seam
Remote access terminates at **environment auth** (Row B's boundary): every route (LAN, tailscale, ssh, relay) ultimately performs the same DPoP pairing against the environment server — t3code's discipline that "tailscale is not a separate environment type" (t3:docs/internals/remote.md:43-45) should be adopted as-is. ACP is untouched by this row: remote access is about reaching the environment's contracts-RPC surface, inside which ACP lanes run (Row A).
---
CONFIRMED vs INFERRED
**CONFIRMED** (read at cited lines for this dossier): fabric adapter contract shape and 3 adapter kinds (adapter-types.ts:24,79,93,168); RunHandle triple nature and deferred prompts (run-handle.ts:1-8,44-51,150); 49-interface AgentEvent union (events.ts, count measured); 22 codec adapters + factory registry (measured; base-adapter.ts:61,75); 12 hook adapter dirs (listing); gateway 17-frame protocol with cited frame lines (v1.ts), server WS wiring (server.ts:4,177,181,572), run manager binding (manager.ts:103-224), journal-sourced kanban (source-discovery.ts:12-34), short-code pairing (short-code.ts:1-20); kradle 81 API route files (measured) and org-route inventory (listing); approval action set (agent-approval-controller.js:15); org-scoping exports (org-scoping.js:5); SSE data layer (live-updates.jsx:8-30, watch/[[...resource]]/route.js:1-20); genty-ui exports/transports/protocol copy (gateway.ts:1-8, transport files, protocol/v1.ts 151 lines/18 interfaces); desktop-app Electron scripts (package.json:9-13); trust-core export surface (index.ts:1-7); babysitter breakpoints export (index.ts:16); t3code ProviderAdapter capabilities/compaction (ProviderAdapter.ts:29-56).
**INFERRED** (flagged): the overlap percentages (judgment over the printed checklists — method stated in §0); the claim that genty-ui's protocol/v1.ts is a *copy* of the gateway's (identical length and frame names read on both sides; a diff was not run line-by-line); the B.6 recommendation that contracts-extension beats gateway-retention (an adoption-model argument for Gate A, not a fact); the weighting notes applied to rows B and C overlap figures.
---
Spec inputs
Concrete facts the spec author (10-t3code-migration.md and 20-functionality-coverage.md) must use:
1. **Fabric already has a drive-side adapter contract** — BaseAgentAdapterInterface + SubprocessAdapter/RemoteAdapter/ProgrammaticAdapter (fabric:packages/adapters/core/src/adapter-types.ts:24,79,93,168) and RunHandle (run-handle.ts:150) with 22 codec implementations. The D6 re-platforming is a *contract replacement with lane restructuring*, not a first drive-layer build; migration WPs must enumerate the 22 codec adapters and the 12 hook adapters as distinct migration units. 2. **The projection target is fixed**: AgentEvent union at fabric:packages/adapters/core/src/events.ts:411 (49 event interfaces) becomes the derived view; its existing consumers (RunHandle iteration, gateway run.event fan-out at manager.ts:103-110, journal binding, cost summaries) define the projection's acceptance tests. 3. **Four-lane shape is copied from t3code**: ACP lane (Cursor/Grok/Antigravity) + Claude-SDK, Codex-app-server, OpenCode-HTTP bespoke lanes behind one contract (t3:apps/server/src/provider/builtInDrivers.ts:49-56, ProviderAdapter.ts:1-60); fabric's extra harnesses (gemini, copilot, pi, oh-my-pi, openclaw, hermes, droid, amp, omp, genty) need a per-harness lane decision (ACP / retained-subprocess-codec / retired) — a mandatory WP table. 4. **effect-acp gives both directions**: adopt the agent side (t3:packages/effect-acp/src/agent.ts) to expose fabric runs as ACP sessions — a new capability, zero fabric precedent, and the cleanest early win (dep = effect only). 5. **Kradle↔environment boundary**: adopt t3code's environment descriptor + capability flags (t3:packages/contracts/src/environment.ts:1-3) as the interface between kradle's multi-tenant plane and per-workspace environment servers; kradle dispatch/workspace controllers become environment clients. 6. **Typed-contract debt is quantified**: kradle-web has **81 untyped route files** (measured) vs the gateway's 151-line zod protocol as fabric's only typed wire slice (gateway/src/protocol/v1.ts) vs contracts' 26,979 LOC / 144+8 methods. The migration spec must map each of the 17 gateway frames to a contracts RPC group (session.start/message→dispatchCommand, run.event→subscribeThread, hook.request/decision→pending approvals, pairing.*→pairing grants) and schedule route-by-route typing (or replacement) of the 81. 7. **Approval mapping table required**: t3code ProviderApprovalDecision/pending-approvals ↔ kradle's 7 gate actions (agent-approval-controller.js:15) ↔ babysitter signed breakpoints with human-root chains (proven-bridge.ts:92-119). secret-access, write-back, release, escalation have no t3code counterpart and must be preserved as fabric extensions of the approval contract. 8. **Journal sovereignty rule**: t3code's SQLite event store may own thread/turn projections inside an environment, but run identity, provenance, and replay stay with the babysitter journal (storage/journal.ts:72-82; kanban reads .a5c/runs at source-discovery.ts:12-34). Any WP that lets the environment store become run source-of-truth violates this dossier. 9. **Client-runtime adoption anchor**: genty-ui's hook API (useRun/useHookRequests, gateway.ts:1-8) is the tested fabric client surface to reproduce; genty-ui's duplicated protocol/v1.ts (151 lines mirroring the gateway package) is the concrete anti-pattern the shared contracts package removes. 10. **Retirement inventory (D2c)**: 7 device shells + desktop-app scaffold (package.json:9-13) retire; apps/desktop's DesktopBackendManager pattern (bundling a host environment) becomes the single-node desktop story (also serves D3); kradle-web survives as org-admin (REST/SSE acceptable there — live-updates.jsx:30), run/thread UX moves to the adapted web app. 11. **Remote-access WPs start from the gateway pairing shim**: upgrade pairing.register/consume (v1.ts:110-122) + ShortCodeStore (short-code.ts:1-20) to DPoP-bound pairing grants; extract t3:packages/{ssh,tailscale} as-is; relay WP must specify Clerk-replacement by kradle org identity and trust-core-based envelope verification (trust-core/src/index.ts:1-7) instead of a second PKI. 12. **Overlap figures for sizing** (capability-count method, §0): A ≈45%, B ≈30%, C ≈20%, D ≈5%. Inverse reading for effort: Row D is pure adoption (nothing to merge), Row A is a true merge (two live adapter systems), Rows B/C are graft-under/graft-over operations where fabric-unique planes (multi-tenant, approvals, journal) wrap adopted t3code cores. 13. **Effect boundary rule candidates (D5)**: keep the fabric-facing adapter contract plain-TS (Promise/AsyncIterable as run-handle.ts today); Effect allowed inside lane implementations (effect-acp, effect-codex-app-server, ssh, tailscale — all effect-only deps per R2 §18.2); contracts adoption forces Effect Schema at the wire boundary — the one place Effect must be allowed to appear in kradle-adjacent code.