ADR 0214: Linear wakes require attribution and rules
Status: accepted (James / Clankie lead, 2026-10-02, VUH-1549).
Amended by James in VUH-1678,
2026-10-04: signed rule-passing webhooks wake one ordinary configured chat,
global-default by default. This supersedes
ADR 0218's Linear work-routing extension
and the separate inbox protocol. Amends
ADR 0189 (Linear echoes).
Amended by James in VUH-1743,
2026-10-06: project activity goes to its configured lead chat, otherwise
global-default with the project named; provider notifications are marked read
only after that target chat confirms receipt of the original wake.
Amended by James, 2026-10-10: only events that wake a chat are written to it.
Nonmatching signed activity stays in the service log (linear.webhook with its
decision) and keeps its replay receipt, but no longer appears in any chat.
Amendment — one ordinary chat receives signed Linear activity, 2026-10-04
Verified webhook activity is the input to the existing VUH-1549 actor/type rule
engine. Each qualifying event wakes one ordinary global conversation. Explicit
linearWebhook.projectChats entries map verified Linear project UUIDs to existing
lead chats. Unconfigured projects and removed lead chats go to global-default
with the project named. Nonproject activity uses linearWebhook.wakeConversationId,
default global-default. The lead chooses any delegation. Assignees, parent-update
authors and native workers do not select destinations. Signed project fields or
bounded connected reads of the exact signed issue/update parent supply project
context; URL slugs cannot establish project identity. The external journal and
delivery history record the project and selected destination. A chat's attached
native operator receives the wake through its existing channel.
The compact event carries the issue ID and title where available, what changed,
who acted, and the Linear link. A Comment hook can contain only the issue UUID.
Retained signed Issue context or a native connected get_issue lookup bounded
to one second can supply its display identifier/title. Fetched display context never changes the
signed actor/resource/change authority. If the title remains unavailable, the
event says Title unavailable and retains its signed UUID/link; it is not dropped.
Bursts coalesce into one wake. Nonmatching signed
activity is logged with its decision and never enters a chat (2026-10-10). The special
Linear inbox conversation, inbox handoff protocol, and old notification checkpoint
are retired. Upgrade drops retained unread inbox items once with a log entry;
old inbox state never schedules a fresh turn.
The 2026-10-06 read-status amendment gives each offered batch a host-issued
original wake ID and an exact native fingerprint/recipient receipt prepared
before delivery. The target chat confirms its original through
linear_wake({ action: "received", wakeId }). The tool derives the chat from live
conversation authority and verifies the current native recipient and exact
outbox original. Another chat or changed occupant cannot confirm it; a transport
ACK alone leaves notifications unread. Pi conversations use their own admitted
turn receipt. Only events actually included in the batch are eligible.
After confirmation, a bounded reader correlates inbox notifications with signed event identities. Unique full or eight-character comment anchors permit delayed inbox creation. Resource-only assignment/reaction matches retain conservative action-time checks; unknown or ambiguous matches stay unread. Read claims persist before dispatch. Lost/error responses remain uncertain and settle by read-only inbox observation, without repeating the mutation. The reader uses the shared background request budget, scans at most 20 pages and retries delayed notifications for ten minutes. Durable references resume that bounded window after restart. It never generates wakes or replays native delivery.
Rules and the target are non-secret settings that Clankie can change himself
through linear_wake, the clankie linear wake / linear target / linear routes
CLI, and their authenticated API. Follow Linear exposes rules, the default chat
and project lead chats. linear deliveries records destinations and consumption
receipts. Defaults select signed comments, mentions, assignment/delegation to
the connected app and reactions on its comments with actor selector owner.
New installs start with empty owner ID/email lists; existing configured identities
remain. Signed owner identity must be configured before the selector can match. This
changes the earlier default of an empty owner list and all nonexcluded types.
Signed user ID/email evidence identifies the human. Display names and notification
subtitles do not. Own-write suppression takes precedence over the selectors;
Clankie's connected account and attributed workers remain quiet. Production
wakes require the connected account identity in the signed event's workspace;
lookup failure or workspace mismatch is logged without entering a chat, with a
decision of identity_unavailable or account_workspace_mismatch.
Local webhook readiness alone is not proof of account lookup or delivery health.
Raw-body signature verification, signed timestamp tolerance, delivery dedupe and restart replay safety remain. The attribution journal and exact write receipts retain their provenance and own-write suppression responsibilities; actor-less recipient notification polling is no longer the wake path. Rule or target edits never promote logged, nonmatching events. Following remains opt-in and requires the stored webhook URL and broker-held secret. Incoming events remain untrusted context, never new authority.
Amendment — recover undelivered activity to the current lead, 2026-10-20
VUH-2511: an unread backlog must not
remain owned by a removed project route or a remembered, disconnected native
head. Before offering retained activity (including after restart), resolve its
verified project against current lead routes. An absent or unavailable project
receiver falls back to global-default. Nonproject activity uses the current
default target, with the same unavailable-receiver fallback.
Move only activity not already carried by an admitted original. Preserve event
IDs, signed context and notification-receiver identity. Persist the destination
copy before its source resolution; a retained destination event or original
receipt makes recovery idempotent. Expose every source resolution as rerouted
or already_handled through the existing deliveries API/CLI. An uncertain native
take stays with its original recipient; it never becomes recovery permission.
Missing receivers produce one durable notice in the default lead chat, rather
than a failed turn for each offer. Retry admission is bounded to one preflight
per coalesced burst or restart, plus a proven returning native poll; there is no
blind retry timer. If the fallback receiver is also absent, retain the backlog
and expose its pending event IDs. Nondefault unavailable chats are labelled
(inactive) without removing their history; a returning receiver restores the
title if it has not been edited. Follow off still consumes queued wakes without
running or transferring them. Pending Linear activity and missing-receiver
histories are protected from automatic conversation pruning. Provider
notifications require the same original
target confirmation as before.
Historical decision — 2026-10-02
The following records the original notification-correlation design. The amendment above replaces its destination, polling path, inbox protocol and default rules; the signed attribution and trust boundaries remain.
Context
The connected Clankie app's Linear MCP notification response has no actor field. Testing an absent actor against the connected account admitted every notification, including bursts of self-authored subscriptions. Notification subtitles and display names cannot establish whether James or a worker acted. The signed webhook carries actor identity and type (Linear's envelope).
Decision
Attribute notifications before deciding to wake. Correlate the workspace, resource, comment when available, action timestamp and relevant revision kind against signed webhook history. Retain a bounded structured attribution index because inbox prompts are truncated for model context. Record exact self echoes in that index before the existing receipt suppression; inbox collection and acknowledgment remain unchanged. Different or missing actors among candidates mean unknown, never a best guess.
Settings beside the follow switch select owner humans, any human, the connected
account/app and workers, or named IDs, and filter notification types. Defaults select
only explicitly configured owner IDs and exclude issueSubscribed. There is no
portable default owner ID; installation setup must name the owner. Signed user type
is required for human selectors, and own-account or worker evidence takes precedence.
No name, email or subtitle establishes ownership. Unknown attribution never wakes.
The CLI, authenticated API and Follow Linear menu share the schema. Changes apply on the next notification decision without restarting. Changing rules never promotes old passive notifications or revises an already accepted wake. Follow off remains the control for suppressing queued turns. Collection stays independent of both filters. No OAuth token crosses from its MCP audience to GraphQL.
Consequences
Missing hooks, ambiguous simultaneous changes, or history outside the index's bounds can miss a desired wake. Those notifications remain readable. This is preferable to waking on an unattributed app echo. The index retains seven days / 2,000 records; correlation allows five seconds before or one second after the notification time. Notifications processed before their hook remain quiet; no periodic reconciliation or retroactive wake queue is added. Owner and coordination preferences are settings, not instructions repeated in Clankie's prompt.
