Skip to content

mcpx: Phase 2 of #1675 — broker retention bound + summary-only elicitation events #1677

Closed

Description

@dezren39

Phase 2 of design #1675 (mcpx elicitation broker hygiene). Design doc: files/designs/code-mcpx-elicitation-broker-hygiene.md in the keep-the-four-repos-healthy goal workspace. Gated on Phase 1 (the owner-only store issue of #1675): same open sequence and store files.

Scope

Bound the broker store's retention and stop broadcasting question/answer content on the event bus.

  • Retention, enforced by the broker on Open (the codebase's expire-on-read discipline: no timer, no sweeper): delete terminal rows (answered/expired) from both elicitations and elicitation_answers when they exceed the bounds in internal/defaults/defaults.json — recorded defaults from the design: 30 days old or more than 1000 terminal rows, whichever bound hits first (triage confirms names/values against the defaults file's conventions). Pending rows are never pruned — a pending question is live state with its own expiry (expireOverdue).
  • Sampling rows obey the same bound (same table): this retires sampling-messages-not-retained-between-requests.
  • Event payloads become summary-only at all publish sites: events.ElicitOpened and events.ElicitAnswered carry id, server, tool, mode, audience, state, expiry — never a question's message, schema or url, and never an answer's content. Sites: internal/daemon/hooks.go ~L164-171, internal/daemon/consumer.go ~L325/424/505, internal/daemon/routes_proto.go ~L669, internal/daemon/server.go ~L1395 (today: Data: mustJSON(req) / mustJSON(ans), the full objects).
  • Tree-wide audit of consumers of these two event kinds' Data before the shrink lands; anything in-tree that read content is rerouted to the broker read path. The design's audit found no in-tree consumer reading answer content from the bus (askers use Broker.Await). Also confirm during implementation whether the out-of-tree OpenCode plugin reads payload fields being removed; if it does, route it through the broker read path — do not restore the leak.
  • Retire conformance gaps sampling-messages-not-retained-between-requests and sampling-both-handle-sensitive-data (client + server sides) in internal/conformance/coverage_clientfeatures.go.

Evidence (main d39e611, 2026-10-28)

Acceptance

  • Broker open with aged terminal rows deletes them (both tables); pending rows and fresh terminal rows are kept; the count bound trims oldest-first.
  • Retention bounds are settings in defaults.json with the documented defaults, not hard-coded constants.
  • Published ElicitOpened/ElicitAnswered event data contains none of a fixture question's message/schema/url or a fixture answer's content (canary strings asserted absent), while id/server/mode/audience/state fields are present.
  • Consumer audit recorded on this issue: every in-tree Data consumer of the two kinds accounted for.
  • Gaps sampling-messages-not-retained-between-requests and sampling-both-handle-sensitive-data (both sides) retired with the tests as evidence.
  • CI green for pkgs/mcpx (tests run in GitHub Actions).

Activity

  1. added
    task/triageQueue packet kind: verify, reproduce, and route this item
    status/blockedQueue or user escalation state
    priority/normalOrdinary priority (the default)
    queue/queuedHas a queued task in the Supabase agent queue
    app/mcpxPackage/app: mcpx (pkgs/ or apps/ in this repo)
    effort/mMedium: expected under 1 day of work
    meta/enrichedIntake review done: acceptance criteria, links, and normalized labels added
    on Oct 28, 2026
  2. added
    queue/claimedQueue worker lifecycle state
    and removed
    status/blockedQueue or user escalation state
    queue/queuedHas a queued task in the Supabase agent queue
    on Oct 28, 2026
  3. 153 remaining items

  4. added
    status/parkedDeliberately paused with a recorded resume condition
    on Oct 29, 2026
  5. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · parked

  6. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · parked

  7. added
    queue/blockedQueue or user escalation state
    status/blockedQueue or user escalation state
    and removed
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    on Oct 29, 2026
  8. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · implementation complete, blocked only on the repo-wide merge-queue freeze (#2403)

  9. added a commit that references this issue on Oct 30, 2026
  10. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    agent-note:v1 — state: handed off — queue task 2946 launched in bound OpenCode session ses_e5e5f40e72cffe3wACUIoZfBi4 by opencode-claimant.

  11. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    Done — PR #2202 merged, acceptance verified on main.

    • Merged: PR mcpx: broker retention bound + summary-only elicitation events (#1677) #2202 squash-merged 2026-10-30 as 7c52ced1 (head 357cb771c), present on current main (git log confirms; f8e356f75 is 2600+ PRs later and still contains all of it).
    • Retention bound: Broker.Open settles overdue rows via expireOverdue then calls pruneTerminal(time.Now()) (pkgs/mcpx/internal/elicit/elicit.go:214). Deletes terminal (answered/expired) rows from both elicitations and elicitation_answers past either bound, oldest-first; pending rows never touched. Bounds live in defaults.json under elicit/: retentionMaxAge: "720h", retentionMaxTerminalRows: 1000, exposed as defaults.ElicitRetentionMaxAge/Rows. Tests: internal/elicit/prune_test.go (age bound both tables, pending immunity, sampling rows, count bound oldest-first).
    • Summary-only events: elicitEventSummary/elicitAnsweredSummary in hooks.go emit only {id, server, tool, mode, audience, state, expiry}. Applied at all five packet sites (hooks.go x2, consumer.go x3) plus the two sampling sites in the same functions. Verified on current main: zero remaining mustJSON(req/ans/opened) full-object publishes in hooks.go/consumer.go/server.go/routes_proto.go (consumer.go has since grown to 6 summary sites via follow-ups). Tests: internal/daemon/elicit_events_test.go with canary strings asserted absent.
    • Conformance: sampling-both-handle-sensitive-data retired on both sides, claimed on the new tests. sampling-messages-not-retained-between-requests deliberately kept as a narrowed gap (commit 20346d0f8, Codex P2 disposition, thread resolved): sampling messages are still retained between requests by design until the terminal prune bound, so the SHOULD NOT is not met — retiring it would have been false. This is a reviewer-approved scope refinement, not a miss.
    • CI note: the basic: test FAILURE shown on PR mcpx: broker retention bound + summary-only elicitation events (#1677) #2202 is post-merge infra noise — run 41008350843 failed in 10s at actions/checkout because the branch was already deleted after merge. The merge itself went through the merge queue (green merge-group checks required), and main has merged cleanly ever since with this code in place.

    Closing as implemented. Unblocks #1678 gate (task 1886).

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

Metadata

Metadata

Assignees

Labels

app/mcpxPackage/app: mcpx (pkgs/ or apps/ in this repo)effort/mMedium: expected under 1 day of workmeta/enrichedIntake review done: acceptance criteria, links, and normalized labels addedpriority/normalOrdinary priority (the default)source/design-architectFiled by the repo design architect jobtask/triageQueue packet kind: verify, reproduce, and route this item

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions