Skip to content

🤖 feat: keep shared sub-agent notebooks safe across workspace removal - #4217

Open
ThomasK33 wants to merge 4 commits into
memory-sharing-l1from
memory-sharing-l1b
Open

🤖 feat: keep shared sub-agent notebooks safe across workspace removal#4217
ThomasK33 wants to merge 4 commits into
memory-sharing-l1from
memory-sharing-l1b

Conversation

@ThomasK33

@ThomasK33 ThomasK33 commented Sep 11, 2026

Copy link
Copy Markdown
Member

Summary

Removal safety for the shared sub-agent notebook introduced in #4210. Removing a workspace in a task tree no longer strands or leaks its shared memory: surviving descendants are pinned to the owner they were using, reads and lists refuse once the owner or actor is tombstoned (no exposure of a removed workspace's data), and a removed child's harvest state is finalized without new provider work while an aborted removal releases the teardown gate again.

Stack: layer 2 of the split of #4139 (base: memory-sharing-l1, #4210). Addresses the two removal-related findings deferred from #4210's review; rollback of a child's refinements through the owner root follows in the next layer.

Background

With #4210 alone, removing an intermediate task made its children's parentWorkspaceId dangle, which resolved them to an empty private store and dropped them out of the tree's notebook. A tombstoned owner also still served reads to its children, and a child's retryable harvest records could be retried after its teardown. The review scope rule sends these to their own stacked PR because they need a persisted field (memoryOwnerWorkspaceId) and removal-path changes.

Implementation

  1. memoryWorkspaceOwner.ts: the resolver honors a memoryOwnerWorkspaceId pin only while the recorded parent is gone (a live parent always wins, so a stale pin heals on the next removal instead of redirecting a child into another tree). pinDescendantWorkspaceMemoryOwners records, for each direct child of the workspace being removed, the owner it resolves to right now.
  2. WorkspaceService.removeWorkspace: pins descendants before any destructive step and verifies the pins persisted (a non-forced removal aborts and stays retryable if they did not); the resolved owner is reused for the owner store lock; finalizeHarvestsForRemoval runs after the session directory is removed; releaseRemovalCancellation runs when the removal aborts before committing.
  3. MemoryService: openWorkspaceStore gates every workspace-store resolve on the actor's and owner's tombstones; reads (view, readFileWithSha, index and hot-set listing) re-check after reading so a workspace tombstoned mid-read exposes nothing. assertMutationCommittable binds the owner from the store path and checks every guarded workspace (guardedWorkspaceId lets a redirected consolidation run act on a removed child's behalf safely).
  4. MemoryConsolidationService: finalizeHarvestsForRemoval marks a removed workspace's outstanding harvest records terminal so recovery never retries them; terminal records are never overwritten; releaseRemovalCancellation lifts the in-process teardown gate for an aborted removal.

Known limitations (later layers)

  1. A child's own refinement rows still cannot be rolled back through the owner root (fails closed) until the rollback layer.
  2. Pre-upgrade child notebooks are not read until legacy adoption.
  3. If config.json is unreadable at removal time the child resolves to itself and the owner store lock is not taken; the strict-load abort is part of the multi-backend layer.
  4. With a second backend (XUM_ALLOW_MULTIPLE_INSTANCES), a child registered between the pin pass and deregistration is not pinned, and harvest finalization is serialized only in-process; both are closed by the multi-backend layer.
  5. Older builds ignore memoryOwnerWorkspaceId; notes a pinned child writes during a downgrade land in its private store and are picked up by the legacy-adoption layer on re-upgrade.

Validation

Ported and new tests: pin resolution (live parent wins, dangling parent uses pin, pin whose owner is gone falls back to self), grandchild keeps the root store after the middle node is removed, non-persisting pin aborts a non-forced removal with nothing destructive run, reads refused once the owner is tombstoned, mid-read tombstone withholds the result (including hot set), pin toggle refused on a tombstoned owner, removal-finalized harvest records stay terminal with no model calls, teardown gate released only on abort. Sibling suites: 1787 pass.

Risks

Medium, scoped to task-tree workspaces. New persisted field memoryOwnerWorkspaceId is optional and ignored by older builds (they fall back to per-workspace behavior). Read gates add tombstone probes on memory reads for workspace scope.


Generated with xum • Model: anthropic:claude-fable-5-1 • Thinking: high • Cost: $1012.00

…oved

Removing an intermediate sub-agent would leave its children's
parentWorkspaceId chain dangling, so they would fall back to private
notebooks. Before any destructive step, removal records
memoryOwnerWorkspaceId on each surviving direct child (the owner it resolves
to now), verified by reading the config back; the resolver honors a pin only
once the recorded parent is gone, and a pin whose owner is gone leaves the
child on its own store. A non-forced removal aborts (workspace intact) when
the pin does not persist.

---

_Generated with `xum` • Model: `anthropic:claude-fable-5-1` • Thinking: `high` • Cost: `$18.74`_

<!-- mux-attribution: model=anthropic:claude-fable-5-1 thinking=high costs=18.74 -->
…s tombstoned

Reads have no commit guard, so a removed child's (or owner's) stream in
another backend could keep viewing the shared store. Every workspace-scope
entry point now opens the store through a tombstone gate on the acting
workspace, the store's physical owner and an optional guardedWorkspaceId
(a child whose consolidation run sweeps under the owner's identity), and
re-checks before any bytes or listing leave the service (view, index,
hot set, UI reads, pin toggles). The commit guard binds the owner to the
resolved store instead of re-resolving it.

---

_Generated with `xum` • Model: `anthropic:claude-fable-5-1` • Thinking: `high` • Cost: `$18.74`_

<!-- mux-attribution: model=anthropic:claude-fable-5-1 thinking=high costs=18.74 -->
…ted removals

Once the session directory is gone, retryable harvest records can never be
retried: removal marks them terminal (finalizeHarvestsForRemoval) and a
residual retryable write can no longer reopen a terminal record — only a
genuine completion may replace it. A removal that aborts before its point of
no return lifts the in-process teardown gate (releaseRemovalCancellation) so
the surviving workspace consolidates again. Redirected child runs carry
guardedWorkspaceId so the child's tombstone refuses the owner-keyed run.

---

_Generated with `xum` • Model: `anthropic:claude-fable-5-1` • Thinking: `high` • Cost: `$18.74`_

<!-- mux-attribution: model=anthropic:claude-fable-5-1 thinking=high costs=18.74 -->
---

_Generated with `xum` • Model: `anthropic:claude-fable-5-1` • Thinking: `high` • Cost: `$18.74`_

<!-- mux-attribution: model=anthropic:claude-fable-5-1 thinking=high costs=18.74 -->
@ThomasK33
ThomasK33 added this pull request to stack #4218 September 11, 2026 12:57
@ThomasK33 ThomasK33 changed the title memory sharing l1b 🤖 feat: keep shared sub-agent notebooks safe across workspace removal Sep 11, 2026
@ThomasK33
ThomasK33 marked this pull request as ready for review September 11, 2026 12:58
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 11, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-11T13:19:07.072678Z ec06f19 Manual request
🔒 Security Review Completed 2026-09-11T13:19:18.325860Z ec06f19 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: ec06f19e3a

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ec06f19e3a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/node/services/workspaceService.ts
Comment thread src/node/services/memoryConsolidationService.ts
Comment thread src/node/services/memoryWorkspaceOwner.ts
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

Reviewed commit: ec06f19e3a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: ec06f19e3a

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

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