Skip to content

Its owner gets a folder set aside back beside the work, or drops it - #1016

Draft
futurepaul wants to merge 4 commits into
claude/restore-sets-aside-what-wont-fitfrom
claude/aside-back-or-dropped
Draft

futurepaul wants to merge 4 commits into
claude/restore-sets-aside-what-wont-fitfrom
claude/aside-back-or-dropped

Conversation

@futurepaul

Copy link
Copy Markdown
Collaborator

Stacked on #1007. A folder a start set aside (it didn't fit on the disk) was kept and told, but its owner had no way to get it back or remove it.

The change

  • Get it back: POST /api/computers/{id}/aside/{record}/back → {back, view}.
    • The computer is woken first, as its owner's wake, if it sleeps.
    • The archive is restored beside the live work, in a folder of its own: /data/work/restored-<work|files>-<YYYY-MM-DD>-<save> (back_dir), so nothing live is replaced and two never meet.
    • Then it's aside no more and its archive is deleted (Aside::release, then Saves::let_go and the forget pass, both rows in one step). Its files are in /data now, and the next save keeps them.
    • A folder that doesn't fit yet (the floor stopped the restore, which wrote nothing) or whose archive will never restore stays aside and answers 409 saying why. So does a second press while one is coming back. None aside by that id is 404.
  • Drop it: DELETE /api/computers/{id}/aside/{record} → the view. The folder leaves and its archive is deleted. None aside by that id is 404.
  • Shell: the set_aside row gains Get it back and Delete it, each naming its folder when several are aside, with a confirm before delete. The words sit beside the notice's in cell/shell/computer.js.
  • Host: entry.mjs's restore takes an optional target directory.
  • Docs: docs/api.md (both routes) and docs/computers.md.

Tests

  • Core a_folder_aside_comes_back_beside_the_work_or_is_dropped:
    • back_dir's names: two folders never meet, a work folder versus its own files;
    • released once, released again none, and an unknown id;
    • the archive let go of.
  • Shell: the buttons' words and the delete confirm (node --test).
  • e2e computers lane, ASIDE_OUT_CHECKS:
    • the folder aside_checks set aside comes back, and the agent reads work/restored-work-…/aside.txt there;
    • it's then told no more, its archive deleted, and a second press is 404;
    • another set aside is dropped: by a stranger 404, by its owner 200, again 404, and it's told no more.
  • Commands run: cargo xtask check exit 0, and cargo xtask e2e --only computers: 284 passed, 0 failed, both on this head (A folder that will not fit is set aside, never a computer that will not start #1007's bc9e302d9 plus this commit).
  • Wasm: 6,485,206 bytes, +19,427 against A folder that will not fit is set aside, never a computer that will not start #1007's 6,465,779. It fits the 6.6 MB budget of The cell's Wasm budget is 6.6 MB #1013.

🤖 Generated with Claude Code

A folder a start set aside (it did not fit) was kept and told, with no
way out. Now its owner can get it back: woken if it sleeps, its archive
is restored beside the live work, in a folder of its own named for what
it was, the day it was saved and its save's number
(/data/work/restored-work-2026-11-03-594), so nothing live is replaced;
then it is aside no more and its archive is deleted, its files in /data
for the next save to keep. One that does not fit yet stays aside (409),
saying so. Or they drop it, its archive deleted.

POST /api/computers/{id}/aside/{record}/back answers {back, view};
DELETE /api/computers/{id}/aside/{record} the view. The set_aside
notice's row gains Get it back and Delete it (each folder named when
there are several), their words beside the notice's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@futurepaul futurepaul left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Codex review @ cf90904: CHANGES

  1. [P1] Do not replace an existing get-back destination — cell/src/computer.rs:1357–1361. back_dir deterministically chooses restored-work-<date>-<save>, but nothing checks or reserves that destination before passing it to DirectoryBackup.restore. The pinned SDK explicitly replaces an existing target directory. A person can already have files at that path; also, if an earlier restore swapped successfully but its response/DO was lost, the archive remains aside and retrying replaces any edits made in the recovered folder. Choose/reserve a fresh destination and publish without replacing an existing directory (or refuse a collision), keeping the archive on conflict. Cover a populated destination and retry after a completed swap with lost acknowledgement.

  2. [P1] Keep the archive until the recovered files have a durable save — cell/src/computer.rs:1367–1373. After the SDK writes the folder onto the running container, the handler immediately removes its Aside record and deletes its archive, without taking or verifying a save containing that folder. All currently kept saves therefore still omit the recovered data. A container crash before the next save—potentially while the get-back request is still in forget/tell_owner—loses the restored files and the only archive that could recover them. Retain the source record/archive until a successful save of the same running generation includes the recovered destination; a failed save, crash or interrupted get-back must leave it recoverable. Add a deterministic crash/save-failure case around that boundary.

Reviewed only the diff above #1007. Both routes enforce person identity and ownership; explicit full/unusable/throwing restore failures return before releasing the archive. All 56 core computer tests and 3 shell aside tests passed; no Docker or e2e run. The current tests cover successful return/drop but not the two loss scenarios above.

futurepaul and others added 3 commits November 3, 2026 10:41
…a save holds it

Codex on #1016 found two ways to lose data. A folder coming back went to
a name it never checked, and the SDK's restore replaces what is there:
a person's own folder at that name, or, on a retry after a move whose
answer was lost, the edits made in the recovered folder since. And its
archive was deleted before any save held the recovered files: a crash
before the next save lost both.

Now it comes back into a folder nothing was at (back_dirs: its name,
else -2 to -8), reserved before anything moves, so a retry in the same
life goes where its first try went. It is restored into a stage beside
it, marked with its record (.fragment-back), and moved into place with
mv -n -T, which moves nothing onto a folder there; a try that finds its
own marked folder there takes it as back, one that finds another's is
refused (409) and reserves afresh next time. And it stays aside, its
archive kept, until a save of that same life begun after it came back
worked (Aside::saved); a crash or a failed save before then loses
nothing, and its next life tells its owner of it again.

Tests: the core's reservation, retry, save gate, crash and drop; the
computers lane past a person's folder at its name, an answer lost (the
lever lose-back-ack) and retried with edits kept, a crash with its saves
failing (kill, fail-saves) and it back again, a save releasing it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ogether

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…at the crash cut

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@futurepaul futurepaul left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Codex review @ acffb09: PASS

Reviewed this PR's own changes on #1007, including the fixes since cf90904: persisted destination reservations, the non-overwriting final move, marked retries after a lost acknowledgement, and archive retention until a successful save of the same life begun after recovery. Both previous blockers are addressed; the new optional stored fields remain backward-readable, owner routing is unchanged, and lose-back-ack is behind the existing test-fleet guard. Validation: 56 core computer tests, the aside UI test, and a local filesystem probe for destination collisions and preserved edits passed; Docker/e2e were not run.

@futurepaul

Copy link
Copy Markdown
Collaborator Author

Local proof on acffb099b: cargo xtask check exit 0; cargo xtask e2e --only computers (with #1013's 6.6 MB budget applied locally, this branch predating it) — 287 passed, 0 failed, 1 skipped (the hosted lane's): a person's folder at its name kept, a lost answer retried with edits kept, a crash before any save (saves failing) told again and back again, a save releasing it, drop. Wasm 6,501,103 bytes, +35,324 against #1007.

@futurepaul
futurepaul marked this pull request as draft November 3, 2026 18:56

This branch has not been deployed

No deployments
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