# Collaboration Source, Projection, and Delivery Model

> Canonical path: `specs/collaboration/source-projection-delivery.md`
> Status: canonical-spec
> Scope: AgentTeam-level classification model for collaboration sources, user-visible/readable projections, author/source identity, and delivery or wake side effects.
> Non-goals: storage migration sequencing, UI visual design, exact outbox payload schema, task timeline implementation details, workflow product modeling, App runtime product details, or future service-kind reservation.
>
> Related specs:
>
> - [`specs/collaboration/collaboration.md`](./collaboration.md)
> - [`specs/identity/identity.md`](../identity/identity.md)
> - [`specs/message/scope-model.md`](../message/scope-model.md)
> - [`specs/message/delivery-runtime.md`](../message/delivery-runtime.md)
> - [`specs/task/task-scope.md`](../task/task-scope.md)
> - [`specs/task/task-timeline-messageization.md`](../task/task-timeline-messageization.md)

## 1. Purpose

AgentTeam has several objects that can look "message-like" at the product surface:
ordinary chat messages, task comments, task status rows, channel membership notices,
reactions, provider updates, workflow requests, agent wakes, schedule runs, and
runtime failure signals. They are not all the same domain object.

This spec defines the classification step that every collaboration feature must
make before choosing a table, message kind, activity kind, outbox payload, or UI
surface. The classification is:

```text
Command or Domain Source
  ├─ Author / Principal / Recipient identity
  ├─ Surface Projection(s)
  └─ Delivery Intent(s)
```

The relationship is not a strict pipeline:

- one domain source can project to multiple surfaces;
- a delivery intent can exist without any human-visible projection;
- a surface projection can exist without an immediate wake or notification;
- commands that request work are distinct from facts that record completed work;
- author identity and source identity are separate even when a single callsite
  currently computes both.

The goal is to prevent hidden workflow, routing, dispatch, source attribution,
activity envelopes, or compatibility fields from being stored as transcript
messages merely because the message subsystem has history, fanout, unread,
reaction, or runtime delivery machinery.

## 2. Vocabulary

### 2.1 Actor, author, principal, source, and recipient

Collaboration records must separate five questions:

| Question | Canonical term | Examples | Must not be modeled as |
| --- | --- | --- | --- |
| Who is displayed as doing or saying this? | `author_actor` / `ActorProfile` | human member, agent member, installed app service member | schedule run, provider event, workflow run id |
| Whose authority executes the command? | `principal_actor` | human owner, agent actor, service principal | author display label |
| Who or what caused the command or fact? | `caused_by_actor` plus `source_ref` | human click, agent runtime, schedule run, provider webhook | author fallback |
| What durable thing happened or was requested? | domain source / command / fact | message contribution, task state change, workflow command | hidden transcript envelope |
| Who receives attention or runtime delivery? | `recipient_actor` in `DeliveryIntent` | human reviewer, agent implementer, service adapter | message author |

The core author family is intentionally small:

```text
human | agent | service
```

`workflow`, `app`, `provider`, and `schedule` are not core
author families. A visible non-human/non-agent author must use `service` plus a
registered current service kind. A schedule, provider event, workflow command,
or app callback may be a `source_ref`; it must not become the displayed author
merely because it started the run. Legacy trigger rows are interpreted only at
the compatibility boundary and project to canonical schedule or service-event
sources before reaching current author/source policy.

Service kinds are current product semantics, not future reservations. The schema,
DTOs, and specs must only admit service kinds that have shipped resolver,
profile, source/run, delivery, UI, guardrail, and tests. Future workflow or
additional service products can add their own service kind through the same complete contract, but
empty enum slots must not be pre-registered.

`workflow` and `schedule` are currently registered service kinds for the
canonical automation author. A message produced by a `workflow_run` or
`schedule_run` contribution source is written under the frozen Human
authorization/audit principal (`author_member_id`, notify/idempotency subjects,
permission fences), but the visible canonical author is the Workflow/Schedule
service author carried in `AuthorProfile` (`ActorType=service`,
`ServiceKind=workflow|schedule`). Source and author stay distinct: the typed
`workflow_run`/`schedule_run` source is provenance/secondary attribution; the
canonical author is the displayed identity. Author != execution authority: the
canonical automation author must never be used to authorize, attribute unread or
notify, self-suppress, or route mentions for the authorizing Human.

### 2.2 Domain Source

A domain source is the authoritative thing that happened, or the authoritative
command to do something. It is the source of truth before any transcript row,
timeline row, detail card, unread badge, or runtime wake exists.

AgentTeam recognizes three top-level source classes.

#### Contribution

A contribution is actor-authored content intended to participate in a
collaboration transcript. It may be authored by a human, agent, or currently
registered service actor, but it exists as content in a surface.

Examples:

- a channel, DM, or reply-thread text message;
- a task-scope comment or automation explanation posted into the task scope;
- an agent visible reply;
- an app/provider visible update;
- a sticker;
- an owner quote or forward message;
- a visible channel share card.

A contribution normally projects to a `TranscriptMessage`.

#### StateChange

A state change is a structured fact about a domain object. It changes object
state, audit state, or derived read models, but it is not itself a transcript
contribution by default.

Examples:

- a task is created, claimed, blocked, unblocked, moved to review, or completed;
- a task checkpoint or artifact fact is added;
- a channel member joins, leaves, or is invited;
- a review is requested or approved;
- a service event subscription status changes;
- a reaction is added or removed;
- an automation run starts, succeeds, or fails.

A state change normally projects to an `ActivityItem`, `DetailProjection`, or
`AttentionState`. It becomes a `TranscriptMessage` only when product semantics
explicitly require a durable chat-history row with message lifecycle behavior.

#### WorkflowCommand / Job

A workflow command is a request for an agent, runtime, or service to do work. A
job is the durable server-owned state for that command.

Examples:

- an agent maintenance workflow request;
- a task assignment or dispatch command that exists to wake a runtime;
- a provider or app command that asks an external system to perform work.

A workflow command should project to job state, audit state, and delivery
intents. It must not be represented as a hidden transcript message. Command
acceptance, execution progress, and completion are separate facts from the
original command.

### 2.3 Surface Projection

A surface projection is a durable or computed read model exposed to humans,
agents, services, or diagnostics. Projection controls where the source can be
read and how it is rendered; it does not by itself imply runtime wake or unread
side effects.

Canonical projection families:

| Projection | Purpose | Typical authority |
| --- | --- | --- |
| `TranscriptMessage` | Conversation history item with message lifecycle. | `scope_messages` and message-owned refs, reactions, search, edit/delete/rewind rules. |
| `ActivityItem` | Timeline, audit, or activity row for non-message facts. | Domain fact table plus centralized activity projection such as `scope_activity_events`. |
| `DetailProjection` | Current object state, cards, summaries, lists, and detail panes. | Domain profile/projection tables or bounded computed reads. |
| `AttentionState` | Unread, badge, needs-attention, and pending indicators. | Membership/read cursors, domain attention facts, and notification policy. |
| `DiagnosticProjection` | Explainability and operational status. | Outbox status, runtime events, domain audit, and source references. |

Human-visible does not mean "must be a message." A task status change, provider
status, or membership change can be visible through an activity feed, detail
surface, badge, or diagnostic view without becoming a chat-history row.

### 2.4 Delivery Intent

A delivery intent is a request to perform side effects after the source and any
required projections are accepted. Delivery is independent of transcript
visibility.

Delivery intent covers:

- recipient selection;
- wake policy;
- realtime fanout;
- notification or attention policy;
- daemon/runtime handoff;
- retry, idempotency, and correlation metadata;
- suppression or revocation checks before delivery.

The minimal typed delivery shape should name:

- source reference type and id;
- scope or target context;
- recipients and wake recipients;
- realtime and attention policies;
- payload needed by the adapter;
- idempotency, retry, and correlation keys.

Delivery intent is not a generic event bus and is not message truth. It is the
durable or bounded handoff request for a current event.

## 3. Core Invariants

### 3.1 Transcript messages are for contributions

`scope_messages` is the authority for `Contribution -> TranscriptMessage`.

A `scope_messages` row should answer message questions:

- is this a transcript item in a scope?
- what render shape does it have?
- who authored it?
- what content, attachments, sources, and mentions belong to it?
- what message lifecycle capabilities apply: history, search, unread, reaction,
  edit/delete, rewind, source refs, and moderation?

It must not be the primary authority for workflow commands, hidden dispatch
envelopes, task state facts, routing facts, automation runs, provider delivery
state, or runtime wake requests.

### 3.2 Author/profile is not source/run

Visible author fields must come from the actor/profile model. Source/run fields
must explain causality. A schedule occurrence, provider event, workflow command,
app callback, or daemon runtime event can explain why a contribution exists, but
it must not be used as the displayed author or as a shortcut for identity
policy.

Existing app/provider visible messages should resolve to a service author
profile. Schedule paths should produce automation-run source references, and
service event paths should produce service-event-delivery source references. If
they also write a visible message, the message author is the resolved human,
agent, or service actor; the schedule or provider event remains source
metadata.

### 3.3 Message kind classifies transcript semantics

Message kind or message semantics may describe the row's transcript class,
render kind, visibility, read capability, mutation capability, routing profile,
and moderation profile. It must not answer "which workflow should run," "which
source/run caused this," or "which domain fact should be created."

Healthy message-kind questions:

- is this transcript, legacy, or compatibility-only?
- is it rendered as text, sticker, owner quote, channel share card, system event,
  or another registered render kind?
- is it human-visible in history/search/read-summary surfaces?
- does it allow reactions, edit/delete, source refs, or rewind?
- which routing and moderation profiles apply to its authored content?

Unhealthy message-kind responsibilities:

- carrying a hidden workflow request;
- carrying an agent dispatch envelope;
- duplicating task activity payloads;
- carrying schedule, app, or provider delivery state as a policy source;
- choosing whether a domain fact should become a message;
- bypassing the domain plan that owns the source.

### 3.4 Activity items are for non-message facts

`scope_activity_events` is for non-message activity sources that need timeline,
notification, wake, diagnostics, or ordering semantics. It must not become a
second message table.

An activity item can reference a message through `source_message_id` when a
message-backed contribution also needs activity ordering or detail grouping. The
activity payload must not copy the message body, attachments, reactions, or
message lifecycle state. Readers must hydrate message-backed content from
message authority.

Typed task activities, reaction signals, provider facts, and other domain facts
must use their domain-owned fact source plus a centralized projection writer.
Feature-local direct inserts into activity rows are not a valid abstraction.

### 3.5 Detail and attention projections are not transcript rows

Detail surfaces, cards, counters, unread indicators, and badges are projections.
They may be human-visible and important without being transcript messages.

Examples:

- a task board card can show status and latest activity without a new message;
- a channel member list can show a newly invited agent without a chat row;
- a task detail page can show a status-change activity row without a message;
- a diagnostics view can show outbox retry status without a message.

### 3.6 Delivery is independent from projection

Delivery side effects must be explicit and source-ref typed.

- A contribution may create a transcript message and a delivery intent.
- A state change may create an activity item and a delivery intent.
- A workflow command may create only a job and delivery intent.
- A surface projection may be visible without waking anyone.
- A delivery intent may wake an agent without creating a human transcript item.

Channel invite/join/leave is an explicit projection-without-attention case. Its
membership fact and system/history row may update member lists, history, and
sync; after commit a named projection-only policy may publish its
`message.created` presentation. That policy does not create ordinary unread, a
user notification, or an Agent wake. Missing realtime is recovered by
membership/history refetch rather than by treating the state change as an
ordinary conversational contribution.

The same rule applies when regular Agent creation, Agent duplication, or role
materialization adds the new private Agent to explicit initial channels. The
Agent-create transaction retains every resulting membership system message;
the standalone create entrypoint publishes each presentation only after its
commit, while caller-owned `CreateWorkspaceAgentInTx` returns the
transaction-bound results for its owner to publish after commit or discard on
rollback. The implicit private `#all` roster does not create a membership
system message. No Agent-create variant may replace this projection-only
handoff with ordinary unread, notification, or wake delivery.

Delivery status does not change message readability, task visibility, or domain
authority. It only describes whether the current event handoff was accepted,
retried, failed, or suppressed.

### 3.7 Commands are not facts

A command requests work. A fact records that something happened. The source
model must preserve this distinction.

For example, a workflow command to distill a Role is not the same thing as:

- the job being accepted;
- a runtime being woken;
- the runtime submitting candidate assets;
- validation passing or failing;
- publication succeeding.

Each of those later outcomes is a separate state change, projection, or delivery
result.

### 3.8 Legacy compatibility is not precedent

Historical or compatibility rows may exist while migrations are staged. They are
not valid examples for new design. Every compatibility surface must be classified
as one of:

| Disposition | Meaning | Requirement |
| --- | --- | --- |
| `deleted` | The old surface is removed and no current consumer remains. | Tests or guardrails prove the production path cannot call it. |
| `migrated` | The old surface is absorbed by a canonical author, source, projection, or delivery owner. | The old write path is removed or delegates to the canonical owner; readers use the canonical owner. |
| `compat-only` | A stable external protocol or retained historical data still needs a thin adapter. | The adapter has named consumers, derives from canonical data where possible, and has a removal condition. |

Compatibility must stay at named decode, projection, or protocol boundaries. It
must not become a new policy source, new write surface, or hidden domain model.
If a compatibility surface has no named consumer and deletion condition, it
should be deleted instead of wrapped.

### 3.9 Service-kind extension is explicit, not pre-reserved

The service actor family is the extension point for non-human/non-agent visible
authors. It is not an excuse to pre-register future products.

Adding a service kind requires the same change set that makes it real:

- canonical spec update;
- service profile resolver and display projection;
- source/run shape for causality;
- delivery and attention policy;
- UI/read-model support;
- guardrail coverage;
- positive and negative tests;
- old-surface disposal entries for any replaced path.

Until that work exists, future workflow or additional service products remain design
possibilities, not schema enum values and not accepted payload values.

## 4. Classification Checklist

Before adding a collaboration feature, answer these questions in order.

1. What is the authoritative domain source?
   - Contribution, StateChange, WorkflowCommand / Job, or a new class that needs
     a spec update?
2. Who is the author actor, if anything is displayed as authored content?
   - `human`, `agent`, or registered current `service` kind.
3. What is the visible contribution source/run reference, if a visible message is created?
   - Examples: `human_compose`, `agent_runtime`, `service_direct_post`,
     `provider_event`, `schedule_run`, `workflow_run`.
   - A Workflow Run `send_message` effect uses an ordinary visible contribution
     with `workflow_run` source provenance and trusted WorkflowContext metadata.
     Its callback authority is bound to the durable Run effect and native
     message; it does not use a runtime-only Workflow delivery handoff.
4. Which projections are required?
   - Transcript, activity, detail, attention, diagnostic, or none.
5. Which delivery intents are required?
   - Human unread, agent wake, runtime handoff, realtime update, push/email, or
     none.
6. What old surfaces are being replaced?
   - Mark each `deleted`, `migrated`, or `compat-only` with consumer and removal
     condition.
7. What must be impossible after the change?
   - Add a negative test or guardrail for that old path.

## 5. Current substrate lane boundaries

The author/source substrate is implemented through coordinated lanes. These lane
boundaries are product-neutral and do not depend on a particular implementation sequence.

### 5.1 Contract and guardrail lane

The contract lane owns this spec, inventories current old surfaces, and enforces
structural rules:

- no new core author family beyond `human | agent | service`;
- no future service-kind enum without a complete service implementation;
- no new policy that infers author/source/profile from legacy compatibility fields,
  message kind, or metadata blobs;
- no unregistered compatibility surface without a disposal entry.

### 5.2 Author/profile lane

The author/profile lane owns "who is displayed".

It must migrate current human, agent, and service/app/provider display paths to
canonical actor/profile fields. Legacy compatibility fields such as `senderName`, `senderType`, and similar
wire fields may remain only as compatibility outputs derived from canonical
profile data or as named input adapters. Core UI surfaces should render from the
canonical profile rather than from fallback chains over legacy fields.

### 5.3 Source/run lane

The source/run lane owns "why this exists".

`service_direct_post` represents a direct contribution by a registered service
actor after its product contract authorizes the ingress. Examples include a
workspace Human-authorized service-account command, a platform-operator-authorized
service-account broadcast delivery, an App runtime notification, and a trusted
business-domain notification. Its native ContributionSource shape is
`kind=service_direct_post`, `refType=message`, and `refId=created message id`.
The source resolver combines that message ref with the service AuthorProfile;
the service principal/member supplies author, routing and notify identity, while
the registered product contract independently resolves permission, caused-by,
execution, Audit, and optional byline facts. This self-reference is the contribution source tuple, while
`scope_message_sources` remains the message-to-message context/quote relation.

An App Notification Intent is a service-authored contribution command. Ordinary
notification content projects to a normal `TranscriptMessage`; an interactive
notification projects to the registered `app_interactive_card` message type and
its message-owned card sidecar. `service_notifications` holds the typed target,
business-object link, notification lifecycle, and revisions. A Human card click
is an interaction command; its callback to the App runtime is a typed
`DeliveryIntent` backed by service-event delivery. The linked business domain
continues to own its object state and version.

It must migrate current human compose, agent runtime, app/provider callback,
provider event, schedule run, and workflow command causality to typed source
references. Legacy trigger and schedule records are run sources; they do not become
authors. Provider visible output is authored by a service profile and linked
back to the service event delivery source.

### 5.4 Integration and compatibility-retirement lane

The integration lane owns cross-surface cleanup.

It verifies that new write, read, UI, notification, unread, and runtime delivery
paths no longer depend on compatibility fields as policy sources. It keeps a
single old-surface disposal inventory for remaining adapters and must reject
facade-only changes that keep old production semantics hidden behind new helper
names.

## 6. Legacy examples and target dispositions

The table below classifies common current or historical surfaces. It is not a
complete implementation inventory; concrete changes must maintain a current
old-surface disposal table in the relevant guardrail or implementation report.

| Surface | Target disposition | Canonical owner |
| --- | --- | --- |
| legacy daemon compatibility author-name fields used as UI label | `migrated` or `compat-only` | actor/profile projection; legacy field is a target or derived display output, not policy source |
| legacy type fields such as `senderType` / `author_type` expanded with product-specific values | `deleted` | core author family remains `human | agent | service` |
| app/provider display metadata assembled at individual message callsites | `migrated` | service profile resolver |
| legacy trigger/source metadata used to explain a sent message | `migrated` | source/run reference such as `schedule_run`, with old trigger-run rows resolved only at the compatibility boundary |
| legacy trigger or provider used as displayed author | `deleted` | author resolves to human, agent, or service profile |
| `message_type` used to carry workflow/runtime envelopes | `deleted` | ordinary visible contribution with `workflow_run` source provenance and trusted WorkflowContext; durable Run/effect owns job state |
| `metadata.card.kind` used as card extension point | `deleted` | registered message render kind and sidecar/domain state |
| owner quote / forward provenance | current canonical pattern | message contribution plus `scope_message_sources` |

## 7. Enforcement expectations

A change that claims to implement part of this contract must include all relevant
evidence:

- an old-surface disposal table covering touched legacy fields, helpers, conditionals,
  metadata keys, and tables;
- tests that demonstrate the canonical path works for the existing product
  behavior;
- negative tests or guardrails that make the replaced path fail;
- long-lived docs or guardrails updated without temporary delivery-specific
  language;
- a reviewer-visible statement of which old paths were deleted, migrated, or
  retained as compatibility adapters.

A facade-only change fails this contract: adding a canonical-looking helper while
leaving production author, source, delivery, or UI policy decisions inside the
old helper is not a migration.
