diff --git a/docs/architecture/acp-forwarding-topology.md b/docs/architecture/acp-forwarding-topology.md
index 8430ff1..411d6aa 100644
--- a/docs/architecture/acp-forwarding-topology.md
+++ b/docs/architecture/acp-forwarding-topology.md
@@ -1,129 +1,103 @@
# ACP Forwarding Topology
-This document describes the bridge-only production forwarding model for `xworkmate-bridge.svc.plus`.
+Last Updated: 2026-04-13
-See also: [adr-unified-bridge-entrypoints.md](/Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-bridge/docs/architecture/adr-unified-bridge-entrypoints.md)
+本文件描述当前 `xworkmate-app <-> xworkmate-bridge` 主链下的 bridge-only forwarding topology。
+
+See also:
+
+- [XWorkmate Core Module Inventory](/Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-app/docs/architecture/xworkmate-core-module-inventory-2026-04-13.md)
+- [ADR: Unified Bridge Entry Points](/Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate-bridge/docs/architecture/adr-unified-bridge-entrypoints.md)
+
+## App-Facing Mainline
+
+对 app 来说,当前主链只有两类面向 bridge 的交互:
+
+- `assistant` surface 进入 ACP control-plane:`acp.capabilities`、`xworkmate.routing.resolve`、`session.*`
+- `settings` surface 进入 gateway runtime / connection flow:`acp.capabilities`、`xworkmate.gateway.*`
+
+不管 bridge 内部还保留哪些 provider / gateway mode / capability flag,app-facing 公共入口都只有 bridge origin。
## Topology
```mermaid
flowchart TD
- U["xworkmate-app"] --> B["https://xworkmate-bridge.svc.plus"]
+ subgraph APP["xworkmate-app"]
+ A1["AssistantPage"]
+ A2["SettingsPage"]
+ A3["https://xworkmate-bridge.svc.plus"]
+ A1 --> A3
+ A2 --> A3
+ end
- B -->|POST /acp/rpc| RPC["ACP RPC handler"]
- B -->|WS /acp| WS["ACP WebSocket handler"]
+ subgraph BRIDGE["xworkmate-bridge"]
+ B1["POST /acp/rpc"]
+ B2["GET /acp (WebSocket)"]
+ B3["acp.capabilities"]
+ B4["xworkmate.routing.resolve"]
+ B5["session.*"]
+ B6["xworkmate.gateway.*"]
+ B7["bridge-owned provider catalog"]
+ B8["bridge-owned routing"]
+ B9["bridge-owned gateway runtime"]
- RPC --> R{"method"}
- WS --> R
+ A3 --> B1
+ A3 --> B2
+ B1 --> B3
+ B1 --> B4
+ B1 --> B5
+ B1 --> B6
+ B2 --> B5
+ B3 --> B7
+ B4 --> B8
+ B5 --> B8
+ B6 --> B9
+ end
- R -->|acp.capabilities| CAP["built-in provider catalog"]
- R -->|xworkmate.routing.resolve| ROUTE["bridge-owned routing resolve"]
- R -->|session.start / session.message| RUN["bridge-owned execution"]
- R -->|xworkmate.gateway.*| GWAPI["gateway runtime proxy"]
- R -->|session.cancel / session.close| LIFE["session lifecycle"]
+ subgraph UPSTREAM["Independent upstream services"]
+ C1["https://acp-server.svc.plus/codex/acp/rpc"]
+ C2["https://acp-server.svc.plus/opencode/acp/rpc"]
+ C3["https://acp-server.svc.plus/gemini/acp/rpc"]
+ C4["wss://openclaw.svc.plus"]
+ end
- RUN --> ACP1["codex -> https://acp-server.svc.plus/codex/acp/rpc"]
- RUN --> ACP2["opencode -> https://acp-server.svc.plus/opencode/acp/rpc"]
- RUN --> ACP3["gemini -> https://acp-server.svc.plus/gemini/acp/rpc"]
-
- GWAPI --> GW["wss://openclaw.svc.plus"]
+ B7 --> C1
+ B7 --> C2
+ B7 --> C3
+ B8 --> C1
+ B8 --> C2
+ B8 --> C3
+ B9 --> C4
```
-## Three-Layer View
-
-This view separates what the app sees, what the bridge owns, and what the
-real upstream production targets are. The upstream ACP and gateway services
-exist independently, but for the app they are all accessed through the single
-public bridge origin: `https://xworkmate-bridge.svc.plus`.
-
-```mermaid
-flowchart LR
- subgraph L1["APP 视角"]
- APP["xworkmate-app"]
- APPENTRY["https://xworkmate-bridge.svc.plus
统一代理入口"]
- APPMETHODS["bridge methods
acp.capabilities / session.* / xworkmate.gateway.*"]
- APP --> APPENTRY
- APPENTRY --> APPMETHODS
- end
-
- subgraph L2["Bridge 视角"]
- BRIDGE["xworkmate-bridge
唯一上游发现真源"]
-
- CAP["Bridge-owned ACP routing catalog"]
- CAP1["codex"]
- CAP2["opencode"]
- CAP3["gemini"]
-
- GW["Bridge-owned gateway routing"]
- GW1["gatewayProviderId=openclaw"]
-
- BRIDGE --> CAP
- CAP --> CAP1
- CAP --> CAP2
- CAP --> CAP3
-
- BRIDGE --> GW
- GW --> GW1
- end
-
- subgraph L3["上游视角"]
- U1["https://acp-server.svc.plus/codex/acp/rpc"]
- U2["https://acp-server.svc.plus/opencode/acp/rpc"]
- U3["https://acp-server.svc.plus/gemini/acp/rpc"]
- U4["wss://openclaw.svc.plus
reported as openclaw.svc.plus:443"]
- end
-
- APPMETHODS --> BRIDGE
-
- CAP1 --> U1
- CAP2 --> U2
- CAP3 --> U3
- GW1 --> U4
-```
-
-Important distinction:
-
-- the upstream services are independent production services, not embedded
- inside the bridge
-- for the app, ACP discovery, session execution, and gateway runtime traffic
- are all proxied through `https://xworkmate-bridge.svc.plus`
-- upstream authentication is unified through
- `Authorization: Bearer $INTERNAL_SERVICE_TOKEN`
-- `acp.capabilities.providerCatalog` currently advertises only the ACP
- single-agent providers: `codex`, `opencode`, and `gemini`
-- `gateway` is not part of that provider catalog; it is exposed through the
- separate `xworkmate.gateway.*` bridge-owned runtime path
-- for `gatewayProviderId=openclaw`, the bridge rewrites the upstream target to
- `wss://openclaw.svc.plus`
-
## Production Truth
-The production upstream services exist independently. The bridge owns the
-routing map used to proxy app traffic to them:
+当前 production forwarding 事实:
-- `codex` -> `https://acp-server.svc.plus/codex/acp/rpc`
-- `opencode` -> `https://acp-server.svc.plus/opencode/acp/rpc`
-- `gemini` -> `https://acp-server.svc.plus/gemini/acp/rpc`
-- gateway -> `wss://openclaw.svc.plus`
+- canonical app-facing origin: `https://xworkmate-bridge.svc.plus`
+- canonical app-facing ACP paths:
+ - `POST /acp/rpc`
+ - `GET /acp`
+- current built-in single-agent provider catalog:
+ - `codex`
+ - `opencode`
+ - `gemini`
+- current production gateway forwarding target:
+ - `openclaw -> wss://openclaw.svc.plus`
-Upstream auth is unified and bridge-internal:
+对 app 而言:
-- `Authorization: Bearer $INTERNAL_SERVICE_TOKEN`
-
-Canonical APP-facing paths stay on the bridge origin:
-
-- `POST https://xworkmate-bridge.svc.plus/acp/rpc`
-- `GET https://xworkmate-bridge.svc.plus/acp`
+- provider catalog、routing、gateway runtime 都是 bridge-owned metadata / behavior
+- upstream URL 存在,但不是 app 的直接合同
+- gateway backend、provider IDs、可选 capability flag 也都不是 app shell 模块分类
## Invariants
-- app-facing cloud entry is only `https://xworkmate-bridge.svc.plus`
-- app traffic reaches upstream ACP and gateway services only through the
- bridge proxy
-- upstream ACP and gateway routes use the same bearer token contract:
- `Authorization: Bearer $INTERNAL_SERVICE_TOKEN`
-- `acp.capabilities` returns the built-in production catalog
-- no production `xworkmate.providers.sync`
-- no app direct call to `acp-server.svc.plus/*`
-- no app direct call to `openclaw.svc.plus`
-- openclaw gateway runtime status is reported as `openclaw.svc.plus:443`, but the app still talks only to the bridge
+- app traffic reaches upstream ACP and gateway services only through the bridge
+- app does not call `acp-server.svc.plus/*` or `openclaw.svc.plus` directly
+- upstream auth stays bridge-internal:
+ - `Authorization: Bearer $INTERNAL_SERVICE_TOKEN`
+- `acp.capabilities` is the provider / capability discovery source
+- `xworkmate.routing.resolve` is the routing resolution source
+- `xworkmate.gateway.*` is the gateway runtime method family
+- bridge may expose additional routing metadata, but that metadata must not be interpreted as extra app surfaces or legacy module shells
diff --git a/docs/architecture/adr-unified-bridge-entrypoints.md b/docs/architecture/adr-unified-bridge-entrypoints.md
index 0862c59..11f2a1d 100644
--- a/docs/architecture/adr-unified-bridge-entrypoints.md
+++ b/docs/architecture/adr-unified-bridge-entrypoints.md
@@ -8,37 +8,51 @@ Accepted
2026-04-11
+## Last Reviewed
+
+2026-04-13
+
## Context
-`xworkmate-bridge` currently proxies app traffic to four independent upstream
-production services:
+`xworkmate-bridge` 代理 app 流量到独立 upstream ACP / gateway 服务,但如果把这些 upstream 直接暴露成 app-facing entrypoint,会带来几个问题:
-- `codex` -> `https://acp-server.svc.plus/codex/acp/rpc`
-- `opencode` -> `https://acp-server.svc.plus/opencode/acp/rpc`
-- `gemini` -> `https://acp-server.svc.plus/gemini/acp/rpc`
-- `gateway` -> `wss://openclaw.svc.plus`
+- app 必须知道 provider-specific 或 gateway-specific hostname
+- routing truth 会在 URL 形状与 bridge 逻辑之间分裂
+- auth contract 更难保持统一
+- upstream implementation detail 会泄漏到 app shell / module 心智
-These upstream services exist independently, but exposing them directly as
-APP-facing endpoints creates several problems:
-
-- the APP would need to know provider-specific or gateway-specific hostnames
-- routing truth would be split between URL shape and bridge-side routing logic
-- auth handling would be harder to keep consistent
-- upstream implementation details would leak into client contracts
-
-The bridge already acts as the single public integration surface for ACP
-discovery, task execution, and gateway runtime operations.
+当前 app 主链已经收敛到 `assistant + settings` 两个 surface,因此 bridge 入口也必须保持同一口径:统一公共入口,bridge 内部负责 provider / gateway 路由细节。
## Decision
-For APP traffic, the canonical public entry point is the bridge origin:
+For APP traffic, the canonical public entry point remains:
- `https://xworkmate-bridge.svc.plus`
-The canonical APP-facing ACP paths are:
+Canonical app-facing contract families are:
-- `POST /acp/rpc`
-- `GET /acp` for WebSocket ACP
+1. ACP control-plane
+ - `POST /acp/rpc`
+ - `GET /acp`
+2. Gateway runtime methods
+ - `xworkmate.gateway.connect`
+ - `xworkmate.gateway.request`
+ - `xworkmate.gateway.disconnect`
+
+Bridge-owned metadata may still include:
+
+- `providerCatalog`
+- `gatewayProviders`
+- `resolvedExecutionTarget`
+- `resolvedProviderId`
+- `resolvedGatewayProviderId`
+
+这些字段属于 bridge 返回给 app 的 routing/capability metadata,不属于 app shell taxonomy。
+
+换句话说:
+
+- app 可以消费这些字段来展示当前可用能力或执行结果
+- app 不应该把 provider/gateway 矩阵抬升成新的顶层模块、别名页面、或直接 URL 合同
The APP should not depend on provider-specific public URLs such as:
@@ -47,76 +61,37 @@ The APP should not depend on provider-specific public URLs such as:
- `/gemini/acp/rpc`
- `/openclaw/`
-Provider choice remains bridge-owned routing, not URL-owned routing.
+If the bridge reports execution-target metadata such as `single-agent`,
+`multi-agent`, or `gateway`, the app should treat those values as routing
+results, not as shell-level surface categories.
-APP-facing routing should be modeled in three layers:
-
-- `executionTarget`
- - `single-agent`
- - `multi-agent`
- - `gateway`
-- `singleAgentProviders`
- - `codex`
- - `opencode`
- - `gemini`
-- `gatewayProviders`
- - `local`
- - `openclaw`
-
-For APP integration, `gatewayProviders` is the stable gateway-facing concept.
-
-APP and UI code should consume bridge state in two phases:
-
-1. `acp.capabilities`
- - discover `singleAgentProviders`
- - discover `gatewayProviders`
-2. `xworkmate.routing.resolve`
- - determine `resolvedExecutionTarget`
- - determine `resolvedProviderId` or `resolvedGatewayProviderId`
- - determine unavailable state
-
-The APP should treat `resolvedProviderId` and `resolvedGatewayProviderId` as
-mutually exclusive routing outputs depending on `resolvedExecutionTarget`.
-
-Gateway access remains bridge-owned via JSON-RPC methods:
-
-- `xworkmate.gateway.connect`
-- `xworkmate.gateway.request`
-- `xworkmate.gateway.disconnect`
-
-Upstream authentication is unified for both ACP and gateway routes:
-
-- `Authorization: Bearer $INTERNAL_SERVICE_TOKEN`
+If the bridge reports gateway provider IDs such as `local` or `openclaw`, the
+app should treat them as bridge-owned gateway backend identifiers, not as
+independent app entrypoints.
## Consequences
### Positive
- APP integration stays stable behind one public origin
-- provider and gateway topology remain internal bridge concerns
-- auth contract is consistent across all upstream forwarding
-- bridge can change upstream mappings without changing APP contracts
+- provider and gateway topology remain bridge concerns
+- auth handling remains consistent across ACP and gateway forwarding
+- app architecture docs can stay focused on `assistant + settings` instead of a fake module matrix
### Trade-offs
-- direct provider-specific bridge URLs, if exposed at all, must be treated as
- aliases or operator/debug paths, not primary client contracts
-- documentation must clearly distinguish canonical APP paths from independent
- upstream targets
+- docs must clearly separate canonical app contracts from independent upstream services
+- optional bridge metadata must be documented as metadata, not as surface taxonomy
## Path Naming Guidance
-Use these terms consistently in docs:
+Use these terms consistently:
-- `canonical APP-facing path`: `/acp/rpc` and `/acp`
-- `independent upstream service`: `acp-server.svc.plus/*` and
- `wss://openclaw.svc.plus`
-- `bridge-owned routing`: bridge logic that selects and proxies to upstreams
-- `gatewayProvider`: the APP-facing identifier for a gateway backend such as
- `local` or `openclaw`
+- `canonical app-facing path`: `/acp/rpc` and `/acp`
+- `gateway runtime method family`: `xworkmate.gateway.*`
+- `independent upstream service`: `acp-server.svc.plus/*`, `wss://openclaw.svc.plus`
+- `bridge-owned routing`: provider / gateway selection performed inside bridge
+- `routing metadata`: execution target and resolved provider/gateway identifiers returned to the app
-Avoid describing upstream URLs as if the APP should call them directly.
-
-If provider-specific public bridge paths are ever introduced, they should be
-documented as optional aliases only. They should not replace `/acp/rpc` as the
-canonical APP-facing contract.
+Avoid describing upstream URLs, provider IDs, or gateway mode IDs as if they
+were independent app modules or alternate primary entrypoints.