# Author/source substrate old-surface disposal inventory

> Canonical path: `guardrails/message/author-source-substrate-inventory.md`
> Status: guardrail-inventory
> Scope: durable inventory for author/profile/source substrate cleanup on current message, app/provider, and trigger paths.

This inventory tracks old surfaces that must not stay as hidden policy after the
author/source substrate lands. Each implementation slice must update the relevant
row to one of `deleted`, `migrated`, or `compat-only`.

`compat-only` is allowed only when a named wire consumer, retained historical row,
or external protocol still requires the old shape. It must derive from canonical
data where possible and carry a removal condition.

## Disposal states

| State | Meaning | Required evidence |
| --- | --- | --- |
| `deleted` | Old surface is removed from production writes/reads. | Negative test or guardrail proves it cannot be called. |
| `migrated` | Old surface delegates to or is replaced by canonical author/profile/source/delivery. | Tests cover the current behavior through canonical helpers. |
| `compat-only` | Old surface is a thin decode/protocol adapter. | Named consumer, canonical derivation, and removal condition are documented. |

## Current surfaces

| Old surface | Current risk | Target owner | Required disposition |
| --- | --- | --- | --- |
| `senderName` / `sender_name` rendered as a default UI label | Mixed routing target and display label. | Author/profile projection | `migrated` for core UI. `deleted` for browser/API/socket/app-callback wire. `compat-only` only for old daemon receive/history wire derived from canonical profile fields. |
| `senderType` / `author_type` expanded with product-specific values | Product concepts can become core sender families. | Author/profile contract | `deleted`: only `human`, `agent`, and `service` are core actor families. |
| App/provider display name, avatar, badge, slug assembled at individual write/read callsites | UI identity drifts per feature path. | Service actor/profile resolver | `migrated`: app/provider visible output resolves through service profile. |
| Trigger/schedule metadata used as author or sender evidence | Trigger run can masquerade as author. | Source/run reference | `migrated`: trigger/schedule is a source/run, not author. |
| Provider webhook/watch metadata used as source explanation in message metadata only | Source causality is hidden in untyped metadata. | Source/run reference | `deleted`: provider-watch visible messages no longer write provider/source metadata; trigger run/source facts carry causality. |
| `message_type` or message semantics used to explain causality | Render kind can become source or workflow policy. | Message semantics + source/run split | `deleted` as causality source; message semantics only classify transcript behavior. |
| UI fallback chains over `senderName`, `senderType`, `messageType`, `metadata` | Core surfaces keep old model alive behind a new DTO. | Author/profile and integration lanes | `migrated`: core UI reads canonical profile/source/render fields. Any fallback must be `compat-only`. |
| Metadata keys such as `metadata.trigger`, `metadata.provider_watch`, or `metadata.sources` used by new policy | New source/run work can become another metadata blob. | Source/run reference | `deleted` for trigger/provider source policy; owner/private `metadata.sources` remains a named adapter only. |
| Direct construction of service/source/delivery payloads in app, trigger, provider, or message callsites | Canonical helpers are bypassed. | Contract guardrail | `deleted` or `migrated`: production code uses canonical resolver/helper boundaries. |
| Frozen Human owner presented as the visible author of `workflow_run` / `schedule_run` messages | Automation messages impersonate the authorizing Human; split-brain author/source model. | Automation service author | `migrated`: the visible service author is the Workflow/Schedule service author (`ActorType=service`, `ServiceKind=workflow\|schedule`), resolved from the typed contribution source in `automation_service_author.go` and overlaid on AppMessage/HistoryMessage/ReceiveMessage read models. The frozen Human stays the separate authorization/audit principal (`author_member_id`, notify/idempotency subjects, permission fences). Source stays provenance; author != execution authority. |



## Current code inventory snapshot

This section names the current production surfaces that downstream lanes must
resolve. It intentionally avoids line-number ownership; file paths are pointers
to the current source of behavior and must be refreshed when the behavior moves.

### Trigger and schedule source metadata

| Current surface | Files to audit | Current disposition | Required migration |
| --- | --- | --- | --- |
| Schedule run message source and idempotency | `internal/trigger/compat.go`, `internal/store/message/contribution_source.go`, `internal/store/message/source_resolver.go`, `internal/schedule/manage/manage_read.go`, `internal/tidb/migrations/00172_time_trigger_schedule_backfill.sql` | `canonical`: stable Trigger-shaped routes use schedule definitions and automation runs; typed contribution sources never read retired Trigger-run rows. | Guardrail rejects production `metadata.trigger` and retired Trigger-table reads/writes. |
| Trigger metadata UI label helpers | `web/src/agentteam/workspace/triggers-route/triggerMetadata.ts` | `deleted` as metadata fallback: UI presentation reads only canonical `message.source`. | Regression test proves `metadata.trigger` alone is ignored. |
| Retained trigger metadata on hot message rows | `scope_messages.metadata.trigger`, `internal/tidb/migrations/00162_remove_trigger_metadata_from_scope_messages.sql` | `deleted`: upgrades remove `metadata.trigger` from retained rows. | Migration test proves mixed metadata keeps non-trigger keys, trigger-only metadata becomes `NULL`, and rerunning is idempotent. |
| Schedule-created message tests and API payloads | `internal/api/message/schedule_effect_writer_test.go`, trigger/schedule API tests | `migrated` | Tests must assert trigger/schedule is source/run and the visible author resolves to human, agent, or service. |

### App/provider source and service-author metadata

| Current surface | Files to audit | Current disposition | Required migration |
| --- | --- | --- | --- |
| Provider-watch visible message metadata | `internal/store/app/provider_watch_*`, `scope_messages.metadata.provider_watch`, `internal/tidb/migrations/00163_remove_provider_watch_message_metadata.sql` | `deleted`: provider-watch visible messages no longer write `metadata.provider_watch` / provider-specific `metadata.app`; retained rows are cleaned on upgrade. | App/provider messages resolve author through service profile and provider event/source through typed `ContributionSource`; delivery facts live in `service_event_deliveries`. |
| Provider-watch service-event runtime | `specs/app/provider-watch-agent-mcp.md`, `specs/app/gitea.md`, `internal/store/app/provider_watch_*` | `canonical` | Specs and code keep the App service author separate from the typed provider-event source and use canonical service-event subscriptions/deliveries. |
| App page or internal-app send metadata such as `source=app_page` or `source=internal_app_send` | `internal/api/app/*`, `internal/store/app/*` | `migrated` or `deleted` | If the value is only diagnostics, move it to source/run or drop it. If it remains, it must be a compat adapter with a named consumer. |
| Remote app mention callback `message.senderId` / `message.senderType` | `internal/store/app/app_handlers.go` | `deleted` | Remote mention callbacks send `message.authorProfile` instead of sender fields; this is not old daemon wire. |

### Message author wire fields and UI fallbacks

| Current surface | Files to audit | Current disposition | Required migration |
| --- | --- | --- | --- |
| `senderType`, `senderName`, `senderServiceKind`, `senderServiceSlug` wire fields | daemon receive/history DTOs, desktop receive DTOs | `compat-only` for old daemon wire only; browser/API/socket/app-callback wire is `deleted` | Canonical `AuthorProfile` drives core UI. Legacy sender wire fields remain only on daemon-compatible `ReceiveMessage`/`HistoryMessage` and are derived from canonical profile/author facts where possible. Removal condition: old daemon/runtime clients no longer require legacy sender fields. |
| Store/runtime `ReceiveMessage` and `HistoryMessage` sender fields | `internal/store/message/api_types.go`, message delivery builders under `internal/store/**`, fanout replay helpers | `migrated` | Store/runtime message delivery and history shapes carry canonical `AuthorProfile` only. Old daemon `sender_type` / `senderName` fields are derived in `internal/api/dto/converter` at the daemon/history DTO boundary. The guardrail fails if store runtime structs or production builders reintroduce `SenderType` / `SenderName`. |
| UI fallback chains over sender fields, message type, or metadata | `web/src/agentteam/workspace/**`, `desktop/internal/channel/agentteam/**` | `migrated` for core message author/source rendering; `compat-only` only for wire/schema adapters and explicitly named non-message payloads | Core message rendering uses `authorProfile` and `source`. The Web guardrail fails any production sender-shaped message/browser naming after the browser/API/socket sender wire deletion. |
| Message grouping, app grouping, owner-private pending context, rewind permission, share eligibility, thread agent activity, unread/self-notification suppression | `threadMessageListGrouping.ts`, `threadAppMessageGroups.ts`, `ownerPrivateDelivery.ts`, `rewind.ts`, `shareFlowTargets.ts`, `threadActivitySummary.ts`, `ReplyThreadPanel.tsx`, `ThreadMessages.tsx`, `ThreadMessageList.tsx`, `useThreadScroll.ts`, `useBrowserNotifications.ts`, `useWorkspaceSocket.ts`, `queryCache.ts`, `TaskDetailRoute.tsx` | `migrated` | These UI/UX policy paths use `resolveMessageAuthorIdentity()` and helper predicates such as `messageAuthorAgentId()` / `isMessageAuthoredByCurrentUser()` instead of raw `sender*` fields. Tests cover authorProfile-only behavior and guardrails reject new sender-shaped browser/API/socket paths. |
| Local pending message construction and realtime payload guards | `ThreadMessages.tsx`, `web/src/types.ts`, `socketPayloadGuards.ts` | `migrated` / legacy browser sender wire `deleted` | Local pending messages construct canonical `authorProfile`; Web socket/API guards require `authorProfile` and do not validate or require legacy sender fields. |
| Browser mention/reaction notification legacy sender fields | `internal/api/realtime/events.go`, `internal/store/reaction/reactions.go`, `web/src/agentteam/pages/workspace-shell/useBrowserNotifications.ts`, `web/src/agentteam/pages/workspace-shell/socketPayloadGuards.ts` | `deleted` | Realtime mention/reaction payloads carry canonical `authorProfile` only; Web guards require it, and notification labels/self checks resolve through `resolveMessageAuthorIdentity()`. |
| Browser/API e2e message response assertions | `e2e/daemon_e2e_test.go` | `migrated` | E2E tests for `/api/messages/channel/...` assert `authorProfile` on normal browser-facing message rows. They must not reintroduce `senderType` / `senderId` as proof of browser/API wire behavior. |
| Reaction agent wake receive payload author fields | `internal/store/message/reactions.go`, `internal/store/reaction/helpers.go`, `desktop/internal/channel/agentteam/mcp_format.go` | `migrated` with old-daemon `compat-only` receive metadata | Reaction receive payloads carry canonical reactor `AuthorProfile`; desktop prompt/MCP formatting reads `AuthorIdentity()`. Old daemon `sender_type` / `senderName` receive wire is derived at the API DTO boundary, while `metadata.reaction.reactor*` remains only for old daemon reaction receive compatibility and must not be the active prompt truth. |
| Runtime delivery prompt compiler and dispatcher author fields/tests | `desktop/internal/agent/process_delivery.go`, `desktop/internal/promptcompiler/model.go`, `desktop/internal/promptcompiler/compile.go`, `desktop/internal/promptcompiler/*_test.go`, `desktop/internal/dispatcher/*_test.go`, `desktop/internal/channel/agentteam/testdata/turn_context_agentteam.rendered_turn.md`, `desktop/prompts/channel/agentteam/reference.md`, `specs/daemon/prompt-compiler-architecture.md` | `migrated` | The receive/process-delivery adapter resolves `types.InboxMessage.AuthorIdentity()` before entering the prompt compiler. `DeliveryCompileInput` carries `AuthorIdentity`, and `DeliveryEnvelope` exposes `authorDisplayName`; normal prompt compiler/dispatcher tests and goldens construct `AuthorProfile` / `AuthorIdentity` inputs, and prompt reference/golden copy describes message authors. Legacy `SenderType` / `SenderName` do not cross into compiler/fingerprint/render or normal dispatcher prompt/launch/deferred paths as delivery truth. Deferred replay retains only an explicitly named old-daemon compatibility reader. |
| Desktop metadata normalization `SenderID` / `AgentTeamMeta.SenderType` | `desktop/internal/types/metadata.go`, `desktop/internal/dispatcher/dispatcher.go`, `desktop/internal/channel/agentteam/handler.go` | `migrated` | Dispatcher-facing channel metadata now exposes `AuthorTargetID` and `AgentTeamMeta.AuthorProfile` / `AuthorTarget`; agentteam receive no longer writes sender-named fields into `AgentTeamMeta`. Legacy receive wire remains on old-daemon DTOs only. |
| Reaction reactor payload actor fields | `internal/api/dto/agent_messages.go`, `internal/api/dto/message_responses.go`, `desktop/internal/channel/agentteam/mcp_tools_reactions.go`, `message-row/reactionReactors.ts`, `web/src/agentteam/workspace/shared/queryCache.ts`, `web/src/agentteam/workspace/shared/queryCache.ts` | `migrated`; browser/API/desktop MCP legacy reactor fields `deleted` | Reaction aggregate/realtime/reactor payloads carry canonical `AuthorProfile`; Web cache/display adapters and desktop MCP reaction summary consume profile identity. Legacy reactor sender fields remain only inside old daemon-compatible reaction receive metadata, not browser/API/MCP reaction records. |
| Reply-thread root receive metadata `senderName` | `internal/store/message/scope_api.go`, `desktop/internal/types/session.go` | `compat-only` for old daemon receive metadata | Reply-thread root metadata now carries canonical `authorProfile` and `authorName`; retained `senderName` is a compatibility alias for older desktop/daemon runtimes and new desktop prefers `authorName` before fallback. Removal condition: old daemon runtimes no longer parse reply-thread root metadata through `senderName`. |
| Owner/private source metadata used by UI (`metadata.sources`) | `web/src/agentteam/workspace/conversation/state/ownerPrivateDelivery.ts` | `compat-only` unless promoted by source/run lane | This path must not become the generic source model. If still needed, it remains a named owner-private adapter with a separate deletion condition. |
| Private-agent context contribution capability | `internal/api/task/api_collaborators.go`, `internal/store/taskboard/private_agent_context_contribution_capability.go`, `web/src/agentteam/api/tasks/collaboration.ts` | `migrated` | The lightweight composer/send-confirmation capability is named contribution capability, not sender capability; it answers whether the current viewer can contribute context without scanning messages. |
| Internal read-model/domain `Sender*` names outside DB/archive/old-daemon adapters | `internal/store/message/*`, `internal/store/app/*`, `internal/store/rewind/*`, `internal/store/reaction/*` | `migrated` | Hot/read-model structs and SQL aliases use author/actor naming. Physical DB columns, archived chunk fields, and old daemon receive/history DTO fields remain compat-only. |

### Current canonical surfaces that downstream work should reuse

| Surface | Files to audit | Contract |
| --- | --- | --- |
| Message provenance for quote/forward/current explicit sources | `scope_message_sources`, `internal/store/message/*`, `specs/message/scope-model.md` | Reuse or extend typed source/ref concepts instead of inventing metadata blobs. |
| Non-message runtime delivery source refs | `DeliveryIntent`, `scope_fanout_outbox.event_ref_type/event_ref_id` | Delivery source refs explain handoff, not visible author. |
| Workflow Run visible contributions | `workflow_run` contribution source, Workflow Run effect message writer, `WorkflowContext` metadata | First-class Workflow Run effects create ordinary visible messages. Native `workflow_run` source facts explain the run/effect; typed `WorkflowContext` carries callback authority. The retired runtime-only `workflow_command` handoff is not a source kind or delivery source ref. |
| Service member identity for installed apps | `specs/identity/identity.md`, `specs/app/app-system.md`, app installation/member code | Visible app/provider output must resolve through service actor/profile. |

### Explicitly deferred surfaces

| Surface | Reason |
| --- | --- |
| `task_dispatch`, `task_discussion`, task timeline/activity | Task product refactor is intentionally deferred until the task-owned migration path is opened and stabilized. T1/T2 must not silently migrate task semantics unless a separate task gate is opened. |
| Bot product model | No bot service kind enum or schema value is registered until a complete product model and guardrail coverage exist. |

## Review checklist

Every author/source substrate implementation report must include:

1. The rows in this inventory that changed.
2. New rows for any old surface discovered during implementation.
3. For each `deleted` row: the test or guardrail that would fail if the old path returns.
4. For each `migrated` row: the canonical helper/owner and positive behavior test.
5. For each `compat-only` row: the named consumer, derivation source, and removal condition.

Rows not present in this inventory are not implicitly allowed. If a reviewer finds
an old surface that is not listed, the implementation must either remove it or add
it here with a disposition before the change is accepted.
