oficina / home Public

design-activity-pull: activity on the front door, pulled not pushed — review record open

On branch design-activity-pull

Per AGENTS.md, this discussion is the branch's review record.

Spec: docs/superpowers/specs/2026-09-05-activity-pull-design.md Status: NOT APPROVED. The required review pass by the highest-capability model available has not run. Recorded here first so the gap is visible rather than assumed away.

What changed about the ask

Paul asked for an activity-stream API that other apps post to, so the home shows a cross-app feed on sign-in. The direction inverted during design. Instances do not post. The home asks each of a person's instances "what's theirs, recently?" at render time, merges, and holds the answers nowhere but process memory.

Why the inversion, and what it buys

This shape was proposed before in the push direction — escritorio, 2026-09-02 — and parked after a Fable review returned REQUEST CHANGES on five counts. Two of those five were the reason to invert:

  • A per-item ACL is not expressible by the poster. Sheets access is a grants join; no app can post "who may see this" as a value. A consumer re-evaluating the rule drifts and fails as a plausible empty list, not an error.
  • Push-based revoke has a weeks-long tail, because the outbox is the instance's and drains on wake and share mutation only.

Both dissolve under pull: the app that owns the grants join answers the question about the grants, after the revoke, every time. Nothing is copied, so nothing drifts.

Provenance caveat, stated because it is weak. Two sessions reported that review in agreeing detail; the primary record was not findable — not a discussion on oficina/home, oficina/sheets or oficina/docs, not a markdown file in the oficina tree. Corroboration is indirect: memoria's README lists escritorio and plausible empty list among phrases searchable in the suite's own specs. Anyone who knows where the record lives should check §2 of the spec against it.

The one doctrine change, and the ruling behind it

Paul ruled (2026-09-05) for a real feed with titles rather than a contentless heat map. That needs one clause of the home design amended, and only its verb:

> §2.2: ~~never renders content from an instance~~ → *never stores > content from an instance.* It may render, transiently and per request, > what an instance hands it for the person it hands it for.

§0 is unamended. Nothing new goes at rest, so the falsifiable claim — publish the database and the worst anyone learns is counts, team names and memberships — survives word for word. This is the load-bearing reason the amendment is small.

AGENTS.md is deliberately left as written. It is binding and says the spec wins where they disagree, but a binding file must not be rewritten to follow a spec whose review has not run. The edit lands on approval.

The cost, disclosed

Instances hibernate; the home does not (§2.1). So the first front-door load of the day will mostly miss its deadline, and every load warms the person's whole fleet. Mitigated by a RAM-only per-person cache and by rendering the waking state honestly. An escape hatch is named with a trigger — a contentless ping so only instances with news get woken — and deliberately not built, because it adds custody to solve a problem nobody has measured.

The load-bearing test (gate 11): an instance that did not answer must be visually distinguishable from an instance with nothing to say. A feed that silently drops what it could not fetch looks exactly like a slow week, and nobody files a bug against a slow week.

Blocked

Call 7 needs §13 step 3 (assertions) and step 4 (person refs). Neither is started; step 2 needs HOME_DEK, which only Paul can mint. This is spec-now, build-later, and it must not acquire a claim on the identity layer's critical path.

What a reviewer should attack first

  1. Whether "never stores" is a real boundary or a rationalisation of "never renders". The spec argues storage is what made the original clause matter, and that a per-request string in RAM is a different thing. That argument is the whole amendment.
  2. Whether the RAM cache is at-rest by another name.
  3. Whether the front door pulling from every instance recreates §5.4's document-level graph in transit rather than avoiding it.
  4. The provenance gap in §2.

Revision: the escritorio sources surfaced, and a peer pass found four things

92ddcc1. Two changes of substance and four corrections, all to claims the first draft made.

The provenance gap is closed, and it changes the argument

The escritorio review was never published: the brief and the Fable verdict existed only as local files in a sweepable cache directory, the verdict itself extracted from a subagent transcript. Both are now in the repo:

  • docs/superpowers/reviews/2026-09-02-escritorio-review-brief.md
  • docs/superpowers/reviews/2026-09-02-escritorio-fable-verdict.md

Prefer these to discussion #6, which is a good-faith reconstruction from a session's private memory, written before the originals were found — its wording is that session's, not Fable's.

Reading the real verdict strengthens this design rather than weakening it, which was not the expected outcome:

  • Its finding 1 — the most severe — says the cross-team list "is exactly what the home spec forbids the home from holding" (§5.4) and that the honest options are "either that ruling is reopened deliberately or the want is declined." Paul reopened it deliberately. That is the first of the two paths the verdict itself named.
  • Its recommendation is this mechanism: "a pull-based, stateless page … fetched from the sibling with the viewer's identity, fail-soft on cold wake, nothing stored." Option B's mechanism, process deleted.
  • Its finding 5 pre-approves the rescue used here: "not disqualifying if escritorio renders what it has after a deadline and marks the rest 'waking', but that is a design the brief does not describe." §6 and §8 describe it.

The nine real findings now replace the five that were recalled. The one place this design departs from the verdict — it puts the page at the home rather than inside an existing app — is argued in a new §2.1, and that is where a reviewer should start.

Four corrections from a peer pass

All four are things the first draft got wrong, not polish.

  1. "No new knowledge at the home" was true and beside the point. Retracted in a new §2.2. It was hiding two costs: (a) call 7 turns §6's passive map into a broadcast — every render fires a signed, timestamped request at all of a person's hosts, so each instance learns "this person opened their front door at 09:14:03", which is the fine-grained version of the signal §3.3 coarsens to the day on purpose; and (b) it spends §9's uniform inbound call direction, which was a detection property — after call 7 an exfiltrating home is indistinguishable from a working one.
  2. Gate 10 tested only the happy path, while §6 says failure is the normal morning. The natural Go spelling of a failed parse puts the whole response body — every title — into an error string, which §3.4 rules is storage. The gate now drives canary titles through seven failure paths.
  3. Ruling 3 is safe only because of aud binding, and §6's own latency pressure is a standing incentive to reuse one assertion across the fan-out — which would be a cross-instance bearer, i.e. full impersonation at every host in a person's fleet. Written into §11 as never, with gate 18 behind it.
  4. §5's claim that assertion safety is "a property of the token, not of how it travelled" is falsified by §5.3's own last line — "delivery is an auto-POST, not a query string" is a travel property. The conclusion survives on a specific argument about headers.

Also added: §5.1 answers the verdict's enumeration-oracle finding, which the first draft missed entirely; §5.2 covers guests and link-share viewers; §10 prices the per-row DEK decrypt that answering call 7 costs Sheets.

Still not approved

The peer that found these four wrote enough of the resulting argument to no longer be independent, and says so itself. The AGENTS.md review pass by the highest-capability model available has not run.

Noted for whoever runs it: AGENTS.md is stale against the spec on three counts, including "assertion expiry is seconds" where §5.3 says 120 seconds. Call 7 authenticates with that assertion, so an implementer reading the binding file gets a retracted number. Already tracked on discussion #1 / fix/agents-md-matches-spec — not this branch's to fix, but flagged in §12.

Correction to this discussion's opening post, and an accuracy pass

c9274bf.

The opening post of this discussion is wrong in two ways, both inherited from a reconstruction of the escritorio review made before the primaries were found. Correcting here rather than editing, so a reader reaches the correction.

  • It says the review "returned REQUEST CHANGES on five counts." "REQUEST CHANGES" is not Fable's word — the verdict carries no verdict keyword at all; the form was imported from this repo's sheets review passes. And there are nine findings, not five.
  • It says Paul's call was to "park it". The verdict's own recommendation was conditional: "Do not build escritorio now; if a merged list must exist, it is Option B's mechanism without Option B's process." Paul did park it — that part is right — but the recommendation was not the flat refusal the summary implies, and the conditional half is the half that authorises this design.

The spec had already replaced the five with the nine in 92ddcc1; only the paraphrased recommendation had reached it, and that is now fixed.

Findings carry their confidence markings

The verdict marks each finding confirmed (verified against a file) or suspected (reasoning only). §2 now preserves that, because flattening the distinction is exactly how a citation degrades back into the recollection it was meant to replace. Notably, finding 5's enumeration-oracle risk is suspected, not confirmed — §5.1 treats it as real anyway, since the answer costs one gate.

Finding 6 was dismissed, and it is the strongest support in the document

The previous revision filed finding 6 as escritorio-specific and said it "evaporates with the process". That was wrong. Its principle:

> "Blast radius is bounded by what a service holds, not by how many > processes it runs in."

Fable wrote that to deny escritorio the defence that its per-team boundary made its holdings safe. Read forward, it is the argument for §3's amendment: if what a service holds is what bounds the damage, then a clause about rendering was never the protection, and one about storing is. It is now quoted in §2 and §3, and it is the single sentence to cite if only one is cited.

New §8.1 — a named gap discloses nothing

Ruling 5 is the most visible thing in this spec, so the objection it attracts is now answered next to it rather than left to be derived. A named gap renders a team and app name; that discloses nothing, because the fan-out set is the person's own §6 directory rows and call 3 already returns the same list to that same person on request. This also covers the verdict's finding 7 (suspected) on guests.

And a consequence recorded rather than fixed. The directory is a hint with a drain lag, so the home will sometimes call an instance the person was removed from weeks ago, or one whose team was deleted (call 2's third event). §5.5 makes that safe — empty list, or a 404 — but ruling 5 then renders a named gap for a team they have left. Correct mechanism, wrong-looking page, which is the shape that gets bug-reported and then "fixed" by widening something. Recorded so that when someone reaches for the fix, it is the right one: prune the stale directory row, never suppress the gap. Suppressing gaps is ruling 5 in reverse.

Still not approved. The review pass has not run.

Coordination with oficina/suite — the sharing doctrine

Found and read oficina/suite, docs/superpowers/specs/2026-09-05-suite-sharing-doctrine.md, revision 4 (worktree doctrine-corrections, last written 2026-09-05 18:41). It has three review passes by two models; revision 4 itself is unreviewed.

It has already adopted this design. Its ruling 11 reads "Activity is pulled at render and held nowhere at rest — oficina/home ruling 18 and the design-activity-pull spec, adopted as suite contract call 7." Its §6 contract table carries call 7 (home → instance, assertion, aud = instance); §6.1 adopts this spec's ruling 3 by name; §6.3 adopts the pre-warm and the loudly-named gap; §9 cites §2.2(a) and (b). Its Appendix A.1 lands the never renders → never stores amendment as home ruling 23 (not 22 — home ruling 22, team selection is navigation state, already exists).

Verified: home rulings 18 and 22 exist and read as the doctrine cites them.

Two collisions raised with the suite session

  1. The doctrine contradicts itself about §7 of this spec. §4.3: authorization is "never cached, at any scope. Not per session, not per page, not for the life of a warm process." The contract table for call 7: "RAM cache only." Both in revision 4. A conformance reader hits §4.3 first and calls §7 non-conformant.

The reconciliation offered: §4.3 governs an authorization answer (S2, sibling to sibling), where a cached VISIBLE is a standing capability. The home caches rendered output and holds no grants. But within the cache's life a removed person still sees titles, so this spec prices that rather than denying it — §7's two ages, the refresh TTL and the hard maximum age that discards rows regardless of pull outcome. That answers the item the doctrine's own header lists as openly unanswered, "the activity path's cached authorization after revocation": bounded, not open. Proposed shape — §4.3 carves out call 7 and requires a bound without carrying the number, exactly as §4.2 already does for retention.

  1. This spec restates the §2.2 amendment text, which the doctrine's Appendix A preamble names as the defect (a second copy of a boundary drifts). Proposed split: A.1 owns the amendment text, this spec keeps the argument for it (§2.1–§3), and this spec gains a Consumed suite rulings section naming the doctrine's commit and revision (their gate 9).

Three questions put to them

  • A.1's other two §2.2 changes give the home a membership roster, so §5.1's non-oracle argument must be re-derived — it survives on "the caller supplies no refs" and the audience-bound assertion, not on the home's ignorance. Same for the §0 citation, since publishing the database now identifies each team's owners.
  • If call 7 moves into the shared Go module, §4 becomes descriptive and the fixtures are theirs — asked that their conformance suite cite gates 16 and 18 rather than restate them.
  • Does call 7's assertion carry roster_epoch? A.1 adds it to §5.3's claims table with a narrow invalidation-hint exception. Call 7 rides the same assertion and inherits the claim set. An instance invalidating anything on it while answering call 7 is an instance deciding from a home-supplied claim — what home §5.4 forbids. Asked that the exception be scoped explicitly to call 8.

Edits held until they answer, so the same sentence is not written twice differently.