Skip to content

fix(registry): preserve live seats during record repair#330

Merged
EtanHey merged 1 commit into
mainfrom
fix/seat-record-repair
Jul 15, 2026
Merged

fix(registry): preserve live seats during record repair#330
EtanHey merged 1 commit into
mainfrom
fix/seat-record-repair

Conversation

@EtanHey

@EtanHey EtanHey commented Jul 15, 2026

Copy link
Copy Markdown
Owner

Summary

  • Repair drifted live managed seat metadata in place while preserving the agent ID, session transport, task, PID, and provenance.
  • Reserve currently occupied managed surfaces before identity-based self-heal so ghost records cannot steal a live seat by iteration order.
  • Keep recoverable ghosts until the normal absence window and a fresh, exact-seat positive discovery prove an active replacement.
  • Pin discovery ingestion and destructive proof to the observer owner/epoch; read errors, shell-only rows, mismatched seats, stale lifecycle, and recycled identities fail closed.

Safety

  • Record-only registry repair; no pane/session mutation path was added or exercised.
  • No live coach or agent session was read or touched.
  • PR-3.5 cleanup intentionally excluded to keep scope isolated.
  • Do not merge; lead review requested.

Verification

  • Full hermetic suite: 104 files, 2,200 tests passed.
  • Focused registry/resync suite: 123 tests passed.
  • Typecheck and build passed.
  • Pre-PR harness: 61 tests passed.
  • Push hook contract/regression/full-suite gates passed.

Review

  • Independent red-team review: CLEAN.
  • Independent final code review: READY, no findings.
  • Local CodeRabbit timed out after a partial finding; final automated retry was unavailable due the OSS rate limit, so the required manual fallback reviews were completed and all findings dispositioned.

Note

Medium Risk
Changes core agent registry reconciliation, eviction, and resync behavior; mistakes could drop live sessions or retain stale ghosts, though observer epoch pinning and fail-closed proof reduce cross-topology corruption.

Overview
Registry repair and eviction now favor in-place metadata fixes and stricter proof before removing rows. Drifted managed seats are updated on the existing agent_id (sessions, PID, task text preserved) instead of aggressive duplicate eviction; evictDuplicateManagedRegistrations is removed.

Self-heal pre-reserves surfaces already bound to live managed records so iteration order cannot let a ghost steal a seat. listMerged pins the observer snapshot at the start of ingestion (including reconcile/purge) so stale discovery cannot mutate state after a reconnect epoch.

Crash-recovery ghosts skip surfaceless eviction unless a same-cycle LiveSeatDiscoveryProof (observer owner/epoch + screen-classified seat/surface pairs) proves an active same-seat replacement; resync_agents builds that proof after repair and passes it into evictSurfaceless.

Repair path treats all discovered surface refs as “live” for pending-ghost logic (read errors no longer hide refs) and drops duplicate managed eviction during repairFromDiscovery.

Reviewed by Cursor Bugbot for commit 3302d1d. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix AgentRegistry to preserve live seats during record repair

  • Adds createLiveSeatDiscoveryProof to build an observer-epoch-pinned proof of live, role-classified seats, used to gate surfaceless eviction in evictSurfaceless.
  • Fixes selfHealManagedRegistrationsFromDiscovery so a live managed record's surface is no longer reassigned to a ghost canonical duplicate due to iteration order.
  • Crash-recoverable seat ghosts are now retained during eviction unless discovery proves a live same-seat sibling in the same observer epoch (via hasLiveManagedSeatSibling).
  • Removes the evictDuplicateManagedRegistrations pass that previously auto-evicted duplicate managed registrations by identity precedence.
  • resync_agents in server.ts now generates a live seat proof and passes the already-scanned discovery snapshot to listMerged to maintain epoch consistency.
  • Risk: removing duplicate-managed-record eviction changes repair behavior — duplicate records are no longer cleaned up automatically and must be resolved by other means.
📊 Macroscope summarized 3302d1d. 1 file reviewed, 0 issues evaluated, 0 issues filtered, 0 comments posted

🗂️ Filtered Issues

No issues evaluated.

Summary by CodeRabbit

  • Bug Fixes

    • Improved agent recovery to avoid evicting seats that are still actively owned.
    • Strengthened reconciliation during observer changes and incomplete discovery results.
    • Preserved live agent registrations and session metadata during repair.
    • Deferred ghost-seat eviction until inactivity is confirmed.
    • Improved handling of duplicate, mismatched, or partially observed seat records.
  • Tests

    • Added coverage for live-seat protection, repair behavior, observer changes, and end-to-end resynchronization scenarios.

@cursor

cursor Bot commented Jul 15, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_b76d04c9-0efd-4a09-9d44-9c6c68c2fd7b)

@EtanHey

EtanHey commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

No new commits to review since the last review.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 29a7dc03-b5f9-4683-8089-d0fce4e49cd5

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Agent reconciliation now validates observer snapshots, repairs managed seat identities in place, and uses fresh discovery proofs when deciding whether crash-recovery ghosts can be evicted. resync_agents supplies the post-repair proof and refreshed discovery data.

Changes

Seat reconciliation and recovery

Layer / File(s) Summary
Observer-pinned discovery reconciliation
src/agent-registry.ts, tests/agent-registry.test.ts
listMerged binds discovery ingestion to the observer snapshot captured before ingestion and rejects stale rewrites after epoch changes.
Managed seat repair and duplicate handling
src/agent-registry.ts, tests/agent-registry.test.ts
Self-healing preclaims candidate surfaces, updates live seat metadata in place, derives live surfaces from discovered entries, and removes duplicate-registration eviction from repair processing.
Live seat proof and crash-recovery eviction
src/agent-registry.ts, tests/agent-registry.test.ts
evictSurfaceless accepts observer-pinned live seat proofs and protects or evicts recoverable ghosts based on sibling identity, seat classification, surface UUID, and observer epoch.
Post-repair resync proof flow
src/server.ts, tests/resync-tool.test.ts
resync_agents rescans after repair, creates a proof, passes refreshed discovery to listMerged, and supplies the proof to eviction.
Estimated code review effort: 4 (Complex) ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant resync_agents
  participant repairFromDiscovery
  participant discovery.scan
  participant createLiveSeatDiscoveryProof
  participant listMerged
  participant evictSurfaceless

  resync_agents->>repairFromDiscovery: repair discovered seats
  resync_agents->>discovery.scan: scan after repair
  resync_agents->>createLiveSeatDiscoveryProof: build proof with observer id and epoch
  resync_agents->>listMerged: reconcile discoveredAfterRepair
  resync_agents->>evictSurfaceless: evict with liveSeatProof
  evictSurfaceless-->>resync_agents: retain or evict recoverable ghosts
Loading

Possibly related PRs

Poem

A rabbit watched the seats align,
Proofs tied to epochs made them shine.
Ghosts stayed when live friends were near,
Drifted records healed without fear.
Fresh scans guided every hop—
And stale old ghosts knew when to stop.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: preserving live registry seats during record repair and eviction.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/seat-record-repair

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.

Comment thread src/agent-registry.ts
// an absent canonical ghost steal a live drifted record's surface.
const claimedSurfaceRefs = new Set(
candidates.flatMap((entry) => {
const occupied = [...this.agents.values()].some(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium src/agent-registry.ts:1248

A terminal (done/error) managed record reserves its surface in claimedSurfaceRefs, so a stale terminal record blocks an eligible live managed record from self-healing onto a live replacement surface that shares identity keys. Because the replacement surface is live, surfaceless/terminal cleanup won't remove the stale row, leaving the stale record bound and the live record unable to relocate. The occupancy predicate at line 1248 omits a TERMINAL_STATES exclusion, causing stale terminal rows to be treated as active occupants. Consider excluding terminal-state records from the surface-claim predicate so only live managed records reserve their surfaces.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @src/agent-registry.ts around line 1248:

A terminal (done/error) managed record reserves its surface in `claimedSurfaceRefs`, so a stale terminal record blocks an eligible live managed record from self-healing onto a live replacement surface that shares identity keys. Because the replacement surface is live, surfaceless/terminal cleanup won't remove the stale row, leaving the stale record bound and the live record unable to relocate. The occupancy predicate at line 1248 omits a `TERMINAL_STATES` exclusion, causing stale terminal rows to be treated as active occupants. Consider excluding terminal-state records from the surface-claim predicate so only live managed records reserve their surfaces.

@EtanHey
EtanHey merged commit ab7dfea into main Jul 15, 2026
5 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.

1 participant