Skip to content

fix(harness): order Terminal binding writes through End [SAP-3292] - #1022

Closed
ynadge wants to merge 1 commit into
assistant-history-review-fixesfrom
terminal-binding-write-order
Closed

ynadge wants to merge 1 commit into
assistant-history-review-fixesfrom
terminal-binding-write-order

Conversation

@ynadge

@ynadge ynadge commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

Primary change type

  • Bug fix

Problem and motivation

A held older Terminal binding write could land after End removed its marker, and failed restart compensation was swallowed.

Summary and scope

Serialize actual sidecar writes independently from launch preparation and snapshot current registry state when the queued write runs. Await and surface repair failure. Return HTTP409 for Resume/Start Terminal during shutdown.

Related work

Related issue: https://linear.app/sapiom/issue/SAP-3292

Depends on #1021. Native stack target: main.

Validation

385 tests passed across five SessionManager suites and REST. Five meaningful red cases reproduced the old failures before correction. Scoped lint and diff checks passed; full Harness typecheck also passed after concurrent source edits settled.

Tests and documentation

Cover End ordering behind held marker writes, ENOSPC compensation before/after commit, successful retry, and shutdown mapping for both Resume and dormant Terminal activation. Updated assistant-history.md. Increment:134 changed lines including tests/docs/changeset; SDK pr:size is absent.

Compatibility and release impact

  • End still retires runtimes immediately; durable marker completion waits for actual ordered IO.
  • Changeset: @sapiom/harness patch.
  • Fix-forward: retain write ordering and report uncertain persistence while correcting Terminal lifecycle handling.

Security

  • No secrets, credentials, private data, or unsanitized logs included.
  • No suspected vulnerability disclosed publicly.

AI assistance

  • AI assistance used: Codex implemented and reviewed the change with the validation described above.

Checklist

  • CONTRIBUTING.md read; maintainer-authorized issue work.
  • One focused increment, checks, documentation and release impact addressed.
  • I can explain and maintain the submitted changes.

Devin Review

@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: f8d44964-7bf2-4f9f-bfc8-0d8169cedd86

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

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.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

Devin Review

Comment on lines +3846 to +3849
const next = this.subsessionBindingWriteQueue.then(() =>
this.writeSubsessionBindings(),
);
this.subsessionBindingWriteQueue = next.catch(() => {});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 Failed writes corrupt End snapshots

When a binding write fails during concurrent End, subsessionBindingWriteQueue can snapshot the failed caller's rollback. createReserved deletes the marker before End's queued snapshot runs. The resulting tombstone lacks a marker, so the next init() rejects the registry as malformed.

Learn more

The stored queue tail converts the active write's rejection into fulfillment. A later End write waits on that tail. When the active write rejects, the caller's catch can roll back memory before the later writer snapshots it. createReserved deletes its marker without checking whether End advanced the close generation; resumePrepared similarly restores the old marker. End has already added the session to userClosedSubsessions, and loadSubsessionBindings rejects any closed ID without a marker.

Example: A reserved create starts marker write A. End adds the closed tombstone and queues write B. Write A fails, so the create catch deletes the marker. Write B then snapshots { markers: {}, closedSessionIds: [id] }; the next launch rejects this file as malformed.

Recommended fix: Make every binding rollback close-generation-aware, as restartFreshPrepared already does, so it cannot overwrite End's in-memory state. Add failure-and-End race tests for both createReserved and resumeBound, including reloading the persisted registry.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@ynadge ynadge closed this Sep 15, 2026
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