Reconcile with the activity-pull design: the call-7 carve-out, and its price
The oficina/home activity-pull session read revision 4 against its own spec and found a contradiction this document could not see from inside. §4.3 says authorization is never cached at any scope, not per session, not per page, not for the life of a warm process. §6's call-7 row says "RAM cache only". Both were in revision 4, and a conformance reader hitting §4.3 first concludes the activity design is non-conformant. The reconciliation is real but not free, and the activity session had already priced it rather than argued its way out. §4.3 governs an authorization *answer*: an S2 VISIBLE held past its request is a standing capability, and it grows, because the consumer re-uses it against items and people the app of record never spoke about. The home's cache holds rendered output for one person, the home holds no grants, and it can only re-serve the same rows to the same person. But inside the cache's life a person removed from a team still sees titles, and calling that "not cached authorization" would be word-play — so the activity spec carries two ages: a short refresh TTL saying when to try again, and a hard maximum age after which rows are discarded whatever the pull did, failure included. The hard maximum bounds the lie; without it the tail is the hibernating fleet's wake schedule rather than a number anybody chose. §4.3 now carries that carve-out, and states the bound must exist while refusing to carry the number — the same shape §4.2 already uses for retention, because the number is a property of the consumer. The epoch exception is scoped. A.1 gave the roster epoch a narrow invalidation-hint exception to home §5.4's claim test. Call 7 rides the same assertion and inherits the claim, so the exception now says explicitly that it covers the call-8 path and nothing else: refetching a roster is not a decision, withholding an activity row would be, and an instance deciding anything from a home-supplied claim is what §5.4 forbids. Call 7's contract row now says the activity design owns the wire shape, that call 7 is not in the module yet, and that when it arrives the fixtures cite that spec's gates 10 to 20 rather than restate them — otherwise this contract rebuilds §2's drift one layer down, which is the whole thing it exists to stop. New leak-list item: a cacheable call-7 response is a cross-person leak at the platform edge, not merely a slow one, because the shared edge would serve one person's rows to the home's next pull for another. Their gate 16 tests it. It belongs in the suite list because the failure lives in the platform between two apps and neither app's own tests would see it. Two items leave the openly-unanswered list: the activity path's cached authorization, answered above, and A.1's ruling number, which was indeed taken and landed as 23.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 file changed,
+51
−8
docs/superpowers/specs/2026-09-05-suite-sharing-doctrine.md+51 −8
Intent
Attributed to this commit: 64.0 g CO₂e (8093332 tokens). This is an attribution, not a measurement — usage counted from this commit's own marker forward to the next one on worktree-activity-reconcile, never backward from it. A turn that produced more than one commit books all of its tokens to the last one, and a concurrent session on this branch can add to the same figure. Some of it was priced using the shared default rate, because no model-specific factor row exists yet for at least one model involved. The Carbon view has the repo total, which is never derived from this attribution.
diff --git a/docs/superpowers/specs/2026-09-05-suite-sharing-doctrine.md b/docs/superpowers/specs/2026-09-05-suite-sharing-doctrine.md| index 304dd32..57b9d81 100644 |
| --- a/docs/superpowers/specs/2026-09-05-suite-sharing-doctrine.md |
| +++ b/docs/superpowers/specs/2026-09-05-suite-sharing-doctrine.md |
| @@ -45,11 +45,15 @@ new findings. All three reports are beside this file, unedited: |
| **Openly not answered, and deliberately left for iteration** (Paul, |
| 2026-09-05): call 8's wire encoding and completion marker; result |
| -precedence when a stale roster and a non-custodial item collide; the |
| -activity path's cached authorization after revocation; pinning sibling |
| -baselines, since four memoria citations resolve only on an unmerged |
| -worktree and A.1's ruling number may collide with an existing home ruling |
| -22; and most of the fixtures the gates now describe. These are recorded |
| +precedence when a stale roster and a non-custodial item collide; pinning |
| +sibling baselines, since four memoria citations resolve only on an unmerged |
| +worktree; and most of the fixtures the gates now describe. |
| + |
| +Two that were on this list are off it. **The activity path's cached |
| +authorization** is answered: §4.3 now carves out call 7 explicitly and |
| +requires a hard maximum age, which the activity-pull session had already |
| +priced before being asked. **A.1's ruling number** is resolved: home ruling |
| +22 was indeed taken and the amendment landed as 23. These are recorded |
| here rather than in a passing sentence so the next reader can see the |
| edge of the work. |
| @@ -309,6 +313,29 @@ team standing, not "may this person read this item". An earlier draft of |
| ruling 3 said "cached no longer than a session", which conflated the two; |
| memoria's session caught it. |
| +**One carve-out, and it is call 7.** The rule above governs an authorization |
| +*answer*: an S2 `VISIBLE` held past its request is a standing capability, and |
| +it grows — the consumer re-uses it against items and people the app of |
| +record never spoke about. The home's activity cache is not that. It holds |
| +*rendered output* for one person, the home holds no grants, and it can only |
| +ever re-serve the same rows to the same person. |
| + |
| +That is a real distinction and it is not a free one. Inside the cache's life |
| +a person removed from a team still sees titles, and calling that "not cached |
| +authorization" would be word-play. So it is priced rather than denied: the |
| +activity cache carries **two ages**, a short refresh TTL saying when to try |
| +again, and a **hard maximum age after which rows are discarded regardless of |
| +the pull's outcome, failure included**. The hard maximum is the one that |
| +bounds the lie. Without it the tail is the hibernating fleet's wake schedule |
| +rather than a number anybody chose. |
| + |
| +This document states that the bound must exist and refuses to carry the |
| +number, exactly as §4.2 does for retention: the number is a property of the |
| +consumer, and the consumer states it. `design-activity-pull` §7 holds it. |
| +*(Reconciled with the activity-pull session, 2026-09-05, which found the |
| +contradiction between this section and §6's call-7 row and priced it before |
| +being asked.)* |
| + |
| **The app of record for an item is the app whose store it was created |
| in.** A copy never becomes an authority, and a derived artefact carries the |
| provenance of every source it was built from, transitively, so two hops |
| @@ -420,7 +447,17 @@ deserves its own words — **the instance decides nothing from the claim**. A |
| claim it cannot parse is ignored. A claim that differs from what it holds |
| buys exactly one call-8 refetch, and call 8's answer is what it enforces. |
| So a hostile claim costs one request and can neither admit nor exclude |
| -anybody. A.1 records that exception in the home spec, which owns the test; |
| +anybody. |
| + |
| +**The exception is scoped to the call-8 path and nothing else.** An instance |
| +answering call 7 sees the same assertion and therefore the same claim, and it |
| +must not invalidate anything on it — that would be an instance making a |
| +decision from a home-supplied claim, which is precisely what home §5.4 |
| +forbids and what this exception is narrow enough to avoid. Refetching a |
| +roster is not a decision; withholding an activity row would be. *(Raised by |
| +the activity-pull session, 2026-09-05.)* |
| + |
| +A.1 records that exception in the home spec, which owns the test; |
| this document cites it rather than asserting the old test already covered |
| it. A person whose sign-in reveals a stale roster is admitted against the |
| fresh roster, never against the claim. *(GPT-6 review, finding 12.)* |
| @@ -592,7 +629,7 @@ spec, which owns them, and are cited here for completeness of direction. |
| | Call | Direction | Credential | Proves | Notes | |
| |---|---|---|---|---| |
| | 1–6 | person → home, instance → home | session; consumer token | — | Home §9. Unchanged. | |
| -| 7 activity | home → instance | **assertion** for the person, `aud` = instance | who the front door is for | `design-activity-pull` §4–5. The home renders and discards; RAM cache only. | |
| +| 7 activity | home → instance | **assertion** for the person, `aud` = instance | who the front door is for | `design-activity-pull` §4–5 owns the wire shape. The home renders and holds rendered output only, under two ages (§4.3). Not in the contract module yet — §12 step 2 is S1, S2 and S3. When it arrives, the fixtures **cite** that spec's gates 10–20 rather than restate them, or this contract rebuilds §2's drift one layer down. | |
| | 8 roster | instance → home | consumer token | which instance asks | §5.3. Scoped to the caller's team. Paged; the copy has a TTL. | |
| | S1 change feed | sibling → sibling | consumer token | same-team sibling | `(item_id, revision, gone, touched_at)`. No title, no content, no visibility flag. Per-consumer TTL; past it, full re-announce. | |
| | S2 authorization | sibling → sibling | consumer token **+ named asker** | same-team sibling, asking for a person | Four values, total decoder, cap 256 pairs. **Never cached**: a passage crosses no request boundary without an S2 `VISIBLE` in the request that carried it (memoria §8.4, gate 26). Asker is a typed subject (§3); the answer is intersected with custody (§4.3). | |
| @@ -916,7 +953,13 @@ apps are a suite, and every one is a consequence of ruling 1 or 4. |
| calls to instances as a matter of course, its outbound traffic stops |
| being a compromise signal (activity design §2.2(b)). Both were |
| disclosed there and belong here too. |
| -11. **A link capability is bearer-shaped** (ruling 15). Whoever holds the |
| +11. **A cacheable call-7 response is a cross-person leak** at the platform |
| + edge, not merely a slow one. The shared edge would serve one person's |
| + rows to the home's next pull for another, so a cacheable response is a |
| + *failed* pull rather than a fast one. `design-activity-pull` gate 16 |
| + tests it; recorded here because the failure is in the platform between |
| + two apps and neither app's own tests would see it. |
| +12. **A link capability is bearer-shaped** (ruling 15). Whoever holds the |
| URL reads or writes the item, the app cannot name them, and a |
| forwarded link is a grant nobody recorded. This is what "anyone with |
| the link" means and there is no version of it that is not this. |