Spec: Memory Cards — Rendering Watch, Brief & Saved-Answer Notifications in Chat¶
File: .ai/specs/app-layer/2026-07-06-memory-cards.md Status: Approved (v0.4 — OQ-1 write-action auth SECURITY COUNTERSIGNED 2026-07-06 by J. Porter: internal single-use person-scoped core.card_write_action token, core performs final authorization; refresh inherits the generation gate with no dead-end; Teams parity + bug-fix obligation; cleared-channel deferred; DM-only for every card, shared watches fan out to authorised members, no channel content in v1) Owners: App-layer lane (clarify-bot rendering); reviewer: J. Porter Related: PR-091 (this spec), chat-delivery-surface spec (OWNS the transport, identity binding, hook verification, nonce lifecycle, idempotent send, per-tenant credentials, and the slack_blocks.ts Block Kit primitives / Adaptive Card rendering that this spec COMPOSES — nothing here re-specifies those), memory-watch spec (PR-087 — producer of notify:watch jobs + durable WatchFire records + chat_person/chat_shared/webhook/internal targets + per-person fire-time RBAC + debounce/cadence; §3.7 no-channel-content rule + OQ-5 cleared-channel deferral), saved-answers-staleness spec (PR-088 — producer of answer_stale transitions + severity axis + the §3.10 render-time permission invariant + /v1/answers/:id/refresh), temporal-clarification spec (existing card copy + discriminated-action/nonce contract this reuses), mcp-tools spec (getTrace shaping the ask card already uses)
1. Overview¶
Defines the three Telha-initiated chat cards that Phase 3d produces and the existing delivery surface does not yet render: the Memory Change card (a watch fired), the Weekly Memory Brief digest (a weekly-cadence watch), and the Saved Answer Updated card (an answer went stale). It also adds a one-tap [Watch this] action to the already-built grounded ask-answer card, closing the product's five human loops (Ask/Prove/Correct already ship; Watch/Brief land here). This spec owns ONLY the card templates, their actions, the notify:watch consumer wiring in clarify-bot, brief deduplication, and the delivery rule Phase 3d forces: every card goes to an RBAC-checked PERSON's DM; a shared/team watch fans out to its authorised members and never posts to a channel (memory-watch §3.7). Everything else — how a card is signed, delivered, verified, made idempotent, and bound to a PERSON — is chat-delivery-surface's and is reused unchanged; the DM-only rule (§3.1 there) is preserved, not excepted.
2. Problem Statement¶
Without this spec, PR-087 and PR-088 produce durable, permission-checked change signals that have no human surface: watch fires sit in notify:watch jobs no worker leases, and answer_stale transitions are invisible unless a user re-opens the answer. Concretely: (a) "tell me when Meridian changes" (memory-watch's core promise) cannot reach a human; (b) the Weekly Memory Brief — the habit-forming digest — has no renderer; (c) the "Saved Answer Updated / partially stale" card, the platform's strongest differentiator, is unbuilt; (d) memory-watch supports shared/team watches, and a naive renderer would post them to a channel — but channel membership is administered in Teams/Slack, outside Telha's RBAC and audit, so even entity names and counts would leak to unauthorised (and future) channel members; chat-delivery-surface §3.1's blanket channel-posting ban exists for exactly this reason and must be preserved, which means shared watches need an RBAC-safe delivery path (per-member DM fan-out), not a channel exception; (e) a naive implementation would rebuild Block Kit/Adaptive Card plumbing that slack_blocks.ts already provides, and reinvent the nonce/verify contract, multiplying the trust surface.
3. Proposed Solution¶
Normative decisions:
- Compose, never fork. All three cards are built from the existing
slack_blocks.tstyped primitives (header/section/context/actions/button +validateMessageBlocks) and the Teams Adaptive Card path; delivery, signing, identity binding, nonce validation, idempotent send, and per-tenant credential resolution are chat-delivery-surface's and are used as-is. This spec adds templates and card actions, not plumbing. - One bot, one more job kind. clarify-bot's
PollJobsset gainsnotify:alongsideclarify:(the fence is memory-watch's — no other audience may leasenotify:watch). The same lease/no-double-send discipline applies: a WatchFire's delivery is idempotent on itsfire_id+ recipient (the durable WatchFire record, memory-watch §5, is the dedup key), so a re-leased notify job never double-posts. - Every card is DM-only; a shared/team watch fans out to authorised members (the delivery rule). All three cards are delivered to the DM of an RBAC-checked bound PERSON. A shared/team watch (memory-watch
chat_shared {audience}) resolves its audience to the individual members who pass the fire-time ACL/role gate and delivers a DM to each; an unauthorised member gets nothing. No card of any kind posts to a channel in v1 — chat-delivery-surface §3.1's ban is preserved, not excepted, because channel membership is not a Telha access boundary (memory-watch §3.7). Literal channel posting waits for the governed cleared-channel model (memory-watch OQ-5); this spec has no channel-post code path, so the leak is structurally impossible rather than merely policy-forbidden. - Render-time permission safety is inherited and hard (saved-answers §3.10). A Saved Answer Updated card renders to the owner's DM only, and only within that owner's RBAC. The card's changed-claims section shows claim summaries + old/new evidence coordinates that the recipient is authorised to see; withheld evidence uses the existing "access decision" note slot (already present, currently unset, in the ask card). No channel path exists to leak into (§3.3).
- New card actions are additive discriminated actions.
[Watch this]→{kind: "watch_create", scope_type, scope_ref, nonce};[Refresh answer]→{kind: "answer_refresh", answer_id, nonce}(with a confirm dialog — it spends generation budget);[Mute]/[Stop watching]→{kind: "watch_mute", watch_id, nonce};[View changed claims]/[View evidence]/[Show timeline]→ authenticated deep links into mercato, not write actions. All ride chat-delivery-surface §6's existing{kind, …, nonce}schema, verify-before-ack ordering, and nonce lifecycle —kindis additive by that spec's own extensibility clause.[Keep original]is a local card dismiss (no call). - Card write-actions execute under a short-lived, person-scoped, single-use action token — the tenant service token NEVER performs the write, only signs the assertion (SECURITY COUNTERSIGNED 2026-07-06, J. Porter). Flow: (1) the platform callback is verified (chat-delivery §6); (2) the callback identity is bound to a tenant PERSON; (3) the bot mints an internal-only action token; (4) core executes the action AS that person; (5) core re-checks authorization and rejects on any mismatch/expiry/replay/denied-permission. The token is
INTERNAL-ONLY(never leaves core↔bot; not a bearer a client ever holds) and carries:{tenant, person_id, action_kind: watch_create|watch_mute|refresh, target_id, scope_fingerprint, generation_id, aud: "core.card_write_action", jti: <single-use-id>, exp: now+~120s}. It MUST be person-, action-, target-, and audience-scoped, short-lived, single-use, deny-by-default, and fully audited. Core REJECTS the request if: the token is expired;audis notcore.card_write_action; thejtihas already been used (replay); theaction_kinddoes not match the requested write; thetarget_iddoes not match; the person no longer has the required authority (watch_create= read authority over the watched scope, checked againstscope_fingerprint;watch_mute= ownership or allowed mute authority;refresh= the generation gate, §3.5 note / OQ-2); the card payload has been tampered with (fingerprint mismatch); or the requested write exceeds the card's original permitted action. This is theRESPONDER_ASSERTION_FORBIDDENtrust shape: the bot may assert that a verified person took a specific action, but core alone decides whether that person is allowed to do it. An unmapped identity gets the link-your-identity reply and no token is minted. Blast radius of a leaked/replayed token: one person, one action, one target, once, ~2 minutes. - Brief dedup is per recipient (memory-watch OQ-3 resolution). The weekly brief collapses fires to one line per
(changed_ref_id, change_tx)for a given recipient even when an entity watch, a label watch, and a query watch all fired on the same change; the brief reads durable WatchFire rows, so dedup is a pure rendering fold over persisted records. - Severity drives presentation, not routing (saved-answers §3.5). A
severity: materialsaved-answer transition renders with the escalated header/emphasis; it does not change who is notified. Digest vs immediate delivery is entirely the watch'scadence(memory-watch §3.5) — this spec never invents its own escalation. - Copy follows house rules and the clarification precedent: the temporal-clarification §7 copy conventions (no em-dashes, world-state framing, one clear action row) extend to these three cards; strings are centralised so localisation stays additive.
4. Architecture¶
PR-087 memory-watch ─▶ notify:watch job (+ durable WatchFire row)
PR-088 saved-answers ─▶ answer_stale ─▶ WatchOps::emit ─▶ notify:watch job
│
clarify-bot worker: PollJobs kinds=["clarify:", "notify:"] (fence: memory-watch)
│ lease ─▶ resolve recipient(s) + tenant credentials (chat-delivery, reused)
│ chat_person ──▶ one DM (RBAC-checked)
│ chat_shared ──▶ fan out: for each audience member passing the fire-time
│ ACL/role gate, one DM; unauthorised members get nothing
│ (NO channel post exists in v1 — memory-watch §3.7)
▼
card templates (THIS spec) over slack_blocks.ts + Adaptive Cards (chat-delivery)
├─ Memory Change card [View evidence] [Show timeline] [Stop watching]
├─ Weekly Memory Brief grouped, per-recipient deduped, [Open changes]
└─ Saved Answer Updated [Refresh answer]✓confirm [View changed claims] [Keep original]
ask-answer card (existing, v0.7) gains [Watch this]
│ callbacks ─▶ chat-delivery hooks (verify→ack→async, nonce) ─▶ intent router
│ +kind watch_create ─▶ POST /v1/watches (owner = bound person)
│ +kind answer_refresh ─▶ POST /v1/answers/:id/refresh (within person role)
│ +kind watch_mute ─▶ POST /v1/watches/:id/disable
5. Data Models¶
No new core state and no new graph objects. Card actions extend chat-delivery-surface §6's action payload with additive kind values (watch_create, answer_refresh, watch_mute) carrying their target id + the existing nonce; the nonce is minted, persisted-before-send, and validated exactly as §6 defines. The clarify-bot delivery store (existing ClarifyDeliveryEntity precedent) gains a sibling NotifyDeliveryEntity {fire_id, recipient, message_ref, nonce, kind, status, created_at} so notify sends are idempotent and callbacks validate against a persisted record — the same shape and guarantees as clarification deliveries, keyed by fire_id (memory-watch's durable WatchFire id) instead of clarification_id. A shared/team fire produces one NotifyDeliveryEntity per resolved-and-authorised recipient (idempotency is per (fire_id, recipient)), so fan-out double-sends are prevented exactly as single sends are. View-models: MemoryChangeView, BriefView, and SavedAnswerView are all DM-grade and RBAC-filtered to the recipient (no channel view-model exists — §3.3); each renders only evidence coordinates the recipient is authorised to see.
6. API Contracts¶
Inbound: no new hook endpoints — /hooks/teams and /hooks/slack (chat-delivery §6) handle the new card actions through the same verify→ack→async path; the intent router gains three additive kind branches, each validating platform signature, tenant, message_ref, and nonce, then minting the §3.6 person-scoped action token and calling the endpoint with it (core enforces aud, jti single-use, person_id authority, action_kind/target_id/scope_fingerprint match, and expiry before any write). New internal token audience core.card_write_action (signed by the existing tenant grpc.token_key; verified by core like the clarify/admin audiences; a used-jti store with a short TTL >= action_token_ttl_secs gives single-use/replay protection). Outbound (core, already specified): notify:watch job payloads (memory-watch §5) are the delivery input; watch_create → POST /v1/watches, answer_refresh → POST /v1/answers/:id/refresh (202; failures — cost-cap, no-[llm] — surface as an ephemeral card note, never a silent drop), watch_mute → POST /v1/watches/:id/disable. Deep links resolve to ${TELHA_APP_URL}/trace/:id, /answers/:id, and a timeline view (mercato). Failure behavior reuses chat-delivery's DeliveryError → skip-code mapping; a shared/team fan-out records a per-recipient skip for any member the delivery can't reach (missing alias) or who fails the gate, so a partial fan-out is auditable rather than silent.
7. UI/UX¶
Three card layouts, copy centralised, no em-dashes, all DM-delivered. Memory Change (per the product plan's watch loop): header "Memory changed · [View evidence] [Show timeline] [Stop watching]. Weekly Memory Brief (the habit loop): header "Weekly Memory Brief · [Open changes] deep link. For a shared/team watch, the same brief is delivered to each authorised member's DM (the "shared" surface is the subscription, not a shared message). Saved Answer Updated (the differentiator, DM-only): header carries status + severity ("… is now partially stale" / "materially stale"), a changed-claims list ("Liability cap updated from EUR 2m to EUR 5m", "Effective-date clarification has now bound"), [Refresh answer] (confirm dialog) [View changed claims] [Keep original]. The ask-answer card gains a [Watch this] button. RBAC reach (OQ-2 resolution): refresh inherits the existing generation gate (steward/admin today); for a non-generation role the Saved Answer card OMITS [Refresh answer] and shows [View changed claims] + deep-link instead of a disabled button, so a viewer still sees WHAT changed with no dead-end. Widening generation to editors/viewers stays the separate deferred allowlist decision (chat-delivery v0.7 note), not re-opened here.
8. Configuration¶
Reuses [clarify.channels] credentials and card_span_max_chars; adds [memory_cards]: brief_max_lines (25 — brief truncation with an explicit "+N more" line, never silent), refresh_confirm (true), action_token_ttl_secs (120 — §3.6 person-scoped action token lifetime), app_url (reuses TELHA_APP_URL from v0.7). Deliberately NOT configurable: DM-only delivery for every card (there is no channel-post path to toggle), per-person RBAC on fan-out, the person-scoped-action-token rule (the tenant key never performs writes directly), idempotent notify send.
9. Alternatives Considered¶
- Edit chat-delivery-surface in place instead of a companion spec. Rejected operationally: that spec was being actively revised by a parallel session (v0.7, same day) — a concurrent edit risks the clobbering this project has already been bitten by. A companion spec that defers all transport to it is collision-free and keeps the single-bot doctrine intact (one bot, one delivery contract, this spec only adds templates).
- A second "notifications" bot/app. Rejected for the same reasons chat-delivery rejected separate ask/ask-back bots: install friction, split identity, doubled credentials. One Telha presence renders everything.
- Post shared-watch cards to a channel, summary-only (the v0.1 position). Rejected at review (2026-07-06): channel membership is administered outside Telha's RBAC and audit, so it is not an access boundary — even names/counts leak to unauthorised present-and-future members, and a summary just lowers the volume of the leak rather than closing it. Per-reader redaction in one shared message is not expressible. Fan-out to authorised members' DMs is the only RBAC-safe delivery; a governed cleared-channel binding (memory-watch OQ-5) is the only acceptable future channel path, and it is not v1.
- Treat channel membership as an implicit access grant. Rejected outright: it would fork Telha's deny-by-default, Entra-group-mapped, audited RBAC to whoever administers the Slack/Teams workspace — a second, weaker, unobserved access boundary. Access stays governed by Telha; a channel may at most be a content-free doorbell.
- Render briefs from a live re-scan at send time. Rejected: the durable WatchFire records (memory-watch §5) already are the fire history; re-scanning would diverge from what actually fired and re-do work. The brief is a fold over persisted fires.
- Expose refresh as a fire-and-forget button with no confirm. Rejected: refresh spends LLM budget (saved-answers §3.6); a one-tap accidental refresh on a shared board answer is a cost and a surprise. Confirm dialog required.
- Put watch-create/refresh actions behind MCP tools instead of card actions. Rejected for the chat surface: the value is the one-tap-in-context loop; MCP is the agent surface. (memory-watch/saved-answers already expose the MCP tools for agents.)
10. Implementation Approach¶
One app-layer PR (PR-091), after PR-087 (needs notify:watch + WatchFire) and best after PR-088 (for the Saved Answer card; the Memory Change + Brief cards and [Watch this] can land on PR-087 alone). Spec-first. Sequence: NotifyDeliveryEntity + notify-job consumer in the clarify-bot worker (poll set + lease/idempotent send) → summary/detail view-models (§5 type split) → three card templates over slack_blocks.ts + Adaptive Card twins → three intent-router kind branches (bind-as-person, nonce-validated) → [Watch this] on the ask card → brief dedup fold → tests per §12 with the existing FakeAdapter/FakeCore harness. Single-writer note: clarify-bot is shared with the chat-delivery work; claim PR-091 in the build doc before editing the package, and rebase onto the landed v0.7 Block Kit builder rather than around an older copy.
11. Migration Path¶
Greenfield and additive on top of a shipping bot: new job-kind consumer, new delivery entity, new card templates, three additive action kinds, one new button on an existing card. No change to existing clarification/ask card behavior, hook verification, or the answer/clarification endpoints. Ships dark until watch.enabled/answers.enabled (core) and the channels are credentialed. All delivery reuses the existing DM path — no new channel-posting surface is introduced — so the behavioral delta is confined to new card types and the fan-out loop over authorised members. Forward compatibility: additional card types (e.g. conflict-for-steward) reuse the same DM view-model; a future cleared-channel path (memory-watch OQ-5) would add its own doorbell renderer without touching these.
12. Success Metrics¶
card_watch_change_dm: a WatchFire for a DM-target entity watch renders a valid Block Kit + Adaptive Card with the event line,[View evidence]/[Show timeline]/[Stop watching], and passesvalidateMessageBlocks.card_shared_fanout_rbac: a shared/team watch delivers a DM to each authorised member and nothing to an unauthorised member of the same audience; no card is ever addressed to a channel conversation (reuseschat_dm_only); per-recipient skips are recorded for unreachable members.card_saved_answer_updated: ananswer_stale→partially_staletransition renders the changed-claims list with old/new evidence coordinates and[Refresh answer]/[View changed claims]/[Keep original];severity: materialescalates the header only.card_saved_answer_render_safety: a saved-answer card is DM-only and RBAC-filtered to the recipient; withheld evidence uses the access-decision note, asserting no false exclusion (saved-answers §3.10); there is no channel path for it to reach.card_notify_idempotent: a re-leasednotify:watchjob with an existingNotifyDeliveryEntityfor(fire_id, recipient)sends nothing new — exactly one live card per fire per recipient.card_brief_dedup: one change matched by three of a recipient's watches appears as ONE brief line (dedup perchanged_ref_id, change_tx); the brief truncates atbrief_max_lineswith an explicit "+N more".card_action_watch_create:[Watch this]from the ask card creates a watch owned by the bound person; an unmapped identity creates nothing and gets the link-identity reply; the action rides the existing nonce/verify path.card_action_refresh_confirm:[Refresh answer]requires the confirm dialog, then calls/v1/answers/:id/refreshas the bound person; a role lacking generation authority is refused with a card note, not a silent drop.card_action_bind_as_person: no card write-action can set a watch owner or act on an answer for anyone other than the verified bound identity (theRESPONDER_ASSERTION_FORBIDDENanalogue); forged target ids are dropped and audited.card_action_person_token_scope: a mintedcore.card_write_actiontoken is single-use (a reusedjtiis rejected), bound to oneperson_id/action_kind/target_id, audience-checked, and expires ataction_token_ttl_secs; a token replayed, or presented against a different action/target, or with a tamperedscope_fingerprint, or exceeding the card's original permitted action, is rejected; the tenant service token cannot itself perform a watch/answer write (it only signs the action token).card_refresh_role_gate: a non-generation role's Saved Answer card omits[Refresh answer]and offers[View changed claims]+ deep-link; a steward/admin card includes the confirm-gated refresh; a viewer's forged refresh action is rejected at the token role check.card_fence: the clarify-bot leasesnotify:watchonly via its own audience; no worker/connector audience can (reuses memory-watchwatch_fence).
13. Open Questions¶
OQ-1: How do card write-actions authenticate on the bound person's behalf?Resolved + SECURITY COUNTERSIGNED 2026-07-06 (J. Porter): a short-lived, single-use, person/action/target/audience-scoped INTERNAL action token (aud: core.card_write_action, §3.6) signed by the tenant key but never itself the write credential; core executes as the person, deny-by-default, and rejects on expiry /audmismatch / usedjti/ action mismatch / target mismatch / lost authority / payload tamper / exceeding the card's permitted action. Chosen over reusing the clarify/service token (widens a leaked token's power) and over deep-linking every write (kills the one-tap loop). Countersign condition — internal-only, single-use, audience-scoped, replay-protected, core performs the final authorization check — is satisfied by the §3.6 design as written. This clears the last pre-code gate for Phase 3d.OQ-2: ShouldResolved 2026-07-06 (Claude, delegated review): inherit the existing generation gate (steward/admin) — refresh spends budget and produces an authoritative answer, so it lives where generation lives; widening generation is a separate deny-by-default RBAC decision, not a card-button smuggle. No dead-end: a non-generation role's card omits the refresh button and shows[Refresh answer]be reachable by editors/viewers?[View changed claims]+ deep-link (§7), so viewers see what changed without one-tap regenerate.- OQ-3: Governed cleared-channel rendering — if memory-watch OQ-5 approves a channel destination, this spec owns its doorbell card (content-free "activity in
, open Telha" with a deep link; the facts still fetched per-person via the authenticated app). Current stance (delegated review 2026-07-06): keep deferred as a named post-Phase-3d candidate, paired with memory-watch OQ-5; v1 has no channel renderer at all. Two constraints pinned NOW so a future implementer cannot get them wrong: (1) implicit channel-membership-as-access is permanently rejected; (2) even under an explicit admin binding the doorbell carries NO entity names or counts (a bound channel still has membership drift the admin accepts, so minimise even under clearance). OQ-4: Teams custom-answer parity?Resolved 2026-07-06 (Claude, delegated review): mirror the Slack action payloads exactly across all Teams action kinds, add a Teams payload-shape test, AND fix the pre-existing broken TeamsanswerCustompath (chat-delivery v0.7 note) in PR-091 — since PR-091 touches the Teams action surface, it retires that bug rather than leaving it dangling. Not a design fork; an implementation obligation now recorded.
14. Changelog¶
- 2026-07-06 — v0.4: Approved — OQ-1 write-action auth SECURITY COUNTERSIGNED by J. Porter. Countersign hardened the design: token gains
aud: "core.card_write_action"(internal-only audience), a single-usejtiwith a used-jti store for replay protection,scope_fingerprint(tamper/exceeds-permitted-action guard), andgeneration_id(ties a refresh to the exact answer/brief the card showed); reject list made explicit in §3.6/§6 (expiry, aud, jti-reuse, action mismatch, target mismatch, lost authority, payload tamper, exceeds-permitted-action); §12card_action_person_token_scopestrengthened accordingly. Condition (internal-only, single-use, audience-scoped, replay-protected, core does the final authorization) is met by the spec as written. This clears the LAST pre-code gate for Phase 3d; spec → Approved. - 2026-07-06 — v0.3: delegated OQ review (Claude; J. Porter's countersign gates the one security item). OQ-1 resolved — card write-actions run under a short-lived, single-use, person-scoped
chat_actiontoken (§3.6, §6): signed by the tenant key but scoped to{person_id, role, action_kind, target_id, exp≈120s}, core executes deny-by-default under that person+role, tenant token never writes directly; chosen for least privilege over reusing the clarify token or deep-linking every write. Adds a core token audience → security countersign pending before PR-091 code, as the responder-assertion rule was. OQ-2 resolved — refresh inherits the steward/admin generation gate; non-generation roles get[View changed claims]+ deep-link, no dead-end button (§7). OQ-4 resolved — mirror Slack payloads, add a Teams shape test, fix the pre-existing TeamsanswerCustombug in PR-091. OQ-3 kept deferred (paired with memory-watch OQ-5) as a named post-Phase-3d candidate with two constraints pinned. New configaction_token_ttl_secs(120); new testscard_action_person_token_scope,card_refresh_role_gate. Status → In Review. - 2026-07-06 — v0.2: channel delivery removed at review (J. Porter): every card is DM-only; shared/team watches fan out to authorised members' DMs. The v0.1 "channel summary-only exception" is withdrawn — channel membership is administered outside Telha's RBAC/audit, so it is not an access boundary and even names/counts leak; chat-delivery §3.1's ban is now preserved rather than excepted. §3.3 rewritten to the fan-out rule, §5 view-models all DM-grade (channel view-model deleted), §4/§6/§7/§8/§11 dechannelled,
card_channel_summary_only→card_shared_fanout_rbac, two alternatives added (summary-only rejected; implicit channel-membership-as-access rejected outright), OQ-3 repointed to the deferred governed cleared-channel doorbell (memory-watch OQ-5). Pairs with memory-watch v0.3. - 2026-07-06 — v0.1: initial draft (PR-091). Companion to chat-delivery-surface (composes its transport +
slack_blocks.ts, defers all plumbing). Three memory cards (Memory Change, Weekly Memory Brief, Saved Answer Updated) +[Watch this]on the ask card;notify:added to the clarify-bot poll set withNotifyDeliveryEntityidempotency keyed onfire_id; three additive discriminated action kinds bound-as-acting-person; brief dedup per recipient. Depends on PR-087 (Memory Change, Brief,[Watch this]) and PR-088 (Saved Answer card).