Skip to content

docs(d8-phase-2): inbound never reads the gate — active pod is its own switch, leaving it is heard, the page writes podId - #1551

Merged
lilyshen0722 merged 4 commits into
mainfrom
docs/d8-phase2-inbound-active-pod
Sep 5, 2026
Merged

lilyshen0722 merged 4 commits into
mainfrom
docs/d8-phase2-inbound-active-pod

Conversation

@lilyshen0722

Copy link
Copy Markdown
Contributor

Follow-up to #1545 from Vera's review of #1550 (pod 63797): isRelayableIntegration decided inbound by the gate for user-scoped rows, so once the reconciler pruned gates[A] for a pod the owner left, every message they typed into their own DM was dropped with a server log line while the page still read linked to A. Fails closed, but silently (REVIEW rule 19).

Ruling (63798): D3 stands. The gate is pod → me; the active pod is me → pod. Two facts, two switches.

What changes in the note:

  • D3: inbound compares podId, never gates; its outer guard is membership, checked at receive as outbound checks it at send; an unrelayable bare message (no podId, or owner not a member) is answered in the chat once per message; /pod was never built, so the page writes podId (owner-only, member-of-pod).
  • D3 prune: dropping the gate for the pod that is also podId unsets podId in the same write.
  • D2: a not linked to a pod row (idle dot, ink Pick a pod).
  • D4: mono active tag on the active pod, bordered Make active in each other pod's collapsed section; expanded picker when podId is unset.
  • §3: PR C carries the inbound comparison, the chat reply and the podId prune; PR D carries the page pieces.
  • §4: seed 6b walks the leave-the-active-pod path both before and after the sweep, plus the owner/creator/non-member PATCH answers.

Docs only. Code lands in #1550 (PR C) and the page PR.

🤖 Generated with Claude Code

lilyshen0722 and others added 2 commits September 4, 2026 22:03
…s own switch, leaving it is heard in the chat, and the page writes podId

Vera's #1550 review (63797): isRelayableIntegration decided inbound by the
gate, so a pruned gate silently dropped every message the owner typed while
the page still read "linked to A". D3 now says inbound compares podId with
membership checked at receive, the prune unsets podId with the gate, and an
unrelayable message is answered in the chat. /pod was never built, so D4's
gate list gains the active tag and Make active; D2 gains the not-linked row;
seed 6b covers the walk.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…1550 already sends (Vera)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@lilyshen0722

Copy link
Copy Markdown
Contributor Author

Gate clear at 2e1aaa48. Docs only, one file, base main.

The ruling is carried correctly rather than patched over the top:

  • D3's /pod claim is retired, not softened. The bullet now states plainly that nothing in the webhook writes podId and that ADR-025 D12 named a command that was never built, then hands the write to the page. That sentence was the load-bearing error — a note that keeps a nonexistent verb as its recovery path describes a system nobody can use.
  • The two switches are now stated as two facts. Gate is pod → me, active pod is me → pod, and the inbound predicate compares podId. That closes the divergence between the note and isRelayableIntegration.
  • Silence is named as the bug. The unrelayable bare message is answered in the chat once per message, on both providers, with the copy given inline.
  • Seed 6b is the right shape. It asserts the chat reply both before and after the sweep, asserts the prune unsets podId alongside the gate, and pins the case the ruling turns on: gates[B] off with podId: B changes nothing inbound. It also covers the two 403s on PATCH { podId } — non-owner and non-member.

One non-blocking note

D4 describes the gate list as "one row per pod the user is in (GET /api/pods filtered as the pod picker already is — never community/showcase)".

The server has never enforced that filter. POST /:installableId/install validates podId as an ObjectId and then checks isPodMember — nothing narrows by pod type — so PATCH { podId } as specified inherits the same gap, and both verbs will accept a community or showcase pod the owner happens to belong to.

The impact is small: it is the owner's own DM authoring into a pod they are already a member of, which they could post to from the web regardless. But the note now leans on that filter in two places while it exists only in the page, which is the inverse of this plan's own rule that the page never renders a control the server does not enforce. Either both verbs narrow by pod type server-side, or D4 should stop citing the filter as if it were a guarantee.

Nothing here blocks the press.

…the server's — the picker's community/showcase filter matches no pod type (Vera)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…at } — the moderated never learns who (Vera)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@lilyshen0722
lilyshen0722 merged commit c69ac7f into main Sep 5, 2026
13 checks passed
@lilyshen0722
lilyshen0722 deleted the docs/d8-phase2-inbound-active-pod branch September 5, 2026 12:15
lilyshen0722 added a commit that referenced this pull request Sep 6, 2026
…tes as rows, gates in the aside (TASK-010 B+D) (#1557)

* feat(v2): Connectors rows keyed by the capability catalog, parent states as rows, gates in the aside (D8 Phase 2 B+D)

Rows come from GET /api/installables (D1/D2): an unavailable provider is
"Not enabled on this instance" with no control; an available one offers
Connect; installing/activating and uninstalling parents read Setting up…/
Removing… and gain Cancel / Retry remove only once the claim is stale
(INSTALL_LOCK_TTL_MS); an error parent shows its errorMessage with Retry
(POST install { podId: boundPodId }) and Remove in the aside; a missing
projection under an active parent is a Retry row; a paused parent shows the
reason and `paused {rel}` with no owner control. A connected row whose
podId is unset reads "not linked to a pod" with Pick a pod (#1551).

The aside's What-the-channel-sees card gains the D4 gate list: one row per
pod the user is in, a switch per gate (the PATCH merges config one level
deep, so the whole gates map travels with each write, since included), mono
since/off, an `active` tag on the active pod, and behind each pod's name the
mode segment, a lead picker over the pod's bots, and Make active
(PATCH { podId }). Disconnect becomes Remove. The connect form's picker no
longer filters pod.type against community/showcase, which are not pod types.

Legacy pod-scoped rows still render from /api/integrations/user/all; an
installable row is keyed by the catalog and skipped there. If the catalog
cannot be read the page falls back to the two providers it drove before.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(v2): a gate write drops pods the owner has left — the server refuses the whole map on one stale key (Vera)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(v2): the gate switch is the system's square checkbox, and row meta wraps instead of ellipsising (lily-shen, 64026)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant