Repository navigation
fix(session): restore safe deletion retries and historical GC - #6357
Conversation
…etion A task-owner refusal can precede every artifact effect. Inferring completion from its failure phase stranded retries and advanced prepared GC journals. Carry verified artifact completion explicitly and preserve pre-effect authority. Lore-id: 3d746fe9 Constraint: preserve sibling authentication and native deletion authority Confidence: high Scope-risk: narrow Reversibility: simple-revert Tested: real preflight refusal preserves artifacts and prepared owner journal
Owner-free legacy deletion must not depend on an unrelated v2 scope. Initial writer capture refusals need a pre-effect retry without replacing captured authority. Publish absent owner fields consistently in memory and on disk so same-Broker reconciliation matches restart behavior. Lore-id: 614ac5b8 Constraint: never recapture owner evidence after cleanup effects Constraint: preserve transcript identity and original artifact authority Confidence: high Scope-risk: narrow Reversibility: simple-revert Tested: legacy direct and restart deletion without v2 initialization Tested: real staging and replacement writer held and released retries Tested: same-Broker protocol alias and canonical reappearance recovery Tested: byte-identical transcript replacement refuses owner recapture
Deleted workspaces should not make ordinary owner-free disk GC fail when an authenticated session scope contains no retirement journal. Authenticate and recheck stored namespaces without granting owner effect authority. Journal-bearing scopes retain the existing live-workspace requirement. Lore-id: 908c62d4 Constraint: never bypass malformed bindings or retirement journals Constraint: verify configured-root security and identity without repair Confidence: high Scope-risk: narrow Reversibility: simple-revert Tested: historical owner-free dry-run and actual retention pruning Tested: malformed binding and protocol symlink fail closed without mutation Tested: genuine owner journal with missing workspace still refuses Tested: external configured-root permissions and replacement preserve authority Not-tested: Windows runtime execution
Advisory critic review receiptReviewed submitted source head: Two independent critic slices finished OKAY after resolving their blocking findings:
Parent verification: 216 passed / 1 platform skip / 0 failed across 14 relevant test files, refreshed native build, coding-agent package check, changed-file Biome and patch whitespace checks. Broader process-heavy runs timed out and Windows runtime was not exercised, as documented in the PR. Existing warnings were neither suppressed nor altered. This is an advisory agent code review, not an approving maintainer GitHub review. A write-access maintainer must approve the exact current head before merge; no merge was performed. |
Dev advanced between the final local fetch and PR creation. Exact-head CI correctly refused a source head that did not contain its immutable event base. Integrate that base without rewriting the reviewed fixes or relaxing the guard. Lore-id: 081ea45b Constraint: retain exact-base ancestry validation and reviewed cleanup authority Confidence: high Scope-risk: narrow Reversibility: simple-revert Tested: original exact-base ancestor check fails before integration Tested: exact-base ancestor check succeeds after integration
Affected-path CI correction — head
|
probepark
left a comment
There was a problem hiding this comment.
Code review (CI still running on fb3db23; approval decision follows once CI finishes).
Review (head fb3db23, gajae-reviewer on behalf of probepark)
CI: pending — 19 checks pass (incl. check:@gajae-code/coding-agent, native-build, gjc-state-gates, 6 targeted test shards); still running: sdk-broker-lifecycle-e2e, sdk-broker-restart, task-artifact-owner-storage, notifications-live-stream, session-manager-resident-cache, ts-build:coding-agent, Windows dev:doctor. No failures so far.
Scope: +602 / -62, 9 files — reviewable source 4 files (+224/-55): src/sdk/broker/lifecycle.ts, src/session/internal/managed-session-scope.ts, src/session/internal/managed-task-owner-cleanup.ts, src/session/session-storage.ts; tests 4 files (+372/-7); changelog fragment.
Conventions: changelog fragment packages/coding-agent/changelog.d/verified-session-cleanup-regressions.md present; no generated files; no released CHANGELOG section touched; no console.* in coding-agent; no mock.module; new vi.spyOn uses are restored (afterEach(vi.restoreAllMocks) in gc-task-artifact-owner-retirement.test.ts:53, finally { …mockRestore() } in sdk-task-artifact-owner-deletion.test.ts); labels none.
Checked:
lifecycle.ts:6416-6418— owner-free replay now returns before deriving owner scope/context, so a legacy-only receipt with zerotaskArtifactOwner*fields no longer needs a v2 scope. Fresh path (:6552-6568) only builds owner scope when the transcript has an owner locator; a locator parse error becomes a capture error instead of an authority failure.inspectProtocolbecomes optional and is only consumed behind owner-evidence checks (session-storage.ts:2612).lifecycle.ts:6442-6494— thewriter_not_quiescentretry is gated on: phaseartifacts,artifactsRemoved !== true, the only owner field beingtaskArtifactOwnerCleanupError, no detached/retained path, no planned path or.removingsibling present (non-ENOENT lstat errors count as occupied, so it fails closed), and an unchanged transcript parent dev/ino.captureTaskArtifactOwnerEvidence(:1083-1115) re-checks the original transcript identity and sha256 before capture. An empty capture result fails closed.session-storage.ts:2701-2702— the pre-artifact sibling refusal now reportsartifactsRemoved === trueinstead of an implied completion.lifecycle.ts:8454-8455sends!artifactsRemovedtopublishTaskArtifactOwnerPending, which keepsartifactsAbsentAtAuthorizationand the artifact tree authority.managed-task-owner-cleanup.ts:362stops GC from publishingartifacts_removedfor the same case.lifecycle.ts:7917-7945—cleanupWithFreshOwnerPayloaddeletes every owner key and then re-adds only the truthy ones, so same-Broker receipts in memory match the strict decoder used on restart. The owner-pending receipt (:7952), the fresh-retirement receipt (:8027) and the artifact-completion receipt (:8339) all use it, which replaces the explicitundefinedfields.managed-session-scope.ts:3003-3083,:3130-3144— thecwd_missinghistorical scope checks the platform, the digest-derived directory name, the configured-root native security and dev/ino before and after, and the binding bytes and receipts dir identity read twice. It onlycontinues when no retirement journal exists. When a journal exists it falls through to the existingmanaged_gc_scope_authority_mismatchthrow.
Notes (non-blocking):
lifecycle.ts:8374-8378—transcriptPhaseCleanupused to cleartaskArtifactOwnerTranscriptDeletedexplicitly. It now depends on the localownerTranscriptDeleted, which is only reset when the outcome iscompleted(:8017). The replay path at:8225probably makes this unreachable withtrue. Consider adding one assertion in the payload_retired same-Broker case so the receipt shape is pinned.- The PR body says the SDK e2e/directory union timed out locally. The
sdk-broker-lifecycle-e2eandsdk-broker-restartshards are still running here, and they cover exactly that. The approval decision waits for them.
Blocking: none found in code review (step 3). Approval is held only because CI is still running.
Hosted CI verification completeExact current head: Dev CI run 37290885011 now passes:
Local evidence remains 376 passed / 9 existing platform skips / 0 failed across all 11 selected affected test shards, plus package/smoke/build/plan/provenance guards. The initial exact-base failure was repaired by including the immutable dev base, not by relaxing validation or changing product code. All current checks are completed with success or intentional not-applicable skips. This supersedes the prior in-progress CI note. No maintainer approval or merge is claimed. |
probepark
left a comment
There was a problem hiding this comment.
Review (head fb3db23, gajae-reviewer on behalf of probepark)
CI finished; no blocking findings. The code review for this exact head is #6357 (review).
CI: green. The approve gate (gajae-approve-gate.py) returned ALLOW on fb3db23 with 0 pending, 0 failed and 0 need-local, and check:@gajae-code/coding-agent is covered. The sdk-broker-lifecycle-e2e / sdk-broker-restart shards that were still running at the code review (the PR body says they timed out locally) have now passed in Dev CI run 37290885011.
Scope: +602 / -62, 9 files. Reviewable source is 4 files (+224/-55) under packages/coding-agent/src/{sdk/broker,session}; the rest is tests and a changelog fragment.
Conventions: changelog fragment present, no generated files, no released CHANGELOG section touched, labels none.
Blocking: none.
Note (non-blocking, carried from the code review): pin the taskArtifactOwnerTranscriptDeleted receipt shape in the payload_retired same-Broker case (lifecycle.ts:8374-8378).
PR body verdict line count=0, not updated. Suggested verdict line: gajae.pr-review-verdict.v1 merge-approved sha256:e618a0fba58cbfb27ea17ad34d6c6266bf429b580fbd97dbc3f5a610a50df2f3 reviewer:human reviewer-id:probepark evidence:ci-green;approve-gate-allow;code-review-5412782094-blocking0
Verdict: gajae.pr-review-verdict.v1 merge-approved sha256:e618a0fba58cbfb27ea17ad34d6c6266bf429b580fbd97dbc3f5a610a50df2f3 reviewer:human reviewer-id:probepark evidence:ci-green;approve-gate-allow;code-review-5412782094-blocking0
|
Merged into dev as
— |
What
Fix four runtime-reproduced session cleanup regressions from #6316/#6335/#6339/#6341, without reactivating the reverted #6349 owner producer:
artifactsRemovedfact in storage owner-pending results. A pre-artifact refusal preserves the original artifact tree/absence authority and the helper's prepared journal instead of falsely advancing completion.Also correct the affected receipt producers to omit absent owner fields in memory as well as JSON, so same-Broker retries obey the same strict decoder as restart retries. No decoder relaxation or corrupt-receipt migration.
Why / bug adjudication
All four original failures reproduced again on fresh
dev380f4aaabb28aa853fd4087849ee0a27fd8a19d0in a dedicated worktree before implementation. They are not intentional owner authentication refusals: they impose unrelated owner requirements on owner-free paths or record/replay facts that never occurred.The regression/safety tests were run with only these product fixes reversed: 19 scenarios, 15 failed / 4 passed. The fixes were then reapplied. Additional same-Broker post-artifact guard cases cover both owned and legacy-only sessions.
Safety boundaries
cleanup_pendingfor genuinely scrubbed but retained native namespaces.Testing
Final verification on the submitted source, after refreshing the local native addon:
bun --cwd=packages/coding-agent run checkpassed (vendor verification, Biome, and package TypeScript check).git diff --checkpassed.bun run build:nativepassed. Its generated local diagnostic digest is not included in this PR.Existing package diagnostics remain visible and unchanged: three warnings (type-only import in state-writer, agent-session file size, unused diagnostic-matrix variable) and one String.raw info diagnostic. Native build retains the existing nix future-compatibility warnings.
Verification limits
A broader process-heavy SDK e2e/directory union timed out at 180 seconds, and the complete session-directory file timed out at 90 seconds without a complete verdict. Neither is reported as passing or attributed to these fixes. Full repository aggregate checks/test suites and Windows runtime execution were not completed. Hosted CI and maintainer review remain required.
Critic review
Two independent bounded critic lanes reviewed the actual patch and both finished OKAY after corrections. Their blocking findings led to additional same-process receipt tests and configured-root security/replacement tests. These are advisory code reviews, not maintainer approval.
Risk classification
Approval
Target is
dev. One approving GitHub review from a write-access maintainer is required on the current head; no merge is requested or performed by this change.devbun run checkpasses (not claimed; targeted package gate passed)packages/coding-agent/changelog.d/