oficina / meet Public

spec: codex round 6 accept with corrections; Meet revision 7

Round 6 closed every prior finding and residue and left two P3
advisories, both taken: the takeover gate asserts a cross-origin POST
refused by origin-checking CSRF and a same-origin confirmation
accepted without a token; the 410 replaced response no longer names a
successor peer. Independent review complete at revision 7.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Paul Campbell pushed by paul@keymail.dev f467cd974ee576c8319a84441e005a24d7d2ff90 parent 61475ed
2 files changed, +67 −8
  • docs/superpowers/specs/2026-09-07-meet-design.md +21 −8
  • docs/superpowers/specs/CODEX-FINDINGS-MEET-R6.md +46 −0
diff --git a/docs/superpowers/specs/2026-09-07-meet-design.md b/docs/superpowers/specs/2026-09-07-meet-design.md
index 7b6bdb2..bf3c1b2 100644
--- a/docs/superpowers/specs/2026-09-07-meet-design.md
+++ b/docs/superpowers/specs/2026-09-07-meet-design.md
@@ -1,9 +1,9 @@
# 🤖 Meet — design
Design, 2026-09-07. Direction approved by Paul in conversation; this is the
-written form. **Revision 6**, answering the three findings of the
-fifth Codex review (`CODEX-FINDINGS-MEET-R5.md`); §14 has all five
-rounds. Drafted by the Meet
+written form. **Revision 7**, carrying the two corrections of the
+sixth Codex review (`CODEX-FINDINGS-MEET-R6.md`, verdict: accept with
+corrections); §14 has all six rounds. Drafted by the Meet
session (Fable 5.1) from a brainstorm with Paul on 2026-09-06 and
2026-09-07. Every ruling in §1 attributed to Paul was given in that
conversation or on the discussion it cites.
@@ -636,7 +636,7 @@ browser's response is fixed per case:
| Resume answer | Meaning | Browser does |
|---|---|---|
-| `410 {reason: "replaced", by: <peer>}` | This participation was ended by a takeover from another tab on this session. The hub remembers ended-by-takeover peer ids for ten minutes so a tab that never received `lost {reason: "replaced"}` learns it here. | Terminal: close every peer connection, show "This meeting is open in another tab" and a link to the takeover confirm page (§4.2). Never automatic. |
+| `410 {reason: "replaced"}` | This participation was ended by a takeover from another tab on this session. The hub remembers ended-by-takeover peer ids for ten minutes so a tab that never received `lost {reason: "replaced"}` learns it here. No successor peer is named: the takeover's own stream may not have opened yet, and terminal handling keys on `reason` alone. | Terminal: close every peer connection, show "This meeting is open in another tab" and a link to the takeover confirm page (§4.2). Never automatic. |
| `410 {reason: "removed" \| "expired" \| "standing" \| "ended"}` | Ended for that reason; remembered ten minutes. | Terminal, show why. |
| `404 {boot: <id>}` with a **different** `boot` from the last `hello` | The process restarted. | Close every peer connection, open a fresh stream without `peer`, `join` again. |
| `404 {boot: <id>}` with the **same** `boot` | The resume window lapsed and nothing else happened. | Close every peer connection, open a fresh stream without `peer`, `join` again. If that fresh stream is refused with 409, another tab owns the room now: show the take-over button, do nothing automatic. |
@@ -1046,8 +1046,10 @@ displaced tab including the case where the `lost` was never delivered
404 followed by a clean rejoin, a different-boot 404 after restart, a
takeover of an admitted, a waiting and an unjoined participation
producing `gone`, `knock-gone` and nothing respectively with an
-admitted guest observing, a takeover POST without the CSRF token
-refused, a resume with a nonzero old cursor after a missed `pair`
+admitted guest observing, a cross-origin takeover POST refused by the
+scaffold's origin-checking CSRF middleware while a same-origin
+confirmation succeeds without any token, a resume with a nonzero old
+cursor after a missed `pair`
receiving the new generation's offer by replay, a stale-`gen` POST
refused with 409, `reset` yielding one `pair` to each
side with the same generation, two simultaneous resets converging, a
@@ -1190,7 +1192,18 @@ revision 6:
| R5-2 takeover broadcasts a waiting departure | Takeover uses the same state-dependent departure as `leave`: `gone`, `knock-gone`, or nothing (§4.2). |
| R5-3 takeover is a destructive GET | Takeover is a confirm page plus a CSRF-protected POST; a `GET` never ends a participation and no query parameter can; the 409 and 410 responses link to the confirm page (§2.5, §4.2, §4.5). |
-Revision 6 awaits Paul's review of the written form and a sixth Codex
-round. Neither has happened; this line is the record.
+**Round 6**, GPT-6 via Codex against revision 6 at 61475ed:
+`CODEX-FINDINGS-MEET-R6.md`: every prior finding and residue closed;
+two P3 advisories; **verdict: accept with corrections**. Both
+corrections are in revision 7:
+
+| Finding | Disposition |
+|---|---|
+| R6-1 CSRF gate wording | The scaffold's `csrf.Protect` checks origin, not a token; the gate now asserts a cross-origin POST refused and a same-origin confirmation accepted (§13). |
+| R6-2 successor peer in the 410 | `by` removed; terminal handling keys on `reason` (§4.5). |
+
+The independent review is complete at revision 7. Revision 7 awaits
+Paul's review of the written form; this line is the record until that
+happens.
diff --git a/docs/superpowers/specs/CODEX-FINDINGS-MEET-R6.md b/docs/superpowers/specs/CODEX-FINDINGS-MEET-R6.md
new file mode 100644
index 0000000..0476f09
--- /dev/null
+++ b/docs/superpowers/specs/CODEX-FINDINGS-MEET-R6.md
@@ -0,0 +1,46 @@
+# Findings on the Meet spec, revision 6
+
+GPT-6 via `codex exec`, read-only, round 6, reviewing commit 61475ed against the five earlier findings files. Verbatim.
+
+Reviewed revision 6 at `61475ed787d945b2e76ccff372ded6295d409c5d`. No files modified. “Closed” means the design survives the reported failure; implementation remains subject to its gates.
+
+| R5 finding | Status | Assessment |
+|---|---|---|
+| R5-1 cursor across generations | **Closed** | Sequence counters survive pair resets. In the reported failure, the new offer now has a sequence above the receiver’s old cursor and is replayed. Learning the generation through the roster no longer suppresses that offer. [§4.6](/home/paulca/amadan.net/oficina/meet-spec/docs/superpowers/specs/2026-09-07-meet-design.md:664) |
+| R5-2 waiting takeover disclosure | **Closed** | Takeover branches on admission state: `gone` to admitted peers, `knock-gone` to admitted members, or no announcement. An admitted guest receives no waiting-peer departure. [§4.2](/home/paulca/amadan.net/oficina/meet-spec/docs/superpowers/specs/2026-09-07-meet-design.md:505) |
+| R5-3 destructive GET | **Closed** | Replacement requires a confirmed POST under the scaffold’s CSRF middleware. Fresh-stream GETs refuse conflicts; query parameters cannot authorize replacement. The new test wording has an advisory defect below. [§4.2](/home/paulca/amadan.net/oficina/meet-spec/docs/superpowers/specs/2026-09-07-meet-design.md:498) |
+
+The entries in R5’s two status tables, checked individually:
+
+| Earlier finding | Status | Assessment |
+|---|---|---|
+| R4-1 reset protocol | **Closed** | Typed reset, authoritative pair generations, roster recovery and monotonic cursors now cover the recorded reset-delivery failures. |
+| R4-2 lost replacement notice | **Closed** | Replacement tombstones make resume terminal; fresh-entry conflicts prevent automatic displacement after tombstone expiry. |
+| R4-3 authorization-table disagreements | **Closed** | Participation-only leave, admitted-member moderation and page-access rules remain aligned. |
+| R4-4 revoked continuation | **Closed** | Authentication refusal and authenticated continuation fallback remain separate; neither mints through the revoked link. |
+| R3-7 downstream signalling loss | **Closed** | Retained messages replay across resets; missing history invokes coordinated recovery. |
+| R3-9 replacement loop | **Closed** | Lost notices cannot cause automatic takeover. |
+| R2-4 resume/restart | **Closed** | Admission-branched resume, roster reconciliation and connection disposal on restart remain specified. |
+| R2-5 delivery | **Closed** | Ordered retries, deduplication, replay and shared reset generations answer the recorded failures. |
+| R1-9 reconnect | **Closed** | Renewal preserves admission; missed departures reconcile; restart and replacement have distinct recovery paths. |
+| R1-10 negotiation | **Closed** | Perfect negotiation remains supported, and reset recovery no longer withholds the retained offer needed to finish it. |
+
+No listed residue remains half-closed or worse.
+
+**R6-1 — P3 — §13: The takeover test requires a CSRF token that the reused protection does not use.**
+
+The new gate requires “a takeover POST without the CSRF token refused.” The scaffold explicitly uses origin checking, and `csrf.Protect` accepts same-origin POSTs without any token. A legitimate confirmation submission therefore satisfies the stated negative-test condition. [New gate](/home/paulca/amadan.net/oficina/meet-spec/docs/superpowers/specs/2026-09-07-meet-design.md:1050), [scaffold middleware](/home/paulca/amadan.net/oficina/meet-spec/internal/meet/app.go:47), [CSRF contract](/home/paulca/go/pkg/mod/amadan.net/rastrillo/rastrillo@v0.26.0/csrf/csrf.go:47).
+
+**Correction:** Test refusal of cross-origin POSTs and requests lacking accepted same-origin evidence. Also assert that a legitimate same-origin confirmation succeeds without a token. This corrects the gate; it does not reopen R5-3.
+
+**R6-2 — P3 — §§4.2, 4.5: The replacement response still requires a successor peer that may not exist.**
+
+Takeover now ends the old participation and redirects. The replacement peer is allocated by a later stream request, which may never happen or may lose the documented race. Nevertheless, the old peer’s resume response still requires `410 {reason: "replaced", by: <peer>}`. Immediately after the POST, there is no successor ID to return. [Deferred allocation](/home/paulca/amadan.net/oficina/meet-spec/docs/superpowers/specs/2026-09-07-meet-design.md:515), [410 response](/home/paulca/amadan.net/oficina/meet-spec/docs/superpowers/specs/2026-09-07-meet-design.md:639).
+
+**Correction:** Remove `by`, or make it optional until a successor exists. Terminal handling depends on `reason`, so this needs no change to the takeover mechanism.
+
+The amended authorization and recovery mechanisms introduce no blocking defect at the stage-spec threshold. Takeover’s standing check is consistent with its fresh-entry semantics; it does not revoke an expired guest’s existing continuation authority. The disclosed allocation race produces another confirmation opportunity, not automatic displacement.
+
+The checked reuse contracts and slice ordering introduce no new blocker. The media API assumptions remain supported by the [WebRTC specification](https://www.w3.org/TR/webrtc/) and [candidate statistics contract](https://www.w3.org/TR/webrtc-stats/#dom-rtcicecandidatestats-usernamefragment). Relay deployment, `rastrillo/rastrillo` discussion 17 and `oficina/suite` discussion 5 remain unverified from the supplied evidence.
+
+**Verdict: accept with corrections.**
\ No newline at end of file