Skip to content

Slack context reads ​

The main agent can ask tools for bounded Slack context without treating messages or files as instructions. The requesting person and the selected read account must both be able to read each source. This is a separate, opt-in capability: merely defining the tool does not give any agent access to it.

  • Code: src/channels/slack/readAccess.ts, src/channels/slackChannelDirectory.ts, src/core/authz/types.ts, src/core/authz/policy.ts, src/core/references/sourceReceipt.ts, src/core/dispatch/contextAccess.ts, src/core/budgets.ts, src/core/runLedger/sessionLog.ts, src/core/references/receipts.ts, src/core/references/denial.ts, src/core/references/types.ts, src/core/dispatch/seed.ts, src/core/runLedger/ledger.ts, src/core/runLedger/inMemory.ts, src/core/runLedger/writeThrough.ts, src/core/runLedgerWorker.ts, deploy/cloudflare-memory/worker.ts, src/core/testing/slackSources.ts, src/tools/slackContext.ts, src/tools/runnableTool.ts, src/tools/toolsets.ts, src/channels/slack/context.ts, src/channels/slack/directAudience.ts, src/channels/slack/references.ts, src/channels/slack.ts, src/channels/slackCatchUpStatus.ts, src/core/dispatch/privateAudience.ts, src/core/dispatch/slackContextBinding.ts, src/core/dispatch/references.ts, src/core/dispatch/admission.ts, src/core/dispatch/provision.ts, src/core/dispatch/record.ts, src/core/dispatch/run.ts, src/core/dispatch/runLoop.ts, src/core/dispatch/reply.ts, src/core/dispatcher.ts, src/core/privateMainEvent.ts, src/core/runRegistry.ts, src/core/refusal.ts, src/core/resumeLaunch.ts, src/core/runLedger/inboxMessage.ts, src/core/runLedger/types.ts, src/core/threadAdmission.ts, src/core/types.ts, src/index.ts, src/channels/slack/threadTurns.ts, src/channels/slack/attachments.ts.
  • Tests: src/channels/slack/readAccess.test.ts, src/channels/slackChannelDirectory.test.ts, src/core/dispatch/contextAccess.test.ts, deploy/cloudflare-memory/sessionLog.test.ts, src/tools/slackContext.test.ts, src/channels/slack/context.test.ts, src/channels/slack/directAudience.test.ts, src/channels/slack.test.ts, src/channels/slackConnectedHook.test.ts, src/channels/slackCatchUpStatus.test.ts, src/core/dispatch/admission.test.ts, src/core/dispatch/provision.test.ts, src/core/dispatch/run.test.ts, src/core/dispatch/privateAudience.test.ts, src/core/dispatch/slackContextBinding.test.ts, src/core/dispatch/runLoop.test.ts, src/core/dispatch/reply.test.ts, src/core/dispatcher.test.ts, src/core/runRegistry.test.ts, src/core/resumeLaunch.test.ts, src/core/runLedger/inboxMessage.test.ts, src/core/threadAdmission.test.ts.
CriterionProof
An optional admin-managed read-only Slack user connection reads linked public channels without joining them. It adds no subscriptions or per-user sign-in. The bot retains current-thread reads and delivery; without a read connection, a pending Slack identity lookup does not block startup. Writable tokens, bot tokens and foreign-workspace read connections fail before use. A named startup error retains the provider or validation cause; private source reuse still needs fresh requester membership.[unit] src/channels/slack/readAccess.test.ts::workspace Slack read access::*; src/channels/slack/context.test.ts::Slack context adapter::reads a public source without adding the bot to its channel
Automatic references and source tools use one access policy. A source requires read-account access. Workspace public reads require a full requester in that workspace; member-only read accounts and private sources require fresh requester membership; its sharing state survives in the source receipt. A changed audience invalidates reuse. Source tools fetch linked content once. Context from another channel cannot be exported into a shared origin.[unit] src/channels/slack/context.test.ts::Slack context adapter::reads a shared public source through both entry points and retains its audience for reuse, ::does not read a shared source for a nonmember or export internal context to another shared channel, ::fetches linked source content once and returns its file addresses, ::reads a private source only into a verified requester DM and drops it after membership changes during fetch, ::revalidates a shared source from its original verified DM after the saved stamp is cleared; src/core/dispatcher.test.ts::dispatch::shared source evidence survives admission and answer checks on both intake paths; src/core/dispatch/contextAccess.test.ts::message-bound context access::checks the current destination before reusing a public source; src/channels/slackChannelDirectory.test.ts::SlackChannelDirectory.isMember — the person's own channel set::a fresh source check observes membership loss without accepting the cached allow
An isolated unavailable Slack audience read may be checked once more before a private answer. Publication requires a fresh complete positive result; a persistently unavailable, changed, shared, foreign or relayed audience still fails closed. Three successful private reads do not make an unrelated failed answer check permanent when its fresh retry verifies the same requester DM.[unit] src/channels/slack/directAudience.test.ts::Slack direct audience verification::rechecks one unavailable provider read and requires a fresh positive result, src/core/dispatch/privateAudience.test.ts::private audience publication gate::keeps a verified private answer after three reads and one recovered unavailable check
A toolset declaring slack_context defers automatic linked text and prior-run file catalogues (including recall lists) to the bound source tool, regardless of references.enabled. Requester URLs survive unchanged. Public and unavailable sources yield independent results; accepted source/file receipts commit before model exposure. Missing binding, failed persistence and changed destinations never fall back to preload.[unit] src/core/dispatcher.test.ts::dispatch::defers configured main references to committed source reads, ::does not fall back to automatic references without a bound source capability, ::excludes prior-run file catalogues from main source intake, ::withholds source content when the receipt write fails, ::refuses source initialization failure before starting the main model, ::refuses unverified main destinations before any reference read
The tool requires an injected, requester-bound Slack capability; unknown kinds and malformed inputs fail without a read.[unit] src/tools/slackContext.test.ts::slack_context tool::refuses a missing capability or invalid request before reading, src/channels/slack/context.test.ts::Slack context adapter::requires the authenticated Slack requester at capability construction
A source denial retains a closed reason and supported recovery action through the model boundary. A parsed link to another DM is categorically forbidden regardless of membership; its recovery asks for shareable text or a file here, never a permission change. Known temporary failures may suggest retry; unknown access and fetch failures stay generic and disclose no source existence or provider error. A refused source does not veto authorized public evidence.[unit] src/channels/slack/context.test.ts::Slack context adapter::keeps categorical source policy separate from temporary and unknown failures, ::does not expose hidden source classification in denial recovery, ::retains retry recovery for the reference rate limit, src/core/dispatcher.test.ts::dispatch::preserves public evidence and categorical source recovery through the model boundary
Current-thread and nearby-channel reads stay on the request's channel, keep a small newest-first window, label the source, and fence its text as data. A private channel requires requester membership and bot membership before the read.[unit] src/channels/slack/context.test.ts::Slack context adapter::reads bounded current and nearby text only from the origin channel, ::refuses private-origin reads without requester and bot membership proof
Long threads page to their newest context; a file target on a later page remains reachable. If the page bound is exhausted, the tool refuses rather than presenting an old partial view.[unit] src/channels/slack/context.test.ts::Slack context adapter::keeps the newest page and finds a file on a later page, ::finds a public linked thread and its file on a later page, ::refuses a thread beyond the page bound instead of quoting an old partial view
A linked thread must be a permalink from this workspace. The existing reference gate verifies requester standing, read-account access and channel authority. The configured workspace read account can read public sources without joining; ordinary workspace membership is required for the requester. Cross-channel private sources require that membership and a verified requester DM destination. Cross-channel DM links are refused; a link in the origin channel remains readable.[unit] src/channels/slack/context.test.ts::Slack context adapter::uses the existing reference gate and refuses cross-channel private or foreign links
The post-fetch channel check accepts sparse public-channel metadata while requiring read-account access and channel visibility; sharing is reported independently, and direct conversations remain unavailable as linked sources.[unit] src/channels/slack/references.test.ts::SlackConversationReader.classifyConversation — closed and fresh::reports shared audience facts and keeps direct conversations unavailable as linked sources
Linked file metadata correlates to the exact Slack message timestamp, never a rounded millisecond that may identify another message.[unit] src/channels/slack/context.test.ts::Slack context adapter::attaches linked file metadata by exact Slack timestamp
A file read names a message permalink and a file ID found on that authorized message. Secret-shaped, missing, oversized and unsupported files fail before download; accepted text is capped and fenced, and accepted image or PDF bytes retain the attachment caps.[unit] src/channels/slack/context.test.ts::Slack context adapter::reads only a file attached to the authorized message under caps
A direct requester DM requires fresh conversations.info proof of an exact one-person IM with is_org_shared: false, no affirmative shared/external/pending flag, and fresh auth.test and users.info proof that the peer belongs to the installed workspace. The proof accepts Slack's sparse IM response and U or W requester IDs; missing team identity, a foreign peer, or a failed API call denies access. The claim rides accepted and replayed follow-ups, but each private action revalidates it. The app needs im:read, im:history, and users:read; missing installed scopes appear in startup diagnostics and /healthz even with catch-up disabled.[unit] src/channels/slack/directAudience.test.ts::Slack direct audience verification::accepts only a fresh unshared DM with this requester, src/channels/slack.test.ts::SlackIO direct audience::revalidates its exact requester DM before private output, src/channels/slack.test.ts::receiveSlackMessage — the intake gate (docs/reference/specs/slack-channel.md item 15)::stamps only a freshly verified requester DM at intake, src/core/dispatch/admission.test.ts::admit — the thread admission claim::a requester DM question followed by fix it keeps one live run and its private address through durable delivery, src/core/runLedger/inboxMessage.test.ts::durable inbox — staged references (record 0033)::carries a verified DM address across replay but drops malformed claims, src/channels/slackConnectedHook.test.ts::connected-hook wiring (Bolt-level harness)::slack.catchUp.enabled: false still diagnoses required IM scopes without scanning, src/channels/slackCatchUpStatus.test.ts::missingBotScopes (pure comparison against the adapter's required set)::the required set is exactly what the adapter needs
The main agent sees slack_context only in the resolved requester's verified direct Slack conversation. The source read checks that same destination before and after the fetch. A permalink or file enters through that tool, not an unproved prompt seed. Any run carrying the private audience claim suppresses model-authored card progress and rechecks the destination before the answer event and again before the channel reply, including private worker reads without a slack_context binding. Shared channels, relays, other agents and missing reply destinations have no Slack read capability.[unit] src/core/dispatch/privateAudience.test.ts::private audience publication gate::requires a matching claim and fresh adapter proof; src/core/dispatch/slackContextBinding.test.ts::Slack context run binding::registers Slack reads only for the requester's verified direct main conversation, ::rechecks the same one-person destination before the model answer can publish, ::refuses an externally shared or unverifiable D conversation before tool registration; src/channels/slack.test.ts::SlackIO direct audience::names only the requester's direct-message destination; src/core/dispatch/runLoop.test.ts::runLoop — the model turn and everything that rides on it::offers the bound Slack read only to the main DM run and suppresses an answer after its audience changes; src/core/dispatcher.test.ts::dispatch::reads a permalink and its file through the verified DM tool without seeding linked text; src/core/dispatch/reply.test.ts::deliverAnswer — the answer reaches the thread::rechecks a Slack read destination immediately before the channel reply, ::drops the private answer if the DM becomes shared while the card closes, ::rechecks a stamped private run without a Slack context binding before its reply, ::keeps a revoked private run sealed even if the DM verifier later answers true
A private DM's validated address claim survives the reserved and live run rows and returns on resume. A prior private tool result in the resumed transcript cannot reach model-authored cards, the answer event or the reply if fresh Slack verification now fails. Malformed, relayed or program-sent claims are dropped.[unit] src/core/dispatch/provision.test.ts::reserveRun — the ledger reservation before the attach::stores only a matching requester DM provenance on the reserved row, src/core/dispatch/run.test.ts::claimRun — the ledger claim once the prompt exists::keeps verified requester DM provenance on the live row, src/core/resumeLaunch.test.ts::the pure pieces::resumeMessage restores only a matching private DM provenance from the durable run row, src/core/dispatch/runLoop.test.ts::runLoop — the model turn and everything that rides on it::keeps a resumed private Slack result out of cards, answer and reply after its DM becomes shared, src/core/runLedger/inboxMessage.test.ts::durable inbox — staged references (record 0033)::carries a verified DM address across replay but drops malformed claims
A linked source is freshly reclassified after its asynchronous text or file fetch and before its data reaches the model. The post-fetch check bypasses the classification cache and rechecks requester standing.[unit] src/channels/slack/context.test.ts::Slack context adapter::drops linked thread and file results when fresh source eligibility changes during the fetch, ::refuses an external or unverifiable D origin before reading and after it changes
A later Slack DM turn must pass fresh requester and private-destination verification before the operator, channel history or saved session can read earlier private tool results. An unverifiable or newly shared DM receives an actionable refusal without exposing the saved result, including when the saved result came from another private tool.[unit] src/core/dispatcher.test.ts::a follow-up seeds from its session (docs/reference/specs/session-log.md item 9)::stops an unverified later Slack DM before reading a prior private session, src/core/dispatch/reply.test.ts::deliverAnswer — the answer reaches the thread::rechecks a stamped private run without a Slack context binding before its reply
A later verified requester DM reuses Slack data only through trusted, complete session metadata. Native reference and tool reads record requester, origin, destination, source, exact consumed message IDs and hashes, file identity and full content hash, and explicit bounded coverage before model delivery. Revalidation fetches every consumed ID under fresh source and destination guards: append and a 1–20 to 2–21 window shift pass when all original messages remain unchanged/readable; edits, deletion, missing IDs, reassociation or inaccessible sources fail closed. Revalidation does not consume the ordinary reference-read allowance.[unit] src/channels/slack/context.test.ts::Slack context adapter::records consumed message identities and rechecks them after a window shift, ::rechecks nearby consumed IDs outside the newest window and rejects requester or destination changes, ::binds a file receipt to its original message, file identity and full content hash, ::rechecks a saved linked source after ordinary reference reads exhaust their quota; src/core/dispatcher.test.ts::a follow-up seeds from its session (docs/reference/specs/session-log.md item 9)::seals a saved linked result if its source is revoked after the new model turn starts
A private main DM follow-up may replay an earlier quoted Slack reference only after its typed receipt rechecks the current requester, source visibility and consumed message IDs/hashes without spending the ordinary read allowance. A revoked, changed or missing receipt excludes the old source bytes; a new admitted request can continue without them. Admitted source content is checked again before private publication.[unit] src/core/dispatcher.test.ts::a follow-up seeds from its session (docs/reference/specs/session-log.md item 9)::omits a revoked saved reference while admitting a fresh private follow-up, ::continues a private follow-up when a saved reference still matches the authorized source
A relayed, app-authored or foreign live follow-up permanently revokes the run's Slack read capability and private publication, even when the DM remains private. Only a verified direct follow-up from the same requester keeps it. The revocation watch lasts through answer delivery. A recovered run cannot reacquire the read capability because an earlier revocation may not be persisted.[unit] src/core/dispatch/privateAudience.test.ts::private audience publication gate::keeps only a direct follow-up from the same requester eligible for private reads, src/core/threadAdmission.test.ts::FollowUpInbox::observes accepted follow-ups before and after subscription, including only one copy of a durable item, src/core/threadAdmission.test.ts::FollowUpInbox — an untrusted follow-up revokes private publication::signals current and later observers once without feeding the model or a fresh turn, src/core/dispatcher.test.ts::a follow-up seeds from its session (docs/reference/specs/session-log.md item 9)::revokes a live private run for relayed and app-authored DM follow-ups before refusing session reads, src/core/dispatch/runLoop.test.ts::runLoop — the model turn and everything that rides on it::revokes Slack context permanently when a relayed follow-up enters the live run, src/core/dispatch/slackContextBinding.test.ts::Slack context run binding::keeps a source revocation sealed even when Slack still reports the DM private, ::registers Slack reads only for the requester's verified direct main conversation
A resumed private run still watches accepted follow-ups when it has no Slack read tool. If an indirect follow-up arrives while the DM remains private, one per-run revocation latch suppresses its model answer, event, card and channel reply.[unit] src/core/dispatch/runLoop.test.ts::runLoop — the model turn and everything that rides on it::seals a resumed private result after a relayed follow-up even while the DM stays private
Revocation during an awaited Slack audience check still blocks the private answer event and final reply. A bound Slack read also checks revocation after verification returns.[unit] src/core/dispatch/runLoop.test.ts::runLoop — the model turn and everything that rides on it::seals a resumed private answer when an unverified follow-up is refused during audience verification; src/core/dispatch/reply.test.ts::deliverAnswer — the answer reaches the thread::seals the card and reply if source trust is revoked during audience verification, ::seals the final reply if source trust is revoked during its last audience verification; src/core/dispatch/slackContextBinding.test.ts::Slack context run binding::keeps revocation sealed when it arrives during Slack audience verification
A recovered private run cannot prove that no indirect follow-up was already consumed before the crash, so it refuses before any saved plan or session reaches the model. Its private publication also stays sealed even if the DM still verifies and no follow-up remains pending. Its reply explains that the restarted run cannot safely reuse private source details and that a new message with the source in the requester's direct Slack chat, outside the sealed thread, starts a fresh check. A fresh private run starts open and keeps the separate DM-verification failure message.[unit] src/core/dispatcher.test.ts::a follow-up seeds from its session (docs/reference/specs/session-log.md item 9)::refuses a recovered private DM before a model can replay its saved conversation, src/core/dispatch/privateAudience.test.ts::private audience publication gate::fails closed on a recovered private run even when no indirect follow-up remains pending; src/core/dispatch/runLoop.test.ts::runLoop — the model turn and everything that rides on it::seals a recovered private result when a prior indirect follow-up was already consumed
A main agent's source text, tool names and results, answer, checklist and repository facts stay out of shared run pages, live subscribers, durable source events and public finished-record projections. The requester still receives the answer after a fresh private-DM check. Replayed events are sanitized before the live page sees them.[unit] src/core/runRegistry.test.ts::RunRegistry.create::keeps a main agent's Slack read private in live, replayed and finished views, src/core/dispatch/runLoop.test.ts::runLoop — the model turn and everything that rides on it::keeps a live Slack context result out of durable source events and the finished run record, src/core/dispatch/provision.test.ts::registerRun — the run's row on every surface before the attach::gives a main DM a generic run label and no private request or repo facts on the live page, src/core/dispatch/provision.test.ts::reserveRun — the ledger reservation before the attach::stores only a matching requester DM provenance on the reserved row, src/core/dispatch/run.test.ts::claimRun — the ledger claim once the prompt exists::keeps verified requester DM provenance on the live row
A verified private main DM's router reason and compound request text stay off the status card from its first frame, and out of the durable reservation, claim, and finished record. Repository facts and accepted operation targets stay out of private rows and records, including a reclaimed older row. Non-private main routes and repository facts remain visible.[unit] src/core/dispatch/provision.test.ts::openAckCard — the ack card the thread sees while setup runs::keeps router reason and compound request text off a verified private main DM card from its first frame, ::a routed run's label carries the route reason at debug alone…; src/core/dispatch/provision.test.ts::reserveRun — the ledger reservation before the attach::stores only a matching requester DM provenance on the reserved row, ::keeps repository facts on a non-private main run's reservation; src/core/dispatch/run.test.ts::claimRun — the ledger claim once the prompt exists::keeps verified requester DM provenance on the live row, ::keeps repository facts when a non-private main run claims its row; src/core/dispatch/record.test.ts::assembleRunRecord — the handoff on the record::omits a private main route even when a caller or replayed event supplies its reason and compound text, ::the reclaim's close drops an older private main row's route, src/core/dispatch/record.test.ts::registerFinishRecord — the finish record, written by the drain after the reply::omits repository facts from a verified private main run's finished record
Before deployment, the operator completes a read-only inventory of old main-agent Slack DM run rows, live and finished. Any such row holds rollout for separate cleanup; absence must be checked again at release. A deployed requester DM can then ask about a linked thread and a file, and the answer remains only in that DM.[gap] Release-time read-only inventory and production acceptance after merge and deploy.
Source provenance is never inferred from user/model prose or a rendered reference header. Legacy or missing metadata is unknown and refuses reuse. A complete empty receipt set permits ordinary marker text.[unit] src/core/dispatch/privateAudience.test.ts::private audience publication gate::does not turn literal reference headers or model prose into source receipts, ::revalidates only trusted receipts and leaves legacy or forged prose unknown; deploy/cloudflare-memory/sessionLog.test.ts::trusted source metadata in the session log::does not infer legacy completeness from forged transcript metadata
Existing session metadata retains cumulative dependencies across restart, compaction, transcript trimming and notepad use. Writes require the session's current run and generation; known metadata cannot clear a dependency, and no write reopens a revoked session. An older writer that appends without source acknowledgment makes the session unknown. Source bytes wait for an acknowledged metadata write; failure or the bounded receipt cap refuses delivery without evicting receipts.[unit] src/channels/slack/context.test.ts::Slack context adapter::persists native and tool read receipts through restart, compaction and a truncated log; src/core/dispatch/slackContextBinding.test.ts::trusted source receipt persistence::waits for the receipt write before delivering content and refuses a failed write, ::fails closed at the cumulative receipt cap without evicting dependencies; deploy/cloudflare-memory/sessionLog.test.ts::trusted source metadata in the session log::fences both run and generation and preserves receipts across trimming and owner restart; src/core/runLedgerWorker.test.ts::WorkerRunLedger::requires explicit source metadata acknowledgment from the memory worker; deploy/cloudflare-memory/sessionLog.test.ts::trusted source metadata in the session log::invalidates complete metadata when an older writer appends without source acknowledgment
Dropping dynamic external-source prompt turns never drops cumulative Slack dependencies. A monotonic session flag from typed GitHub/MCP/plane/worker tool calls preserves the existing fresh-source replay restriction through compaction and tail cuts; old external text, summaries and notepad content are not replayed.[unit] src/core/dispatcher.test.ts::a follow-up seeds from its session (docs/reference/specs/session-log.md item 9)::retains Slack receipts when a dynamic source discards replayable prompt turns…; deploy/cloudflare-memory/sessionLog.test.ts::trusted source metadata in the session log::preserves the dynamic-source replay restriction after its tool row leaves the tail

Saved context admission ​

A complete context envelope that passes the current source reader admits saved notes, compacted summaries and typed child context without a second shape-based replay veto. The private destination check still applies. Slack receipts alone do not prove the other inputs to a summary: legacy or incomplete full provenance remains unavailable, while independently proved context can continue. Internal finished records may retain original source-action evidence for revalidation; public run projections exclude these archives and context envelopes.

CriterionProof
Full current validation admits compacted and inherited context while preserving the private-destination check; absent or denied validation remains unproved.[unit] src/core/dispatch/mainAudience.test.ts::main conversation source audience::uses the checked full context for compacted notes and handoffs while keeping the private audience gate

Retained-source bounds ​

A session retains at most eight receipts, 80 message hashes counted across receipts, and 32 KiB of serialized metadata. Repeated identical receipts are deduplicated; changing reads accumulate dependencies. The separate metadata allowance is below the session log's minimum 16 MiB row/attachment budget. Exceeding any bound refuses the new content and seals reuse; it never evicts earlier dependencies. Start a new top-level DM message with the source to establish fresh context. Legacy or revoked sessions follow the same reset path.

Synthetic adapter checks measure these retained nearby-read sizes: one receipt of 20 messages uses 2,715 bytes and 32 Slack calls; four of 20 use 10,101 bytes and 128 calls; eight of 10 use 11,869 bytes and 176 calls. These count fresh source/requester checks and exact-ID reads. The binding adds two destination checks per receipt plus a final check (three Slack calls each). A full pass is bounded by 227 Slack calls, plus at most eight bounded 2 MiB file downloads, before retries. Each pass has a 30-second fail-closed deadline inside the existing three-minute write-up allowance; timeout starts no further receipt. An already running adapter check may finish, but its answer cannot authorize publication. Real Slack latency and rate limiting are not proved by these synthetic counts.

CriterionProof
Retained sources have byte, receipt and message bounds without dependency eviction; synthetic sizes and adapter request counts stay within the documented limits.[unit] src/channels/slack/context.test.ts::Slack context adapter::bounds metadata and adapter requests…; src/core/dispatch/slackContextBinding.test.ts::source metadata bounds::rejects oversized metadata and cumulative message hashes without truncating them; src/core/dispatch/slackContextBinding.test.ts::trusted source receipt persistence::fails closed at the cumulative receipt cap without evicting dependencies
A source check exceeding the per-pass deadline refuses reuse and starts no subsequent receipt, even when the pending check eventually succeeds.[unit] src/core/dispatch/privateAudience.test.ts::private audience publication gate::refuses a timed out source check and never starts another receipt
Source-bearing transcript, attachment and notepad writes require the owning run ID as well as its generation, including when two runs share one process generation.[unit] deploy/cloudflare-memory/sessionLog.test.ts::trusted source metadata in the session log::rejects delayed transcript and notepad writes from a previous run in the same generation
The per-read receipt bound equals the native reference window of 50 messages; the cumulative session limits still apply.[unit] src/channels/slack/context.test.ts::Slack context adapter::persists every message from the full native reference window before delivery
A withheld answer records a closed audience cause after the final check; delivery of a safe refusal remains distinct from publishing model content.[unit] src/core/dispatch/reply.test.ts::deliverAnswer — the answer reaches the thread::records the typed source refusal after the card closes without publishing model content
Direct-DM verification returns a closed refusal code: explicit Slack denial is distinct from absent facts or an unavailable API; no provider detail enters the result.[unit] src/channels/slack/directAudience.test.ts::Slack direct audience verification::distinguishes a changed audience from unavailable verification without returning private facts
An earlier withheld answer remains withheld when a later check succeeds; neither model text nor the PR note returns to the chat.[unit] src/core/dispatch/reply.test.ts::deliverAnswer — the answer reaches the thread::retains earlier answer withholding when the last check passes and omits the PR note
A failed pre-card private check latches its first code and commits the fenced state write before card close. Coding and main replies remain withheld if membership recovers, and a card-close error cannot erase the diagnostic. Ownership loss stops the card, reply and old owner's record; unavailable storage still permits only a safe refusal.[unit] src/core/dispatch/reply.test.ts::deliverAnswer — the answer reaches the thread::latches the first private check before card close…, ::retains the first private refusal when card close throws before the reply, ::checks ownership before closing a withheld card…
A latched answer-event refusal awaits its durable state write before card close even if the private check recovers. Card errors retain the first cause, ownership fences stop card/reply/old-owner record, and unavailable storage never restores model text or PR notes.[unit] src/core/dispatch/reply.test.ts::deliverAnswer — the answer reaches the thread::awaits the answer-event refusal before card close…