Skip to content

sminos bridge: scope the delivery key by project (#229) - #231

Merged
SSFSKIM merged 8 commits into
mainfrom
bridge-dedupe
Oct 18, 2026
Merged

SSFSKIM merged 8 commits into
mainfrom
bridge-dedupe

Conversation

@SSFSKIM

@SSFSKIM SSFSKIM commented Oct 18, 2026

Copy link
Copy Markdown
Owner

Fixes #229.

The bridge mod deduplicated sminos deliveries with a per-writer seq mark (hooks/mods/bridge.tsx deliver()), but each project numbers a writer's events on its own. A writer that had delivered a higher seq to a session in one project had that writer's messages to the same session in another project, at or below the mark, answered duplicate. sminos_control.deliver then read that as success, and deliver_next settled them. An audit of every addressed message on this machine (283 pairs, matched against the recipients' transcripts) found 13 such losses:

The audit found nothing delivered twice and nothing sent to the wrong session.

Change

  • The delivery key carries the project: {project, writer, seq}, from push_event / send / the seat send path through push_member → push_live → sminos_control.deliver.
  • The mod's mark is <project>/<writer>. A key with no project (from a CLI older than this) is held to the bare writer mark, the only mark such a CLI ever raised. Kept bare marks are left as they are, so nothing is migrated or dropped, and a poisoned bare mark stops mattering once scoped keys arrive.
  • The child (sminos_bridge.py) keeps the same scoped marks. Refusals keep the project in their key. sminos_outcomes.report_refused skips another project's refusal.
  • A duplicate answer is no longer silent: sminos_control.deliver warns (the sender's say prints it) and writes a daemon.log line, and still settles, since with scoped keys a duplicate really was submitted before.
  • DX5 (docs/doperpowers/plans/2026-10-15-arkho-desk-c2-bridge.md) updated where it defines the kept marks, the deliver key and the delivered event.

Tests

  • bridge.test.ts:
    • one writer/seq in two projects is submitted in both;
    • a scoped key below its project's mark is a duplicate;
    • a project-less key is checked against the bare mark;
    • CI-opt's exact case: a kept bare mark of 98, then {project: p2, …, seq: 6} is submitted.
  • test-bridge-control.sh, through the real CLI: the second project's lower seq is delivered beside an existing bare mark, and a duplicate warns and logs.
  • test_policy_requests.py: a refusal kept by another project is not reported here.
  • Each new test fails with its fix reverted.

Review: medium rung, 2 findings, both about version skew, dismissed:

  • A new mod with an old child can't happen: the mod starts the child from its own plugin directory ($.plugin.root, bridge.tsx:645).
  • An old daemon reading scoped refusals is possible only for the one tick before the daemon restarts on its changed code, only for hub projects, and only for a refusal whose writer/seq repeats in another project.

ci-gate GREEN on 039c10b: https://github.com/SSFSKIM/doperpowers/actions/runs/39097186323.

Version 7.172.0: merge after #228 (7.171.0). A running session keeps its old mod until it restarts or reloads its mods.

🤖 Generated with Claude Code

…retried whole, a resume unlinks, one writer's lines read in order

- reconciliation looks, for each seat whose session no longer runs, for the
  record naming a job it continues in: the job may join before the harness
  writes it. transcript_path keeps its hits per invocation, so the pass
  costs no extra glob beside state().
- hand_off copies the cursor and moves the seat before it retires the old
  session; a seat lock another command holds leaves it joined, for the next
  pass to do again; the rename runs whoever won the retirement.
- an own joined after a continued retirement ends the link: a resumed
  session is its own member, and the job keeps its own events.
- a chain's in-charge events are merged in each writer's seq order.
…d-off is the target the verbs and the bridge see
A writer id is per session, but every project numbers a writer's events on
its own, so the bridge's per-writer high-water mark dropped a writer's
events in a second project as duplicates once its seq passed them in the
first, and the CLI settled them as delivered: lost silently.

- sminos_control.deliver sends key {project, writer, seq}; push_live and
  push_member carry the project id from deliver_next and send.
- The mod's mark is <project>/<writer>, the bare writer for a key naming no
  project; kept marks stay as they are (no migration). delivered events and
  refusals carry the full key.
- The child keeps the same marks, and refusals with their project.
- report_refused skips a refusal naming another project.
- A duplicate answer is still settled, but warned and written to daemon.log.

Closes #229.
@SSFSKIM
SSFSKIM merged commit b74c1e0 into main Oct 18, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sminos bridge drops a writer's messages in a second project as duplicates (per-writer seq watermark ignores the project)

1 participant