docs(ADR-016): close a residual #797 fixed, and add the writer row that was missing - #824
Merged
Merged
Conversation
…at was missing Two defects found by reading §1-75, which three seats had flagged as unread. 1. The "Residual divergence, not a leak (open, low priority)" section described the agent discovery route composing COMMUNITY_LISTING_QUERY while human Discover also excluded invite-only. #797 (b2fc6cd) adopted DIRECTLY_JOINABLE_QUERY at routes/agentsRuntime.ts:2470 — flags plus joinPolicy $ne invite-only — and the handler comment now carries the per-clause reasoning, including why the members clause is not adopted. The paragraph has read as current for three days with nothing marking it stale. Kept and dated rather than deleted, because that staleness is the lesson. 2. The enforcement-gap table enumerates read surfaces and visibility writers. It has no row for membership writers — so invariant 2 (self-joinable => listed) was tracked only at the human joinPod path, while the agent-side join path is the pod-create dedup branch, which gated on nothing and made a guessed pod name a credential for joining any non-DM pod. Closed by #817 and #821; recorded here with the rule the omission earns. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
One conflict, in the residual-divergence section. This branch and #802 both closed it; #802 merged first and carries the fuller treatment — the merged-is-not-deployed window, the retiring predicate, and the record of that predicate firing. Took main's side wholesale rather than interleaving two accounts of the same closure. What survives from this branch is what #802 did not have: the membership-writer paragraph and the rule it earns — the gap table must enumerate every writer that can change who is in a pod, not only the writers that change its tier. That omission is why the #821 bypass survived a per-surface audit, so the rule is now load-bearing rather than editorial. Net effect on main: purely additive, 4 lines.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Found by reading ADR-016 §1–75 — the item three seats have listed as unread all day. Docs-only, based on
main.1. A gap the ADR still lists as open was closed three days ago
The Residual divergence, not a leak (open, low priority) paragraph said the agent discovery route composes
COMMUNITY_LISTING_QUERY(flags only) while human Discover also excludes invite-only — "the route's own comment says it reuses the flag so this route cannot drift … and it does still differ."It doesn't. #797 (
b2fc6cde) landedDIRECTLY_JOINABLE_QUERYatroutes/agentsRuntime.ts:2470— listing flags plusjoinPolicy: { $ne: 'invite-only' }— and the handler comment now reproduces this ADR's own per-clause table, including whymembers: { $ne: callerId }is deliberately not adopted.Rewritten as CLOSED, dated, with the old text quoted rather than deleted — because the staleness is the point. It was accurate on 2026-08-01, has read as current for three days, and nothing on the page marked it. An ADR that records live gaps needs its entries dated and closed, or it becomes the confident-and-wrong surface it exists to prevent.
2. The gap table has no row for membership writers
That table enumerates read surfaces and visibility writers. Invariant 2 — self-joinable ⇒ listed — is a join rule, and the only join path in it is
joinPod(human).The agent-side join path isn't
joinPod. It'sPOST /api/agents/runtime/pods, whose dedup branch does a globalPod.findOne({ name })and, on a hit, pushes the caller intomembers, installs them (which is what grants posting — auth goes throughAgentInstallation.find, notpod.members), and installs commonly-bot withcontext:read. It gated on none of the tier logic, so a guessed pod name was a credential for joining any non-DM pod in the instance — includingprivate + joinPolicy:'open', the dormant-declaration state at §46 that ajoinPolicy-only check waves straight through.Closed by #817 (DM types) and #821 (
isDirectlyJoinablefor the rest) — #821 is literally an encoding of this ADR's §44 biconditional.The rule the omission earns: the gap table must enumerate every writer that can change who is in a pod, not only the writers that change its tier. The ADR argues a per-surface enumeration catches an absent predicate where no grep can — true, and only for the surfaces it enumerates.
🤖 Generated with Claude Code