Staging Agent Content Before the Idle Window
A filesystem replay of Orkas checks how staged agent content crosses an idle publication gate, and what remains recoverable when activation or rollback fails.
Two synthetic agent turns were active while a replacement directory finished staging. The installed directory still contained the old bytes. Releasing the first turn changed nothing. Releasing the second started activation, and a third turn waited until the commit callback finished.
That is the useful boundary in Orkas's built-in content publication path: prepare expensive work outside the runtime gate, then hold the gate around the short activation window. The filesystem helper also preserves a prior copy when rollback fails. These are separate guarantees. A retained backup is recovery evidence; it is not proof that the live directory matches committed metadata.
I examined revision dce78adb2a2ffdfbf9159cc89352e4335940bc64 and ran two unchanged TypeScript modules with Node 24.19.0 on Linux. The replay used real temporary directories and filesystem operations, synthetic turn registrations and callbacks, and explicitly injected filesystem failures. It did not launch Electron, run a model, download a bundle, or execute the full application.
The Gate Belongs Around Activation
Orkas is a desktop agent application with a bundled agent runtime, marketplace content, skills, scheduled tasks, and connectors. Its package configuration declares application version 2026.10.27, Electron 42.11.18, and Node >=22.12.0. Those describe the inspected source configuration, not an installed desktop build.
Three files explain the publication path:
| Implementation | Responsibility |
|---|---|
builtin_marketplace_startup.ts | Supplies a per-user idle gate through activationGuard |
builtin_marketplace.ts | Copies the existing baseline into staging, prepares managed content, and invalidates listings after activation |
atomic-directory-replace.ts | Owns staging, activation, directory rollback, and backup recovery |
The startup adapter passes activate => withIdleRuntimePublish(uid, activate) to the built-in seeder. The replacement helper invokes prepare(staged) before entering that guard. Copying and validation therefore do not need to hold the publication barrier.
The separate runtime_content_publish.ts tracks active turns, queued publications, and an in-progress publication promise per user. The last registered turn's release drains queued work. A turn arriving during publication waits on that promise. The release function is idempotent; calling it twice did not release the other active turn in my fixture.
This is coordination among callers that use the same module in the same process. It is not a filesystem lock or a barrier for arbitrary external readers. The helper's guard is optional, too: inspecting this built-in startup path does not establish that every directory replacement in the application waits for idle.
Follow the Bytes Through the Callback
For an existing target, activation performs these operations in order:
target -> sibling .item.install-<suffix>.previous
staged -> target
readiness check
commit callback
remove previous-directory backup
The helper checks readiness before moving the old target and again after installing the staged directory. In the replay, failure at the second check restored the old tree and skipped the callback.
The successful callback observed the new tree. A callback that threw before making an external change caused the new tree to be removed and the prior tree to return. For a first install, the same callback failure left no target directory.
Despite the helper's name, this is a sequence of renames and an awaited callback. There can be a gap between moving the old target away and moving staging into place. The registered-turn barrier protects its cooperating consumers across that sequence; a rename alone does not make the whole sequence atomic to every observer.
The integrated gate fixture paused inside the commit callback. At that point the new directory was already live, but the third turn had not entered. This directly tested that the barrier includes the callback, rather than ending as soon as the directory rename completes. It did not measure how long real content activation takes.
A Failed Rollback Must Keep Its Evidence
Rollback first tries to remove the rejected target. If removal fails, it tries to rename that target back to the vacant staging path, then restore the backup. I injected an EACCES rejection for target removal only. The rename fallback succeeded, the old bytes returned, and transaction artifacts were cleaned up.
I then injected failures for both removal and quarantine rename after the callback rejected. The replacement promise rejected, but the live target still contained the rejected candidate. The previous directory survived beside it. Two maintenance sweeps retained that prior copy.
The cleanup policy is deliberately conservative:
| Observed fixture state | Sweep or recovery result |
|---|---|
| Staging directory belongs to an in-flight replacement in this process | Sweep preserves it |
Rejected target and prior .previous directory both exist | Sweep preserves the prior copy |
| No target; exactly one matching prior directory | Replacement restores it before preparation |
| No target; two matching prior directories | Replacement rejects ambiguous recovery without preparing |
| Successful replacement; backup deletion fails | Replacement resolves, reports cleanup error through the supplied callback, and retains backup |
An existing target makes cold recovery return immediately. In the failed-rollback fixture, another preparation callback therefore saw the rejected candidate, not the retained prior copy. The helper does not infer which directory is authoritative from a successful-looking target name.
The cold-recovery test constructed an interrupted state with one prior directory and no target. It then performed a real restore and merge preparation: the callback read the restored old bytes before adding a new file. This was a constructed recovery fixture, not a process-kill or power-loss experiment.
The active-artifact set is process-local. There is no cross-process ownership protocol in this helper, and its implementation has no fsync durability sequence. The replay establishes caught-error behavior and specific recovery states, not crash consistency on every filesystem.
Directory Rollback Has a Narrow Scope
The helper cannot reverse arbitrary effects of its commit callback. To make that boundary concrete, my synthetic callback wrote a separate JSON manifest with version new, then threw. The directory returned to old; the external manifest remained new.
That callback is a deliberately adversarial fixture, not a reproduction of an observed Orkas manifest bug. The inspected built-in wrapper uses listing invalidation as this callback and prepares install metadata inside staging. Other callers have their own commit behavior. The result shows why a caller must understand which effects can occur before a callback rejects.
Backup cleanup has a different outcome again. With an injected failure deleting .previous, the helper resolved successfully and called the supplied cleanup-error handler. The new content remained live. A later sweep retained the old backup. A leftover prior directory can therefore mean either a failed rollback or a successful activation with failed cleanup; its presence alone cannot identify the transaction outcome.
Replay the Boundary
The replay probe checks SHA-256 digests for the two source modules, then imports their TypeScript directly using Node's native type stripping. No compiler output or implementation replacement is involved. The injected failures temporarily replace selected built-in filesystem functions and restore them in finally blocks. All other file operations use the real temporary filesystem.
Given source files from the pinned revision, the actual cloud command was:
node public/blog/staging-before-idle-activation/replacement-probe.mjs \
/tmp/orkas-note-nov3
It exited 0. Selected terminal output:
idle_gate: staged with 2 turns active; sweep retained live staging; third turn waited through commit
readiness_failure: second check rejected activation; old tree restored
quarantine: injected target removal failure; rename fallback restored old tree
failed_rollback: rejected target remains; prior backup survives two sweeps; existing target prevents cold recovery
cold_recovery: unique backup restored before merge preparation
ambiguous_recovery: two prior copies retained; preparation refused
external_metadata: old tree restored; synthetic external manifest remains new
backup_cleanup: replacement resolved; cleanup error reported; old backup retained
PASS: 12 scenarios; real temporary filesystem; synthetic turns, metadata, and injected failures; no Electron or model execution
I also inspected the upstream replacement tests and publication tests. I did not run their Vitest suite. The 12 reported scenarios belong to the linked probe.
The design keeps staging out of a busy agent runtime's critical window while retaining useful recovery material. Its limits are visible in the same replay: cooperating turns wait through publication, rollback covers the directory, and a preserved backup still needs an explicit interpretation when the live target survives rejection.