oficina / meet Public

Merge branch 'main' of amadan.net/oficina/meet

Slice 6's docs landed while mid-call device switching was in the
gates. Documentation only — no Go, no client, no templates — so the
binary already on edge (9434395) is behaviourally identical to one
built from this merge, and is left serving rather than redeployed:
every deploy restarts the instance and drops live calls.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Paul Campbell pushed by paul@keymail.dev b6e8f29cada4493b12b502dc9d4b5236b1b15c67 parent 9434395parent e4f5d9e
6 files changed, +962 −45
  • AGENTS.md +24 −5
  • PRODUCT.md +269 −0
  • README.md +175 −0
  • docs/superpowers/plans/2026-09-14-meet-slice-6-deploy.md +157 −0
  • docs/superpowers/reviews/2026-09-14-slice-6-record.md +209 −0
  • docs/superpowers/specs/2026-09-07-meet-design.md +128 −40

CI passed — run details

diff --git a/AGENTS.md b/AGENTS.md
index 3bc2096..a163561 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -50,11 +50,13 @@ mechanically.
a rename is indistinguishable from a drop plus an add.
- Money is integer cents. A float never touches a value a person will
be held to.
-- Screens work with JavaScript disabled, with one exception: browser
+- Screens work with JavaScript disabled, with two exceptions: browser
media participation (the holding screen's camera preview and join
- button, and the call itself in the room) needs script; both pages
- render their title, standing, floor notice and navigation without it.
- Destructive actions get their own confirm-page URL.
+ button, and the call itself in the room), and, once a call exists,
+ the room's chat panel and its admission queue's Admit controls; both
+ pages still render their title, standing, floor notice and
+ navigation without script. Destructive actions get their own
+ confirm-page URL.
- The gate is `make ci` (vet + gofmt + tests) — the same definition
CI runs. Run it before every push. `CGO_ENABLED=0` throughout: the
stack is cgo-free by design.
@@ -81,9 +83,26 @@ the golden deployment target, not a requirement: `./app -addr :8080`
serves anywhere. `GET /healthz` and `GET /api/version` are answered
by the framework, outside app middleware.
+## Deploying
+
+`make release VERSION=<sha>` builds and stamps the artifact; `carlos
+deploy --app meet --account oficina --channel edge --version <sha>
+--target linux-arm64 releases/meet-linux-arm64` ships and promotes it,
+waiting until `/api/version` serves it — but only once `carlos apps
+build-probe --app meet --account oficina /api/version` has been run
+for the app (set on the flagship app since 2026-09-09); a new app or
+account needs that first, or the wait watches `X-Carlos-Version`
+instead, which can report the version adopted before the process has
+actually changed. A migration applied under an earlier filename fails
+at boot; otherwise migrations never re-run. Every deploy, and every
+relay config change, restarts the instance and drops live calls — no
+drain exists. The wait can time out before
+hand-over — check `/api/version` before retrying. See the README's
+Deploying section for the canary recipe.
+
## UX conventions (seeded from profile: considered)
-Accessible by construction, progressively enhanced, and never dependent on JavaScript to be usable, with the one exception named above: browser media participation, on the holding screen and in the room.
+Accessible by construction, progressively enhanced, and never dependent on JavaScript to be usable, with the exceptions named above: browser media participation, on the holding screen and in the room, and, once a call exists, the room's chat panel and admission controls.
These are rastrillo's own conventions, and the profile is named for what it does rather than after anyone else's work. For wider reading on interface quality, impeccable.style, the WAI-ARIA Authoring Practices and Inclusive Components are all worth your time — none of them is endorsed by, or authoritative for, this framework.
diff --git a/PRODUCT.md b/PRODUCT.md
new file mode 100644
index 0000000..871ef8e
--- /dev/null
+++ b/PRODUCT.md
@@ -0,0 +1,269 @@
+# Product
+
+<!-- impeccable:product-schema 1 -->
+
+> **How this file was written.** Written unattended, on the same
+> instruction that produced `oficina/docs`' `PRODUCT.md`: every fact
+> below comes from repository evidence, not an interview —
+> `docs/superpowers/specs/2026-09-07-meet-design.md` (revision 9, plus
+> the 2026-09-10 and 2026-09-12 amendments), `AGENTS.md`, the slice
+> records under `docs/superpowers/reviews/`, `native/README.md`, and the
+> shipped code in `internal/meet/` on branch `slice/6-deploy`, main
+> `9225ec2`. Facts are cited to a spec section or a file. A reading
+> rather than a citation is marked **[inferred — confirm]** and should
+> be corrected, not trusted. A bare "ruling N" is the Meet spec's §1; a
+> doctrine ruling is always written "suite ruling N".
+
+## Platform
+
+Web, plus native Mac and iPhone/iPad clients over the same server
+contracts (spec §8.4's 2026-09-14 amendment, `native/README.md`).
+
+## Users
+
+**Primary: a member of one team**, making and joining that team's video
+calls. The instance boundary is the team — one instance per team
+(preamble; suite ruling 1, 5, §0.2), the "lone shape" until a suite home
+exists (§7.1). **Secondary: an outside guest**, admitted with a real
+identity, never as an anonymous URL holder: every participant signs in,
+and a meeting link gives an outsider a **guest grant**, never a way in
+without a name (ruling 1; suite ruling 15); a guest waits until an
+admitted member lets them in (ruling 9, §3.3).
+
+Access is a magic link gated by `MEET_ALLOWED_ADDRESSES`
+(`internal/meet/config.go`), with optional Home sign-in when an operator
+sets all four `MEET_HOME_*` settings (§7.1.1) — Home only *links* an
+existing member after independent local sign-in; it never creates a
+person or grants membership. Unlike Docs' §8.1, the spec states no
+passkey ruling of its own. **[inferred — confirm]** the team is assumed
+small enough that an address allowlist is appropriate; the spec states
+no target size.
+
+## Product Purpose
+
+Meet is **video calling for one Oficina team**: a member makes a
+meeting, sends one link, and the people who follow it set up camera and
+microphone, wait to be let in if not on the team, and talk (preamble).
+"The instance carries the pages, the sign-in, the signalling and the
+in-call chat, and never a frame of media" (§0.1) — falsifiable: publish
+the SQLite file, its WAL, and every log line, and the worst anyone
+learns is the shape of the team's meetings and who minted what, never a
+title, chat text, or signalling payload, enforced by a WAL-grep test and
+a captured-logger test, not merely asserted (§0.1).
+
+## Positioning
+
+1. **The claim is made per process, not once** (§0.1): Meet's own
+ process never touches a media packet; the platform TURN relay
+ forwards encrypted DTLS-SRTP it cannot read; a future platform SFU
+ decrypts to forward and gets its own spec revision when it ships
+ (§5.7).
+2. **No relay of its own, by ruling** (suite ruling 6, §5.2): present
+ `CARLOS_RELAY` mints ICE credentials per call; absent means host
+ candidates only, stated before the join button (§5.5).
+3. **Two authorities, not one, for a room.** *Standing* (§2.3, computed
+ per request, never stored) decides who may **enter**; a
+ *participation* (§2.4, hub state) decides who may **continue** — an
+ admitted guest keeps talking past its grant's expiry while a member
+ who loses standing is dropped at once (ruling 14).
+
+## Operating Context
+
+- **One Go binary per instance**, `CGO_ENABLED=0`, deployed on CARLOS as
+ a **unit route** (§7.3). Native Apple bindings under `native/` are the
+ one place cgo is used, isolated in `native/go.mod` (`AGENTS.md`).
+- **Routes today** (`internal/meet/app.go`): `GET /`, `POST /meetings`;
+ `GET /j/{token}` (outside the session group); the holding screen and
+ room; signalling (`stream`, `signal`, `ice`); `POST /m/{sqid}/share`
+ (2026-09-12); five destructive confirmation pairs (remove, takeover,
+ replace invitation, end meeting, revoke agent); Settings and agent
+ tokens; sign-in including optional Home routes; native handoff
+ endpoints; a bearer-only `/api/v1/*` group (`api.go`) outside CSRF and
+ the session group. `GET /healthz`/`GET /api/version` answer from
+ rastrillo itself, outside app middleware.
+- **Scriptless, with browser media participation as the exception, and
+ two script-only parts inside a live room.** Reaching a camera and
+ another browser needs script; so, once a call exists, do the chat
+ panel (`partials/chat.html` renders it `hidden` until script un-hides
+ it) and the admission queue's Admit controls (`room.js`). Every other
+ screen — the list, creating a meeting, a link shown once, every
+ confirm page, sign-in, `/welcome`, the agent token page, the Share
+ page and the Home account-link screens (§7.1.1), and the
+ server-rendered parts of the holding screen and room — works with
+ scripts off (§9; `AGENTS.md`'s UX line carries the media exception;
+ full list below).
+- **Nothing stored beyond a meeting's bones.** Rooms, peers, admission,
+ chat and presence live in an in-process hub and die with the process
+ (§2.2); a restart drops every live call (§4.5). Stored: `persons`,
+ `meetings` (sealed title, `internal/meet/seal/`), `meeting_links`
+ (hash-indexed plus a sealed copy for sharing since 2026-09-12),
+ `pending_joins` (a keyed address fingerprint, never plaintext),
+ `guest_grants`, `agents` (a member's minted principal; the token
+ itself is never stored, only its SHA-256) (models.go; §2.1).
+
+## Capabilities and Constraints
+
+**Shipped:** meeting creation and listing; a link shown once, replaced
+or shared via `POST /m/{sqid}/share`; sign-in with the pending-join
+exception admitting an outsider only while holding a live link (ruling
+12, §3.2); optional Home account-linking (§7.1.1); the holding screen
+and a P2P mesh room capped at four with perfect negotiation, resume, and
+a replay ring (§4); relay-minted ICE credentials renewed before deadline
+(§5.2, §5.3); ephemeral chat and screen share (§6, §5.4); a bearer-token
+drive surface for agents re-checking operator membership on every
+request (§8, §15.17); native Mac and iPhone/iPad apps sharing the Go
+protocol core (`native/README.md`).
+
+**Deliberately not built yet** (§11, as amended): see "What is
+deliberately not here" below for the full, cited list.
+
+**Binding technical constraints:**
+
+- **Media never touches Meet's own process** (§0.1); no handler accepts
+ a media packet. **`scope.Owned`, no bound request body**: every
+ user-owned query goes through it, reads and writes alike; a row that
+ isn't yours 404s, never 403s (`AGENTS.md`).
+- **Standing is computed, never cached** — every request, every
+ 15-second hub tick (§2.3, §2.4). A shipped migration is never edited.
+ The agent API's rate limit keys on the resolved agent, not the client
+ address, deliberately (§15.17; see below).
+- **`CGO_ENABLED=0 make ci` is the server's cgo-free gate.** Two
+ things run under `CGO_ENABLED=1` and never ship: `make race`, for
+ any change to `internal/meet/hub` or the stream, and `make
+ native-test`'s own `meetcore` race sub-step. The native Mac and
+ iPhone/iPad apps are the one place real cgo does ship — `MeetBridge`
+ is a binary target `MeetKit` links (`native/apple/Package.swift`,
+ `native/scripts/build-mac.sh`) — while every server release stays
+ `CGO_ENABLED=0` (`AGENTS.md`). **`make browser`** needs UDP, which
+ the Claude Code bash sandbox blocks (`AGENTS.md`).
+
+## Brand Commitments
+
+- **Name:** Meet, `amadan.net/oficina/meet`, first served at
+ `meet.oficina.works` (preamble; ruling 2 chose `oficina/meet` over
+ `oficina/conferencia`). Built beside Docs, Calendar, Slides and the
+ suite doctrine, consuming a decision made there rather than restating
+ it (preamble, §0.2).
+- **The theme is Estilo's**, adopted at whatever Estilo holds when each
+ slice lands. As recorded on 2026-09-14 (`git ls-remote --tags`
+ against `oficina/estilo`, empty at that check), no tag exists;
+ consumed at a pseudo-version, already adopted by the Home integration
+ on main. **[inferred — confirm]** the exact commit at any later date.
+- **The chat panel is Meet's contribution to Estilo** — "one
+ self-contained template partial and one script file so a second app
+ can adopt it," the suite's first chat, built to be taken (§6).
+- **Voice: [inferred — confirm].** Copy review was waived for this
+ documentation round (steering on `oficina/suite` discussion 5), so
+ shipped strings were not checked against a review record.
+
+## Evidence on Hand
+
+- **Spec:** revision 9 plus the 2026-09-10 and 2026-09-12 amendments,
+ 2100+ lines, six rounds of Codex review (§14). **Plans:**
+ `docs/superpowers/plans/`, one programme (slices 2–6) plus a plan per
+ slice. **Slice records:** `docs/superpowers/reviews/`, one per slice
+ 2–5 (room, relay, chat/screen-share, drive surface), each an index
+ into the spec's own §15 rulings for that slice.
+- **Native:** `native/README.md`, `native/DESIGN.md`,
+ `native/VERIFICATION.md` — build, gates, stated acceptance limits (no
+ real-device pass claimed for camera permissions, audio-device
+ switching, sleep/wake, or Home passkeys).
+- **What does not exist and must not be invented:** customers,
+ testimonials, benchmarks, pricing, usage numbers, launch date, press,
+ or a production promotion from this slice — edge
+ (`meet.oficina.works`) is promoted by hand by Paul, separately from
+ documentation (slice 6 plan, "What slice 6 was"). The acknowledgements
+ page and the Estilo theme pin are the other two slice-6 items with a
+ disposition rather than a delivery; spec §15.18 is the record for all
+ three.
+
+## Product Principles
+
+1. **State the degradation rather than let it be discovered.** The
+ no-relay floor says calls usually connect only on the same network
+ (§5.5); §0.3 states what the privacy claim excludes (signalling in
+ flight, a replay ring of up to 32 messages for 30 seconds, a link as
+ a bearer); the mail-budget residue is written down, not claimed away
+ (§3.2).
+2. **A verification that cannot distinguish "no" from "I can't see it"
+ is not a verification.** The §0.1 claim is enforced by grepping raw
+ SQLite bytes and the WAL and capturing the logger during a real call;
+ `make browser` asserts a received track by `ontrack` and frame count,
+ never a label (§13).
+3. **Two authorities are named because one would be wrong in some
+ direction** (§2.3–§2.4, ruling 14): a single authority either drops
+ an admitted guest at the grant's two-hour mark or lets an expired
+ grant re-enter.
+4. **A contract is written for the next client, not the current one.**
+ Signalling, `/ice` and the meeting API are contracts a native client
+ could speak (ruling 15, §8.4); the native apps are that ruling paying
+ off.
+5. **Actors are `person:<id>` or `agent:<id>`, never fabricated**
+ (§7.2, doctrine ruling 9).
+
+## Accessibility & Inclusion
+
+`AGENTS.md`'s vendored UI conventions apply: labelled fields with one
+error envelope, destructive actions on their own confirm-page URL and
+never a modal (`ui/confirm-form` — §3.1, §3.5, §4.2), state shown with a
+text label and not colour alone, and empty states that say what the
+screen is for.
+
+**The scriptless floor is the accessibility constraint this app adds
+beyond that shared list.** Reaching a camera and another browser needs
+script, and so, once a call exists, do the room's chat panel and its
+admission-queue Admit controls; nothing else does. With scripts off
+the holding screen's join button becomes one paragraph saying a call
+needs JavaScript and everything else on the page works without it
+(§3.3); the room does the same (§3.4).
+
+No target locale count or WCAG conformance level is stated in the Meet
+spec or `AGENTS.md`. **[inferred — confirm]**: Docs states "WCAG 2.2 AA,
+no exemptions" for itself; nothing here says the same target binds Meet,
+and this file does not assume it does.
+
+## What is deliberately not here
+
+- **Scriptless media, chat and admission** — the exceptions to the
+ scriptless floor: reaching a camera and another browser needs script,
+ and so, once a call exists, do the room's chat panel and its
+ admission-queue Admit controls; every other screen, including the
+ server-rendered parts of the holding screen and the room, works with
+ scripts off (spec §9; `AGENTS.md`'s UX-conventions line carries the
+ media exception in the same words).
+- **No STUN or TURN server of Meet's own.** ICE servers come from the
+ platform relay, present or not at all — never a server Meet names
+ itself (suite ruling 6, §5.2, §5.5).
+- **No SFU, and the room is capped at four admitted peers** — not a
+ claimed product limit, the mesh's own (ruling 5), lifted only when a
+ platform SFU that decrypts to forward ships, in its own spec revision,
+ not a quiet cap change (§5.7, §0.3, §11 "rooms above four").
+- **No client-address limiter on the drive surface.** The agent API's
+ per-agent rate limit (one request a second sustained, burst 60) keys
+ on the resolved agent, not the client address: behind the platform's
+ relay socket every request can share one address, and an
+ address-keyed limiter would let one flood take out every real agent
+ at once (§15.17).
+- **No passkeys ruling of its own.** Unlike Docs §8.1, the spec states
+ no instance-local-passkey decision. **[inferred — confirm]**.
+- **Chat is never stored.** A `chat` message fans out to admitted peers
+ and renders client-side; not replayed to a peer who joins or resumes
+ later, gone with the tab (ruling 8, §6).
+- **No recurring meetings, Calendar integration, recording, attendance
+ records, reactions, dial-in, captions, multi-host signalling,
+ push-to-talk, or virtual backgrounds, and no way to deny a knock** — a
+ guest who isn't admitted simply waits or leaves (§11).
+- **No native client sign-in method of its own** — a native client
+ authenticates through the same Home/local sign-in the browser uses,
+ handed off through a five-minute, one-use authorization; no separate
+ bearer or token scheme for native apps (§8.4's 2026-09-14 amendment,
+ `native/README.md`).
+ **No attendance claim, ever**: §0.3 says Meet does not record who was
+ in a call or for how long; an earlier revision implied one and the
+ spec dropped the implication rather than building the model.
+
+---
+
+*See also:* the design spec, `README.md`, `AGENTS.md`, and the slice
+records under `docs/superpowers/reviews/`.
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..53753fb
--- /dev/null
+++ b/README.md
@@ -0,0 +1,175 @@
+# Meet
+
+Meet is video calling for one team. A member makes a meeting, now or
+for later, and sends one link; anyone who follows it sets up their
+camera and microphone on a holding screen, waits to be let in if they
+are not on the team, and talks in a room of up to four, with chat
+beside the call and a screen to share. An agent can also create,
+list, relink and end meetings over an API. Every screen works with
+JavaScript disabled except browser media participation — the camera
+preview and join button on the holding screen, and the call itself in
+the room — and, once a call exists, the room's two other script-only
+parts: the chat panel and the live admission queue with its Admit
+controls. Both pages still render their title, start time, standing,
+floor notice, the paragraph that says what needs script, and a
+member's navigation with scripts off (spec §9).
+
+ICE servers come from the CARLOS platform relay, delivered as
+`CARLOS_RELAY` and minted fresh for each call at `GET /m/{sqid}/ice`;
+Meet names no STUN or TURN server of its own (suite ruling 6). With no
+relay configured, calls are direct only: two browsers connect when ICE
+can find a working path straight between them, which the holding
+screen states plainly above the join button: "This team's Meet has no
+relay yet. Calls usually connect only between people on the same
+network." Enabling or changing the relay restarts the instance. When
+the relay is in the path it forwards DTLS-SRTP packets encrypted
+between the browsers, so it carries bytes it cannot read; Meet's own
+process never receives a media packet at all. With the relay on, a
+call that still fails says "could not connect, even through the
+relay" and offers retry; it does not diagnose the network. One known
+cause is a network that allows only TCP 443, because the relay's TLS
+listener is deferred by the platform — a possibility, not a verdict
+(spec §0.1, §5.2, §5.5, §5.6, §7.3, §15.13).
+
+## Sign-in
+
+Sign-in is a magic link to an address on the team's allowlist
+(`MEET_ALLOWED_ADDRESSES`). Following a meeting link gives an outsider
+a guest grant at sign-in — never a way into a room without a name —
+and the allowlist otherwise gates sign-in exactly as it does for a
+member (spec §3, §7.1). An operator can also enable Home sign-in with
+all four of `MEET_HOME_ISSUER`, `MEET_HOME_CONSUMER_ID`,
+`MEET_HOME_KEYS_BASE64` and `MEET_HOME_DEK_BASE64` — never a partial
+set — which links an existing member after a fresh, independent local
+sign-in and explicit confirmation; it never creates a person or grants
+membership on its own (spec §7.1.1). Because a member's address can be
+rate-limited on purpose and gets a different answer from an unknown
+one, whether a given address is on the team is not a secret the
+sign-in form can keep; that residue is stated rather than claimed away
+(spec §3.2).
+
+## The room
+
+The room is a peer-to-peer mesh of up to four admitted participants,
+negotiated with the W3C perfect-negotiation pattern over an SSE stream
+and a POST signal per peer. A guest waits in a queue until an admitted
+member lets them in; removing a participant and ending a meeting each
+go through their own confirm page — there is no in-room control that
+skips one (spec §3.5). A member can retrieve the current invitation
+again from the holding screen or the room with Share (`POST
+/m/{sqid}/share`). On a meeting whose only link predates the
+2026-09-12 sharing update, the first Share mints a new recoverable
+invitation alongside the old, still-valid one rather than opening it;
+every Share after that retrieves the same recoverable one. Replacing a
+link revokes every live one. Chat is fanned out live to admitted peers
+and never stored (spec §6). Screen share adds a second tile per sharer
+(spec §5.4). The room lives in one process's memory, so every deploy
+drops every live call (spec §15.11).
+
+## Agents
+
+A member mints an agent's bearer token from Settings; it is shown once
+and the row after it keeps only its hash. The token works on
+`/api/v1` only — never a cookie there, and never a bearer on an HTML
+route — for five operations: list meetings, create one, replace its
+link, end it, and read one meeting's detail. It cannot join a call
+(spec §8). An agent's reach is re-derived from its operator's current
+standing on every request, so an operator who leaves the team takes
+their agent's reach with them at once, with no separate revocation
+step. Requests are limited per agent to one a second sustained, with a
+burst of 60 (spec §8, §15.17).
+
+## Native apps
+
+The SwiftUI Mac and iPhone apps use this same server and a shared Go
+core (`meetcore/`, `protocol/`); there are no embedded webviews. Build
+the Mac app with `make native-mac`. See `native/README.md` and
+`native/ios/README.md` for setup, native sign-in and the native gates.
+
+## Building and gates
+
+`CGO_ENABLED=0 make ci` (vet, gofmt, tests, migration check) is the
+one gate a runner and a push share; `ci` itself does not set the
+variable, so the runner and every push must. Three things ship or run
+under `CGO_ENABLED=1`, each isolated from the server: `make race`
+builds a server test binary, never a release, for any change to
+`internal/meet/hub` or the signalling stream; `make native-test`'s own
+`meetcore` race sub-step does the same for the shared Go core; and the
+native Mac and iPhone/iPad bindings under `native/` bridge Go to
+Objective-C with real cgo, isolated in `native/go.mod` and built with
+`make native-mac`. `make browser` drives a real call across two real
+Chromium contexts, 31 scenarios in about nine minutes; it needs the
+Claude Code bash sandbox off, because the sandbox blocks UDP and every
+call then fails with `frames` stuck at 0. `make release VERSION=<sha>`
+stamps that version into `/api/version`, so a deploy can be checked
+against what it is meant to be running.
+
+## Deploying
+
+Meet deploys as a CARLOS unit route. Build the artifact first — `make
+release VERSION=<sha>` produces `releases/meet-linux-arm64`, stamped
+with the version `/api/version` will report (`strings -a
+releases/meet-linux-arm64 | grep -cx <sha>` confirms the stamp) — then
+deploy it: `carlos deploy --app meet --account oficina --channel edge
+--version <sha> --target linux-arm64 releases/meet-linux-arm64`,
+which waits until `/api/version` reports it — but only once a build
+probe is configured for the app: `carlos apps build-probe --app meet
+--account oficina /api/version`, set on the flagship app since
+2026-09-09. A new app or account needs that command run once before
+the wait means anything; without it `carlos deploy` watches
+`X-Carlos-Version` instead, which can report the new version adopted
+before the running process has actually changed. `--account` is a
+global flag; `carlos ship` uploads only, while `carlos deploy` is ship,
+promote and wait (its own help text). Building for your own
+architecture and shipping that uploads fine and fails to exec on the
+instance.
+
+These are the settings a deploy needs (`config.go`, `standing.go`).
+Five refuse startup when empty outside `-env development`:
+`MEET_ORIGIN` (the external origin, scheme included);
+`MEET_INSTANCE_KEY`; `MEET_DEK` (base64 of exactly 32 decoded bytes,
+not 32 base64 characters); `MEET_ALLOWED_ADDRESSES` (comma-separated,
+non-empty — this is the team); and mail, `MEET_SMTP_*` or the
+platform's `CARLOS_SMTP_*`. Two are optional, each with its own
+default rather than a refusal: `MEET_ZONE` (an IANA zone, defaulting
+to UTC when unset) and `CARLOS_RELAY` (empty means no relay, §5.5 —
+only a *malformed* value refuses startup), delivered by `carlos relay
+enable --app meet` rather than set by hand. Secrets —
+`MEET_INSTANCE_KEY`, `MEET_DEK` and `MEET_SMTP_PASS` — go through
+`carlos secrets`; the rest go through `carlos env`. Mail reads
+`MEET_SMTP_*` with `CARLOS_SMTP_*` as a per-variable fallback; the
+platform supplies a sender too
+(`CARLOS_SMTP_FROM`), which `MEET_SMTP_FROM` can optionally override,
+so `carlos email enable` alone is enough to send mail. `carlos relay
+enable --app meet` turns on the platform relay. Every deploy, and
+every relay change, restarts the instance and drops every live call —
+no drain exists, so deploy outside meeting hours. Migrations apply
+once each at boot (`msg=migrate` in the log) and are never re-run; a
+database that already applied a migration file under an earlier name
+fails at boot rather than silently diverging. The deploy wait can time
+out before the hand-over while the instance then serves the new
+version a minute later — check `/api/version` before retrying.
+
+Canary a build on its own host: `carlos canary --app meet --name
+slice2` composed `slice2.meet.bds.oncarlos.com`, and the console
+refused it ("invalid host, an instance host is one DNS label followed
+by .bds.oncarlos.com") — as recorded on 2026-09-10 in platform
+discussion #124, open as of that record. The recipe actually used:
+`carlos instances create --app meet --account oficina --host
+slice2-meet.bds.oncarlos.com --channel canary/slice2 --environment
+slice2`. The `slice2` environment bundle must already exist, created
+separately, carrying `MEET_ORIGIN` for the canary host; without it
+sign-in refuses on the canary while GETs look healthy. Then `carlos
+deploy --account oficina --app meet --channel canary/slice2 --host
+slice2-meet.bds.oncarlos.com --version <sha>
+releases/meet-linux-arm64`.
+
+## Where things are written down
+
+The design lives in `docs/superpowers/specs/2026-09-07-meet-design.md`,
+the slice plans in `docs/superpowers/plans/`, and each slice's review
+in `docs/superpowers/reviews/`. `PRODUCT.md` covers what Meet is for
+and what it deliberately leaves out; `AGENTS.md` covers the
+conventions the code is held to. Spec §15.18 is the disposition of
+record for what slice 6 delivered, deferred (the acknowledgements
+page) and declined (the Estilo theme pin, the production promotion).
diff --git a/docs/superpowers/plans/2026-09-14-meet-slice-6-deploy.md b/docs/superpowers/plans/2026-09-14-meet-slice-6-deploy.md
new file mode 100644
index 0000000..dbbce1a
--- /dev/null
+++ b/docs/superpowers/plans/2026-09-14-meet-slice-6-deploy.md
@@ -0,0 +1,157 @@
+# Meet Slice 6: Deploy Docs Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Close the Meet programme (spec §12 item 6) with the documents a deployed app owes: README, PRODUCT.md, the *Consumed suite rulings* commit for suite Appendix A.6, AGENTS.md's final notes, and the slice record, each true of main as it is on 2026-09-14.
+
+**Architecture:** Documentation only. Nothing under `internal/`, `native/`, `meetcore/` or `protocol/` changes. Every sentence about behaviour is checked against the code on main `9225ec2` before it is written, and cites the spec section or file it comes from. Items of the programme's slice 6 that other sessions have already delivered or are delivering are recorded as such, not redone.
+
+**Tech Stack:** Markdown; the spec at `docs/superpowers/specs/2026-09-07-meet-design.md` (revision 9 plus the 2026-09-10 and 2026-09-12 amendments on main); the suite sharing doctrine at `oficina/suite` `docs/superpowers/specs/2026-09-05-suite-sharing-doctrine.md` revision 8, main `1ac4323`.
+
+**Spec:** `docs/superpowers/specs/2026-09-07-meet-design.md` §7.3, §9, §10, §11, §12 item 6, §0.2. Programme: `docs/superpowers/plans/2026-09-09-meet-slices-2-6-programme.md` (slice 6 line and §5).
+
+## Global Constraints
+
+- Branch `slice/6-deploy` from main `9225ec2`; docs only; `make ci` stays green (it is unaffected by Markdown, run it once at the end).
+- No user-facing app string changes. Copy review is waived for this round by Paul's steering on `oficina/suite` discussion 5 ("copy review is explicitly waived for this entire massive round"); README and PRODUCT.md prose is catalogued in the record, not reviewed.
+- Every claim about behaviour is verified against the code on `9225ec2` (not against memory of earlier slices) and cites its source. A claim that cannot be verified is not written.
+- The README's first paragraph carries the scriptless-floor sentence (spec §9: browser media participation needs script, everything else works with scripts off) and the second paragraph is the relay paragraph (spec §5.2, §5.5: ICE servers come from the platform relay through `CARLOS_RELAY`, Meet names no STUN or TURN server of its own; with no relay, calls are direct only).
+- Nothing here restates a suite ruling; §0.2 names rulings by number (doctrine Appendix A intro: "delete the restatement, name the ruling").
+
+## What slice 6 was, and what it is now
+
+The programme (2026-09-09) listed for slice 6: PRODUCT.md, the README with the floor and relay paragraphs first, AGENTS.md's final notes, the acknowledgements page, the *Consumed suite rulings* commit for suite Appendix A.6, the Estilo theme pin if a tagged release exists, and the production promotion with its notification. Since slice 5 shipped (a283c53, 2026-09-11) other sessions landed on main: Home sign-in (`7.1.1`, `internal/meet/home.go`), the shared Estilo day theme and brand (`f9cb47f`, `34fc367`; `amadan.net/oficina/estilo.git` as an indirect pseudo-version), the invitation `Share` route (`POST /m/{sqid}/share`, 2026-09-12 amendment), and the native Mac, iPhone and iPad apps (`native/`, `meetcore/`, `protocol/`, tag `v0.1.0` at `9225ec2`). Therefore:
+
+| Programme item | Disposition in this slice |
+|---|---|
+| PRODUCT.md | Task 2, here. |
+| README with floor and relay paragraphs first | Task 1, here. |
+| AGENTS.md final notes | Task 1, here (deploy and gate notes only; the file already carries the native and sharing notes added on main). |
+| Acknowledgements page | **Not built here.** `oficina/meet` discussion 10, branch `feat-open-source-credits` (`117b6e4`, `bbc126e`): a shared Estilo credits page with a runtime dependency inventory, Fable-approved, verified live on the overnight canary, not yet merged to main. Slice 6 records it and defers to it; building a second one would be the duplication the programme forbids. |
+| Estilo theme pin if a tagged release exists | **No tag exists** (`git ls-remote --tags` on `oficina/estilo` is empty on 2026-09-14). The theme is nevertheless already adopted on main at a pseudo-version by the Home integration. Recorded in §15.18; not changed. |
+| Consumed suite rulings commit | Task 3, here: §0.2 refreshed against doctrine main `1ac4323` (still revision 8) and against what main now consumes (Home hand-off, the Share amendment), in one commit titled so Appendix A.6 can point at it. |
+| Production promotion with its notification | **No deploy from this slice.** The change is documentation; edge (`meet.oficina.works`) is being promoted by hand by Paul (`651cff2` on 2026-09-12) with other sessions' work queued behind live checks (discussion 14). A docs-only redeploy would drop live calls for nothing. The notification posted on discussion 3 says so in those words; a queued promotion is never announced as live (programme §5). |
+
+---
+
+### Task 1: README.md and AGENTS.md's final notes
+
+**Files:**
+- Create: `README.md`
+- Modify: `AGENTS.md` (append a short "Deploying" section after "Serving"; do not touch existing lines)
+
+**Interfaces:**
+- Consumes: spec §3.3/§3.4 (the floor sentence on the holding page and room: find the exact shipped sentence in `internal/meet/templates/join.html` and `room.html`), §5.2/§5.5 (relay), §7.1.1 (Home), §7.3 (deploy), §8 (agents), `native/README.md`, `Makefile` targets, `cmd/meet/main.go` (env vars), `internal/meet/config.go` (which are refused when empty outside development).
+- Produces: the README that PRODUCT.md (Task 2) and the record (Task 4) link to.
+
+- [ ] **Step 1: Verify each fact you will state** by reading the file it comes from on this branch. Keep a list of `claim → file:line` for the report.
+
+- [ ] **Step 2: Write `README.md`** with this shape (the paragraphs in this order; write them as prose, not as this outline):
+
+```markdown
+# Meet
+
+<Paragraph 1: what Meet is (video calls for one team: create or schedule a
+meeting, share a link, a holding screen, a room of up to four, chat, screen
+share, an agent API), and the floor sentence: every screen works with
+JavaScript disabled except browser media participation, the camera preview
+and join button on the holding screen and the call itself in the room; both
+pages render their title, standing, floor notice and navigation without
+script (spec §9).>
+
+<Paragraph 2: the relay. ICE servers come from the CARLOS platform relay,
+delivered as CARLOS_RELAY and minted per call at GET /m/{sqid}/ice; Meet
+names no STUN or TURN server of its own (suite ruling 6); with no relay
+configured, calls are direct only and the holding screen says so. Enabling
+or rotating the relay restarts the instance (spec §5.2, §5.5, §15.13).>
+
+## Sign-in
+<magic link to an allowlisted address (MEET_ALLOWED_ADDRESSES); a meeting
+link gives an outsider a guest grant at sign-in, never a way in without a
+name; optional Home sign-in with the four MEET_HOME_* settings, all or
+none (spec §7.1.1).>
+
+## The room
+<P2P mesh of up to four, perfect negotiation, SSE stream + POST signal,
+waiting queue for guests, removal and takeover through confirm pages,
+chat fanned out and never stored, screen share as a second tile; every
+deploy drops live calls because the hub is in-process (spec §4, §5, §6).>
+
+## Agents
+<Settings mints a token shown once; bearer-only /api/v1 (list, create,
+replace links, end, get); reach re-derived per request; 60/min per agent;
+spec §8 and §15.17.>
+
+## Native apps
+<one paragraph pointing at native/README.md and native/ios/README.md;
+make native-mac; same server and shared Go core; no embedded webviews.>
+
+## Building and gates
+<make ci (CGO_ENABLED=0), make race (the cgo exception, tests only),
+make browser (real Chromium, 31+ scenarios, about nine minutes, must run
+with the Claude Code bash sandbox off: UDP is blocked inside it), make
+native-test; make release VERSION=<sha> stamps /api/version.>
+
+## Deploying
+<unit route on CARLOS; carlos deploy --app meet --account oficina with the
+build probe on /api/version; secrets MEET_INSTANCE_KEY, MEET_DEK (32 base64
+bytes), mail via MEET_SMTP_* with CARLOS_SMTP_* as fallback; env
+MEET_ORIGIN, MEET_ALLOWED_ADDRESSES, MEET_ZONE; carlos relay enable --app
+meet; migrations apply at boot once each (log line msg=migrate); every
+deploy restarts the instance and drops live calls, so deploy outside
+meeting hours; the canary recipe (carlos instances create + carlos deploy
+--channel) because carlos canary forms a two-label host, platform #124.>
+
+## Where things are written down
+<spec, programme plan, the slice records under docs/superpowers/reviews/,
+PRODUCT.md, AGENTS.md.>
+```
+
+- [ ] **Step 3: Append to `AGENTS.md`**, after the "Serving" section, a "Deploying" section of at most eight lines: unit route; `carlos deploy` with the probe; migrations at boot, never re-run (already in the rules; say only that a database which applied a differently named migration file fails at boot); every deploy and every relay config change restarts the instance and drops live calls; the `carlos deploy` wait can time out before the hand-over, check `/api/version` before retrying; the canary recipe. No other change to AGENTS.md.
+
+- [ ] **Step 4: Check** `grep -c '' README.md` is under 140 lines, no sentence claims something you did not verify, no suite ruling is paraphrased where a number will do.
+
+- [ ] **Step 5:** Do not commit. Report the claim list.
+
+### Task 2: PRODUCT.md
+
+**Files:**
+- Create: `PRODUCT.md`
+
+**Interfaces:**
+- Consumes: `oficina/docs` `PRODUCT.md` as the shape (impeccable product schema 1: Platform, Users, Product Purpose, Positioning, Operating Context, Capabilities and Constraints, Brand Commitments, Evidence on Hand, Product Principles, Accessibility & Inclusion), the spec's §1 (goals), §9 (the floor), §11 (deliberately not built), §13 (gates), the slice records.
+- Produces: the section "What is deliberately not here" that spec §9 says slice 6 records the scriptless exception in.
+
+- [ ] **Step 1: Read** `/home/paulca/amadan.net/oficina/docs/PRODUCT.md` in full for the shape and the disclaimer header (written unattended; facts cited to spec or code; readings marked **[inferred — confirm]**).
+
+- [ ] **Step 2: Write `PRODUCT.md`** in that shape, for Meet, citing spec sections and files. It must contain a section **"What is deliberately not here"** listing: the scriptless-floor exception (browser media participation, spec §9); no reactions, dial-in, captions, multi-host signalling, recordings (§11, as amended on main); no STUN/TURN of Meet's own (ruling 6); no passkeys at the instance (as Docs); no SFU (roadmap 4b later); no client-address limiter on the API (§15.17); chat never stored; the room capped at four. Mark inferred readings. Under 240 lines.
+
+- [ ] **Step 3:** Do not commit. Report the claim list.
+
+### Task 3: The *Consumed suite rulings* commit and §15.18
+
+**Files:**
+- Modify: `docs/superpowers/specs/2026-09-07-meet-design.md` (§0.2; §12 item 6; a new §15.18)
+
+**Interfaces:**
+- Consumes: doctrine main `1ac4323` revision 8 (`/home/paulca/amadan.net/oficina/suite/docs/superpowers/specs/2026-09-05-suite-sharing-doctrine.md`, verified current by `git ls-remote` on 2026-09-14), Appendix A's intro (name the ruling, delete the restatement), spec §7.1.1 and the 2026-09-12 sharing amendment on main, `internal/meet/home.go`, `internal/meet/api.go`.
+- Produces: the commit suite Appendix A.6 will cite (its hash goes on suite discussion 5 in Task 4).
+
+- [ ] **Step 1: Refresh §0.2.** Keep the table's shape. Confirm the doctrine citation (revision 8 at 1ac4323, still main on 2026-09-14). Update the row for rulings 4 and 12: the home now mints assertions and Meet consumes them through `home/connect` (§7.1.1) while the allowlist remains the membership authority; the row must name the rulings and §7.1.1, not restate them. Add a row for ruling 16 as amended by the 2026-09-12 sharing update (invitation tokens are hash-indexed with a sealed copy for members' repeat sharing; agent credentials remain hash-only and shown once). Keep "Also consumed" current (discussion 4; the media roadmap; the relay spec, now live). Do not paraphrase any ruling.
+
+- [ ] **Step 2: §12 item 6:** annotate as delivered on 2026-09-14 with the disposition table from this plan's "What slice 6 was" section in one sentence per item.
+
+- [ ] **Step 3: Add §15.18** "§12 item 6 — what slice 6 delivered, deferred and declined": the README and PRODUCT.md; the acknowledgements page deferred to discussion 10's branch; the Estilo pin (no tag exists; adopted at a pseudo-version by the Home integration on main; the programme's condition is recorded as unmet and the adoption as done by another route); no deploy from a docs-only slice, edge hand-managed at 651cff2.
+
+- [ ] **Step 4:** Do not commit. Report the exact lines changed.
+
+### Task 4: Record, reviews, merge, notification
+
+**Files:**
+- Create: `docs/superpowers/reviews/2026-09-14-slice-6-record.md`
+
+- [ ] **Step 1:** Commit Tasks 1 to 3 as three commits (README+AGENTS; PRODUCT.md; "Consumed suite rulings: §0.2 against doctrine 1ac4323 rev 8, §15.18").
+- [ ] **Step 2:** Whole-branch review (Opus): every claim in the three documents checked against the code on the branch; findings W6-n.
+- [ ] **Step 3:** Codex round(s) until accept.
+- [ ] **Step 4:** Record in the shape of the slice 5 record: rulings, dispositions, carried items (the acknowledgements page, the Estilo tag, the production promotion, the credits inventory freshness gate that discussion 10 adds), the copy catalogue note, the gates (`make ci` on the head; the browser gate is not run: no page or client changed).
+- [ ] **Step 5:** `make ci`, push, remote CI, fast-forward main, the notification on `oficina/meet` discussion 3 and the checkpoint on `oficina/suite` discussion 5 naming the §0.2 commit for Appendix A.6, close the slice discussion, hub tasks Done, memory.
diff --git a/docs/superpowers/reviews/2026-09-14-slice-6-record.md b/docs/superpowers/reviews/2026-09-14-slice-6-record.md
new file mode 100644
index 0000000..85f8a03
--- /dev/null
+++ b/docs/superpowers/reviews/2026-09-14-slice-6-record.md
@@ -0,0 +1,209 @@
+# Meet slice 6 — the record
+
+Branch `slice/6-deploy`, `9225ec2..HEAD` (this commit; started on main
+`9225ec2`, tag `v0.1.0`). Spec: `docs/superpowers/specs/2026-09-07-meet-design.md`,
+revision 9 (§15.18 carries this slice's dispositions). Plan:
+`docs/superpowers/plans/2026-09-14-meet-slice-6-deploy.md`.
+
+Slice 6 is the programme's closing slice (spec §12 item 6) and is
+documentation only: `README.md`, `PRODUCT.md`, a Deploying section in
+`AGENTS.md`, the *Consumed suite rulings* commit (`fa875c7`, the hash
+suite Appendix A.6 points at) and the spec amendments that commit and
+the review made. No file under `internal/`, `cmd/`, `native/`,
+`meetcore/` or `protocol/` changed. The ledger is
+`.superpowers/sdd/2026-09-14-meet-slice-6-deploy/` (gitignored; this
+file is its substance).
+
+---
+
+## 1. Rulings and decisions
+
+- **The slice was re-scoped against main as it is on 2026-09-14, not
+ as the programme imagined it on 2026-09-09.** Between slice 5
+ (`a283c53`, 2026-09-11) and this slice, other sessions landed Home
+ sign-in (§7.1.1), the shared Estilo day theme and brand, the
+ invitation `Share` route (the 2026-09-12 amendment), and the native
+ Mac, iPhone and iPad apps. The plan's "What slice 6 was, and what it
+ is now" table is the disposition of each programme item; §12 item 6
+ and §15.18 restate it in the spec.
+- **The acknowledgements page is not built here.** `oficina/meet`
+ discussion 10, branch `feat-open-source-credits` (`117b6e4`,
+ `bbc126e`), carries a shared Estilo credits page with a runtime
+ dependency inventory and its freshness gate, Fable-approved and
+ verified live on the overnight canary. Building a second one would
+ be the duplication the programme forbids.
+- **The Estilo theme pin.** The programme's condition was a tagged
+ release; `git ls-remote --tags` on `oficina/estilo` is empty on
+ 2026-09-14. The theme is nevertheless adopted on main at an indirect
+ pseudo-version (`go.mod`), brought in by the Home integration.
+ Recorded as: condition unmet, adoption done by another route,
+ nothing changed here.
+- **No deploy from this slice.** The change is documentation,
+ checkable from this diff. As observed on 2026-09-14 from `carlos
+ channels` and `oficina/meet` discussions 11 and 14, edge
+ (`meet.oficina.works`) was being promoted by hand by Paul (`651cff2`
+ on 2026-09-12), with discussion 14's web UI work queued behind a
+ live check — state as of that observation, not asserted as current
+ here. A redeploy would drop live calls for nothing; a queued
+ promotion is never announced as live (programme §5). The domain was
+ attached before this slice.
+- **Copy review is waived** for this round by Paul's steering on
+ `oficina/suite` discussion 5. README and PRODUCT.md prose is
+ catalogued here (§3) rather than reviewed.
+- **Writers did not commit.** Three writers worked concurrently in one
+ worktree on disjoint files; the controller committed each set. The
+ index is the shared resource, not the files.
+- **Every behavioural claim is verified against the code on the
+ branch and cites its source.** The whole-branch review's finding was
+ that the writers verified the documents against the code but not the
+ code's documents against the documents: four spec sentences on main
+ asserted things about "the README" that the README, once it existed,
+ did not say (§0.1/§5.6's encryption sentence, §5.5's TCP-443 cause
+ and "first paragraph", §3.2's residue). All four now hold, and the
+ fix wave swept every spec sentence naming README, AGENTS.md or
+ PRODUCT.md (twelve remaining, all true).
+- **The two meanings of "floor".** §5.5's floor is "no relay"; §9's is
+ the scriptless floor. §12 item 6 now says which goes where (the
+ scriptless sentence in the README's first paragraph, the relay
+ paragraph carrying §5.5's sentence second); §15.18 notes the two
+ senses.
+- **Platform facts stated from this project's own deploys**, not from
+ the platform source: `carlos canary` composed a host the console
+ refused (platform discussion 124, open); the recipe used is
+ `carlos instances create --host <name>-meet.bds.oncarlos.com
+ --channel canary/<name> --environment <name>` then `carlos deploy
+ --channel …`, with the environment bundle mandatory because a canary
+ without one serves `MEET_ORIGIN` for the main host and refuses
+ sign-in while GETs look healthy. The release command is `carlos
+ deploy --app meet --account oficina --channel edge --version <sha>
+ --target linux-arm64 releases/meet-linux-arm64` after `make release
+ VERSION=<sha>`; the deploy wait can time out before the hand-over
+ while the instance serves the new version a minute later.
+- **Ruling 16** stays cited as on the doctrine's
+ `worktree-worktree-token-at-rest` branch, not doctrine main (whose
+ rulings run 1 to 15 at `1ac4323`, revision 8, confirmed current by
+ `ls-remote` on 2026-09-14), as amended by the 2026-09-12 sharing
+ update for invitation tokens; agent credentials remain hash-only and
+ shown once.
+
+---
+
+## 2. Per-task review dispositions
+
+Three writing tasks (Sonnet) in parallel, then one whole-branch review
+(Opus) and one fix wave, then Codex rounds.
+
+- **Task 1, README and AGENTS.md** (`93f1c2e`): 106 lines, the two
+ paragraphs first. Flagged and acted on: spec §11 still listed a
+ native client as not built (`aa9b227`). Flagged and initially left,
+ then closed by the review: §5.5's "first paragraph" cross-reference;
+ `MEET_SMTP_PASS` as a secret.
+- **Task 2, PRODUCT.md** (`e684b97`): impeccable product schema 1 in
+ `oficina/docs`' shape with the unattended-writing disclaimer; "What
+ is deliberately not here" carries the scriptless exception spec §9
+ asked for.
+- **Task 3, the consumed-rulings commit** (`fa875c7`): §0.2's 4/12 row
+ consumes the home's assertions through §7.1.1 with the allowlist as
+ membership authority; a ruling-16 row as amended; §12 item 6
+ annotated; §15.18 added. Revision line left at 9, following what
+ §15.13 to §15.17 did.
+- **Whole-branch review** (`e684b97`): verdict **merge with named
+ fixes**, W6-1 to W6-15, all applied in `bf3b86b` and `c22cae7`
+ (reflow), plus the handoff's spec sweep, the two-floors
+ disambiguation, §15.11's stale "until slice 3, or until a drain
+ exists" (no drain exists; every deploy drops live calls) and
+ `token_sealed` added to §2.1's `meeting_links` columns. Roughly
+ sixty behavioural claims were traced to file and line and found
+ correct; the fifteen findings were the remainder.
+
+**Codex round 1** (independent, static, scope `9225ec2..c22cae7`):
+verdict **request changes** — thirteen findings, S6-1 through S6-13.
+All applied in one commit (`.superpowers/sdd/2026-09-14-meet-slice-6-deploy/fix-wave-report.md`,
+round-1 section):
+
+- **S6-1 — P2, deployment settings undocumented. Fixed:** README's Deploying section enumerates every required setting and which refuse startup outside `-env development`.
+- **S6-2 — P2, the deploy-wait guarantee had an unstated prerequisite. Fixed:** README and AGENTS.md now state the `carlos apps build-probe` prerequisite and the `X-Carlos-Version` fallback it stands in for.
+- **S6-3 — P2, the scriptless exception was narrower than shipped. Fixed, then completed in Codex round 2 (S6-16):** README, PRODUCT.md and spec §9 name the chat panel and the live admission controls as script-gated too, alongside browser media participation; AGENTS.md's two matching lines, left behind in round 1, were widened the same way, so §9's "the binding file and the code agree" holds again; §15.18 notes both.
+- **S6-4 — P2, the cgo claims didn't match the Makefile. Fixed, then corrected again in Codex round 2 (S6-14):** README and PRODUCT.md say `CGO_ENABLED=0 make ci` and name `make race` and `make native-test`'s `meetcore` sub-step as the two `CGO_ENABLED=1` cases that never ship; the native Mac and iPhone/iPad apps are named separately as the one place real cgo does ship (`MeetBridge`, linked by `MeetKit`), while server releases stay cgo-free.
+- **S6-5 — P2, the SMTP fallback description was backwards. Fixed:** README now says the platform supplies `CARLOS_SMTP_FROM` too, which `MEET_SMTP_FROM` optionally overrides.
+- **S6-6 — P2, suite restatements remained under §0.2. Fixed, then completed in Codex round 2 (S6-15):** rows 3, 6, 9 and 15 cite section and ruling number only; the discussion-4 paragraph does too; the §5.2 paragraph, shortened but not emptied in round 1, is now the bare citation "Doctrine §5.2 is also relied on, for §2."; PRODUCT.md's actor sentence likewise; ruling 16's off-main provenance kept as before.
+- **S6-7 — P2, PRODUCT.md cited a section that denies native clients shipped. Fixed:** spec §8.4 gained a 2026-09-14 amendment recording the shipped milestone (`v0.1.0` at `9225ec2`) and the browser-approval handoff (`86ed9f3`); PRODUCT.md's two native-client citations now point at that amendment.
+- **S6-8 — P2, the shipped same-network sentence was missing from the README. Fixed:** the relay paragraph now quotes `join.html`'s floor sentence verbatim.
+- **S6-9 — P2, the route inventory undercounted confirmation pairs. Fixed:** PRODUCT.md now says "five destructive confirmation pairs"; spec §9 now says "every confirm page (five)".
+- **S6-10 — P3, Share's legacy behavior was omitted. Fixed:** README now distinguishes a legacy meeting's first Share (mints a new recoverable invitation alongside the old one) from every retrieval after.
+- **S6-11 — P3, "60 requests a minute" obscured the permitted burst. Fixed:** README and PRODUCT.md now say "one request a second sustained, with a burst of 60".
+- **S6-12 — P2, the canary creation command relied on undocumented app/account defaults. Fixed:** README's `instances create` now carries `--app meet --account oficina` and states the named environment bundle must already exist, created separately.
+- **S6-13 — P3, disposition coverage and external-state attribution were incomplete. Fixed, then completed in Codex round 2 (S6-17):** README and PRODUCT.md link to spec §15.18 as the disposition of record; the platform-124, no-Estilo-tag and credits-branch-canary claims are attributed to dated records. Round 1 left the production-promotion state itself (edge's served commit, the queued work) asserted as current fact in §15.18 and this record's own §3; both now attribute it to what `carlos channels` and `oficina/meet` discussions 11 and 14 showed on 2026-09-14, keeping "this slice changes documentation only" as the one claim checkable from the repository.
+
+**Codex round 2** (independent, static, scope `2a6eee4`): verdict
+**request changes** — four findings, S6-14 through S6-17, against
+round 1's own fixes; the other twelve confirmed fixed as claimed
+(table in the round's own report). All four applied in one commit
+(`.superpowers/sdd/2026-09-14-meet-slice-6-deploy/fix-wave-report.md`,
+round-2 section):
+
+- **S6-14 — P2, PRODUCT.md still claimed the native cgo bridge never ships. Fixed:** "three things run under `CGO_ENABLED=1`, none of them shipped" is now two — `make race` and `make native-test`'s `meetcore` sub-step, both test-only — with the native Mac and iPhone/iPad apps named separately as the one place real cgo does ship (`MeetBridge`, a `native/apple/Package.swift` binary target `MeetKit` links, built by `native/scripts/build-mac.sh`), while server releases stay `CGO_ENABLED=0`. This record's own S6-4 line is corrected to match.
+- **S6-15 — P2, the guest-grant sentence was shortened, not removed. Fixed:** "Doctrine §5.2 is also relied on, for §2: a guest grant is stored, not asked for" drops its second clause — still a paraphrase of doctrine §5.2's grant authority — leaving the bare citation "Doctrine §5.2 is also relied on, for §2."
+- **S6-16 — P2, the widened scriptless exception left `AGENTS.md` behind. Fixed:** `AGENTS.md`'s two scriptless lines (the "Screens work with JavaScript disabled" rule and the UX-conventions sentence) now name the chat panel and admission controls alongside browser media participation, matching §9, README and PRODUCT.md; §9's own "the binding file and the code agree" is corrected to say when each half landed; §15.18 records the correction.
+- **S6-17 — P3, production state was asserted as current rather than attributed. Fixed:** §15.18's production-promotion bullet and this record's own §1 and §3 now attribute edge's served commit and the queued web UI work to what `carlos channels` and `oficina/meet` discussions 11 and 14 showed on 2026-09-14, rather than presenting it as established from this repository; "this slice changes documentation only" stays stated as fact, since the diff itself proves it.
+
+**Codex round 3** (independent, static, scope `cd02a59`): verdict
+**accept** — S6-14 to S6-17 closed, no new finding, "fit to merge as
+documentation of the code on this branch". Three rounds in all: 13, 4,
+0 findings.
+
+---
+
+## 3. Carried items, and where they go
+
+- **The acknowledgements page.** Discussion 10's branch
+ `feat-open-source-credits`, not yet on main. **Destination: that
+ branch's owner**; when it merges, the README's "Where things are
+ written down" section should gain the credits page's path (one
+ line).
+- **The Estilo theme tag.** No tagged release exists; the pseudo-version
+ pin stays until one does. **Destination: oficina/estilo.**
+- **The production promotion of main's head.** As observed on
+ 2026-09-14 from `carlos channels` and `oficina/meet` discussions 11
+ and 14, edge was serving `651cff2` with discussion 14's web UI work
+ queued behind it — state as of that observation, not asserted as
+ current here. Main is at `9225ec2` plus this slice, checkable from
+ this repository: `9225ec2` changed native code only, and this slice
+ changes documents only. **Destination: Paul**, who promotes by hand.
+- **Unauthenticated flood control on `/api/v1`** (from slice 5).
+ **Destination: carlos/platform**, unchanged.
+- **Platform discussion 124** (`carlos canary` and the one-label host).
+ Open. The README carries the recipe that works. **Destination:
+ carlos/platform.**
+- **Spec §9's "four confirm pages" is five** (remove, takeover,
+ replace-link, end, revoke-agent). **Resolved in Codex round 1
+ (S6-9):** §9 now says "every confirm page (five)" and PRODUCT.md
+ says "five destructive confirmation pairs".
+- **Copy catalogue** (copy review waived): the README's and
+ PRODUCT.md's prose are the new user-facing text this slice adds;
+ neither is an app string. Nothing in `internal/meet/templates`
+ changed.
+- **The ledger's process note.** A writer's flag should become a task,
+ not a paragraph: two of the three task reports flagged the right
+ problems (W6-3, W6-6) in their "not corrected" sections and the
+ controller took them as recorded rather than as work.
+
+---
+
+## 4. Deploying this slice
+
+Nothing to deploy. No binary, migration, template, client or
+configuration changed. When main is next promoted, the README's
+Deploying section is the recipe, and the `carlos deploy` wait note in
+§1 applies.
+
+---
+
+## 5. The gates, and how to run them
+
+| Gate | Command | This slice |
+|---|---|---|
+| CI | `CGO_ENABLED=0 make ci` | Green on every commit of the branch; remote CI passed on `e684b97` and `c22cae7`. Markdown cannot fail it; it is run so the branch's head is known green. |
+| Race | `make race` | Not run: no change under `internal/meet/hub` or the stream. |
+| Browser | `make browser` (sandbox off) | Not run: no page, template or client file changed. The last run on a shipping head is slice 5's, 31 of 31 on `4f896c2`. |
+| Native | `make native-test` | Not run: nothing under `native/`, `meetcore/` or `protocol/` changed. |
diff --git a/docs/superpowers/specs/2026-09-07-meet-design.md b/docs/superpowers/specs/2026-09-07-meet-design.md
index f4a7dba..8eae61f 100644
--- a/docs/superpowers/specs/2026-09-07-meet-design.md
+++ b/docs/superpowers/specs/2026-09-07-meet-design.md
@@ -89,20 +89,17 @@ This app cites the suite sharing doctrine (`oficina/suite`,
| Ruling | Used for |
|---|---|
| 1, 5 | One instance per team; the first deploy is the **lone shape** (§7.1). |
-| 3 | Authorization is asked per request and never cached. Standing (member, guest, neither) is derived at each request and re-derived on every hub tick (§2.3). |
-| 4, 12 | Identity and roster belong to the home. Until the home mints assertions, the lone instance keeps its own allowlist exactly as Docs and Slides do. |
-| 6 | No product-wide egress. Meet never names a STUN or TURN server of its own; ICE servers come from the platform (§5.2) or there are none (§5.5). |
-| 9 | Every write names its real actor: a member, a guest who signed in, or an agent (§8). |
-| 15 | Three rungs plus a read-only link. A meeting link is a capability to obtain a **guest grant at sign-in**, the `granted` rung, and never a way into a room without a name (§3). Ruled for Meet on `oficina/suite` discussion 3, Paul, 2026-09-06. |
-| 16 | A link token is stored hashed and shown once (§3.1). Ruling 16 is on the doctrine's `worktree-worktree-token-at-rest` branch (suite discussion 2), not yet on main; Meet adopts it now so it never has the Docs defect that produced it. |
-
-Doctrine §5.2 is also relied on: a guest grant held by the instance is
-authoritative for that grant, which is why §2 stores it and does not
-ask anyone.
-
-Also consumed: `oficina/suite` discussion 4 (agents as their own
-principal, on behalf of a person, bound to the operator's reach and no
-more) for the drive surface (§8); the CARLOS media roadmap
+| 3 | §2.3: standing (member, guest, neither) is derived at each request and re-derived on every hub tick. |
+| 4, 12 | The home now mints assertions; Meet consumes them through `home/connect`'s bridge, resolving to the existing person and re-checking membership on every request, while the allowlist remains the membership authority throughout (§7.1.1). |
+| 6 | §5.2, §5.5: Meet never names a STUN or TURN server of its own; ICE servers come from the platform, or there are none. |
+| 9 | §8: a member, a guest who signed in, or an agent. |
+| 15 | §3: a meeting link is a capability to obtain a **guest grant at sign-in**, the `granted` rung, and never a way into a room without a name. Ruled for Meet on `oficina/suite` discussion 3, Paul, 2026-09-06. |
+| 16 | The token at rest (§3.1), as amended by the 2026-09-12 sharing update at the top of this file: invitation tokens are hash-indexed with a sealed copy so a member can share the current one again; agent credentials stay hash-only and shown once (§15.17). Ruling 16 is on the doctrine's `worktree-worktree-token-at-rest` branch (suite discussion 2), not on main on 2026-09-14; Meet adopts it early so it never has the Docs defect that produced it. |
+
+Doctrine §5.2 is also relied on, for §2.
+
+Also consumed: `oficina/suite` discussion 4, for the drive surface
+(§8); the CARLOS media roadmap
(`carlos/platform`,
`docs/superpowers/specs/2026-09-07-media-platform-roadmap-design.md`,
merged at 8291cb2) and the stage 4a relay spec
@@ -171,9 +168,12 @@ that the subject is an address, not an id. Standing is not a column
its links answer 404 exactly as a revoked one does.
**`meeting_links`** — `id`, `meeting_id`, `token_hash` (SHA-256 of the
-token, ruling 16), `created_by`, `created_at`, `revoked_at`. One meeting
-may hold several links so a revoked one can be replaced. The token is
-returned once, in the response that minted it, and never again.
+token, ruling 16), `token_sealed` (nullable; added by the 2026-09-12
+sharing amendment, top of file, so a member can retrieve a current
+invitation again), `created_by`, `created_at`, `revoked_at`. One
+meeting may hold several links so a revoked one can be replaced. The
+token is returned once, in the response that minted it, and again on
+demand through `POST /m/{sqid}/share` once a sealed copy exists.
**`pending_joins`** — `id`, `meeting_id`, `link_id` (the
`meeting_links` row the browser followed), `address_fingerprint` (keyed
@@ -823,9 +823,10 @@ is usually true on a permissive local network, usually false across the
internet, and false on a local network with client isolation. The
pre-join screen says, above the button: "This team's Meet has no relay
yet. Calls usually connect only between people on the same network."
-The README's first paragraph says the same. An operator enables the
-platform relay with `carlos relay enable --app meet`, after which this
-paragraph no longer applies to that instance.
+The README's second paragraph, the one that introduces the relay, says
+the same; its first paragraph carries the scriptless floor (§9). An
+operator enables the platform relay with `carlos relay enable --app
+meet`, after which this paragraph no longer applies to that instance.
With `CARLOS_RELAY` present, a failed connection's tile says "could not
connect, even through the relay" and offers retry. It does not diagnose
@@ -974,6 +975,19 @@ A thin adapter over §8.2, deferred until a consumer asks by name.
### 8.4 Native clients
+**2026-09-14 amendment.** The SwiftUI Mac, iPhone and iPad apps this
+section specifies shipped on `main` (`native/`, `meetcore/`,
+`protocol/`, tag `v0.1.0` at `9225ec2`), closing the authentication gap
+the bullets below name with a browser-approval hand-off
+(`native/README.md`): a five-minute, one-use authorization bound to a
+proof secret held only by the app; approving in the browser creates an
+ordinary framework session, no cookie copied and no new bearer type
+introduced. The production handoff endpoints were deployed in
+`86ed9f3`. This supersedes the closing paragraph's "version one does
+not ship a native client" below; the rest of this section, written
+before that milestone, otherwise stands as the contract the shipped
+apps speak.
+
Ruling 15: a native app is the next client after the browser, and
nothing in Meet's server contracts may assume the browser. Concretely,
in version one:
@@ -1000,19 +1014,25 @@ until an OpenAPI document earns its place.
## 9. The scriptless floor
-The exception is **browser media participation** and nothing wider:
-reaching a camera and another browser needs script. Every other screen
-works with scripts off, including the meeting list, creating a meeting,
-seeing and copying a link, all four confirm pages, sign-in and
-continuation, the `/welcome` page, the agent token page, and the server-rendered
-parts of the holding screen and the room (§3.3, §3.4): title, start
-time, standing, floor sentence, the paragraph that says what needs
-script, and navigation to everything that does not.
-
-`AGENTS.md`'s line "screens work with JavaScript disabled" gains, in
-slice 2, the one exception in those words, so the binding file and the
-code agree from the commit that introduces the exception. `PRODUCT.md`
-records it in "what is deliberately not here" in slice 6.
+The exception is **browser media participation**: reaching a camera
+and another browser needs script. Inside a room that exists, script
+also gates the chat panel (`chat.html`'s whole panel is rendered
+`hidden` until script un-hides it, because a Send button with no
+script behind it would be a lie, §6, §10) and the admission queue's
+Admit controls (`room.js`), since neither can do anything before a
+call does. Every other screen works with scripts off, including the
+meeting list, creating a meeting, seeing and copying a link, every
+confirm page (five), sign-in and continuation, the `/welcome` page,
+the agent token page, and the server-rendered parts of the holding
+screen and the room (§3.3, §3.4): title, start time, standing, floor
+sentence, the paragraph that says what needs script, and navigation to
+everything that does not.
+
+`AGENTS.md`'s line "screens work with JavaScript disabled" gained, in
+slice 2, the one exception in those words; on 2026-09-14, alongside
+this section's own widening, it gained the other two (§15.18), so the
+binding file and the code agree again. `PRODUCT.md` records it in
+"what is deliberately not here" in slice 6.
Hand-written static JavaScript, no bundler, no npm, in three files:
`devices.js` (the holding screen), `room.js` (signalling, resume and
@@ -1040,8 +1060,7 @@ are drafts for that review, not approved copy.
Recurring meetings. Calendar integration. Recording. Attendance
records. A relay of Meet's own. Rooms above four. Persisted chat.
Reactions. Dial-in. Captions. Multi-host signalling.
-Push-to-talk. Virtual backgrounds. A native client and its sign-in
-(§8.4). Denying a knock (a guest who is not
+Push-to-talk. Virtual backgrounds. Denying a knock (a guest who is not
admitted simply waits or leaves). Each is named so the next session
does not rediscover it.
@@ -1082,9 +1101,22 @@ own gates from §13; nothing is deferred to the deploy slice.
5. **Drive surface.** `agents`, the five operations, the per-request
reach check, the badge.
6. **Deploy.** Domain, secrets, canary, `PRODUCT.md`, the README with
- the floor sentence in its first paragraph and the relay paragraph,
- and the `Consumed suite rulings` commit that suite Appendix A.6 will
- point at.
+ the scriptless-floor sentence (§9) in its first paragraph and the
+ relay paragraph, carrying §5.5's no-relay sentence, second, and the
+ `Consumed suite rulings` commit that suite Appendix A.6 will point
+ at.
+
+ **Delivered 2026-09-14 (§15.18).** The README, with the floor and
+ relay paragraphs first, was written. `PRODUCT.md` was written.
+ AGENTS.md's deploy notes were appended. This is the `Consumed suite
+ rulings` commit, refreshing §0.2 against doctrine main `1ac4323`
+ revision 8. The acknowledgements page is deferred to `oficina/meet`
+ discussion 10's branch `feat-open-source-credits`, not duplicated
+ here. The Estilo theme pin did not happen, because no tagged release
+ exists; the theme is nonetheless already adopted on main at a
+ pseudo-version through the Home integration. No production
+ promotion happened, because this slice is documentation only; edge
+ stays hand-managed by Paul.
---
@@ -1482,8 +1514,9 @@ they were otherwise recorded only in an untracked ledger:
not solved). Slice 2 is the first release where that has a
user-visible cost: **every deploy drops every live call.** Members
rejoin automatically; guests go back to waiting and must be admitted
- again. Deploys should avoid meeting hours until slice 3, or until a
- drain exists.
+ again. No drain exists, and slice 3 shipping did not change that:
+ every deploy still drops every live call, so deploying during
+ meeting hours means exactly that.
### 15.12 §2.5 — the last row corrected: standing is not consulted *for a guest*
@@ -2130,3 +2163,58 @@ truncated before the `~` — the badge's icon needs no separate
every icon "sits beside its own visible text label") is already
satisfied by the badge's own word, alongside AGENTS.md's own line —
"State — never colour alone — always a text label too."
+
+### 15.18 §12 item 6 — what slice 6 delivered, deferred and declined
+
+Slice 6 is documentation only: nothing under `internal/`, `native/`,
+`meetcore/` or `protocol/` changed. Against the programme's slice 6
+line (`docs/superpowers/plans/2026-09-09-meet-slices-2-6-programme.md`
+§5) and this spec's own §12 item 6:
+
+The word "floor" names two different things in this spec: §5.5's "The
+floor" is the no-relay condition, and §9's "The scriptless floor" is
+the JavaScript exception; item 6's bullet above now names each rather
+than leaving the bare word to carry both.
+
+§9's exception was also corrected here, on Codex round 1: browser
+media participation was never the only script-gated part of a room —
+the live admission controls have needed script since slice 2 and the
+chat panel since slice 4 — and §9, the README and `PRODUCT.md` now say
+so rather than "and nothing wider." Codex round 2 found the binding
+file left behind: `AGENTS.md` still named only the media exception, so
+§9's own "the binding file and the code agree" was false again the
+moment §9 widened. `AGENTS.md`'s two scriptless lines were widened to
+match on 2026-09-14, in the same commit.
+
+- **Delivered.** `README.md`, with the scriptless floor sentence and
+ the relay paragraph first. `PRODUCT.md`. `AGENTS.md`'s deploy notes.
+ §0.2 above, refreshed against doctrine main `1ac4323`, still
+ revision 8 on 2026-09-14 — this is the `Consumed suite rulings`
+ commit suite Appendix A.6 will point at. The domain was already
+ attached outside this slice — `meet.oficina.works` serves the
+ flagship instance (§7.3's `carlos domains attach`) — so item 6's
+ domain row needed nothing from slice 6.
+- **Deferred, not duplicated.** The acknowledgements page lives on
+ `oficina/meet` discussion 10, branch `feat-open-source-credits`
+ (`117b6e4`, `bbc126e`): a shared Estilo credits page with a runtime
+ dependency inventory. As recorded on that branch's own review, it is
+ Fable-approved and was verified live on the overnight canary, not
+ yet merged to main — not independently reverified here. Building a
+ second one here would be the duplication the programme forbids.
+- **Declined: the Estilo theme pin.** The programme's condition for
+ pinning was a tagged release; `git ls-remote --tags` against
+ `oficina/estilo` is empty on 2026-09-14, so no tag exists and no pin
+ was made. The theme is adopted on main anyway, at a pseudo-version
+ (`amadan.net/oficina/estilo.git`), by the Home integration
+ (`f9cb47f`, `34fc367`) — the programme's condition is recorded here
+ as unmet, and the adoption as done by another route, not as the
+ condition having been met after all.
+- **Declined: the production promotion.** A docs-only slice has
+ nothing to promote that changes behaviour, and a redeploy would drop
+ every live call for no reason (§15.11). As observed on 2026-09-14
+ from `carlos channels` and `oficina/meet` discussions 11 and 14,
+ `meet.oficina.works` was being promoted by hand by Paul (`651cff2`,
+ 2026-09-12) with other sessions' work queued behind live checks —
+ state as of that observation, not asserted as current here. This
+ slice does not touch that queue or announce a deploy of its own,
+ which is checkable from this diff alone.