docs: fix wave for slice 6 review (W6-1..W6-15)
Applies the whole-branch report's fifteen findings plus the handoff's three follow-ups against README.md, AGENTS.md, PRODUCT.md and the spec. README.md: add the relay's encryption sentence and the TCP-443 cause (W6-1, W6-2); rewrite the sign-in residue sentence (W6-7); rewrite the Deploying section with a real artifact, target and channel, the ship/deploy distinction, and the version-stamp check (W6-5); file the secrets clause correctly, MEET_SMTP_PASS included (W6-6); rewrite the canary recipe from the platform-verified 2026-09-10 facts, citing platform discussion #124 (W6-4); widen the scriptless-render sentence to match spec §9 (W6-12); document the Share route in the room paragraph (W6-13). AGENTS.md: cut the canary lines to point at the README's Deploying section, and correct the Deploying section's own deploy command to match (W6-4). PRODUCT.md: disambiguate the ruling-1/suite-ruling-15 citation and add the namespace convention to the disclaimer (W6-9); list `agents` among the stored tables (W6-10); correct the scriptless enumeration to every confirm page plus Share and the Home account-link screens (W6-14). Line-wrap reflow is its own commit (W6-15). Spec: fix the "README's first paragraph" cross-reference to name the relay paragraph correctly (W6-3); stop restating ruling 16 and lead with the current shape (W6-8); disambiguate §12 item 6's two senses of "floor" and note it in §15.18, which also now accounts for the domain row (handoff item 3); replace §15.11's stale "until slice 3, or until a drain exists" with the fact that no drain exists and every deploy still drops live calls (handoff item 5); add `token_sealed` to §2.1's meeting_links column list (handoff item 5). Spec sweep (handoff item 1): every sentence in docs/superpowers/specs/2026-09-07-meet-design.md asserting something about README.md, AGENTS.md or PRODUCT.md is now true against the shipped files; see fix-wave-report.md for the line-by-line check. W6-11 is folded into the §15.18 edit above.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CzvnuKhLzudvq7jZ7SvVt2
4 files changed,
+105
−50
AGENTS.md+9 −8PRODUCT.md+10 −6README.md+59 −23docs/superpowers/specs/2026-09-07-meet-design.md+27 −13
diff --git a/AGENTS.md b/AGENTS.md| index ad8a355..2617dbb 100644 |
| --- a/AGENTS.md |
| +++ b/AGENTS.md |
| @@ -83,14 +83,15 @@ by the framework, outside app middleware. |
| ## Deploying |
| -Meet deploys as a CARLOS unit route: `carlos deploy --app meet |
| ---account oficina`, checked against a build probe on `/api/version`. |
| -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. The wait |
| -can time out before hand-over — check `/api/version` before |
| -retrying. Canary: `carlos instances create` then `carlos deploy |
| ---channel` — `carlos canary` forms a two-label host (platform #124). |
| +`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, promotes and |
| +waits until `/api/version` serves it. 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) |
diff --git a/PRODUCT.md b/PRODUCT.md| index d24f6e2..791ce01 100644 |
| --- a/PRODUCT.md |
| +++ b/PRODUCT.md |
| @@ -9,7 +9,8 @@ |
| > `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. |
| +> not trusted. A bare "ruling N" is the Meet spec's §1; a doctrine ruling is always written |
| +> "suite ruling N". |
| ## Platform |
| @@ -22,7 +23,8 @@ Web, plus native Mac and iPhone/iPad clients over the same server contracts (spe |
| 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, 15); a guest |
| +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 |
| @@ -69,14 +71,16 @@ test and a captured-logger test, not merely asserted (§0.1). |
| /api/version` answer from rastrillo itself, outside app middleware. |
| - **Scriptless, with one named exception: browser media |
| participation.** Every other screen — the list, creating a meeting, a link shown once, |
| - all four confirm pages, sign-in, `/welcome`, the agent token page, and the server-rendered |
| - parts of the holding screen and room — works with scripts off (§9; `AGENTS.md`'s UX line |
| - carries the same exception; full list below). |
| + 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 same 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` (models.go; §2.1). |
| + (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 |
diff --git a/README.md b/README.md| index 3d7d810..c8101f5 100644 |
| --- a/README.md |
| +++ b/README.md |
| @@ -8,16 +8,24 @@ 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 both pages still render their title, standing, floor |
| -notice and navigation with scripts off (spec §9). |
| +the room — and 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, and the holding screen |
| -says so. Enabling or changing the relay restarts the instance (spec |
| -§5.2, §5.5, §7.3, §15.13). |
| +says so. 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 |
| @@ -30,7 +38,11 @@ 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). |
| +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 |
| @@ -39,8 +51,11 @@ 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). Chat is fanned out live to admitted peers and |
| -never stored (spec §6). Screen share adds a second tile per sharer |
| +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`); 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). |
| @@ -80,22 +95,43 @@ can be checked against what it is meant to be running. |
| ## Deploying |
| -Meet deploys as a CARLOS unit route: `carlos deploy --app meet |
| ---account oficina`, checked against a build probe on `/api/version`. |
| -Secrets — `MEET_INSTANCE_KEY` and `MEET_DEK` (32 base64 bytes) — go |
| -through `carlos secrets`; mail reads `MEET_SMTP_*` with `CARLOS_SMTP_*` |
| -as a fallback, per variable; `MEET_ORIGIN`, `MEET_ALLOWED_ADDRESSES` |
| -and `MEET_ZONE` go through `carlos env`. `carlos relay enable --app |
| -meet` turns on the platform relay. Every deploy, and every relay |
| -change, restarts the instance and drops every live call, 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 `carlos deploy` wait can time out before the |
| -instance hands over — check `/api/version` again before retrying. The |
| -canary recipe is `carlos instances create` then `carlos deploy |
| ---channel`, because `carlos canary` itself now forms a two-label host |
| -(platform #124). |
| +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`, |
| +checked against the build probe on `/api/version`. `--account` is a |
| +global flag; `carlos ship` uploads only, while `carlos deploy` is ship, |
| +promote and wait until the URL serves the version (its own help text). |
| +Building for your own architecture and shipping that uploads fine and |
| +fails to exec on the instance. |
| + |
| +Secrets — `MEET_INSTANCE_KEY`, `MEET_DEK` (32 base64 bytes) 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, so `carlos email enable` covers everything but |
| +`MEET_SMTP_FROM`. `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" — 2026-09-10, filed as platform discussion #124, |
| +still open). The recipe actually used: `carlos instances create --host |
| +slice2-meet.bds.oncarlos.com --channel canary/slice2 --environment |
| +slice2` — the environment bundle carries `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 |
diff --git a/docs/superpowers/specs/2026-09-07-meet-design.md b/docs/superpowers/specs/2026-09-07-meet-design.md| index c5b90bb..911f834 100644 |
| --- a/docs/superpowers/specs/2026-09-07-meet-design.md |
| +++ b/docs/superpowers/specs/2026-09-07-meet-design.md |
| @@ -94,7 +94,7 @@ This app cites the suite sharing doctrine (`oficina/suite`, |
| | 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. As amended by the 2026-09-12 sharing update (top of file): invitation tokens move to hash-indexed with a sealed copy for members' repeat sharing; agent credentials stay hash-only and shown once (§15.17). | |
| +| 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: a guest grant held by the instance is |
| authoritative for that grant, which is why §2 stores it and does not |
| @@ -171,9 +171,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 +826,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 |
| @@ -1081,9 +1085,10 @@ 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. |
| @@ -1493,8 +1498,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* |
| @@ -2149,11 +2155,19 @@ Slice 6 is documentation only: nothing under `internal/`, `native/`, |
| 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. |
| + |
| - **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. |
| + 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 |