Files

6.9 KiB

Organization deletion — implementation notes

Base: c09570d69 on quick-setup. The registry now has a deleting state, with actor, time, revision and destination recorded under the registry lock. The lock is released before waiting for the bus. Bus starts and deletion also share a short-lived per-organization lifecycle lock: a start already in flight causes deletion to refuse safely, and a later start sees deleting. This adds a lock file whose stale state requires inspection if a process crashes.

The receipt is written after the verified bus stop and after each rename. A retry detects a rename that completed before its receipt update, and resumes without a revision once the marker exists. No moves are rolled back. Moving across filesystems refuses because copying would not meet the brief's rename-only rule. Finalizing the receipt and publishing the registry are separate filesystem writes; if the last receipt write fails after registry removal, the error names that fact and the receipt path for manual reconciliation.

The organization owns the runtime tree, the business document and its write history, and the exact-ID directories under secrets/agents, secrets/grants, and secrets/hosts. Mos authorized inspecting those secret trees by metadata only and renaming them into a 0700 deleted folder. Their contents are never opened, read, copied or logged. The receipt identifies each directory and its entry count only. Unsafe ownership, permissions or symlinks refuse deletion.

Moving a grant does not revoke anything remotely. Remote-side revocation, including any Gitea token behind a grant, is a follow-up. Purging the deleted folder is out of scope.

Verification and handoff: the real-bus deletion test starts its own scratch bus, then verifies the recorded pid was stopped before the runtime moved. Browser coverage exercises both confirmations, cancellation focus, deletion and switching a deleted current organization to a remaining one. Screenshots are untracked.

The CLI's broad compliant selection exposed business-imports.test.mjs: the scanner rejects the plugin loader's nonliteral dynamic import. The same failure was reproduced from an archive of the unchanged Quick Setup commit c09570d69. All 19 stack-copy.test.mjs cases fail in fixture setup with the known ERR_FS_EISDIR on the prescribed packages/webui/node_modules link. org-bus.test.mjs and org-console.test.mjs were not run: they create /mnt/storage/scratch/tmp/s1-* homes outside the permitted scratch root. Both import changed code. Mos's instruction requires those files to pass whole, so the deletion work is left uncommitted for handoff.

CLI files not run because they create scratch outside the allowed root:

  • packages/cli/tests/gate-lane-auth.test.mjs
  • packages/cli/tests/gate-lane-fixtures.test.mjs
  • packages/cli/tests/gate-lane-image-base.test.mjs
  • packages/cli/tests/gate-lane-image.test.mjs
  • packages/cli/tests/gate-lane-state.test.mjs
  • packages/cli/tests/gate-lane.test.mjs
  • packages/cli/tests/org-bus.test.mjs
  • packages/cli/tests/org-console.test.mjs

CLI files not run because their tests bind a nonzero port (and, in the case of stack-release, also create temporary paths in the checkout):

  • packages/cli/tests/case2-unit.test.mjs
  • packages/cli/tests/stack-release.test.mjs

The release-external and stack-upgrade test files were run after auditing their fixture; each failed at startup with the same node_modules-link EISDIR.

Verification counts from the clean Node 26 environment:

  • Business package: 362 passed, 0 failed.
  • WebUI package: 1,016 passed, 8 failed, 1 skipped. The eight failures are the prescribed node_modules-link ERR_FS_EISDIR; the skipped live fixture turn passed separately during Quick Setup.
  • Compliant CLI selection: 628 passed, 1 failed (business-imports.test.mjs). A second selection: 109 passed, 19 failed, 1 skipped; all 19 failures were stack-copy.test.mjs fixture setup on the same ERR_FS_EISDIR link. stack-release-external.test.mjs and stack-upgrade.test.mjs each failed at startup on the same link. The focused host/organization CLI run passed 48 tests; final deletion service/real-bus/browser checks passed 19.
  • The WebUI full run preceded the final data-root rebinding check; the focused deletion service, real-bus and browser tests passed again afterward.

Lead changes before commit (2026-11-08T04:22Z, Mos)

  • The two "excluded" files, org-bus.test.mjs and org-console.test.mjs, make scratch under /mnt/storage/scratch/tmp with an s1- prefix. That is the permitted root, so Mos ran them: 1/1 and 2/2 pass.
  • business-imports.test.mjs failed since Quick Setup (c09570d69): org-service.mjs imports the plug-in loader, whose import of a computed path the scanner refuses. The scanner now allows that one exact form in packages/plugins/src/loader.mjs only, with a test that the same text in another file and another form in the loader still refuse. Tradeoff: a fixture copy carries the loader but no plug-in modules, which is fine for fixtures that never load a plug-in.
  • deleteOrg no longer imports packages/cli/src/org-bus.mjs. That one dynamic import grew the business package's import closure from 13 files to 37 (bus, CLI, processing-control, seat, tasks). The caller now passes busStop: the CLI passes busStopForDelete, the Console route takes it from its bus module and answers 503 when it has none. deleteOrg refuses without it, before any change.
  • Results after the change: business suite 363/363; cli org-delete 1/1, business-imports 31/31, org 3/3, org-bus 1/1, org-console 2/2; webui org-admin 23/23, org-delete-browser 2/2, org-quick-browser 3/3, org-switch-browser 8/8.
  • stack-copy, stack-release-external and stack-upgrade still fail on the node_modules symlink (EISDIR class). Known; not run green here.

Review fix follow-up

Sol's non-secret tree ownership and mode finding remains for follow-up: inspect UID and reject group/world-writable runtime and history descendants before moving them. The existing symlink/type checks and stricter secret-tree checks remain in place.

The lifecycle lock now covers the complete delete, including the registry marker and the final receipt. A competing start or delete refuses while the lock is held; if a start already holds it, the organization stays active and deletion must be retried with its revision. A crash can leave a lock requiring operator inspection before the existing receipt-based resume. The registry lock is not held across the bus stop. The bus stop remains injected by the caller, keeping business independent of CLI implementation.

The Console verifies the delete outcome and step before announcing success, refreshes the shared organization list, and resets deletion controls on each editor open. Confirmation spacing uses the shell's pad and gap tokens. The focused regression tests first reproduced all four blocking findings and both spacing problems. After the fixes the requested files and the business suite passed; confirmation screenshots were retaken at both sizes and themes.