Skip to content

Design: publisher-scoped notice visibility — let a route read the state of notices it published #460

Description

@ScriptedAlchemy

Problem (design question)

A route can publish a notice but never learn what became of it. (await agent()).notices.read() returns only the deliveries attempted on the current invocation, and inbox() returns only pending notices addressed to the current principal. Neither shows the publisher the state of the notices it published (pending / attempted / acknowledged / expired), and no route-level surface shows the ledger as a whole. AgentNoticeLedger.read() does exist, but only the generated wiring holds a ledger — routes get the request-bound handle.

Concretely, examples/worktree-proximity's coordinator/status tool cannot report "N notices still pending for actor X"; it reports topology facts only.

This is deliberately how #264 landed ("cross-recipient reads are structurally impossible"), so this issue is a design question, not a bug: is there a scope narrower than "whole ledger" that a coordinator legitimately needs?

Evidence (main @ 284141958)

  • packages/rsc-runtime/src/notices/contract.ts:179-185AgentNoticesHandle = { acknowledge, inbox, publish, read }.
  • packages/rsc-runtime/src/notices/ledger.ts:393-396, 471-476read() returns deliveries computed at admission for this invocation only; :284-296inboxProgram filters state === 'pending' && recipientMatchesPrincipal(...).
  • packages/rsc-runtime/src/notices/ledger.ts:487-490AgentNoticeLedger.read() returns the full snapshot, but is not reachable from a route.
  • examples/worktree-proximity/src/mcp/coordinator/tools/status.tsx:36-64 — result carries actors, activeActivities, refusals, revision; no notice counts.
  • examples/worktree-proximity/README.md:103-105: "(await agent()).notices.read() exposes only deliveries attempted for the current invocation. The coordinator status therefore reports topology facts only and does not claim a whole-ledger pending count."
  • Add recipient-aware notices and capability-gated delivery #99 stage-3 comment (feat(notices): expose the recipient-scoped MCP inbox resource (#99 stage 3) #264): "cross-recipient reads are structurally impossible (recipient matching runs against the invocation's observed identity axes only)".

Proposed shape (pick one; both are small)

A. Publisher-scoped read (preferred). Record the publishing principal on the notice at publish() (identity axes + lineage.conversation, no content duplication) and add

interface AgentNoticesHandle {
  /** Notices this principal published, with their current state and receipts; never other publishers' or other recipients' notices. */
  published(): Promise<readonly AgentNotice[]>;
}

Authorization gets a new phase: 'published' so the authorizer can still refuse. This gives a publisher "was my warning attempted / acknowledged?" without any cross-recipient exposure.

B. Aggregate counts only. notices.summary(){ pending, attempted, acknowledged, expired } for notices whose recipient is under the caller's lineage root. Coarser, no content, but depends on the lineage-addressed recipient axis (sibling issue).

Explicitly not proposed: exposing AgentNoticeLedger.read() on the request handle.

Out of scope / related

Acceptance

coordinator/status in examples/worktree-proximity reports, for the publishing actor, how many of its proximity notices are pending vs attempted vs acknowledged; a route-unit test in tests/route-unit/routes.test.ts publishes from actor A, admits on actor B's next event, and asserts A's published() shows attempted while B's inbox() is empty and A's inbox() never showed the notice.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestquestionFurther information is requestedruntimeRuntime context, state, rendering, or execution

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions