diff --git a/modules/workstation/claude/lib/remote-seat-provision.mjs b/modules/workstation/claude/lib/remote-seat-provision.mjs
index c6a665e83..c5f56e02e 100644
--- a/modules/workstation/claude/lib/remote-seat-provision.mjs
+++ b/modules/workstation/claude/lib/remote-seat-provision.mjs
@@ -1189,6 +1189,7 @@ export function buildRuntimeGcScript(rev, keep = 2, minAgeMinutes = RUNTIME_GC_M
     // A seat session that started before this release resolved its hooks and skills
     // through the tree it was provisioned with, so evicting that tree breaks it mid-run.
     '  if [ -z "$(find "$dir" -maxdepth 0 -mmin +"$min_age" 2>/dev/null)" ]; then continue; fi',
+    '  find "$dir" -type d -exec chmod u+w {} + 2>/dev/null || true',
     '  rm -rf -- "$dir"',
     "done",
   ].join("\n");
diff --git a/modules/workstation/claude/tests/remote-seat-provision.test.mjs b/modules/workstation/claude/tests/remote-seat-provision.test.mjs
index a622f0ad0..f4ecf2f79 100644
--- a/modules/workstation/claude/tests/remote-seat-provision.test.mjs
+++ b/modules/workstation/claude/tests/remote-seat-provision.test.mjs
@@ -2136,6 +2136,18 @@ for (const line of buildSeatProxyPrereqScript("[Unit]\n").split("\n")) {
   makeRev("content-ffffffffffff", { complete: false });
   mkdirSync(join(base, ".staging-content-999999999999-1-x"), { recursive: true });
 
+  // Managed releases are immutable. GC may reopen directories so entries can be
+  // unlinked, but must not chmod hard-linked managed file content in the process.
+  const immutableDir = join(base, "content-bbbbbbbbbbbb", "readonly", "nested");
+  mkdirSync(immutableDir, { recursive: true });
+  const immutablePayload = join(immutableDir, "payload");
+  writeFileSync(immutablePayload, "immutable\n");
+  chmodSync(immutablePayload, 0o444);
+  const preservedPayload = join(home, "preserved-payload");
+  linkSync(immutablePayload, preservedPayload);
+  chmodSync(join(base, "content-bbbbbbbbbbbb", "readonly"), 0o555);
+  chmodSync(immutableDir, 0o555);
+
   const result = runInHome(buildRuntimeGcScript("content-dddddddddddd", 2, 60), home);
   assert.equal(result.status, 0, result.stderr);
   const left = readdirSync(base).sort();
@@ -2145,6 +2157,8 @@ for (const line of buildSeatProxyPrereqScript("[Unit]\n").split("\n")) {
   assert.ok(left.includes("content-dddddddddddd"));
   assert.ok(left.includes(".staging-content-999999999999-1-x"));
   assert.ok(!left.includes("content-eeeeeeeeeeee"));
+  assert.equal(readFileSync(preservedPayload, "utf8"), "immutable\n");
+  assert.equal(statSync(preservedPayload).mode & 0o777, 0o444);
 
   // A tree younger than the floor is never evicted, complete or not: a seat session
   // started before this release still resolves its hooks and skills through it.
