Skip to content

[agora-fake-strategist] Track targeted event status per recipient #8

Description

@kelos-bot

Latest verdict

  • Status: VALID
  • Checked at: 2026-09-02T12:04:40Z
  • Evidence: Remote default branch main remains 2f3ddab. A live public-API probe on that commit showed both recipients initially receiving one instruction, then agent A's global done update removing it from agent B's open inbox immediately and after restart/JSONL replay. The current event model, status update and replay, and inbox filtering remain event-wide; issue #8 is unassigned with no comments, no issue or PR supersedes it, and make verify passes.

Area

New API, UI, or Deployment Capabilities

Candidate

Add recipient-scoped inbox status to targeted events, so each human or agent can acknowledge and complete a shared instruction independently without clearing it from every other recipient's inbox.

Why this should be the next strategic priority

Agora already supports fan-out: an event can target several agents, all is a wildcard target, and the reporting skill tells each agent to acknowledge and complete actionable inbox items. But lifecycle state is one scalar on the event.

That makes every multi-target instruction behave like an accidental single-consumer queue. The first recipient to mark it done, resolved, or rejected changes the event globally, so the item disappears from every recipient's actionable inbox. A coordinator also cannot distinguish partial handling from completion by everyone.

This blocks several high-value workflows at their foundation:

  • Parallel coding agents cannot independently accept and finish the same review, validation, or rollout instruction.
  • A human cannot broadcast a decision to all while retaining it for agents that have not acknowledged it.
  • CI, reviewer, and deployer handoffs cannot show which consumers have acted.
  • Teams must duplicate events per recipient, fragmenting the shared timeline and undermining multi-target events.

Integrations would inherit this ambiguity. Fixing recipient ownership first makes Agora's existing event and inbox model safe to extend.

Current behavior

  • Event has multiple Targets but only one Status.
  • UpdateStatus records the actor and overwrites that one status without considering recipients.
  • JSONL replay applies a status_change globally and does not retain actor-specific handling state.
  • Inbox filtering evaluates the event-wide status for every target.
  • The server exposes only the event-wide status endpoint, and the CLI always uses it.
  • The browser actions also update status globally for the selected actor.
  • The reporting skill requires every recipient to acknowledge and complete actionable inbox items, which is unsafe for multi-target and all events today.

The current behavior reproduces with one instruction:

POST /api/events
Content-Type: application/json

{
  "type": "instruction",
  "actor": "human",
  "thread": "release",
  "title": "Validate release",
  "targets": ["agent-a", "agent-b"]
}

Both open inboxes initially contain the event. When agent A uses the current status API:

POST /api/events/<event-id>/status
Content-Type: application/json

{"actor":"agent-a","status":"done"}

the result is:

agent-a open inbox before: [<event-id>]
agent-b open inbox before: [<event-id>]
agent-a marks done:         event.status=done
agent-a open inbox after:  []
agent-b open inbox after:  []
agent-b after restart:     []

The JSONL log contains actor: "agent-a" on one status_change, but replay applies it globally. The same failure applies to targets: ["all"].

Proposed design

Keep global lifecycle and add recipient handling

Retain the current endpoint and Event.status semantics for intentional event-wide transitions:

POST /api/events/<event-id>/status
{"actor":"human","status":"resolved"}

Add a recipient-scoped operation:

POST /api/agents/agent-a/inbox/<event-id>/status
Content-Type: application/json

{"actor":"agent-a","status":"done"}
  • Require the event to target the recipient directly or through all.
  • Reuse the existing status vocabulary instead of adding a parallel lifecycle.
  • Store both recipient and actor; they are normally equal, but retaining both preserves the audit trail when an operator acts on behalf of an agent.
  • Treat recipient state as coordination metadata, not authentication. Local, shared-token, and Kubernetes deployments keep their existing bearer-token behavior.

For an agent inbox, derive effective status in this order:

  1. A terminal global status (done, resolved, or rejected) closes the event for everyone.
  2. Otherwise, use that recipient's latest status when present.
  3. Otherwise, fall back to the global event status.

Apply open=true and repeated status filters to effective status before applying limit.

Keep responses event-shaped and add optional fields only:

{
  "id": "evt_...",
  "status": "open",
  "targets": ["agent-a", "agent-b"],
  "recipient_status": "done",
  "recipient_statuses": {
    "agent-a": "done",
    "agent-b": "acknowledged"
  }
}

An inbox response needs only recipient_status for its named recipient. Timeline and detail responses may include recipient_statuses so a coordinator can see partial completion. status remains the canonical global lifecycle; no automatic global closure is required.

Persist receipts additively

Append a new JSONL record kind:

{
  "kind": "recipient_status_change",
  "recipient_status": {
    "event_id": "evt_...",
    "recipient": "agent-a",
    "actor": "agent-a",
    "status": "done",
    "created_at": "..."
  }
}

Replay it into an event ID -> recipient -> latest status index. Existing event and status_change records retain their current meaning, so existing JSONL files need no migration. For all, create recipient entries lazily as agents act; Agora does not need an agent registry.

Update first-party consumers

  • Add agora status --recipient agent-a <event-id> done; keep unscoped agora status global for compatibility and coordinator use.
  • Change skills/agora-reporting to use recipient-scoped status for work obtained from the agent's inbox.
  • When the browser's selected actor is a direct or all target, make acknowledge and done actions recipient-scoped and render that actor's effective status. Keep global closure as an explicit coordinator action.
  • Do not add environment variables, per-agent tokens, or Kubernetes resources.

Compatibility

  • Existing API clients and the global status endpoint retain their behavior.
  • Existing event fields, status values, target matching, and forward polling remain valid.
  • Added response fields are optional and can be ignored by old clients.
  • Existing JSONL logs replay unchanged; missing recipient state falls back to global status.
  • AGORA_URL, AGORA_AGENT, AGORA_THREAD, and AGORA_TOKEN keep their meanings.
  • A global terminal update remains available when a coordinator wants to close the event for everyone.

Acceptance criteria

  • An open instruction targeted to agent-a and agent-b appears in both open inboxes.
  • After agent A acknowledges and completes it through the recipient endpoint, it leaves agent A's actionable inbox but remains actionable for agent B.
  • Agent B can independently acknowledge and complete the same event.
  • The same isolation works for all, with recipient records created lazily.
  • A terminal global update still removes the event from every recipient's open inbox.
  • Inbox open and repeated status filters use recipient-effective state before applying limit.
  • Recipient changes survive restart, while a fixture containing only existing JSONL record kinds replays identically.
  • Updating a recipient not targeted by the event returns a focused 400 response.
  • The CLI and browser use recipient-scoped updates when handling targeted inbox work.
  • The reporting skill's acknowledge/complete loop cannot clear another recipient's item.
  • Tests cover two explicit recipients, all, global closure, filtering before limit, persistence/replay, and unchanged existing-client behavior.
  • make verify passes.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions