diff --git a/docs/reference/plans/2026-07-14-ssh-relay-github-release-implementation-checklist-summary.md b/docs/reference/plans/2026-07-14-ssh-relay-github-release-implementation-checklist-summary.md index ecd15f44519..284af9b6c37 100644 --- a/docs/reference/plans/2026-07-14-ssh-relay-github-release-implementation-checklist-summary.md +++ b/docs/reference/plans/2026-07-14-ssh-relay-github-release-implementation-checklist-summary.md @@ -9,16 +9,16 @@ keeps commands, hashes, runner identities, timings, and failure details. A checked box means the work has evidence in the detailed ledger. Design approval alone does not complete a box. -Active checkpoint: **Milestone 6 / Work Package 5 bounded runtime transfer — Windows borrowed-chunk -pipe-write correction locally green, exact-head native qualification next, 2026-07-17, Codex -implementation owner.** Exact diagnostic head -`ce4c7c481c790eacbf73162e5e03570fc2d3b490`, run `29563868935`, and Windows x64/ARM64 jobs -`87832001707`/`87832001736` independently complete the 18-byte first file and stall before accepting -any bytes of `THIRD_PARTY_LICENSES.txt`. The isolated correction uses sequential zero-copy 16 KiB -Windows pipe views while retaining the borrowed source buffer until all subwrites settle. Focused, -broad, release, typecheck, lint/reliability/max-lines, formatting, diff, and resolver isolation are -green. Commit/push only this package for fresh native proof. No product/default caller, Beta, -fallback, tuple, publication, or signing behavior is authorized. +Active checkpoint: **Milestone 6 / Work Package 5 bounded runtime transfer — matched 4 KiB Windows +pipe correction locally green, native qualification next, 2026-07-17, Codex implementation +owner.** Exact head +`c93111a14bdb5390f99aa44a9004449625cbdcb5`, run `29564980476`, and x64/ARM64 jobs +`87835460661`/`87835460642` falsify client-only 16 KiB subdivision: x64 stalls after the 18-byte +first file, while ARM64 completes the 180,827-byte license and stalls on `node.exe`. Client pipe +writes and remote .NET reads now match at 4 KiB while preserving the source 64 KiB buffer; focused, +broad, release, typecheck, lint/reliability/max-lines, formatting, diff, and resolver isolation pass. +Commit/push only this package. No product/default caller, Beta, fallback, tuple, publication, or +signing behavior is authorized. `E-M6-WINDOWS-SYSTEM-SSH-TREE-LIVE-PORTABLE-FIXTURE-LOCAL-001` records the portable-fixture correction locally green: the no-Orca-publication workflow oracle permits the one exact pinned Microsoft fixture release URL while Orca release URLs, `gh release`, and `contents: write` remain diff --git a/docs/reference/plans/2026-07-14-ssh-relay-github-release-implementation-checklist.md b/docs/reference/plans/2026-07-14-ssh-relay-github-release-implementation-checklist.md index 6fc0009ee8a..08c3096d140 100644 --- a/docs/reference/plans/2026-07-14-ssh-relay-github-release-implementation-checklist.md +++ b/docs/reference/plans/2026-07-14-ssh-relay-github-release-implementation-checklist.md @@ -8,8 +8,8 @@ work; keep exact commands, runner identities, hashes, metrics, and residual gaps Date created: 2026-07-14
Last updated: 2026-07-17
-Current phase: Milestone 6 / Work Package 5 bounded runtime transfer — **In progress — Windows borrowed-chunk pipe-write correction locally green, exact-head native qualification next, 2026-07-17, Codex implementation owner**. Exact diagnostic head `ce4c7c481c790eacbf73162e5e03570fc2d3b490`, run `29563868935`, and Windows x64/ARM64 jobs `87832001707`/`87832001736` independently prove the first borrowed 64 KiB payload write stalls. `E-M6-WINDOWS-PIPE-SUBWRITE-LOCAL-001` subdivides only that Windows destination write into sequential zero-copy 16 KiB views while retaining the source buffer until every subwrite settles. Commit/push only this correction and require fresh native proof. Legacy remains the sole production path; real Apple/SignPath rehearsals remain deferred.
-Session checkpoint: **WINDOWS 16 KIB PIPE SUBWRITE LOCAL GREEN / NATIVE RUN NEXT — 2026-07-17, Codex implementation owner** — 55 focused cases with three declared skips, 698 broad relay cases with ten declared skips, 285 release contracts with eight declared skips, typecheck, targeted/full lint, reliability/max-lines, formatting, diff, and resolver isolation pass. The remote 64 KiB .NET buffer, source 64 KiB worker buffer, every other transfer family, and all product/default behavior remain unchanged.
+Current phase: Milestone 6 / Work Package 5 bounded runtime transfer — **In progress — matched 4 KiB Windows pipe correction locally green, native qualification next, 2026-07-17, Codex implementation owner**. Exact head `c93111a14bdb5390f99aa44a9004449625cbdcb5`, run `29564980476`, and Windows x64/ARM64 jobs `87835460661`/`87835460642` falsify client-only subdivision. `E-M6-WINDOWS-MATCHED-PIPE-LOCAL-001` matches client writes and remote .NET reads at 4 KiB while preserving the source 64 KiB buffer. Commit/push only this correction and require fresh native proof. Legacy remains the sole production path; real Apple/SignPath rehearsals remain deferred.
+Session checkpoint: **MATCHED 4 KIB PIPE LOCAL GREEN / NATIVE RUN NEXT — 2026-07-17, Codex implementation owner** — focused, broad relay, release-contract, typecheck, full lint/reliability/max-lines, formatting, diff, and resolver isolation pass. No product caller, default/fallback, Beta, tuple publication, or signing behavior changed.
Primary design: [SSH relay GitHub Release plan](./2026-07-14-ssh-relay-github-release-plan.html)
Motivating issues: [#8450](https://github.com/stablyai/orca/issues/8450), [#1693](https://github.com/stablyai/orca/issues/1693) @@ -20555,6 +20555,41 @@ src/main/ssh/ssh-relay-runtime-windows-system-ssh-openssh-full-size.test.ts` exi byte/tree validation, connection reuse, and exact fixture profile/directory teardown before advancing. +### E-M6-WINDOWS-PIPE-SUBWRITE-CI-RED-001 — client-only subdivision is insufficient + +- Date/source/run: 2026-07-17; exact head `c93111a14bdb5390f99aa44a9004449625cbdcb5`, run + [29564980476](https://github.com/stablyai/orca/actions/runs/29564980476), x64 job + [87835460661](https://github.com/stablyai/orca/actions/runs/29564980476/job/87835460661), and + ARM64 job [87835460642](https://github.com/stablyai/orca/actions/runs/29564980476/job/87835460642). +- Exact RED: x64 completes only the 18-byte `.version` file before the 60-second watchdog and retains + `THIRD_PARTY_LICENSES.txt`. ARM64 completes `.version` plus the 180,827-byte license file, then + stalls and retains `bin/node.exe` at 180,845 aggregate bytes. Tests finish in 72,580/77,910 ms; + both profile teardowns correctly expose retained fixture processes and locked `UsrClass.dat`. +- Classification/correction: architecture-variable progress falsifies client-only 16 KiB subdivision + and file-specific failure. Match Windows pipe writes and PowerShell/.NET reads at a conservative + 4,096-byte quantum, keep the source worker's 65,536-byte reusable buffer and zero-copy borrowed + views, and require fresh two-architecture full-size metrics and clean teardown. The HTML plan and + both Markdown checklists are updated before code. + +### E-M6-WINDOWS-MATCHED-PIPE-LOCAL-001 — matched 4 KiB Windows pipe quantum is locally green + +- Date/base/runner: 2026-07-17; uncommitted correction atop exact RED head + `c93111a14bdb5390f99aa44a9004449625cbdcb5` on Apple arm64, macOS 26.2, Node 26.5.0, pnpm + 10.24.0. Native Node 24 jobs remain authoritative. +- Boundary: client zero-copy subwrites and PowerShell/.NET reads are both capped at 4,096 bytes; the + source worker still owns one 65,536-byte buffer and releases it only after every subwrite callback. + Framing, size/tree verification, cancellation, POSIX/SFTP, SSH behavior, and product/default paths + are unchanged. +- Evidence: focused Windows tests pass 55 runnable cases with three skips in 3.31 seconds real + (141,262,848-byte max RSS). Broad relay passes 698 cases with ten skips in 34.40 seconds real + (306,413,568-byte max RSS). Release contracts pass 285 cases with eight skips in 28.62 seconds; + typecheck passes in 4.30 seconds; full lint passes with the existing 26 warnings, 41 reliability + gates, and 355-entry max-lines ratchet in 26.32 seconds. Formatting, diff, and resolver isolation + pass. The initial focused run failed only because its expected write count still encoded 16 KiB; + the limit-derived oracle is green. +- Residual: local Darwin skips native OpenSSH/PowerShell. Fresh x64/ARM64 full-size metrics and exact + teardown remain mandatory. + ## Accepted Gaps No product gap is accepted merely because it appears in this list. Each entry requires explicit @@ -20613,8 +20648,8 @@ The project is not complete until every applicable item below is checked with ev ## Next Required Action -Commit and push only the Windows pipe-subwrite correction recorded by -`E-M6-WINDOWS-PIPE-SUBWRITE-LOCAL-001`, then require exact-head Windows x64/ARM64 full-size +Commit and push only the matched 4 KiB Windows pipe correction recorded by +`E-M6-WINDOWS-MATCHED-PIPE-LOCAL-001`, then require exact-head Windows x64/ARM64 full-size serial/concurrent/collision/cancellation metrics, bounded memory, connection reuse, and exact profile/directory teardown evidence. Do not check the broad Milestone 6 Windows system-SSH item or add an Electron/startup/product importer, per-target mode wiring, fallback, tuple enablement, release diff --git a/docs/reference/plans/2026-07-14-ssh-relay-github-release-plan.html b/docs/reference/plans/2026-07-14-ssh-relay-github-release-plan.html index e4cb3357ea4..0cbcebacfd1 100644 --- a/docs/reference/plans/2026-07-14-ssh-relay-github-release-plan.html +++ b/docs/reference/plans/2026-07-14-ssh-relay-github-release-plan.html @@ -1004,12 +1004,14 @@ Windows uses a bounded binary PowerShell/.NET path and must not materialize the runtime as one JSON/base64 buffer. The client may subdivide each borrowed 64 KiB source chunk into smaller awaited Windows pipe writes, but must not release or - reuse that source buffer until every subwrite settles; the receiver retains its - fixed 64 KiB .NET buffer. SFTP uses bounded concurrency. After locally verifying - signed metadata and archive/tree hashes, transfer through authenticated SSH, - restore every declared executable bit, then execute the transferred bundled Node - to hash the entire staging tree. Verify the full tree, run bundled - Node/native-module and minimal PTY/watcher probes, write a structured + reuse that source buffer until every subwrite settles. Windows pipe writes and + .NET reads use the same conservative 4 KiB quantum to avoid platform pipe + backpressure cycles; the source worker retains its fixed 64 KiB buffer. SFTP uses + bounded concurrency. After locally verifying signed metadata and archive/tree + hashes, transfer through authenticated SSH, restore every declared executable bit, + then execute the transferred bundled Node to hash the entire staging tree. Verify + the full tree, run bundled Node/native-module and minimal PTY/watcher probes, + write a structured .install-complete containing schema, mode, and digest, atomically publish the immutable install, and only then launch. Preserve current locks, reconnect, and garbage collection. diff --git a/src/main/ssh/ssh-relay-runtime-windows-file-destination.test.ts b/src/main/ssh/ssh-relay-runtime-windows-file-destination.test.ts index 49d4030c1f1..42806251ef2 100644 --- a/src/main/ssh/ssh-relay-runtime-windows-file-destination.test.ts +++ b/src/main/ssh/ssh-relay-runtime-windows-file-destination.test.ts @@ -90,7 +90,7 @@ describe('SSH relay runtime Windows system-SSH file destination', () => { expect(script).toContain('[Console]::OpenStandardInput()') expect(script).toContain('[IO.FileMode]::CreateNew') expect(script).toContain('[IO.FileShare]::None') - expect(script).toContain('New-Object byte[] 65536') + expect(script).toContain('New-Object byte[] 4096') expect(script).toContain("-ne 'ORCAEND1'") expect(script).not.toContain('$inputStream.ReadByte()') for (const forbidden of ['ReadToEnd', 'CopyTo(', 'FromBase64String', 'ConvertFrom-Json']) { @@ -113,8 +113,11 @@ describe('SSH relay runtime Windows system-SSH file destination', () => { const destination = await opening await destination.write(Buffer.alloc(65_537, 7)) const closing = destination.close() - await vi.waitFor(() => expect(channel.write).toHaveBeenCalledTimes(7)) - expect(channel.write.mock.calls[6]?.[0]).toEqual(completionFrame) + const expectedWrites = + Math.ceil(65_537 / SSH_RELAY_RUNTIME_WINDOWS_FILE_DESTINATION_LIMITS.maximumPipeWriteBytes) + + 2 + await vi.waitFor(() => expect(channel.write).toHaveBeenCalledTimes(expectedWrites)) + expect(channel.write.mock.calls.at(-1)?.[0]).toEqual(completionFrame) resolve() await closing }) diff --git a/src/main/ssh/ssh-relay-runtime-windows-file-destination.ts b/src/main/ssh/ssh-relay-runtime-windows-file-destination.ts index 286193430e0..6dd0f656acf 100644 --- a/src/main/ssh/ssh-relay-runtime-windows-file-destination.ts +++ b/src/main/ssh/ssh-relay-runtime-windows-file-destination.ts @@ -17,8 +17,8 @@ const HEADER_MAGIC = Buffer.from('ORCARLY1', 'ascii') const COMPLETION_MAGIC = Buffer.from('ORCAEND1', 'ascii') const FIXED_HEADER_BYTES = 20 const MAXIMUM_PATH_BYTES = 32 * 1024 -const PAYLOAD_BUFFER_BYTES = 64 * 1024 -const MAXIMUM_PIPE_WRITE_BYTES = 16 * 1024 +const PAYLOAD_BUFFER_BYTES = 4 * 1024 +const MAXIMUM_PIPE_WRITE_BYTES = PAYLOAD_BUFFER_BYTES export const SSH_RELAY_RUNTIME_WINDOWS_FILE_DESTINATION_LIMITS = Object.freeze({ maximumHeaderBytes: FIXED_HEADER_BYTES + MAXIMUM_PATH_BYTES, @@ -181,8 +181,8 @@ export async function openSshRelayRuntimeWindowsFileDestination( if (payloadBytes + chunk.length > options.expectedSize) { return abortForSizeMismatch() } - // Why: native Win32-OpenSSH stalls on one borrowed 64 KiB pipe write; awaited subviews keep - // the source buffer borrowed until every byte has crossed the same retention boundary. + // Why: matched 4 KiB reads/writes avoid Win32 pipe backpressure cycles while zero-copy + // subviews keep the source's 64 KiB buffer borrowed until every byte settles. for (let offset = 0; offset < chunk.length; offset += MAXIMUM_PIPE_WRITE_BYTES) { await destination.write(chunk.subarray(offset, offset + MAXIMUM_PIPE_WRITE_BYTES)) }