Skip to content

Delete only settled Sessions and confirm repeated owner deletion - #60

Merged
SaladDay merged 8 commits into
mainfrom
codex/session-deletion
Sep 23, 2026
Merged

SaladDay merged 8 commits into
mainfrom
codex/session-deletion

Conversation

@SaladDay

@SaladDay SaladDay commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

Session DELETE now follows the observed official lifecycle rules. A Session can be deleted only when it is durably idle or failed with no pending work. A repeated delete by the owner is idempotent. The pinned baseline is unchanged (SDK 3.13.0 / d7c41ef / agents=v1).

Behavior

  • 409 when busy: DELETE returns 409 while a root Turn is queued, in progress or waiting, or while an input reservation is pending. The response has type and code conflict_error, param null, and message "session must be durably idle or failed without required actions before deletion". The check runs under the same Session row lock as Turn and input admission. Nothing changes on 409: no cancellation, no deletion marker, no event, no Runtime or placement release.
  • Allowed deletion: DELETE of an idle Session (including hosted provisioning without input) or a failed Session keeps the existing public deletion, managed Runtime cleanup and hosted-placement release.
  • Repeated owner delete: DELETE of the owner's already-deleted Session returns the same 200 {id, object: "agent.session.deleted", deleted: true} without writing anything. Foreign, missing and malformed IDs still get a byte-identical 404.
  • Core Web: after a busy 409, the dialog offers "Cancel work and delete". It cancels once, waits for idle, then deletes once, with no automatic retries. For an idle Session with pending input (where cancel can't work), it explains that the input must start, expire or fail first. The TS client exports isSessionDeletionConflict().
  • Native acceptance scripts: cleanup cancels a busy Session and then deletes it, without masking the original failure.

Documented decisions

  • Core admits Turns synchronously, so a DELETE right after an events.create 202 returns 409. Official returned 200 in that window.
  • Subagent child Turns and pending Environment file writes don't block deletion. This matches the previous behavior; the official behavior is unobserved.
  • Deleting a provisioning hosted Session with reserved input now returns 409. Its placement keeps counting toward node capacity until the input is admitted or expires (a 5-minute deadline).

Evidence

Campaign scan 1 findings SES-29 and SES-30, from owned official Sessions (all deleted), plus the 2026-09-22 official cleanup 409. Recorded in official-semantics-alignment.md and operation-evidence.md row 10.

Validation

  • Live acceptance through real Core, the daemon, native Codex and Kimi K3 (environment: none):

    Row main candidate
    DELETE while a Turn runs 200, the Turn was killed 409 with the exact body; the Turn kept streaming
    Cancel → idle → DELETE 404, the Session was already gone 200
    Repeated DELETE 404 the same 200
    Foreign / missing / malformed (6 variants) 404 byte-identical 404
    self_hosted with reserved input deleted 409, unchanged
    Hosted idle DELETE 200 with Runtime cleanup 200 with Runtime cleanup

    One Turn per phase. Cleanup and secret scans passed.

  • Real-PostgreSQL tests:

    • an HTTP lifecycle matrix of 7 busy and 8 settled states, with whole-database no-write snapshots;
    • admission-vs-deletion races in both orders;
    • placement retention on 409 and release once the input expires.

    The pinned-SDK official_session_delete.py covers 409 → cancel → 200 → repeat 200 → 404.

  • Server gate on the rebased code: all make check targets plus the Web typecheck, core-doctor, the unit tests and the build pass. The Playwright browser cases were not run on the server (no Google Chrome; the user approved the skip). make openapi is byte-identical.

  • Reviews:

    • A fresh full-diff blind review by a Claude Code subagent (the user-approved replacement for GPT-6 Astra) found no blockers.
    • A focused review of the merge with feat: manage hosted sandboxes across local and remote nodes #46's placement release confirmed it runs only on allowed deletions.
    • Small follow-ups (docs, Web message, the cleanup helper, a placement test) were verified by tests and the gate, per the user's rule.

Deferred

Physical purge and retention. Child-work and pending-file-write gating, pending official evidence.

No full protocol compatibility is claimed.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith with what you need. Autofix is disabled.

Session DELETE now follows the observed official lifecycle (SES-29/30).
Under the Session lock that orders Turn and input admission, a Session with
a queued, running or waiting Turn or a pending input reservation returns
409 conflict_error with the official message and nothing changes. The
owner's repeated deletion returns the same 200 without writing; foreign,
missing and malformed IDs keep the identical 404.

Tests cover the HTTP/PostgreSQL state matrix with a no-write digest,
deletion racing Turn and input admission on one row lock, a Worker
cancel-then-delete flow and the pinned-SDK script. Tests that exercise
hidden work under deletion markers from earlier releases now create those
markers explicitly.
The client exposes isSessionDeletionConflict for the 409 conflict_error
that Core returns for a Session with pending work or input. The Web delete
dialog explains that conflict and replaces its action with an explicit
Cancel work and delete: one cancellation, bounded reads until the Session
is idle or failed without required actions, then one deletion. Rejected or
uncertain cancellation, a timeout, a connection change or another 409 stop
without retrying; other 409 responses keep the generic message.
Document rows D1-D5, the lock-ordered decision, legacy deletion markers and
the stricter local 409 right after an events.create 202 where the official
service returned 200. Update the contributor rules, contract README,
Environment reservation note and operation evidence row 10.
Session deletion now returns 409 for busy Sessions, so a finally-block
delete after a failed assertion could replace the original error. A shared
helper cancels once on the 409 conflict_error, polls briefly for idle or
failed and deletes again. A cleanup failure is logged while another error
propagates and raised otherwise.
Hosted provisioning with input and a connected self-hosted Session with a
queued later input read idle, or await their Environment, while deletion
returns 409, and Core rejects cancellation in those states. After a busy
conflict the Web reads the Session once and, when only such input remains,
explains that it must start, expire or fail first instead of offering
Cancel work and delete. Turn-busy Sessions keep the existing flow.
When the Core connection changes after the cancellation but before any
deletion, say that the cancellation was sent and no deletion was attempted,
instead of blaming an earlier-connection deletion confirmation.
Deletion checks only root Turns and input reservations; subagent child
Turns and pending Environment file writes do not block it, as before, and
their official behavior is unobserved. State this in the handler
annotation, OpenAPI, contributor rules and contract docs, and record it as
a follow-up. Describe the Web pending-input explanation, the 30-second
bound checked between reads, and re-wrap the Core Web paragraph.
A provisioning hosted Session with reserved initial input on a configured
sandbox node now conflicts on deletion, so its placement keeps counting
toward node capacity until the input is admitted or expires. A real store
test checks the unreleased placement and unchanged node counts on the 409,
release on the allowed deletion after expiry, unchanged timestamps on a
repeat and 404 for another tenant. Record the capacity change in the
contributor rules and the deletion alignment section.
@SaladDay
SaladDay merged commit 09411f6 into main Sep 23, 2026
2 of 3 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