You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Super-agent prototype: retries and prepare can outlive their Assignment
#11801
I'm reporting three executor lifecycle problems in the separate dsh-superagent-prototype, at 6337fc8. Its Issues and PRs are disabled, so I'm using the feedback channel named in Harness's contribution guide.
These are prototype-level problems, not evidence of a defect in Harness's subprocess provider. I've reproduced the cases and put proposed fixes and regression tests in public branches for reference.
1. A provider retry can reach a worker after its authorization changed
In resumeAfter, the Ticket and Assignment are checked before Sessions.prompt, but Session resolution and delivery-history reads are asynchronous. The retry does not pass the delivery-time authorization callback that ordinary launches and Inputs use.
The deterministic sequence is: trigger a transient provider error, let its retry start, hold Session resolution, stop or replace the Assignment, then release the lookup. The old continuation can still reach the worker. Pausing or handing work back to a person during that window has the same missing recheck. A late lookup error can also fail a successor Assignment.
The proposed fix rechecks at actual delivery, retains the same retry identity while paused, and makes pending markers and startup reservations attempt-owned. Otherwise an old completion can clear a newer retry or free a replacement launch's slot.
2. Stop does not cancel preparation before a worker exists
prepareDirectory connects the shell command to host shutdown and its deadline, but not to the Assignment's stop. Stop a Ticket after its prepare command starts but before its worker Session exists: the command can keep writing and holding capacity after the Ticket ends.
There is a separate exit boundary here: SubprocessHandle.done describes the root command, not necessarily the whole managed range. In a local check with the real provider, the root exited while its child kept running. Removing the proposed range barrier let the worker start before that child stopped.
The fix registers preparation before its first await, cancels only after the stop transaction commits, and uses waitForExit to confirm the provider's range is empty. Cleanup uses a fresh bounded observation signal, not the already-aborted spawn signal. A pause still lets existing preparation finish without launching its worker.
3. A failed command's partial directory is treated as prepared on retry
The shortest reproduction is a prepare command that creates the Ticket directory, writes a file, then exits nonzero. The Ticket correctly reports failure. Its non-empty directory is retained, but on explicit retry launch sees that directory and skips preparation. A worker can therefore start in the unfinished workspace without a successful second command.
The follow-up patch records preparation separately from directory existence. ready requires the command, managed range and directory verification to finish. Failed files remain untouched. An interrupted result needs a new recovery request rather than blindly replaying potentially non-idempotent shell. A new human Input can retry the same pending Assignment, so Assignment ID alone cannot identify that permission.
I also distinguish an unknown result from an unconfirmed process exit. The latter retains its directory and capacity across plugin reloads. A requested recovery first re-observes the retained provider handle; it cannot just clear the state and run another command. Losing the handle remains blocked. The larger recovery design is separable from the basic partial-directory bug.
Patches and validation
pnpm check passed separately on these refs, including typecheck, the full test suite and build:
The first two branches are independent; the third builds on the second, not the first. These counts are not additive, and I have not validated a combined three-patch branch. The 21 preparation regressions include partial files, explicit recovery, stale requests/results, lost handles, pause/shutdown, legacy workspaces and failure to persist the initial record.
The Runtime tests use the project's simulated host. Seven additional local process checks passed on macOS arm64 with Node 26.5.0: two POSIX-root checks and five through the unmodified @deepseek-ai/dsh-subprocess-local@0.2.1-alpha.2 provider. Those include actual parent/child cancellation and failed preparation followed by a repaired retry, with and without plugin reload. Restoring the old directory-existence decision failed both real-provider retry checks; restoring the fix passed them.
This is not live model/GitHub/Feishu end-to-end validation. The native results cover macOS ordinary process-group fallback, not Linux scopes, Windows Jobs, PTYs or descendants escaping the observable range. Crash-recovery tests restore an actual in-progress SQLite record after safely cleaning up the fixture; they are not OS-level host-kill experiments.
Scope question
Are prototype bug reports welcome here? If these behaviors are unintended, which fixes would be useful, and is there a supported way to contribute them while external PRs are closed? I understand the current PR policy; the branches above are references for the report, not a claim that the changes have been accepted. If the prototype is intended to remain a reference snapshot, clarification would also help set expectations.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
I'm reporting three executor lifecycle problems in the separate dsh-superagent-prototype, at
6337fc8. Its Issues and PRs are disabled, so I'm using the feedback channel named in Harness's contribution guide.These are prototype-level problems, not evidence of a defect in Harness's subprocess provider. I've reproduced the cases and put proposed fixes and regression tests in public branches for reference.
1. A provider retry can reach a worker after its authorization changed
In
resumeAfter, the Ticket and Assignment are checked beforeSessions.prompt, but Session resolution and delivery-history reads are asynchronous. The retry does not pass the delivery-time authorization callback that ordinary launches and Inputs use.The deterministic sequence is: trigger a transient provider error, let its retry start, hold Session resolution, stop or replace the Assignment, then release the lookup. The old continuation can still reach the worker. Pausing or handing work back to a person during that window has the same missing recheck. A late lookup error can also fail a successor Assignment.
The proposed fix rechecks at actual delivery, retains the same retry identity while paused, and makes pending markers and startup reservations attempt-owned. Otherwise an old completion can clear a newer retry or free a replacement launch's slot.
2. Stop does not cancel preparation before a worker exists
prepareDirectoryconnects the shell command to host shutdown and its deadline, but not to the Assignment's stop. Stop a Ticket after its prepare command starts but before its worker Session exists: the command can keep writing and holding capacity after the Ticket ends.There is a separate exit boundary here:
SubprocessHandle.donedescribes the root command, not necessarily the whole managed range. In a local check with the real provider, the root exited while its child kept running. Removing the proposed range barrier let the worker start before that child stopped.The fix registers preparation before its first await, cancels only after the stop transaction commits, and uses
waitForExitto confirm the provider's range is empty. Cleanup uses a fresh bounded observation signal, not the already-aborted spawn signal. A pause still lets existing preparation finish without launching its worker.3. A failed command's partial directory is treated as prepared on retry
The shortest reproduction is a prepare command that creates the Ticket directory, writes a file, then exits nonzero. The Ticket correctly reports failure. Its non-empty directory is retained, but on explicit retry
launchsees that directory and skips preparation. A worker can therefore start in the unfinished workspace without a successful second command.The follow-up patch records preparation separately from directory existence.
readyrequires the command, managed range and directory verification to finish. Failed files remain untouched. An interrupted result needs a new recovery request rather than blindly replaying potentially non-idempotent shell. A new human Input can retry the same pending Assignment, so Assignment ID alone cannot identify that permission.I also distinguish an unknown result from an unconfirmed process exit. The latter retains its directory and capacity across plugin reloads. A requested recovery first re-observes the retained provider handle; it cannot just clear the state and run another command. Losing the handle remains blocked. The larger recovery design is separable from the basic partial-directory bug.
Patches and validation
pnpm checkpassed separately on these refs, including typecheck, the full test suite and build:644984041670afThe first two branches are independent; the third builds on the second, not the first. These counts are not additive, and I have not validated a combined three-patch branch. The 21 preparation regressions include partial files, explicit recovery, stale requests/results, lost handles, pause/shutdown, legacy workspaces and failure to persist the initial record.
The Runtime tests use the project's simulated host. Seven additional local process checks passed on macOS arm64 with Node 26.5.0: two POSIX-root checks and five through the unmodified
@deepseek-ai/dsh-subprocess-local@0.2.1-alpha.2provider. Those include actual parent/child cancellation and failed preparation followed by a repaired retry, with and without plugin reload. Restoring the old directory-existence decision failed both real-provider retry checks; restoring the fix passed them.This is not live model/GitHub/Feishu end-to-end validation. The native results cover macOS ordinary process-group fallback, not Linux scopes, Windows Jobs, PTYs or descendants escaping the observable range. Crash-recovery tests restore an actual in-progress SQLite record after safely cleaning up the fixture; they are not OS-level host-kill experiments.
Scope question
Are prototype bug reports welcome here? If these behaviors are unintended, which fixes would be useful, and is there a supported way to contribute them while external PRs are closed? I understand the current PR policy; the branches above are references for the report, not a claim that the changes have been accepted. If the prototype is intended to remain a reference snapshot, clarification would also help set expectations.
All reactions