Skip to content

fix(tabularius): a fixed relay session id is frozen by the first client that claims it - #2003

Closed
4444J99 wants to merge 1 commit into
mainfrom
fix/relay-identity-freeze-kills-dispatch-receipts
Closed

fix(tabularius): a fixed relay session id is frozen by the first client that claims it#2003
4444J99 wants to merge 1 commit into
mainfrom
fix/relay-identity-freeze-kills-dispatch-receipts

Conversation

@4444J99

@4444J99 4444J99 commented Aug 7, 2026

Copy link
Copy Markdown
Owner

The first of the stacked defects behind #1995 / the 19-day jules stall.

Symptom

Every serial dispatch receipt has 409'd at broker registration since 2026-07-19. The jules
sessions launch for real; the receipt is never recorded, and the traceback lands in stdout that
the beat's sensor runner swallows (#1989), so nothing surfaced it for 19 days.

ConductError: conduct broker rejected request (409):
  {"detail": "session_id is already registered to another identity"}

Root cause

register() binds agent/surface/session_id from the authenticated principal, then rejects
any re-registration whose whole identity object differs (identitiesEqual is a
stableStringify compare on the worker; prior.identity != session.identity in the python
broker). Post-binding those three fields are forced equal — two come from the principal, one is
the lookup key — so the only fields that can still differ are provider_identity and
native_run_id, both client-declared.

That check is therefore structurally incapable of catching an authority mismatch. Authority is
guarded two lines later by session_principals (session_id is already bound to another principal). The identity check can only ever fire on cosmetic provider drift — and with the
fixed relay id dispatch-serial-results, the first client to register owns that literal forever.

This is the #1408 relay freeze, recurring on the fields the #1408 fix did not normalize —
_bind_conductor_identity carries the comment describing exactly this failure mode.

Reproduced against the real broker with one principal throughout:

attempt provider_identity result
1 limen-cli OK
2 limen-cli (re-register) OK — idempotent
3 limen-cli-2 CONFLICT
4 limen-cli-2 again CONFLICT — permanent
5 limen-cli + native_run_id CONFLICT
6 limen-cli OK — first claimant still owns it

Why the symptom looked so narrow

The freeze is per-session-id-literal. Writers with their own literals (harvest,
dispatch-async/reserve, heal-board/lifecycle-repair, …) kept landing receipts all month — the
published projection holds 177 August receipts — while the one path that records a dispatch
was dead. So the board kept moving and only the dispatch receipt vanished.

Downstream: lane_throughput_window counts dispatch_log entries with status == "dispatched",
found none, and logs/throughput-governor.jsonl recorded "dispatched": 0, "mode": "bootstrap", "cap": 25 on every pass. The lane can never leave bootstrap, so the requested 100/day is
structurally unreachable (#1995).

Fix

Key the relay session id on exactly the identity fields the keeper compares but does not
normalize, so a drift opens a new session instead of conflicting. The digest is computed over
whatever is left unbound, so adding an identity field keys it automatically rather than re-opening
the freeze; and it derives from stable per-build values (never per-process), so the keeper's
session table does not grow without bound.

OLD relay session id: dispatch-serial-results
NEW relay session id: dispatch-serial-results-675fff6bc45c

Verification

A real limen dispatch --agent jules --limit 1 --live run against the live keeper now gets
past registration and reaches submit(). It stops there on a different, legitimate 409 —
task ORG-artist-organ-face-0705 exact revision moved — because the local projection has been
frozen at track.date: 2026-07-26 ever since the board-publication PR rung was retired. That is
the second stacked defect and #2001 carries it.

3 regression tests added, incl. one that drives the real ConductBroker through the freeze and
then through the keyed id. cli/tests/test_tabularius.py: 25 passed. Ruff clean.

Non-deploy: cli/** is not a deploy-trigger path (the api rail is dormant — GCP_SA_KEY exists
nowhere).

Refs #1995

…nt that claims it

Every serial dispatch receipt has 409'd at broker registration since 2026-07-19:

    ConductError: conduct broker rejected request (409):
      {"detail": "session_id is already registered to another identity"}

`register()` binds agent/surface/session_id from the authenticated principal and then
rejects any re-registration whose WHOLE identity object differs. Post-binding those three
are forced equal, so the only fields that can still differ are `provider_identity` and
`native_run_id` — both client-declared. That check is therefore structurally incapable of
catching an authority mismatch (`session_principals`, two lines later, is what guards
authority); it can only ever fire on cosmetic provider drift. With the fixed relay id
`dispatch-serial-results`, the first client to register owns the literal forever and every
later build takes a permanent 409.

This is the #1408 relay freeze, recurring on the fields the #1408 fix did not normalize.
Reproduced against the real broker with ONE principal throughout:

    #1 provider_identity='limen-cli'          : OK
    #2 same again (idempotent re-register)    : OK
    #3 provider_identity='limen-cli-2'        : CONFLICT
    #4 'limen-cli-2' again (permanent?)       : CONFLICT
    #5 native_run_id set, provider unchanged  : CONFLICT
    #6 original identity still works          : OK

The freeze is per-session-id-literal, which is why the symptom looked so specific: writers
using their own literals (`harvest`, `dispatch-async/reserve`, `heal-board/lifecycle-repair`)
kept landing receipts all month — the published projection holds 177 August receipts — while
the one path that records a *dispatch* was silently dead. `logs/throughput-governor.jsonl`
then read `dispatched: 0` on every pass and pinned the jules lane in `bootstrap` at 25/day,
making the requested 100/day structurally unreachable (#1995).

Fix: key the relay session id on exactly the identity fields the keeper compares but does not
normalize, so a drift opens a NEW session instead of conflicting. The digest covers whatever
is left unbound, so adding an identity field keys it automatically rather than re-opening the
freeze, and it is derived from stable per-build values (never per-process) so the keeper's
session table does not grow without bound.

Verified live: a real `limen dispatch --agent jules --limit 1 --live` now gets PAST
registration and reaches `submit()`. It stops there on a different and legitimate 409 —
`task ORG-artist-organ-face-0705 exact revision moved` — because the local projection has
been frozen at `track.date: 2026-07-26` since the board-publication PR rung was retired
(#2001 carries that). Two stacked defects; this commit is the first.

Refs #1995
Copilot AI lite review requested due to automatic review settings August 7, 2026 16:22
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@4444J99, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 41 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 823d3f94-9409-4963-a88b-5f175652d3f1

📥 Commits

Reviewing files that changed from the base of the PR and between 519f289 and 3286f8d.

📒 Files selected for processing (2)
  • cli/src/limen/tabularius.py
  • cli/tests/test_tabularius.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@4444J99

4444J99 commented Aug 7, 2026

Copy link
Copy Markdown
Owner Author

Withdrawn — cut from a 5-commit-stale base, so it silently reverted this file's TaskAlreadyHomed / _is_tolerated_already_homed tolerance work and dropped a test from test_tabularius.py. Superseded by a branch rebuilt on current origin/main with only the intended additions (verified: git diff origin/main removes nothing but the two lines the fix replaces).

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a Tabularius relay-session registration conflict where a fixed relay session_id could be permanently “claimed” by the first client that registers it, causing subsequent relay attempts (notably serial dispatch receipts) to 409 and never record receipts.

Changes:

  • Key the relay session_id with a short digest derived from the identity fields that register() compares but does not normalize, so provider/native-run drift opens a new session instead of conflicting.
  • Update compatibility-ticket registration to use the keyed relay session_id rather than the bare literal.
  • Add regression tests covering the freeze scenario and validating the identity-key behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
cli/src/limen/tabularius.py Generates a keyed relay session id based on unbound identity fields to prevent permanent 409 conflicts on relay re-registration.
cli/tests/test_tabularius.py Adds regression tests ensuring the relay session id is keyed and demonstrating that keyed ids avoid the provider-drift freeze.
Suppressed comments (1)

cli/src/limen/tabularius.py:1157

  • _relay_identity_key() includes client-declared native_run_id (and any future unbound fields) in the digest, so the docstring claim that it is derived from “stable per-build values, never per-process” is too absolute. If a caller sets a per-run native_run_id, this intentionally creates new sessions and the table can grow; the docstring should reflect that constraint.
    Keying the session id on that metadata turns a drift into a NEW session instead of a
    conflict. The digest covers whatever is left unbound, so adding an identity field keys it
    automatically rather than re-opening the freeze. It is derived from stable per-build
    values, never per-process, so the keeper's session table does not grow without bound.

Comment on lines +1135 to +1137
# register() binds these three identity fields from the authenticated principal; every OTHER
# identity field is client-declared and is compared verbatim against the stored session.
_RELAY_PRINCIPAL_BOUND_IDENTITY_FIELDS = ("agent", "surface", "session_id")
@4444J99
4444J99 deleted the fix/relay-identity-freeze-kills-dispatch-receipts branch August 17, 2026 21:07
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.

2 participants