path.resolve('') is the process cwd, so an empty configured cwd would
silently reintroduce the launch-directory fallback the parent-session
cwd resolution removed. Fail at plugin load with an actionable message
instead.
The ACP backend resolved an unset cwd to the harness process's launch
directory, so a delegated child ran — and announced its ACP session —
in the server's cwd instead of the delegating session's workspace.
Resolve the working directory explicitly: the config cwd override
(made absolute and validated at load), else the parent session
header's cwd (validated at start), failing loud before spawning when
neither exists.
Condense the app-bundle sentence after combining the current-master CLI wiring with the recovery architecture changes. The text still identifies the TUI, line-oriented, headless, and ACP front doors and their output contracts.
This restores the enforced architecture.md word ceiling without raising the budget or relocating an architecture-level seam fact.
Document every context-window path returned by mapStopReason: recognized provider error wording, successful stop usage beyond the resolved window, and zero-output length stops that fill the window.
This keeps the public return contract aligned with usage-based overflow classification without duplicating the provider-specific detection table owned by pi-ai.
Use the repository-mandated 回放 terminology for replayable pressure and replace the literal cancellation-owned rendering with idiomatic Chinese that preserves boundedness, cancellation authority, and monotonic retry behavior.
The corresponding English clauses remain accurate. Re-record the English and Chinese pair after checking the corrected text against the implemented recovery contract.
Dispatch RequestErrorDecision and CompactionTrigger through explicit discriminant switches. End each closed union with assertNever so new variants fail compilation instead of silently inheriting fail or pressure behavior.
This preserves the current retry, fail, pressure, and overflow semantics while aligning the new recovery seams with the repository closed-union contract.
Contain arbitrary plugin and runtime failures even when a thrown Proxy traps instanceof checks or its string coercion throws. Fall back to a stable diagnostic instead of letting executeCli reject outside its exit-code contract.
Route abort reasons through the same total renderer so cancellation cannot escape containment through an exotic reason value.
Add a focused regression that exercises both hostile inspection paths and verifies stdout remains empty, stderr remains labelled, and the CLI resolves with exit code 1.
Report context-disposal failure as an independent outcome even when argument, boot, task, or output handling has already produced a primary diagnostic.
Keep the primary error first, append the cleanup error, and retain the nonzero exit status so operators can see both the initiating failure and the possibility that teardown or persistence did not complete.
Add a regression that combines an invalid app composition with a failing disposer and asserts both ordered stderr lines.
Align the one-shot app with the shared agent-spine contract that landed on master after this branch began. Expose maxParallelToolCalls, dshHome, toolBash, and toolTasks through the Loader schema and route them with pickSpineConfig().
This restores deployment control over tool-call concurrency, the shared Harness home, background bash, and task_output wait bounds instead of silently retaining owner defaults.
Exercise all four fields through the composed runtime, document the package-level contract, and regenerate the config catalog from the owning interface.
Race Loader startup with the process abort signal so SIGINT and SIGTERM can settle the one-shot CLI even when initialization has not returned a Context.
If boot settles after cancellation, dispose the late context asynchronously instead of recreating the wait. Contain late boot rejection and report a late disposal failure on stderr.
Cover prompt interruption, late context disposal, late boot rejection, and cleanup failure with focused CLI regressions.
The slow-binding test used a 250 ms compute budget, which is below the instrumented worker startup cost seen intermittently in the four-worker Windows coverage job. That startup activity could exhaust the budget before the program settled into the awaited binding, making the timing assertion depend on runner load.
Use a one-second compute budget and a two-second binding delay. The awaited wall time still exceeds the busy-time allowance by a clear factor, so the test continues to prove that binding wait time is not charged while leaving enough headroom for worker initialization under coverage.
The existing /opt footer case reaches the ordinary outside-home return on POSIX, but Windows resolves it on the checkout drive while the user profile is on another drive. That exercises the cross-drive guard instead and leaves the same-volume fallback uncovered in Windows coverage.
Add the resolved parent of the home directory as a platform-neutral outside-home path. The case now covers the fallback on every host while retaining /opt to exercise the Windows cross-drive path, restoring per-file branch, statement, and line coverage without platform-specific expectations.
Pass each resolved catalog model capacity into pi-ai stream conversion so the upstream full-message classifier can recognize provider-specific, silent, and length-stop overflow signals. Retain the harness text fallback for legacy provider wording and cover the catalog-resolution path with a mock-provider regression.
Windows does not guarantee that an ordinary child process will continue after its parent exits. The fake ACP agent exited immediately after spawning its late-output descendant, so Windows could tear down that descendant and close the protocol stream before the delayed ACP update was written.
Launch the descendant in detached mode while continuing to inherit stdout and stderr, then unref it as before. This preserves the intended regression boundary: launcher shutdown must wait for descendant-held stdio and parse the final buffered frame after the direct ACP parent exits.