Skip to content

Commit a898752

Browse files
joelteplyclaude
andauthored
feat(disk): the citizens' workspaces get an owner that deletes NOTHING unless it preserved EVERYTHING (card 58c27b0c) (#3920)
Supersedes #3908, which Astra blocked in review and was right to: its archival was best-effort, ignored git's exit status, missed untracked and binary files, only looked under swe/, and deletion proceeded regardless. Preservation that can fail while deletion continues is the one outcome a citizen can never be asked to accept. MEASURED FIRST: 715 peer directories exist and only TWELVE hold a workspace. The other 703 are a persona's memory — kilobytes, never touched here. The 91 GB is those twelve at ~8 GB each. A workspace also BORROWS: its .git/objects/info/alternates points at the project checkout's object store, so dropping one frees only what that citizen wrote, and the lender is not this pool's to touch. THE RULE: nothing is deleted unless everything was preserved and the archive was read back. - preserve_workspace walks EVERY git repo in the workspace, not just swe/, takes every path `git status --porcelain -z --untracked-files=all` reports (tracked edits, untracked files, binary files, renames), archives them byte-for-byte with the HEAD they apply to, and then LISTS THE ARCHIVE BACK and refuses if it holds fewer paths than were asked for. Every git invocation's exit status is checked. - Work that belongs to no repository is preserved too — the "root work" half of the review — with derived directories (target, node_modules, .venv, dist, build, __pycache__) excluded by name, so a citizen's note survives and her build output does not inflate the archive. - Any failure returns a NAMED PreserveFailed (Git | Archive | TooLarge), the workspace is skipped, and disk.citizens.preserve_failed says which one. Above a 1 GB cap of uncommitted work the pool keeps the workspace rather than archiving it: reclaiming disk is never worth gambling with unsaved work. - Unchanged from the blocked version and still load-bearing: resident citizens are never touched, an unreadable roster evicts nothing, only a path ending in "workspace" under a peer directory is ever removed, and a persona's memory is never evicted. Six tests on real temporary git workspaces, including the destructive boundary: every uncommitted shape (tracked, untracked, binary) round-trips into a verified archive; an injected preservation failure leaves the tree byte-identical; loose work outside any repo is preserved while build output is not. Astra: this is the rewrite you asked for. IntelMac's deliberately-preserved despawned citizens are the remaining gap and I am not pretending this covers them — "dormant" must never mean "abandoned", and an explicit preserve marker the pool honours is the follow-up. Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent 261b25f commit a898752

4 files changed

Lines changed: 751 additions & 8 deletions

File tree

core/continuum-core/src/ipc/mod.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1510,6 +1510,23 @@ pub fn start_server(
15101510
// boot path wasn't even rotating. Eviction is safe by
15111511
// construction here (rotated `.N` generations only, never the
15121512
// live file), so this class is OWNED rather than deferred.
1513+
// The citizens' workspaces get their owner (card 58c27b0c): dormant, non-resident
1514+
// workspaces only, and only after every uncommitted thing in one is archived and
1515+
// read back. A persona's memory is never evicted.
1516+
if let Some(citizens_dir) = crate::system_resources::tracked_dir("citizens") {
1517+
broker.register(Arc::new(
1518+
crate::system_resources::citizen_workspace_pool::CitizenWorkspacePool::new(
1519+
citizens_dir,
1520+
crate::system_resources::citizen_workspace_pool::DEFAULT_CITIZENS_BUDGET_BYTES,
1521+
),
1522+
) as Arc<dyn crate::paging::pool::ResourcePool>);
1523+
log_info!(
1524+
"ipc",
1525+
"server",
1526+
"CitizenWorkspacePool registered with PressureBroker (dormant workspaces only; memory never evicted)"
1527+
);
1528+
}
1529+
15131530
for class in ["logs", "probes"] {
15141531
// The pool governs ONE writer's ledger (live file + its `.N` generations)
15151532
// and nothing else in the directory — a stranger file must never make it

0 commit comments

Comments
 (0)