# Delivery Ledger Optimization

> Canonical path: `specs/message/delivery-ledger-optimization.md`
> Related specs:
> - [`specs/storage/storage.md`](../storage/storage.md)
> - [`specs/message/scope-model.md`](scope-model.md)
> - [`specs/message/delivery-runtime.md`](delivery-runtime.md)
> - [`specs/task/collaborator-policy.md`](../task/collaborator-policy.md)

Status: current.

This spec defines AgentTeam's fanout and activity cost contract for the scope message model. It is an implementation constraint: ordinary message readability comes from `scope_messages` plus membership/visibility predicates, while current-event agent delivery is captured in `scope_fanout_outbox` payload/status. Non-message activity facts stay explicit, auditable, and bounded by the user action that created them.

## 1. Cost model

The supported write cost model is:

```text
O(messages)
+ O(non-message activities)
+ O(task/execution context ingress)
+ O(policy, membership, notification, or grant changes)
```

The unsupported write cost model is:

```text
O(messages * scope participants)
O(messages * public agents)
O(messages * private agents)
O(messages * task collaborators)
O(messages * descendant tasks)
```

Ordinary channel, DM, reply-thread, and task discussion messages must not materialize one durable delivery row per recipient. Dense readability, unread, and public-agent auto-wake eligibility are derived from scope membership, notification state, policy, and cursors.

## 2. Read/fanout fact owners

AgentTeam uses separate fact owners for different read/fanout concepts:

| Concept | Fact owner | Notes |
| --- | --- | --- |
| Message body | `scope_messages` | One canonical row per message. |
| Message provenance | `scope_message_sources` | Explicit quote/context/source refs only. |
| Dense readability | `scope_memberships` | Membership and visibility intervals. |
| Wake/read preference | `scope_notification_settings` | Current notification state keyed by `(scope_id, member_id)`. |
| Non-message activity source | `scope_activity_events` | Reaction add and typed task/share/attachment activities that do not have a message row. |
| Task/execution context ingress | Task-native context/audit facts | It is a context/read source, not a delivery-event disguise. |
| Transactional side effects | `scope_fanout_outbox` | Wake, realtime, daemon handoff, and fanout intent. |

No single table is allowed to impersonate all delivery concepts.

## 3. Dense and broad delivery

Dense and broad delivery policies include:

- ordinary scope message readability;
- service-account message readability under the target contract;
- public auto-wake / no-wake handling;
- controlled broad policies such as all/online-style broadcast;
- membership-continuation delivery in DM and reply-thread contexts.

These policies do **not** write per-recipient non-message activity events. A normal message can be readable by many members or agents without creating recipient rows.

Runtime or diagnostics can explain dense/broad delivery using:

- the message's scope and sequence;
- membership intervals;
- current notification state;
- read cursors;
- non-message activities only when a non-message fact must enter notify/wake/diagnostics.

Broad policy diagnostics must not claim an exact durable recipient snapshot unless a concrete product feature creates an explicit bounded snapshot fact.

## 4. Non-message activity source

`scope_activity_events` is reserved for non-message activity. Supported examples:

- reaction activity;
- typed task/share/attachment activity that must enter timeline, notify, wake, or diagnostics without creating a message row.

Each activity event must identify:

- workspace and scope;
- activity type and referenced fact;
- optional source message;
- activity global cursor;
- suppressed lifecycle when the source fact is removed or rewound.

Non-message activity events are not membership, read-state, private authority, or owner_quote source records. A consumer may use them as one bounded activity/signal source, but it must not infer broad membership, default delivery, or future authority from them.

## 5. Task and execution context ingress

Task/execution ingress is an independent context/read source. It is used when a message, artifact, task discussion, or collaborator action explicitly enters a task or execution context.

Rules:

- ingress facts live in ordinary owner_quote source-ref audit or task-native audit ownership;
- source attribution is required;
- owner/collaborator authorization is required;
- the ingress fact is not copied into `scope_activity_events`;
- ordinary messages do not fan out to all task collaborators or descendant tasks.

History/search/read-check and diagnostics may consult task/execution ingress as its own source, but it must preserve source type so diagnostics can explain why the item is visible. It must not become a hidden message delivery ledger or server-side receive queue.

## 6. Private agent visibility

Private-agent readability is a visibility problem, not a default delivery fanout problem.

Private visibility may come from:

- owner/private membership intervals;
- explicit private grants;
- same-owner private collaboration within a scope;
- source refs attached to an authorized context message;
- message visibility from membership/grants plus source-message visibility for a non-message activity;
- owner_quote source-ref or task-native ingress facts.

Private visibility must not come from:

- public member display;
- roster entries alone;
- ordinary owner messages bulk-materialized per private agent;
- migration/audit labels that do not grant authority;
- reaction or outbox status and current agent runtime lifecycle failure summaries.

## 7. Hidden materialization paths are forbidden

Removing per-recipient dense delivery is not enough. The following hidden materialization paths are also forbidden:

- hidden auto-flow transcript copies for ordinary messages;
- private-context creation backfill of historical messages;
- source refs used as implicit dedupe or default fanout pipeline;
- attachment copies created only to satisfy delivery fanout;
- eager inherited participant/subscription rows for every descendant scope;
- human/reaction side ledgers that bypass `scope_activity_events`;
- parent mirror rows for agent output;
- descendant hidden source rows created from ordinary scope messages.

Source refs, attachment refs, owner_quote source-ref rows, and non-message activity rows are valid only when the user action explicitly creates that fact.

## 8. Agent delivery and read contract

Agent delivery and readable history are intentionally different surfaces:

1. **Current-event delivery** is computed at message write time and stored in `scope_fanout_outbox` payload/status. It drives `agent:start` / `agent:deliver` and the daemon-local continuation inbox.
2. **Readable history/search/read-check** uses `scope_messages`, membership/private authority predicates, source refs, non-message activity source-message visibility, and task-native ingress facts.
3. **Human unread/read-state** uses `scope_memberships` cursors and browser sync cursors. Agent `read_history` does not advance those cursors.
4. **Non-message activity** remains a bounded fact for reactions and similar signals; it does not act as an ordinary message-created log or server-side receive backlog.

The combined model must:

- preserve source type;
- never treat task ingress as a non-message activity event;
- never backfill old readable messages into daemon delivery merely because an agent was offline;
- expose bounded continuation state when a read/search candidate budget is exhausted;
- keep the continuation inbox daemon-local over accepted payloads only and consume it through automatic runtime delivery rather than an agent pull tool.

## 9. Diagnostics contract

Delivery diagnostics must distinguish:

- dense scope readability;
- notification / wake eligibility;
- non-message activity source;
- task/execution context ingress;
- outbox status and current agent runtime lifecycle failure summaries.

Diagnostics may combine facts, but must not rewrite history from current membership alone when a stronger historical fact exists. Non-message activities are activity facts; membership, private grants, and current notification state are authority facts. For a concrete already-written message, `scope_fanout_outbox` payload/status is the execution fact for agent delivery.

## 10. Acceptance criteria

- Ordinary messages write at most their message row, explicit source refs, and necessary search projection.
- Ordinary messages do not create per-recipient non-message activity rows.
- Direct mention, explicit recipient, task dispatch, and private targeted intent are message fanout reasons when they are backed by a message; they do not use `scope_activity_events`. Reaction activity and future non-message activities use `scope_activity_events`.
- Task/execution context ingress uses task-native facts and remains a separate context/read source.
- Private visibility is enforced by the shared SQL readable predicate / visible-row mapper and concrete authority facts.
- Runtime wake/realtime/daemon handoff side effects are emitted from `scope_fanout_outbox`, not from DB transactions directly.
- Rewind and cleanup delete/suppress suffix-derived message, non-message activity, reaction state, search, share-card, timeline, and outbox facts consistently.
- Query descriptors and guardrails prevent dense/broad fanout from returning as per-recipient materialization.
