Clean successor to V3 (forge: lilith/atlilith). Seeded from local Mac working tree at ~/Code/@projects/@cocottetech/. node_modules and build artifacts excluded via .gitignore. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
13 KiB
13 KiB
peer-feed.screen
Implements brief AE §AE9 — the aggregated peer feed. "What your peers are doing this week" as anonymized aggregations only. No avatars, no per-peer breakdowns, no PII, no cross-provider prospect rows. Powered by the peer-aggregator worker (extension of prospect-resolver per brief L), which enforces a k=5 anonymity floor before any rollup is published.
Reached from chat-home top-bar overflow → "Peer feed" (per FEED-Q2 lean: feed only at P0; chat-home placement deferred). Voice register: working by default, hearth on positive movement, plain on degraded / opted-out states (per 00-system-voice.md §V2).
Layout (iPhone — scrolling card stream; web companion mirrors)
┌─────────────────────────────────────────────────┐
│ ◄ Chat ⚙ │ 56pt — top bar; ⚙ = mute mgmt
├─────────────────────────────────────────────────┤
│ Peer feed · this week │ working register header
│ refreshed 4h ago · daily │ cadence per FEED-Q1 lean
├─────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────┐ │ working register card
│ │ Threads activity │ │
│ │ Your colleagues posted 4× more on Threads │ │ anonymized stat
│ │ this week than last. │ │
│ │ from 12 peers · methodology → │ │ k≥5 disclosure
│ └─────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────┐ │ hearth register card
│ │ Berlin coop │ │
│ │ A salon you're in published 2 shared │ │
│ │ playbooks. Worth a look in the drawer. │ │
│ │ from 7 peers · methodology → │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────┐ │ working — low-confidence flag
│ │ Coop intel │ │
│ │ 3 of your colleagues marked Berlin reports │ │
│ │ low-confidence this week. │ │
│ │ from 8 peers · methodology → │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────┐ │ platform-wide rolling card
│ │ Platform-wide · 90d │ │
│ │ Tryst bumps are accepting 12% slower this │ │
│ │ quarter than last. │ │
│ │ from 240 peers · methodology → │ │
│ └─────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────┘
Components
| Component | Notes |
|---|---|
| Top bar | Back + ⚙ (settings: muted card types, refresh cadence preview, posture quick-link to brief S settings). |
| Cadence header | "this week" anchor + last-refresh stamp + cadence label (per FEED-Q1 lean: daily). |
| Aggregator card | One per anonymized rollup. Three parts: kind label (e.g. "Threads activity", "Coop intel", "Platform-wide · 90d"), the anonymized stat sentence, a peer-count + methodology link. NO avatars, NO peer handles, NO per-peer detail. |
| k≥5 disclosure | Every card shows from N peers where N ≥ 5 per the AE9 anonymity floor. Cards with N < 5 are auto-suppressed (see sensitive-segment state). |
| Methodology blurb | Tap to expand: short explainer of what's measured, the comparison window, and the anonymization floor. Working register. |
| Register cue | Card-level register matches the sentiment: hearth on positive ("worth a look"), working on neutral, plain when a number signals trouble (e.g. "12% slower"). |
States
empty— no peers in any coop yet, OR all peers below k=5 across all aggregators. Copy: "Nothing to show yet. Once your coops have a handful of active peers, weekly aggregations land here." Hearth register, single line, no fake-empty placeholders.populated— default; the layout drawn above. 3–8 cards typical; pull-to-refresh re-pulls (rate-limited).degraded—peer-aggregatorworker hit the k=5 anonymity floor on most card types this cycle. Single visible card: "Not enough peer data this week to show aggregations safely. Back next cycle." Plain register. Per AE9 brief — never lower the anonymity floor to fill the surface.opted-out— provider switched posture to incognito (AE11). The feed surface disappears from the overflow menu entirely (no "you're opted out" placeholder — incognito means no peer-facing surface, per AE11). If reached by deep link: plain cue: "Peer feed is off while you're incognito. Switch posture in settings to surface it."sensitive-segment— a candidate card would identify < 5 peers (e.g. "1 of your colleagues did X"). Auto-suppressed at worker level; never reaches the surface. If somehow surfaced: card replaced by methodology stub: "Held a card back — too few peers to publish safely."stale— last successful aggregator run > 36h ago (brief M §M2 degraded mode). Cadence header shows "refreshed 38h ago · stale" with plain register tint. Existing cards remain readable; no new ones added.muted-card-type— provider long-pressed a card kind ("mute Threads activity"). That kind suppresses; ⚙ shows it in the muted list with un-mute affordance.cold-load— skeleton placeholders per card (4 typical).VoiceOver— cards read top-to-bottom; methodology link explicitly announced;from N peersread aloud as part of each card.
Interactions
- Tap card → expand context / methodology blurb inline (what's measured, comparison window, k-floor disclosure, last refresh).
- Long-press card → "Mute this card type" / "Mute for one cycle" / "Why this card?" sheet. Mute persists until un-muted from ⚙.
- Pull-to-refresh → re-pulls the latest aggregator output (rate-limited; daily cadence per FEED-Q1 lean — pull-refresh just re-fetches the same daily rollup, doesn't trigger a fresh worker run).
- Tap ⚙ → settings sheet (muted card types, refresh cadence preview, link to brief S posture settings).
- Tap methodology → → expand inline; tap again to collapse.
- VoiceOver swipe order — cadence header → each card (kind label → stat sentence → peer count) → methodology where expanded.
In-the-wild copy
- (working, default) "Your colleagues posted 4× more on Threads this week than last. from 12 peers · methodology →"
- (hearth, positive) "A salon you're in published 2 shared playbooks. Worth a look in the drawer."
- (working, watch-out) "3 of your colleagues marked Berlin reports low-confidence this week."
- (working, platform-wide 90d) "Tryst bumps are accepting 12% slower this quarter than last. from 240 peers · methodology →"
- (plain, degraded) "Not enough peer data this week to show aggregations safely. Back next cycle."
- (plain, opted-out via deep link) "Peer feed is off while you're incognito. Switch posture in settings to surface it."
- (working, methodology expand) "Counts posts published by peers who share at least one coop with you, rolling 7 days. Comparison: prior 7d. Floor: 5 peers minimum before publishing."
Edge cases
- Incognito posture (AE11) ↔ feed surface — feed-disable is automatic; the surface is hidden from the overflow menu entirely. No badges, no nudges, no "you're missing out" copy. Per AE11: incognito means no peer-facing surface, ever.
- Rare event with only one matching peer — auto-suppressed at the
peer-aggregatorworker per k=5. Never reaches the device. If a worker bug surfaces one anyway, the surface drops the card silently + logs to audit (brief I). - Platform-wide aggregations — drawn from the rolling 90d window across all consenting providers (not coop-scoped). Always show "Platform-wide · 90d" label so the scope is unambiguous.
- Peer leaves all shared coops — their contribution drops out of next cycle's rollup; no retroactive recompute. Past cards remain (they were anonymized at publish time).
- All peers in one coop block the provider — that coop's aggregations disappear from the feed (no peers visible → k=5 not met). No notice given (giving notice would leak the block).
- Provider changes preferred language — card stat sentences re-author per AD opacity (no "translated from" annotation); methodology blurb localized from the per-locale
voice-{locale}.yamlbank. - Worker disagreement —
peer-aggregatorandprospect-resolverproduce conflicting counts (brief M §M7 conflict): card is suppressed for the cycle; plain row in audit. - Reduced motion — card-enter animations replaced by crossfade.
- Dynamic Type XXL — peer count moves to its own line under the stat sentence.
- VoiceOver + dense feed — group cards under a single landmark; each card announces its kind label first.
Related
- Brief AE §AE9 — parent spec; aggregator + anonymity floor.
- Brief AE §AE11 — incognito posture; feed-disable.
- Brief AE §AE2 — coop / connection scope informs which peers feed into a rollup.
- Brief AE §AE5 — directory scope informs the peer pool for platform-wide cards.
- Brief L —
peer-aggregatorworker is aprospect-resolverextension. - Brief I — each aggregator run + each surfaced card writes audit rows.
- Brief K §K3 — PII gate on any free-text fragment surfaced in a card.
- Brief AD — locale-faithful re-authoring of stat sentences + methodology blurbs.
- Brief M §M2a — stale / degraded surface behavior.
- analytics-dashboard.screen.md — panel-card register pattern shared.
- daily-digest.screen.md — register usage + working/hearth/plain split pattern shared.
00-system-voice.md§V2, §V5 — register selection.
Out of scope
- Per-peer breakdowns of any kind ("Sarah posted X") — never. Per AE9 brief.
- Cross-provider prospect rows — never; preserved from brief Y §Y7.
- Specific prospect data leaking into aggregations — gated by K3 + the aggregator's anonymization step.
- Real-time / push-driven feed (P0 is daily-pull only per FEED-Q1 lean).
- Chat-home placement (per FEED-Q2 lean: feed-only at P0; chat-home embed deferred).
- Web-first feed companion (P0 surfaces iOS; web inherits later).
- Configurable card types beyond mute (custom cards / SQL-style queries are P5+).
- Provider-authored aggregations (this surface is read-only; peers can't publish into the feed).
Open questions
- FEED-Q1 Refresh cadence — daily / per-vigil / live? Lean: daily. Per-vigil risks oversharing micro-movements; live risks small-N leaks under the k=5 floor. Daily lets the aggregator run cleanly + matches the daily-digest rhythm.
- FEED-Q2 Surface in chat-home as well as dedicated feed? Lean: feed only at P0. Chat-home is already dense; the dedicated feed proves the aggregations are valuable before embedding. Revisit after a cycle of usage data.