diff --git a/adventure_party_known_flag.md b/adventure_party_known_flag.md deleted file mode 100644 index 88d9560..0000000 --- a/adventure_party_known_flag.md +++ /dev/null @@ -1,75 +0,0 @@ -# Wire contract — `party_known` on the public detail blob (gogobee side) - -Status: **Pete side BUILT + TESTED. gogobee side NOT BUILT.** Written 2026-07-24 out -of a code review of the W9 verbs branch. Small: one boolean, set unconditionally, -no new endpoint and no new verdict. Companion to `adventure_expansion_spec.md`. - -## The hole - -`offersToUndo` in `internal/web/who.go` decides which of "Call the whole thing off" -(abandon) and "Leave the party" (leave) an owner's page offers. On a live expedition -with an EMPTY party list it took the "solo run ⇒ this player is the leader" branch, -because a solo run publishes no seats at all. - -But a gogobee too old to push party seats sends a perfectly valid detail blob with no -`party` key, and that decodes to the same empty slice. So a party MEMBER served by an -old game box was shown the leader's button — the one that throws away four people's -day. gogobee refuses it with `rejected_not_leader`, so the live cost was a misleading -offer rather than a lost expedition, but the page was asserting something it could -not know. - -Nil and empty cannot be told apart on the current wire, hence a new field. - -## What gogobee must send - -The public detail blob on the roster push (the one Pete decodes into `whoDetail`) -gains: - -```json -"party_known": true -``` - -**Set it unconditionally**, on every sheet, from any gogobee build that knows what a -party seat is: - -- on a solo run — YES, this is the case the flag exists for; -- when the party list is omitted because there is nobody else — YES; -- when the player is standing in town — YES; -- when the sheet does carry seats — YES, harmless there. - -It is a capability flag about the **sender**, not a fact about the character. It must -never be computed from whether there is a party, whether the run is shared, or -anything else on the sheet. A conditional `party_known` is worse than none: it would -read as "this player is solo" and put the flag straight back into the hole it was -added to close. - -Anything that omits it (old builds, JSON that fails to decode) lands as `false` in -Go, which is the fail-closed answer Pete wants. - -## What Pete already does with it - -- `whoDetail.PartyKnown bool \`json:"party_known"\`` — `internal/web/who.go:47`. -- `offersToUndo`'s third parameter is `partyKnown`; the call site passes - `page.HasDetail && page.Detail.PartyKnown`, so a blob that failed to decode is - still excluded. -- The flag gates the `len(party) == 0` branch **only**. The `len(party) > 0` branch - reads the viewer's own seat and is self-evidencing — a seat that says "member" - cannot be mistaken for leadership — so it keeps working against every sender, - including ones that never set the flag. -- Cases pinned in `internal/web/orders_undo_test.go`. - -## Rollout - -Pete is already deployed-safe: it ships first and costs nothing but one button. Until -gogobee sets the flag, a genuine SOLO leader on a live run does not see the abandon -button on the party branch. They are not stuck — the `self.Resume != nil` clause -still offers it for an extracted run from town, and abandoning over Matrix was never -affected. The moment gogobee ships the flag the button comes back with no Pete change. - -## Done when - -- gogobee sets `party_known: true` on every public detail sheet it builds. -- A solo adventurer on a live expedition sees "Call the whole thing off" on their web - page again. -- A party member on the same build still sees "Leave the party" and never the abandon. -- Delete this file.