# Docs team chooser controller

This unmounted slice follows approved directory designfa64e28 and store4c0d848. Discussion26 records review. It adds only handlers and templates: no production routing, directory schema mounting, provisioning, roster producer/client or item authority. The old bare Docs alias and its resource links are untouched. Guest/former-member directory tiles remain a separate requirement; this chooser lists current team memberships only.

A directory App constructs the chooser with `newDocsChooser(store)`. Credential and legacy-role Apps are refused. The store interface is directorydata.Choices; its injected app registry/destination policy remains authoritative for allowed origins. Integration mounts Entry at GET /apps/docs and Select at POST /apps/docs/team under the existing directory sessions middleware and exact-host guard. The selection handler additionally carries the real same-origin CSRF middleware itself. Do not mount these handlers on the credential origin.

Entry accepts no query parameters. It reads identity exclusively from sessions.Current and requires Home's opaque person-ref shape. Signed-out navigation redirects to the shared consumer with fixed return_to=/apps/docs; a test follows the actual credential issuer, callback, inbox and finish back to that route. No email, team selection, arbitrary return URL or document path is accepted as identity/navigation state.

Zero live choices renders an honest empty state. One redirects to that validated stored origin's root, / (amended 2026-09-23, shared-chrome spec §6.2(a): it was /auth/start with local return /, which re-ran the exchange even with a live app session; the app's own guard now decides). A configured preview origin for the app takes precedence over team choices here too, redirecting to the preview's root without a directory session (§6.2(b)). Several render ordinary POST buttons carrying only team sqid. Selection re-queries current memberships and compares the requested sqid; foreign, removed and unknown teams receive the same 404. Redirecting changes no session/cookie and grants no item access. Apps must still validate roster standing and individual grants after their own authentication. Two simultaneous tabs carry independent explicit POST selections.

The existing Home renderer, layout, Estilo theme assets and Rastrillo attribute components are reused. Team names stay template-escaped; no script is added. Every controller response including redirects, method/query errors, CSRF rejection and storage failure is no-store. A failed read renders fixed text without stored names or origins. Runtime routing remains unchanged.

Copy pipeline: gpt-5.6-luna drafted seven strings via the writing-pipeline skill; plain-writing and humane-docs passes made the failure message specific to loading teams rather than falsely declaring Docs down. Artifacts are in ignored writing/docs-chooser. The user waived human copy review for this work. No copy approval was requested or invented.

Tests use the actual directory store/schema, actual Home renderer/session middleware and real shared handoff. They cover empty/one/many destinations, membership removal after rendering, foreign/unknown teams, escaped hostile labels, no script tags, exact fixed return, query/form bounds and duplicates, hostile Origin including the sibling credential origin, email-shaped session subject refusal, no-store and no new cookie, unavailable-store privacy, and constructor credential-role refusal. Existing full Home script/markup/privacy gates apply to the new templates. Two overlay mutations (ignoring the selected sqid, and discarding /apps/docs return) fail the intended tests. No live browser/canary claim is made for unmounted handlers.

## Native app extension, 2026-09-11

The same chooser now serves Docs, Sheets, Slides, Calendar, Meet and Memoria at /apps/{app} and /apps/{app}/team. App identifiers, display names and form actions come from the closed compiled registry. Templates remain shared and scriptless. The catalog now offers Meet through its team chooser instead of Coming soon; explicit preview destinations still take precedence. Provisioning remains disabled. See directory-runtime.md for consumer evidence and Home discussion53 for the Fable review.
