# AGS Workspace Routing Migration Decision Ledger

> Status: non-canonical, active, merge-blocking
> Canonical routing rules: [`specs/api/request-workspace-routing-matrix.md`](../../specs/api/request-workspace-routing-matrix.md)
> Purpose: record unresolved compatibility and owner decisions. Test files and delivery scripts below are evidence of repository contracts, not proof that a released environment ran them.

## Fixed constraints

- Preserve supported AgentTeam product behavior while replacing default-group guesses with authenticated workspace placement. Standalone/`all` profiles retain the complete AGS surface; split does not retain non-product compatibility merely for AGS, CLI, documentation, or smoke callers.
- `RegisteredScope` describes the actual AGS authentication middleware (public, optional-auth, or authenticated). It does not activate an operation at the split edge.
- Route capability and authority mode are separate. A client `W` is only a claim; it is never authentication or a public/credential locator.
- Native REST, raw, and Git URLs remain unchanged. Unknown or unresolved identity fails before placement or upstream I/O, without group fanout.

## Compatibility evidence

`main split` means merge base `e64f1b0d`; `current` means this worktree. “No runtime evidence” means no independent release record or production observation was found in the repository.

| Operation | Existing AGS router/test contract | Real release/runtime evidence | Main split | Current actual | Target | Product decision | This-PR activation |
| --- | --- | --- | --- | --- | --- | --- | --- |
| Discovery/OpenAPI and OAuth/device/connected bootstrap | Public/optional routes; old smoke and AGS tests expect success | No AgentTeam Web/Desktop production caller found | Configured global owner | 404 before identity, placement, or upstream | Remain 404 in split; preserve Standalone/`all` AGS capability | Closed: non-product compatibility is not retained | No |
| Public agent registration | `POST /api/ext/v1/agents` is unauthenticated | Delivery script explicitly expects split 404; no release observation found | 404 | 404 | Preserve AGS capability; split placement/lifecycle unresolved | Open | No; existing split gap |
| Session-local user/search/token state | Authenticated AGS handlers; delivery script exercises representative reads | No independent runtime evidence found | Configured global owner | Desktop representatives work with selected-workspace claim; Browser native AGS calls intentionally send no claim, so current Gateway returns 400 | Verified AgentTeam session + claim -> membership -> placement | Typed Web transport unresolved | Partial; merge-blocking |
| Named-repository REST/raw with session | Optional-auth reads, authenticated writes; AGS tests cover public and authenticated access | Delivery script exercises credential REST/raw; no execution record found | Configured global owner | Desktop session path can place; Browser intentionally sends no claim and receives 400 | Verified session + claim -> membership -> placement | Closed for session mode | Partial; merge-blocking |
| Repository credential REST/raw | Token/Bearer/Basic accepted by AGS data-plane middleware | Delivery script uses a native token; no execution record found | Configured global owner validates credential | Missing claim returns 400; a supplied claim reaches 422 discovery-unavailable | Credential -> non-forgeable workspace/repository/capability locator | Open | No; PR regression/blocker |
| Anonymous public repository REST | `TestPublicRepoAnonymousAccess`: public reads 200, private 404, writes 401 | No independent runtime evidence found | Configured global owner | No claim 400; candidate claim without auth 401 | Public resource -> trustworthy workspace locator, preserving published status semantics | Open | No; PR regression/blocker |
| Git smart HTTP | Optional-auth; `TestInfoRefs_AnonymousReadAccess`: public 200, private 401 | Delivery script exercises credential clone/fetch; no execution record found | Configured global owner | Credential/anonymous paths return 422 discovery-unavailable | Session, credential, and anonymous modes each resolve a trusted workspace before placement | Credential/public locators open | No; PR regression/blocker |
| Public starred lookup | Optional-auth; anonymous test expects 200 and hides private stars | No independent runtime evidence found | 404 | 404 | Public resource locator, or signed-in session placement when a verified claim exists | Open | No; existing split gap |
| GraphQL and unclassified management | Authenticated AGS registrations exist | Delivery script expects split 404 | 404 | 404 | Remain unreachable until an owner contract is approved | Open | No; existing split gap |

The configured-default behavior is implementation evidence, not a compatibility goal. Required compatibility is the AGS success/error contract with safe placement; neither a new 400/401/422 nor continued default routing closes it. Anonymous AGS tests still require a TiDB-capable `-count=1` rerun.

Current blocker root cause: `x/agent-git-service/repositoryroutecontract/scope.go` copies AGS `RegisteredScope` directly into AgentTeam `EdgePolicy`, and assigns synthetic raw one credential-or-session mode before authority is known. `internal/gateway/gateway.go` then activates the workspace family and returns anonymous 400/401 or credential/Git 422. The tests asserting those statuses document a known activation regression, not the target contract. Exit requires operation-capability classification followed by separate session, credential, and anonymous modes that preserve established behavior with zero default, fanout, or unintended upstream calls. Do not mask this with a runtime switch, configured-default fallback, or a larger handwritten scope table.

## Operation capability x authority mode

| Capability | Registered AGS auth scope | Ordinary AgentTeam session + verified claim | Repository credential | Anonymous/public |
| --- | --- | --- | --- | --- |
| Discovery/OpenAPI and OAuth/device/connected bootstrap | Public or optional-auth | Split activation blocked | Split activation blocked | Split activation blocked |
| Public agent registration | Public | Split activation blocked | Not applicable | Placement/lifecycle owner unresolved |
| Session-local user/search/token | Authenticated | Membership then workspace placement | Not a repository capability | AGS 401 |
| Named repository REST | Optional-auth reads; writes require auth | Membership then workspace placement | Credential locator plus repository/capability binding | Public resource locator for readable resources |
| AgentTeam synthetic raw | AgentTeam-owned, not AGS-registered | Membership then workspace placement | Credential locator plus read binding | Public resource locator |
| Git smart HTTP | Optional-auth | Membership placement only when a trusted transport supplies a verified claim | Credential locator plus upload/receive capability | Public repository locator for anonymous fetch |
| Starred/public lookup | Optional-auth | Membership placement; handler still applies resource visibility | Not applicable | Public resource locator |
| GraphQL/management | Authenticated or route-specific | Split activation blocked | Not activated unless capability explicitly permits it | Not activated |

Authority mode is selected from strict Authorization parsing plus an authenticated principal. Candidate workspace headers are checked only after the operation capability is known and cannot choose the mode.

## Desktop ingress inventory

- Host/base URL is not a discriminator: Browser and Desktop callers share `fetchServer`; Desktop maps `serverApi` to one loopback `localApi`, and the daemon `NoRoute` proxies every unmatched remote request to the selected server origin.
- `RuntimeTransport.fetch` carries only standard `RequestInfo/RequestInit`; `NoRoute -> desktopGateway` receives no typed AGS marker. `createDesktopLocalApi` removes caller `R/W` and daemon-owned headers before forwarding.
- Production Web native AGS traffic currently consists of `/api/v3` calls owned by `web/src/meshx/api/*` plus one synthetic raw-image builder. There are no production Web callers for `/api/ext/v1`, `/api/graphql`, OAuth, or connected-login paths. Desktop provisioning also constructs direct `/:owner/:repo.git` URLs outside this Web transport.
- Server namespaces `/api/v3`, `/api/ext/v1`, `/api/graphql`, `/login/device|oauth`, `/auth/connected`, `/avatars/:login`, exact synthetic raw, and smart Git do not overlap Central registrations, but namespace membership does not distinguish activated product operations from session, credential, public, or unsupported operations. Treating a prefix as the workspace-claim decision would recreate a second classifier.
- Therefore proposal A (Desktop injects a claim without operation classification) remains blocked. Prefer a typed signal at the existing MeshX/Web request owner if one can cover its callers without a new framework; raw browser resources and direct Git still need their own explicit transport owners. Do not delete the temporary Desktop descriptor until that coverage is proved.

## Open decisions and exit conditions

| Decision | Owner | Exit condition |
| --- | --- | --- |
| Public resource discovery | Product + routing architecture | Preserve anonymous REST/Git/starred contracts with a non-forgeable resource-to-workspace locator; no default or fanout. |
| Repository credential discovery | Credential + routing owners | Global verifier index, signed self-describing claim with target-local validation, or another trusted locator binds workspace/repository/capability. |
| Registration-owned scope | AGS router owner | Auth middleware group and method-pattern metadata are co-owned; every registration is covered exactly once. |
| Split activation overlay | Gateway composition owner | Activated operation x mode keys all exist in AGS registration metadata; default is unsupported and public/credential modes remain explicitly blocked until decided. |
| Typed Desktop/Web AGS transport | Web/runtime owners | Prove complete caller coverage and Central/non-AGS isolation without a path-prefix classifier; candidate headers are overwritten from validated Protocol identity. |

## Completion evidence

Merge remains blocked until the temporary handwritten scope tree is replaced by a smaller registration-derived owner, public and credential locators are resolved, existing anonymous/credential behavior is restored safely, two workspace groups pass end-to-end wire tests, failure paths prove zero unintended resolver/placement/retry/upstream calls, and targeted plus aggregate checks pass with review attached.
