Skip to content

Start and stop one isolated application without restarting the shared workers - #3339

Merged
kriszyp merged 8 commits into
mainfrom
isolated-app-scoped-deploy-and-drop
Oct 24, 2026
Merged

kriszyp merged 8 commits into
mainfrom
isolated-app-scoped-deploy-and-drop

Conversation

@kriszyp

@kriszyp kriszyp commented Oct 20, 2026 •

Copy link
Copy Markdown
Member
Galaxy brain meme: one new server added while the rest sleep

⊙ Problem

Every new PR-preview deploy (isolated: true, restart: true) restarts every shared worker on every node, and its first release is not canary-certified. Cleanup is worse: drop_component leaves the preview's worker running unless restart: true, restarts the shared workers when the app is not on a node, and reports nothing per node.

🤔 Should #3045 and #3091 ship as one PR?

Yes: both change the same drop path, and one drop to the cluster URL helps only once it both stops the worker and reports per node. Docs: Document starting and stopping one isolated application, and per-node drop results (documentation#750).

💡 Solution

⚖️ Alternatives

  • Guess which shared workers hold an app from config and the directory — a worker can load an app that appeared after it started; each worker now reports what it loaded.
  • Stop an isolated app with a scoped restartWorkers — that reloads every root component on main; main never loads isolated apps.
Also weighed
  • Fail the drop when a peer fails — drop never did; the per-node error shows it.
  • Report a fork-removal failure to a worker that dropped itself — it is stopped first; the failure is logged, and a second drop retries. Tracked in A fork-removal failure after drop_component answers early is only logged (#3463).
  • Make the config refresh after a drop non-fatal — it exits on a config that parses but fails validation. Every main-thread deploy and drop already runs it, so that is a separate fix.

🤔 Should dropping an absent app still discard branches an earlier drop kept?

Yes, chosen: otherwise keepBranches, or a shared drop without restart, leaves storage no later drop can remove. Nothing restarts, and while a shared worker may still hold the fork it stays.

🤔 Should keepBranches be refused together with restart: true?

Chosen: an older peer ignores keepBranches, and its restart path removes the forks; without a restart it keeps them. A shared app with forks then drops and restarts in two calls; easy to relax later.

🤔 Is the node's own result at top level, with peers in replicated, the right shape?

Chosen to match deploy_component, where a peer returns the same object. The alternative: one nodes array including the origin.

🔧 Changes

✅ Verification

🤖 Generated with Claude Code

https://claude.ai/code/session_015gDmwTaExpc8uXcdBNr8eA

🤖 Generated by Anthropic Claude (Opus 5.5, dispatch dev-agent); posted via @kriszyp.

Release target: main — new behavior and a new drop_component field, no linked milestone

Related PRs: 3 independent
Complexity: complicated

Deferred findings

  • f7a59ff0061ac9c2 nit reliability — components/operations.js — branch-removal failure after an early answer only logged — NOT FILED
Origin — the dispatch brief this PR was written from

Implement #3045 (start and stop one isolated application without restarting the shared HTTP workers) together with #3091 (per-node results from a replicated drop_component; dropping an absent component is a no-op). One PR closing both. Part of epic #3042 (PR preview deployments): today every new PR preview's first deploy rolling-restarts every shared worker on every node, and cleanup has to call each node directly and verify it by hand.

Decided behavior:

  1. DEPLOY of a NEW isolated application (component did not exist / was never loaded by any pool worker) with restart: true restarts ONLY its own dedicated worker: the restart scope is the application name, not undefined. It goes through the canary held start added by Certify a restarting deploy in a canary worker before it rolls out #2981 (merged), so the first deploy is canary-certified and the worker publishes its socket mirror only after a clean load. A flip of an EXISTING shared application to isolated (or back) still restarts the pool, because pool workers loaded it. Invariant to enforce: a restart's scope covers exactly the workers that loaded or will load the component.
  2. DROP of an isolated application always stops only its dedicated worker and removes its socket mirrors, whether or not restart: true is passed. No pool restart.
  3. DROP removes the application's branched databases by default (after its worker is stopped, as today's restart:true path does), with an opt-out request field (suggested name keepBranches: true) that retains them and keeps today's BRANCH_STORAGE_RETAINED message. Validate the new field in the drop validator.
  4. A replicated drop_component returns a per-node result, { node, componentRemoved, workerStopped, branchesRemoved, error }, the way deploy_component reports its peers (onPeerResult / response.replicated). Dropping a component that is not present on a node (no component directory and no root config entry) is a successful no-op there and NEVER restarts anything — today dropComponent with restart: true falls to restartScope undefined and restarts the whole pool for an absent or shared-not-running component.
  5. Replicated deploys and drops behave the same on every node (each peer computes its own scope from its own local state).

Acceptance

  • Integration tests (multi-worker, and replicated where the harness supports it) proving: deploying a new isolated app with restart:true, and dropping it (with and without restart:true), leaves every other worker running with the same thread ids and no restart events, on the origin and on peers.
  • The first deploy of a new isolated app is canary-certified; a failing load is refused and leaves no socket mirror and no running worker.
  • Dropping an absent component (restart true and false) succeeds, reports componentRemoved:false, and restarts nothing.
  • Dropping an isolated app with branched databases removes its branches by default; with the opt-out they are retained and the message says so.
  • Replicated drop_component response includes one { node, componentRemoved, workerStopped, branchesRemoved, error } entry per node.
  • Flipping an existing shared app to isolated still restarts the pool (regression test).
  • Show each new test failing on origin/main sources (forced rebuild; integration tests run against dist, so npm run build first).
  • Update operation docs/types for the drop response and new field. Do not change payload-deploy field handling (Payload deploys can set host, urlPath, isolated and branchedDatabases #3043) or deploy permissions (Scope deploy permissions to component names #3046).

Dispatch: task harper-3045 · queued by kris-session · ran by claude/opus/xhigh · worker kzyp-xps-1

Review-Coverage: authored=claude; ran=gemini,codex; adjudicated=domain; declined=cursor-grok,cursor-composer,cursor-kimi,cursor-muse,muse; rounds=8; full=2 @ d37b2a1

Review-Attention: deep ~25m (hot: heldStart.ts, manageThreads.js +1; decisions: do-less-alternative, test-retries-until-reported, config-order-reservation, error-identity-by-name, self-drop-waits-for-peers, keepbranches-refused-with-restart, withdrawal-fences-redeploy, default-branch-deletion, absent-drop-succeeds) @ d37b2a1

kriszyp and others added 7 commits October 20, 2026 08:34
… workers

A new isolated application's first deploy with restart: true now starts only
its own dedicated worker, held as the canary that certifies the release. A drop
of an isolated application stops only its worker, with or without restart, and
removes its branched databases unless keepBranches is set. Dropping a component
that is not on a node restarts nothing there, and a replicated drop reports each
node's componentRemoved, workerStopped, branchesRemoved and error.

Main owns a whole-component drop (a worker hands it over and waits), and adds
the shared pool to a named restart only when a pool worker reported loading that
application. Fixes #3045, fixes #3091.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gDmwTaExpc8uXcdBNr8eA
Dispatch-Task: harper-3045
…the restart's turn

Read isolation for a drop from the root config file instead of a refresh that
exits the process on a malformed file; evaluate pool ownership when a restart
takes its turn and count every live pool worker; withdraw a refused isolated
slot, surviving worker included, before the decision publishes; start no new
dedicated worker when refusals cannot be read, and hold an in-memory refusal
only while its release is live; keep the error class across the worker hand-off;
decide branch retention before an early answer; let main add the pool to an
isolated deploy instead of a disk heuristic; size the dedicated slot's ready
bound from the canary window; separate the drop's start deadline from its wait.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gDmwTaExpc8uXcdBNr8eA
Dispatch-Task: harper-3045
…lot's withdrawal

Refuse keepBranches together with restart: true, since a peer on an earlier
version ignores the field and its restart removes the branches. Withdraw a
refused isolated application's slot by force, queued before the decision is
read and awaited by the rollout's close, so a worker the withdrawal stops can
still read the decision of the deploy it requested. Carry the error class and
code across the worker hand-off, read peer outcomes flat or wrapped, stop an
isolated worker before waiting for peers, and keep a scoped reconcile's heap
share to the workers it actually runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gDmwTaExpc8uXcdBNr8eA
Dispatch-Task: harper-3045
…'s admission

Answer an isolated application that drops itself only after the peers have
answered, so replication errors and peer results reach it. Keep a reconcile of
one application from counting or reporting other applications that are not
running, preserve the error name across the worker hand-off for classes other
than ClientError and ServerError, and log when an incomplete restart keeps an
application's branched databases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gDmwTaExpc8uXcdBNr8eA
Dispatch-Task: harper-3045
Errors render by constructor name, so a drop error other than ClientError or
ServerError that a worker rebuilds from main's answer is rebuilt as a class
carrying the original name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gDmwTaExpc8uXcdBNr8eA
Dispatch-Task: harper-3045
A reconcile of one application now admits in the same order boot does, so a
restart keeps the same applications running where capacity is short; it still
reports refusals only for the application it reconciles. A deferred load no
longer records an application whose isolated value is invalid as placed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gDmwTaExpc8uXcdBNr8eA
Dispatch-Task: harper-3045
The record only ever widens a restart, and a value fixed before the deferred
load runs would otherwise leave a worker that loaded the application unreported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gDmwTaExpc8uXcdBNr8eA
Dispatch-Task: harper-3045

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request refactors the component drop and restart lifecycle, ensuring that starting, stopping, or dropping an isolated application does not affect unrelated workers. It centralizes whole-component drops on the main thread under a component lock, introduces a keepBranches option, and implements targeted worker reconciliation. Feedback on these changes suggests enhancing robustness by safely handling nullish or primitive errors in catch blocks and IPC message handling, as well as wrapping secondary package.json updates in a try-catch block to prevent minor failures from disrupting the core drop operation.

Comment thread components/operations.js Resolved
Comment thread components/operations.js Resolved
Comment thread server/threads/manageThreads.js Resolved
…t fork to go

On Bun a replacement starts only once its predecessor has exited, so the thread
list shows only new workers while one of them has not yet reported what it
loaded; until it has, the drop correctly keeps the fork.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gDmwTaExpc8uXcdBNr8eA
Dispatch-Task: harper-3045

@dawsontoth dawsontoth left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yusss! I can run local tests of this tomorrow if you like, or merge and abuse it later, whatever works. Concept looks great!

@dawsontoth

Copy link
Copy Markdown
Contributor

A new isolated app deployed from a worker thread never gets its worker, so every replicated peer misses it

Found while testing locally at d37b2a1 (macOS, Node 24.18).

What happens. A first deploy_component of a new isolated app with restart: true, run on an HTTP worker thread, answers 200 with Successfully deployed: <app>, restarting Harper and certification: "uncertified", but no dedicated worker starts. The app only comes up after a later restart_service. A replicated peer receives the operation on a worker, so this is the path every peer takes.

  • Two harper-pro nodes, with core at this PR merged onto pro's pin 489eebcf0: a replicated first deploy of a new isolated app started its worker on the origin (certified). On the peer it did not, 4 times out of 4. The peer's entry in replicated said "certification":"uncertified", "message":"Successfully deployed: rep_app, restarting Harper".
  • One core node, no replication: threads.count: 2, tls.unixDomainSockets: true, and a component whose resource runs server.operation({ operation: 'deploy_component', project, package: 'file:<tgz>', isolated: true, restart: true, replicated: false }). This reproduces every time. system_information shows only the two pool threads afterwards. On base 1fd591ba2 the same deploy does start the app's worker: base's scope there is undefined, so it restarts the pool, and that reconcile is what starts the worker.
  • Not affected: a deploy through the operations API, which runs on main, and a worker-run redeploy of an isolated app main already knows.

Root cause (from temporary log lines in dist). At components/operations.js:923 the scope is now the app's name. Main commits the certification and reconciles only that app (server/threads/socketRouter.ts:296). presentIsolatedApplicationNames() reads main's in-memory config, which doesn't have the entry the worker just published yet:

PROBE arm       wd_one bb5125ab-… true wd_one
PROBE commit    wd_one bb5125ab-… armed wd_one true
PROBE reconcile {"only":"wd_one","refused":false,"wanted":[],"present":[],"slots":[]}
… 20 s later, a restart_service:
PROBE reconcile {"refused":false,"wanted":["wd_one"],"present":["wd_one"],"slots":[]}

With nothing wanted, no canary starts, and completeCertification decides uncertified ("no replaced worker loaded it").

Possible fix. The drop path already handles this case: configuredIsolated (components/operations.js:1808) reads isolation from the root config file as well as from this thread's view. The scoped reconcile, or commitCertification for a release a worker asked for, could do the same. Main could also refresh its config before committing.

Test gap. Every deploy in isolated-application-lifecycle.test.ts goes through the operations API, which runs on main in the harness. A first deploy through a worker resource, like the existing WorkerDrop, would have caught this.

The rest checked out locally:

  • The PR's own suites pass.
  • Replicated drops return one correct per-peer { node, componentRemoved, workerStopped, branchesRemoved, error } entry.
  • Dropping an absent app restarts nothing on either node.
  • A down peer gets an error entry, and the drop still answered in 56 ms.

🤖 Generated with Claude Code

@kriszyp
kriszyp merged commit f1784b1 into main Oct 24, 2026
74 of 79 checks passed
@kriszyp
kriszyp deleted the isolated-app-scoped-deploy-and-drop branch October 24, 2026 01:52
dawsontoth added a commit that referenced this pull request Oct 24, 2026
Brings in #3339 (start and stop one isolated application), which this
branch's PR lists as overlapping. Its two new DESIGN.md index entries
left the root index at 251 lines, over its 250 budget (main's own lint
fails on it), and this branch's notes on top took components/DESIGN.md
to 1035 of 1000. Related index entries now share a bullet, this branch's
deployment-record notes are tighter, and the dangling-symlink note drops
its history, so both budgets hold.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kriszyp added a commit to HarperFast/documentation that referenced this pull request Oct 29, 2026
… results

For HarperFast/harper#3339 (v5.4.0): a new isolated application's first
restart deploy starts only its own worker, as its canary; drop_component
stops an isolated application's worker with or without restart, removes its
forks by default (keepBranches keeps them), restarts nothing on a node that
does not have the application, and reports each node's outcome. Adds the
one-call preview cleanup for clusters on v5.4.0, and a 5.4 release note.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gDmwTaExpc8uXcdBNr8eA
Dispatch-Task: harper-3045
Dispatch-Task: documentation-750-rebase
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
kriszyp added a commit to HarperFast/documentation that referenced this pull request Nov 2, 2026
… results

For HarperFast/harper#3339 (v5.4.0): a new isolated application's first
restart deploy starts only its own worker, as its canary; drop_component
stops an isolated application's worker with or without restart, removes its
forks by default (keepBranches keeps them), restarts nothing on a node that
does not have the application, and reports each node's outcome. Adds the
one-call preview cleanup for clusters on v5.4.0, and a 5.4 release note.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gDmwTaExpc8uXcdBNr8eA
Dispatch-Task: harper-3045
Dispatch-Task: documentation-750-rebase
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Dispatch-Task: pr-maint-da77693db6a28c0dbcc9d24f2a6a2e45
Co-Authored-By: Gemini <noreply@google.com>
Ethan-Arrowood pushed a commit to HarperFast/documentation that referenced this pull request Nov 3, 2026
… drop results (#750)

* Document scoped start/stop of isolated applications and per-node drop results

For HarperFast/harper#3339 (v5.4.0): a new isolated application's first
restart deploy starts only its own worker, as its canary; drop_component
stops an isolated application's worker with or without restart, removes its
forks by default (keepBranches keeps them), restarts nothing on a node that
does not have the application, and reports each node's outcome. Adds the
one-call preview cleanup for clusters on v5.4.0, and a 5.4 release note.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gDmwTaExpc8uXcdBNr8eA
Dispatch-Task: harper-3045
Dispatch-Task: documentation-750-rebase
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Dispatch-Task: pr-maint-da77693db6a28c0dbcc9d24f2a6a2e45
Co-Authored-By: Gemini <noreply@google.com>

* Address review: scope claims to whole-project drops and restart=true, check every node's outcome

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gDmwTaExpc8uXcdBNr8eA
Dispatch-Task: harper-3045
Dispatch-Task: documentation-750-rebase
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Dispatch-Task: pr-maint-da77693db6a28c0dbcc9d24f2a6a2e45
Co-Authored-By: Gemini <noreply@google.com>

* Address review: a self-drop answers before its worker stops

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gDmwTaExpc8uXcdBNr8eA
Dispatch-Task: harper-3045
Dispatch-Task: documentation-750-rebase
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Dispatch-Task: pr-maint-da77693db6a28c0dbcc9d24f2a6a2e45
Co-Authored-By: Gemini <noreply@google.com>

* Address review: the Operations API drop waits for its restart; name when the shared workers restart

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gDmwTaExpc8uXcdBNr8eA
Dispatch-Task: harper-3045
Dispatch-Task: documentation-750-rebase
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Dispatch-Task: pr-maint-da77693db6a28c0dbcc9d24f2a6a2e45
Co-Authored-By: Gemini <noreply@google.com>

* Address review: qualify the pool restart when isolation turns on; cite the issue that set log-only removal

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gDmwTaExpc8uXcdBNr8eA
Dispatch-Task: harper-3045
Dispatch-Task: documentation-750-rebase
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Dispatch-Task: pr-maint-da77693db6a28c0dbcc9d24f2a6a2e45
Co-Authored-By: Gemini <noreply@google.com>

* Address review: forks a drop could not remove are retried automatically (harper#3600)

Since harper#3600 (v5.4.0-alpha.2), a drop that does not keep its forks
records the removal as owed and Harper retries it until it succeeds,
instead of only logging a failure or waiting for a second drop. Also lists
the main-thread-serves case among those that keep forks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dispatch-Task: fix-kriszyp_documentation_750-2919ad88
Dispatch-Task: pr-maint-da77693db6a28c0dbcc9d24f2a6a2e45
Co-Authored-By: Gemini <noreply@google.com>

* Address review: check workerStopped in the one-call cleanup, date the preview restart allowance, qualify certification for linked directories and single-file drops

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dispatch-Task: fix-kriszyp_documentation_750-2919ad88
Dispatch-Task: pr-maint-da77693db6a28c0dbcc9d24f2a6a2e45
Co-Authored-By: Gemini <noreply@google.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: GPT-5 Codex <noreply@openai.com>
Co-authored-by: Gemini <noreply@google.com>
kriszyp added a commit that referenced this pull request Nov 5, 2026
… drop results (HarperFast/documentation#750)

* Document scoped start/stop of isolated applications and per-node drop results

For #3339 (v5.4.0): a new isolated application's first
restart deploy starts only its own worker, as its canary; drop_component
stops an isolated application's worker with or without restart, removes its
forks by default (keepBranches keeps them), restarts nothing on a node that
does not have the application, and reports each node's outcome. Adds the
one-call preview cleanup for clusters on v5.4.0, and a 5.4 release note.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gDmwTaExpc8uXcdBNr8eA
Dispatch-Task: harper-3045
Dispatch-Task: documentation-750-rebase
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Dispatch-Task: pr-maint-da77693db6a28c0dbcc9d24f2a6a2e45
Co-Authored-By: Gemini <noreply@google.com>

* Address review: scope claims to whole-project drops and restart=true, check every node's outcome

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gDmwTaExpc8uXcdBNr8eA
Dispatch-Task: harper-3045
Dispatch-Task: documentation-750-rebase
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Dispatch-Task: pr-maint-da77693db6a28c0dbcc9d24f2a6a2e45
Co-Authored-By: Gemini <noreply@google.com>

* Address review: a self-drop answers before its worker stops

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gDmwTaExpc8uXcdBNr8eA
Dispatch-Task: harper-3045
Dispatch-Task: documentation-750-rebase
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Dispatch-Task: pr-maint-da77693db6a28c0dbcc9d24f2a6a2e45
Co-Authored-By: Gemini <noreply@google.com>

* Address review: the Operations API drop waits for its restart; name when the shared workers restart

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gDmwTaExpc8uXcdBNr8eA
Dispatch-Task: harper-3045
Dispatch-Task: documentation-750-rebase
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Dispatch-Task: pr-maint-da77693db6a28c0dbcc9d24f2a6a2e45
Co-Authored-By: Gemini <noreply@google.com>

* Address review: qualify the pool restart when isolation turns on; cite the issue that set log-only removal

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gDmwTaExpc8uXcdBNr8eA
Dispatch-Task: harper-3045
Dispatch-Task: documentation-750-rebase
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Dispatch-Task: pr-maint-da77693db6a28c0dbcc9d24f2a6a2e45
Co-Authored-By: Gemini <noreply@google.com>

* Address review: forks a drop could not remove are retried automatically (harper#3600)

Since harper#3600 (v5.4.0-alpha.2), a drop that does not keep its forks
records the removal as owed and Harper retries it until it succeeds,
instead of only logging a failure or waiting for a second drop. Also lists
the main-thread-serves case among those that keep forks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dispatch-Task: fix-kriszyp_documentation_750-2919ad88
Dispatch-Task: pr-maint-da77693db6a28c0dbcc9d24f2a6a2e45
Co-Authored-By: Gemini <noreply@google.com>

* Address review: check workerStopped in the one-call cleanup, date the preview restart allowance, qualify certification for linked directories and single-file drops

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dispatch-Task: fix-kriszyp_documentation_750-2919ad88
Dispatch-Task: pr-maint-da77693db6a28c0dbcc9d24f2a6a2e45
Co-Authored-By: Gemini <noreply@google.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: GPT-5 Codex <noreply@openai.com>
Co-authored-by: Gemini <noreply@google.com>
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.

2 participants