Step 11d: stream params for logs and exec, and stdin EOF #29

Open
opened 2026-09-24 08:39:12 +00:00 by kayg · 24 comments
Owner

Found by step 15c (cli-stream, #11). The stream protocol's OPEN params lack:

  1. logs: server-side tail (last N lines) and follow (keep streaming, or stop at the end), plus since and timestamps if the engines support them. Today the CLI and web can't ask the engine for these.
  2. exec: argv (the command to run) and tty (true or false), plus env and workdir if sensible, per the Docker and Podman exec APIs.
  3. stdin EOF: the client sends EOF, but the engine PTY path never delivers it, so a real cat stays open until Ctrl-C. Define EOF in the contract (for example a half-close frame) and deliver it: close the exec's stdin for non-TTY, and send ^D or close for TTY, per the engine docs.

Contract first (docs/contracts.md §3.7 and the proto ExecParams and LogsParams), then the server, agent and engine, then the CLI and web use them. Backend step; packages/stream and the CLI adopt it after.

Found by step 15c (`cli-stream`, #11). The stream protocol's `OPEN` params lack: 1. **logs:** server-side `tail` (last N lines) and `follow` (keep streaming, or stop at the end), plus `since` and `timestamps` if the engines support them. Today the CLI and web can't ask the engine for these. 2. **exec:** `argv` (the command to run) and `tty` (true or false), plus `env` and `workdir` if sensible, per the Docker and Podman exec APIs. 3. **stdin EOF:** the client sends EOF, but the engine PTY path never delivers it, so a real `cat` stays open until Ctrl-C. Define EOF in the contract (for example a half-close frame) and deliver it: close the exec's stdin for non-TTY, and send `^D` or close for TTY, per the engine docs. Contract first (`docs/contracts.md` §3.7 and the proto `ExecParams` and `LogsParams`), then the server, agent and engine, then the CLI and web use them. Backend step; `packages/stream` and the CLI adopt it after.
Author
Owner

Starting step 11d on branch stream-params, based on dev HEAD ee62153839982d068586e3890bfe8749db8f1dab. I’m reading the settled stream contract and tracing logs/exec through proto, server, infraternal agent, engine, and packages/stream. I’ll commit and post the §3.7/§3.11 plus proto contract first, then implement the changes and run the requested Docker/rootless Podman tests and gates. I’ll keep changes confined to logs and exec paths.

Starting step 11d on branch `stream-params`, based on `dev` HEAD `ee62153839982d068586e3890bfe8749db8f1dab`. I’m reading the settled stream contract and tracing logs/exec through proto, server, infraternal agent, engine, and `packages/stream`. I’ll commit and post the §3.7/§3.11 plus proto contract first, then implement the changes and run the requested Docker/rootless Podman tests and gates. I’ll keep changes confined to logs and exec paths.
Author
Owner

Finding from the existing paths and engine references: run_logs in crates/agent/src/lib.rs currently forces follow: true, and BollardEngine::stream_logs in crates/engine/src/bollard_adapter.rs always asks the engine for timestamps: true. The exec adapter always creates Tty: true, with run_exec defaulting to /bin/sh. The new optional defaults will preserve those behaviors (follow=true, timestamps=true, tty=true, omitted argv uses /bin/sh).

Docker's logs endpoint exposes follow, tail, since, and timestamps, while exec create accepts Cmd, Env, WorkingDir, AttachStdin, and Tty: https://docs.docker.com/reference/api/engine/version/v1.40/ and https://docs.docker.com/reference/cli/docker/container/logs/.

Podman's current system service documents its Docker v1.40 compatibility API; its logs docs confirm follow, since, tail, and timestamps: https://docs.podman.io/en/latest/markdown/podman-system-service.1.html and https://docs.podman.io/en/latest/markdown/podman-logs.1.html. The Podman compatibility API schema documents the matching exec create fields: https://docs.podman.io/en/latest/_static/api.html.

Finding from the existing paths and engine references: `run_logs` in `crates/agent/src/lib.rs` currently forces `follow: true`, and `BollardEngine::stream_logs` in `crates/engine/src/bollard_adapter.rs` always asks the engine for `timestamps: true`. The exec adapter always creates `Tty: true`, with `run_exec` defaulting to `/bin/sh`. The new optional defaults will preserve those behaviors (`follow=true`, `timestamps=true`, `tty=true`, omitted `argv` uses `/bin/sh`). Docker's logs endpoint exposes `follow`, `tail`, `since`, and `timestamps`, while exec create accepts `Cmd`, `Env`, `WorkingDir`, `AttachStdin`, and `Tty`: https://docs.docker.com/reference/api/engine/version/v1.40/ and https://docs.docker.com/reference/cli/docker/container/logs/. Podman's current system service documents its Docker v1.40 compatibility API; its logs docs confirm `follow`, `since`, `tail`, and `timestamps`: https://docs.podman.io/en/latest/markdown/podman-system-service.1.html and https://docs.podman.io/en/latest/markdown/podman-logs.1.html. The Podman compatibility API schema documents the matching exec create fields: https://docs.podman.io/en/latest/_static/api.html.
Author
Owner

Contract-first slice committed: 46f6c64833b4f715b3d6a84a450eb1f43cb6b439.

Contract excerpt (§3.7/§3.11): logs accept bounded tail, follow, since and timestamps; omitted follow and timestamps preserve their current true defaults. Exec accepts bounded argv, tty, env and workdir; omitted tty preserves the web terminal's PTY behavior. EOF shuts down stdin for non-TTY exec and sends EOT (0x04) to TTY exec while output continues. The contract maps these to the Docker and Podman APIs with citations.

Proto gate: cargo test: 56 passed (3 suites, 1.66s).

Contract-first slice committed: `46f6c64833b4f715b3d6a84a450eb1f43cb6b439`. Contract excerpt (§3.7/§3.11): logs accept bounded `tail`, `follow`, `since` and `timestamps`; omitted `follow` and `timestamps` preserve their current `true` defaults. Exec accepts bounded `argv`, `tty`, `env` and `workdir`; omitted `tty` preserves the web terminal's PTY behavior. `EOF` shuts down stdin for non-TTY exec and sends EOT (`0x04`) to TTY exec while output continues. The contract maps these to the Docker and Podman APIs with citations. Proto gate: `cargo test: 56 passed (3 suites, 1.66s)`.
Author
Owner

Real Docker and rootless Podman engine adapter fixtures pass. The stream E2E then exposed a Docker edge case: a short tty:true command exits before the initial PTY resize and Docker returns HTTP 500. The adapter now treats a resize error as benign only if exec inspection confirms the process has exited; running execs still return the resize error. I also added stage and target labels to stream E2E open failures so later integration evidence identifies the failing case.

Real Docker and rootless Podman engine adapter fixtures pass. The stream E2E then exposed a Docker edge case: a short `tty:true` command exits before the initial PTY resize and Docker returns HTTP 500. The adapter now treats a resize error as benign only if exec inspection confirms the process has exited; running execs still return the resize error. I also added stage and target labels to stream E2E open failures so later integration evidence identifies the failing case.
Author
Owner

Step 11d complete.

Branch: stream-params
Head: 46763d786fe34fd7d51ecf100d8963e34614f398

Contract excerpt (docs/contracts.md §§3.7, 3.11):

exec OPEN params retain cols and rows and add optional argv, tty, env and workdir. Omitted argv runs the existing interactive shell (/bin/sh on the supported Linux engines); supplied argv is executed directly without shell expansion. Omitted tty means true, preserving the web terminal.

logs OPEN params add optional tail, follow, since and timestamps. tail is 0–100,000 lines from the end (0 returns no records); omission returns all available history. Omitted follow means true, preserving the current stream behavior. follow: false returns the available snapshot, then sends output EOF and a normal CLOSE.

EOF on an exec channel half-closes only client-to-process input. It is sent after the client's last DATA; the client may not send further DATA on that direction. For tty: false, the infraternal agent shuts down the engine's attached stdin write half while continuing to read stdout and stderr until the process exits. For tty: true, it writes the terminal EOT byte (0x04, Ctrl-D) to the PTY and keeps reading output.

API references in the contract: Docker Engine API, Docker logs options, Podman REST API, Podman service API modes, and Podman logs options.

scripts/verify fast output:

PASS fast: migrations are append-only
PASS fast: JavaScript dependencies
PASS fast: build web app
PASS fast: cargo fmt --check
PASS fast: cargo clippy
PASS fast: cargo test
PASS fast: svelte-check
PASS fast: UI package svelte-check
PASS fast: stream package typecheck
PASS fast: Vitest
PASS fast: UI package Vitest
PASS fast: stream package Vitest
PASS fast: OpenAPI and TypeScript client drift
PASS fast: terminal TypeScript
PASS fast: terminal Vitest

scripts/verify integration output:

PASS integration: JavaScript dependencies
PASS integration: build web app
PASS integration: build release binary
PASS integration: real HTTP behavior
PASS integration: Docker socket _ping
PASS integration: rootless Podman socket _ping
PASS integration: engine adapters against Docker and Podman
PASS integration: TypeScript stream client against Docker and Podman
PASS integration: stream authorization and CLOSE regressions against Docker
PASS integration: persistent PTY termination preempts blocked send
PASS integration: resource HTTP API against Docker and rootless Podman
PASS integration: compiled CLI and MCP against real server and engines
PASS integration: SSH contract against isolated real sshd

No push; progress.md remains excluded and uncommitted.

Step 11d complete. Branch: `stream-params` Head: `46763d786fe34fd7d51ecf100d8963e34614f398` Contract excerpt (`docs/contracts.md` §§3.7, 3.11): > `exec` OPEN params retain `cols` and `rows` and add optional `argv`, `tty`, `env` and `workdir`. Omitted `argv` runs the existing interactive shell (`/bin/sh` on the supported Linux engines); supplied `argv` is executed directly without shell expansion. Omitted `tty` means `true`, preserving the web terminal. > > `logs` OPEN params add optional `tail`, `follow`, `since` and `timestamps`. `tail` is 0–100,000 lines from the end (`0` returns no records); omission returns all available history. Omitted `follow` means `true`, preserving the current stream behavior. `follow: false` returns the available snapshot, then sends output `EOF` and a normal `CLOSE`. > > `EOF` on an `exec` channel half-closes only client-to-process input. It is sent after the client's last `DATA`; the client may not send further `DATA` on that direction. For `tty: false`, the infraternal agent shuts down the engine's attached stdin write half while continuing to read stdout and stderr until the process exits. For `tty: true`, it writes the terminal EOT byte (`0x04`, Ctrl-D) to the PTY and keeps reading output. API references in the contract: [Docker Engine API](https://docs.docker.com/reference/api/engine/version/v1.40/), [Docker logs options](https://docs.docker.com/reference/cli/docker/container/logs/), [Podman REST API](https://docs.podman.io/en/latest/_static/api.html), [Podman service API modes](https://docs.podman.io/en/latest/markdown/podman-system-service.1.html), and [Podman logs options](https://docs.podman.io/en/latest/markdown/podman-logs.1.html). `scripts/verify fast` output: ```text PASS fast: migrations are append-only PASS fast: JavaScript dependencies PASS fast: build web app PASS fast: cargo fmt --check PASS fast: cargo clippy PASS fast: cargo test PASS fast: svelte-check PASS fast: UI package svelte-check PASS fast: stream package typecheck PASS fast: Vitest PASS fast: UI package Vitest PASS fast: stream package Vitest PASS fast: OpenAPI and TypeScript client drift PASS fast: terminal TypeScript PASS fast: terminal Vitest ``` `scripts/verify integration` output: ```text PASS integration: JavaScript dependencies PASS integration: build web app PASS integration: build release binary PASS integration: real HTTP behavior PASS integration: Docker socket _ping PASS integration: rootless Podman socket _ping PASS integration: engine adapters against Docker and Podman PASS integration: TypeScript stream client against Docker and Podman PASS integration: stream authorization and CLOSE regressions against Docker PASS integration: persistent PTY termination preempts blocked send PASS integration: resource HTTP API against Docker and rootless Podman PASS integration: compiled CLI and MCP against real server and engines PASS integration: SSH contract against isolated real sshd ``` No push; `progress.md` remains excluded and uncommitted.
Author
Owner

Independent defensive review started. Branch: stream-params. Base: ee62153. Review head: 46763d7. Scope: stream OPEN log/exec params, stdin EOF, authorization, compatibility. Source remains read-only.

Independent defensive review started. Branch: stream-params. Base: ee62153. Review head: 46763d7. Scope: stream OPEN log/exec params, stdin EOF, authorization, compatibility. Source remains read-only.
Author
Owner

Independent defensive review started. Branch: stream-params. Base: ee62153. Review head: 46763d7. Scope: stream OPEN log/exec params, stdin EOF, authorization, compatibility. Source remains read-only.

Independent defensive review started. Branch: stream-params. Base: ee62153. Review head: 46763d7. Scope: stream OPEN log/exec params, stdin EOF, authorization, compatibility. Source remains read-only.
Author
Owner

Independent review of ee62153..46763d7 (stream-params)

F1 — blocks-merge: EOF can overtake a pending DATA send

Evidence: packages/stream/src/index.ts:319-345 lets send() wait asynchronously for input credit, while eof() immediately sends an EOF and sets localEof. When credit later arrives, the waiting send() rejects at line 336 and never sends its bytes. This violates docs/contracts.md §3.11's last-DATA-before-EOF rule and loses caller input. A targeted Bun probe using the unmodified StreamClient and an OPEN_OK with one byte of input credit produced frames=["Open","Eof"] and sendError="Error: channel is closed" after a waiting send("must-arrive"), eof(), then CREDIT. The probe is /tmp/review-stream-params-eof-race.ts (outside the worktree).

Test case: On each real Docker and rootless Podman fixture labelled with a unique infraternal.test run id, open non-TTY /bin/cat, constrain client input credit to one byte through a bounded stream test transport, call const pending = channel.send(marker), then channel.eof() before issuing the remaining credit. Assert the complete marker arrives before output EOF, pending resolves, and CLOSE has exit code zero. Also assert only one client EOF appears on the wire.

F2 — blocks-merge: follow:false can remain open indefinitely if an engine ignores the option

Evidence: crates/agent/src/lib.rs:919-1007 passes follow:false to the adapter, then loops on stream.next() until it returns None. There is no snapshot deadline, output bound, or other completion path for a compatible engine that ignores the flag. With a live container, the channel can stay open forever and never send the promised output EOF and normal CLOSE from docs/contracts.md §3.7. The existing packages/stream/src/e2e.ts covers compliant engines only.

Test case: For separately labelled, bounded Docker and rootless Podman containers, place a failure-injection REST proxy between Bollard and the real engine that forces the engine logs request to follow=true while the client OPEN requests follow:false; emit a fixed small history and then keep the container alive without further output. Assert the stream finishes with EOF and normal CLOSE within a defined bound and returns exactly the history. The current stream.next() wait does not finish.

F3 — blocks-merge: audit cannot identify an exec OPEN or record its argv shape

Evidence: crates/server/src/stream.rs:1067-1081 and :1687-1711 audit all channel kinds as action="stream.open" with only a target, outcome and error code. AuditEvent and audit_records have no channel-kind or argv-shape field (crates/server/src/storage/mod.rs:217-226, migration 0001_auth.sql:89). Thus a successful logs OPEN and a successful exec OPEN on the same container produce indistinguishable audit records apart from request id and time; exec argv count/shape is absent. This fails the job's requirement to record that an exec happened, with its argv shape, while redacting values. The new Debug implementations redact values but do not solve audit attribution.

Test case: On each labelled Docker and rootless Podman fixture, OPEN logs and then exec with argv=["/bin/echo", "secret-marker"], env=["SECRET=secret-marker"], and workdir="/tmp". Query the durable audit rows by request id. Assert the exec row has a distinct action/kind and a safe argv shape (e.g. argument count and byte lengths), while no row, log or error contains secret-marker or the environment value. The current schema cannot satisfy the kind/shape assertions.

Checked without a finding

Proto limits cover argv count, per-entry/total UTF-8 bytes, NUL, env key syntax/uniqueness, workdir length and NUL, tail and since range; the server and infraternal agent call that validation. ExecOptions maps command to Bollard cmd, leaves user=None, and does not expose container or privilege selection. Existing scopes and the final per-OPEN authorization check still apply; direct argv does not add a capability beyond the prior interactive shell for an ai_agent token. Omitted params retain the web terminal defaults. OpenAPI and generated TypeScript client add optional fields. Real-engine gate result follows below.

Gate output (verbatim)

scripts/verify fast:

PASS fast: migrations are append-only
PASS fast: JavaScript dependencies
PASS fast: build web app
PASS fast: cargo fmt --check
PASS fast: cargo clippy
PASS fast: cargo test
PASS fast: svelte-check
PASS fast: UI package svelte-check
PASS fast: stream package typecheck
PASS fast: Vitest
PASS fast: UI package Vitest
PASS fast: stream package Vitest
PASS fast: OpenAPI and TypeScript client drift
PASS fast: terminal TypeScript
PASS fast: terminal Vitest

scripts/verify integration (run once, started with load5 4.39 < 8):

PASS integration: JavaScript dependencies
PASS integration: build web app
PASS integration: build release binary
PASS integration: real HTTP behavior
PASS integration: Docker socket _ping
PASS integration: rootless Podman socket _ping
PASS integration: engine adapters against Docker and Podman
PASS integration: TypeScript stream client against Docker and Podman
PASS integration: stream authorization and CLOSE regressions against Docker
PASS integration: persistent PTY termination preempts blocked send
PASS integration: resource HTTP API against Docker and rootless Podman
PASS integration: compiled CLI and MCP against real server and engines
PASS integration: SSH contract against isolated real sshd

Review head: 46763d786fe34fd7d51ecf100d8963e34614f398. Source changes: none; commits: none.

# Independent review of `ee62153..46763d7` (`stream-params`) ## F1 — blocks-merge: EOF can overtake a pending DATA send Evidence: `packages/stream/src/index.ts:319-345` lets `send()` wait asynchronously for input credit, while `eof()` immediately sends an EOF and sets `localEof`. When credit later arrives, the waiting `send()` rejects at line 336 and never sends its bytes. This violates `docs/contracts.md` §3.11's last-DATA-before-EOF rule and loses caller input. A targeted Bun probe using the unmodified StreamClient and an OPEN_OK with one byte of input credit produced `frames=["Open","Eof"]` and `sendError="Error: channel is closed"` after a waiting `send("must-arrive")`, `eof()`, then CREDIT. The probe is `/tmp/review-stream-params-eof-race.ts` (outside the worktree). Test case: On each real Docker and rootless Podman fixture labelled with a unique `infraternal.test` run id, open non-TTY `/bin/cat`, constrain client input credit to one byte through a bounded stream test transport, call `const pending = channel.send(marker)`, then `channel.eof()` before issuing the remaining credit. Assert the complete marker arrives before output EOF, `pending` resolves, and CLOSE has exit code zero. Also assert only one client EOF appears on the wire. ## F2 — blocks-merge: `follow:false` can remain open indefinitely if an engine ignores the option Evidence: `crates/agent/src/lib.rs:919-1007` passes `follow:false` to the adapter, then loops on `stream.next()` until it returns None. There is no snapshot deadline, output bound, or other completion path for a compatible engine that ignores the flag. With a live container, the channel can stay open forever and never send the promised output EOF and normal CLOSE from `docs/contracts.md` §3.7. The existing `packages/stream/src/e2e.ts` covers compliant engines only. Test case: For separately labelled, bounded Docker and rootless Podman containers, place a failure-injection REST proxy between Bollard and the real engine that forces the engine logs request to `follow=true` while the client OPEN requests `follow:false`; emit a fixed small history and then keep the container alive without further output. Assert the stream finishes with EOF and normal CLOSE within a defined bound and returns exactly the history. The current `stream.next()` wait does not finish. ## F3 — blocks-merge: audit cannot identify an exec OPEN or record its argv shape Evidence: `crates/server/src/stream.rs:1067-1081` and `:1687-1711` audit all channel kinds as `action="stream.open"` with only a target, outcome and error code. `AuditEvent` and `audit_records` have no channel-kind or argv-shape field (`crates/server/src/storage/mod.rs:217-226`, migration `0001_auth.sql:89`). Thus a successful logs OPEN and a successful exec OPEN on the same container produce indistinguishable audit records apart from request id and time; exec argv count/shape is absent. This fails the job's requirement to record that an exec happened, with its argv shape, while redacting values. The new Debug implementations redact values but do not solve audit attribution. Test case: On each labelled Docker and rootless Podman fixture, OPEN logs and then exec with `argv=["/bin/echo", "secret-marker"]`, `env=["SECRET=secret-marker"]`, and `workdir="/tmp"`. Query the durable audit rows by request id. Assert the exec row has a distinct action/kind and a safe argv shape (e.g. argument count and byte lengths), while no row, log or error contains `secret-marker` or the environment value. The current schema cannot satisfy the kind/shape assertions. ## Checked without a finding Proto limits cover argv count, per-entry/total UTF-8 bytes, NUL, env key syntax/uniqueness, workdir length and NUL, tail and since range; the server and infraternal agent call that validation. `ExecOptions` maps command to Bollard `cmd`, leaves `user=None`, and does not expose container or privilege selection. Existing scopes and the final per-OPEN authorization check still apply; direct argv does not add a capability beyond the prior interactive shell for an `ai_agent` token. Omitted params retain the web terminal defaults. OpenAPI and generated TypeScript client add optional fields. Real-engine gate result follows below. ## Gate output (verbatim) `scripts/verify fast`: ```text PASS fast: migrations are append-only PASS fast: JavaScript dependencies PASS fast: build web app PASS fast: cargo fmt --check PASS fast: cargo clippy PASS fast: cargo test PASS fast: svelte-check PASS fast: UI package svelte-check PASS fast: stream package typecheck PASS fast: Vitest PASS fast: UI package Vitest PASS fast: stream package Vitest PASS fast: OpenAPI and TypeScript client drift PASS fast: terminal TypeScript PASS fast: terminal Vitest ``` `scripts/verify integration` (run once, started with load5 4.39 < 8): ```text PASS integration: JavaScript dependencies PASS integration: build web app PASS integration: build release binary PASS integration: real HTTP behavior PASS integration: Docker socket _ping PASS integration: rootless Podman socket _ping PASS integration: engine adapters against Docker and Podman PASS integration: TypeScript stream client against Docker and Podman PASS integration: stream authorization and CLOSE regressions against Docker PASS integration: persistent PTY termination preempts blocked send PASS integration: resource HTTP API against Docker and rootless Podman PASS integration: compiled CLI and MCP against real server and engines PASS integration: SSH contract against isolated real sshd ``` Review head: `46763d786fe34fd7d51ecf100d8963e34614f398`. Source changes: none; commits: none.
Author
Owner

Job #29 fix round started.
Branch: stream-params
Base head: 46763d786f
I am inspecting the three review findings and will add real Docker and rootless Podman regressions before the fast and integration gates.

Job #29 fix round started. Branch: stream-params Base head: 46763d786fe34fd7d51ecf100d8963e34614f398 I am inspecting the three review findings and will add real Docker and rootless Podman regressions before the fast and integration gates.
Author
Owner

Fix-round findings and evidence:

  • F1: EOF now waits behind an accepted credit-blocked send. The Docker/rootless Podman /bin/cat fixture checks that no DATA/EOF escapes before added credit, then verifies byte-exact DATA precedes one EOF. The first assertion also saw client CREDIT frames from echoed output; it now permits those while preserving the ordering check.
  • F2: follow:false snapshots end on engine completion, the OPEN-time timestamp edge, or a five-second monotonic deadline. The real-engine test forces follow:true against labelled, bounded Docker and rootless Podman fixtures; both passed and received clean EOF/CLOSE within the bound.
  • F3: audit details_json identifies channel kind and, for exec, argument count, program basename, effective TTY, and environment entry count. It omits argument values, environment values, and workdir. Malformed OPEN rows use channel_kind: unknown. Migration 0012 appends the field. Docker/Podman stream e2e queries persisted rows and checks log/exec distinction and redaction.

Test fixtures use per-run labels and bounded logging. The stale-container sweep that could remove another run's containers was removed. Fast and integration both passed once after the main fixes; I am rerunning them after the malformed-OPEN metadata addition.

Fix-round findings and evidence: - F1: EOF now waits behind an accepted credit-blocked send. The Docker/rootless Podman `/bin/cat` fixture checks that no DATA/EOF escapes before added credit, then verifies byte-exact DATA precedes one EOF. The first assertion also saw client CREDIT frames from echoed output; it now permits those while preserving the ordering check. - F2: `follow:false` snapshots end on engine completion, the OPEN-time timestamp edge, or a five-second monotonic deadline. The real-engine test forces `follow:true` against labelled, bounded Docker and rootless Podman fixtures; both passed and received clean EOF/CLOSE within the bound. - F3: audit `details_json` identifies channel kind and, for exec, argument count, program basename, effective TTY, and environment entry count. It omits argument values, environment values, and workdir. Malformed OPEN rows use `channel_kind: unknown`. Migration 0012 appends the field. Docker/Podman stream e2e queries persisted rows and checks log/exec distinction and redaction. Test fixtures use per-run labels and bounded logging. The stale-container sweep that could remove another run's containers was removed. Fast and integration both passed once after the main fixes; I am rerunning them after the malformed-OPEN metadata addition.
Author
Owner

Fix round complete.

Head: bb5ab185e79ee08b991b74ae708fdacde31ca931 (branch stream-params; not pushed).

  • F1 — Fixed. EOF waits behind an accepted send blocked on credit. The unit test checks DATA-before-EOF; the Docker/rootless Podman /bin/cat regression verifies the held send, exact bytes, and one EOF.
  • F2 — Fixed. follow:false stops when the engine ends, a timestamp passes the OPEN-time boundary, or the five-second monotonic deadline fires. Forced-follow real-engine coverage passed on Docker and rootless Podman and checked clean EOF/CLOSE.
  • F3 — Fixed. Migration 0012 adds durable OPEN details_json with channel kind and safe exec shape (argument count, program basename, TTY, environment entry count). Cross-engine coverage distinguishes logs from exec and verifies argv values, environment key/value, and workdir are absent.

Fixtures carry per-run labels and bounded logging. Removed the stale-container sweep that could select another run's fixture. progress.md remains ignored and uncommitted.

scripts/verify fast output:

PASS fast: migrations are append-only
PASS fast: JavaScript dependencies
PASS fast: build web app
PASS fast: cargo fmt --check
PASS fast: cargo clippy
PASS fast: cargo test
PASS fast: svelte-check
PASS fast: UI package svelte-check
PASS fast: stream package typecheck
PASS fast: Vitest
PASS fast: UI package Vitest
PASS fast: stream package Vitest
PASS fast: OpenAPI and TypeScript client drift
PASS fast: terminal TypeScript
PASS fast: terminal Vitest

scripts/verify integration output:

PASS integration: JavaScript dependencies
PASS integration: build web app
PASS integration: build release binary
PASS integration: real HTTP behavior
PASS integration: Docker socket _ping
PASS integration: rootless Podman socket _ping
PASS integration: engine adapters against Docker and Podman
PASS integration: bounded follow:false log snapshots against Docker and rootless Podman
PASS integration: TypeScript stream client against Docker and Podman
PASS integration: stream authorization and CLOSE regressions against Docker
PASS integration: persistent PTY termination preempts blocked send
PASS integration: resource HTTP API against Docker and rootless Podman
PASS integration: compiled CLI and MCP against real server and engines
PASS integration: SSH contract against isolated real sshd
Fix round complete. Head: `bb5ab185e79ee08b991b74ae708fdacde31ca931` (branch `stream-params`; not pushed). - F1 — Fixed. EOF waits behind an accepted send blocked on credit. The unit test checks DATA-before-EOF; the Docker/rootless Podman `/bin/cat` regression verifies the held send, exact bytes, and one EOF. - F2 — Fixed. `follow:false` stops when the engine ends, a timestamp passes the OPEN-time boundary, or the five-second monotonic deadline fires. Forced-follow real-engine coverage passed on Docker and rootless Podman and checked clean EOF/CLOSE. - F3 — Fixed. Migration 0012 adds durable OPEN `details_json` with channel kind and safe exec shape (argument count, program basename, TTY, environment entry count). Cross-engine coverage distinguishes logs from exec and verifies argv values, environment key/value, and workdir are absent. Fixtures carry per-run labels and bounded logging. Removed the stale-container sweep that could select another run's fixture. `progress.md` remains ignored and uncommitted. `scripts/verify fast` output: ```text PASS fast: migrations are append-only PASS fast: JavaScript dependencies PASS fast: build web app PASS fast: cargo fmt --check PASS fast: cargo clippy PASS fast: cargo test PASS fast: svelte-check PASS fast: UI package svelte-check PASS fast: stream package typecheck PASS fast: Vitest PASS fast: UI package Vitest PASS fast: stream package Vitest PASS fast: OpenAPI and TypeScript client drift PASS fast: terminal TypeScript PASS fast: terminal Vitest ``` `scripts/verify integration` output: ```text PASS integration: JavaScript dependencies PASS integration: build web app PASS integration: build release binary PASS integration: real HTTP behavior PASS integration: Docker socket _ping PASS integration: rootless Podman socket _ping PASS integration: engine adapters against Docker and Podman PASS integration: bounded follow:false log snapshots against Docker and rootless Podman PASS integration: TypeScript stream client against Docker and Podman PASS integration: stream authorization and CLOSE regressions against Docker PASS integration: persistent PTY termination preempts blocked send PASS integration: resource HTTP API against Docker and rootless Podman PASS integration: compiled CLI and MCP against real server and engines PASS integration: SSH contract against isolated real sshd ```
Author
Owner

Starting verification on branch stream-params at bb5ab185e79ee08b991b74ae708fdacde31ca931; integrating dev at a5fec35ee358542e471fd2ad9e019e84dd458bc0. I will renumber the unmerged stream audit migration to 0014_stream_open_audit_details.sql, then verify the upgrade path and review cases on Docker and rootless Podman.

Starting verification on branch `stream-params` at `bb5ab185e79ee08b991b74ae708fdacde31ca931`; integrating `dev` at `a5fec35ee358542e471fd2ad9e019e84dd458bc0`. I will renumber the unmerged stream audit migration to `0014_stream_open_audit_details.sql`, then verify the upgrade path and review cases on Docker and rootless Podman.
Author
Owner

Finding: origin/dev already contains SQLite migrations 0012_ssh_hosts.sql and 0013_stream_session_creator.sql; the branch-only audit details migration was renamed to 0014_stream_open_audit_details.sql in commit e363c9d before merging. The merge is in progress; the current remaining content conflict is limited to migration version assertions, which must account for versions 1–14.

Finding: `origin/dev` already contains SQLite migrations `0012_ssh_hosts.sql` and `0013_stream_session_creator.sql`; the branch-only audit details migration was renamed to `0014_stream_open_audit_details.sql` in commit `e363c9d` before merging. The merge is in progress; the current remaining content conflict is limited to migration version assertions, which must account for versions 1–14.
Author
Owner

Post-merge compile check found two SSH stream failure branches in crates/server/src/stream.rs still called the audit helper without the merged details_json argument. Both now pass the OPEN's safe audit details; generated OpenAPI/client output is still pending because the first generator invocation failed at compile time.

Post-merge compile check found two SSH stream failure branches in `crates/server/src/stream.rs` still called the audit helper without the merged `details_json` argument. Both now pass the OPEN's safe audit details; generated OpenAPI/client output is still pending because the first generator invocation failed at compile time.
Author
Owner

Another merged-source compile mismatch was the new dev CLI stream constructor omitting this branch's optional exec fields (argv, tty, env, workdir). It now uses None for each, preserving the existing interactive shell defaults.

Another merged-source compile mismatch was the new dev CLI stream constructor omitting this branch's optional exec fields (`argv`, `tty`, `env`, `workdir`). It now uses `None` for each, preserving the existing interactive shell defaults.
Author
Owner

Migration upgrade verification passes from a database initialized with the exact 13 SQLite migrations on origin/dev (a5fec35): upgrades_dev_a5fec35_database_with_stream_audit_details reported 1 passed; 0 failed. After server startup it recorded versions 1–14 and the pre-existing audit row had details_json = '{}'. OpenAPI and the TypeScript client were regenerated from the merged proto/server types.

Migration upgrade verification passes from a database initialized with the exact 13 SQLite migrations on `origin/dev` (`a5fec35`): `upgrades_dev_a5fec35_database_with_stream_audit_details` reported `1 passed; 0 failed`. After server startup it recorded versions 1–14 and the pre-existing audit row had `details_json = '{}'`. OpenAPI and the TypeScript client were regenerated from the merged proto/server types.
Author
Owner

Integration commit: 53ccddd (merge of origin/dev at a5fec35 into stream-params). The migration collision is resolved: dev migrations 0012/0013 remain unchanged and stream audit details is 0014. The migration upgrade test from dev’s exact 1–13 history passes. Merge compile mismatches in SSH audit failures and the CLI exec constructor were fixed; OpenAPI and the API client were regenerated from merged Rust types.

Integration commit: `53ccddd` (merge of `origin/dev` at `a5fec35` into `stream-params`). The migration collision is resolved: dev migrations 0012/0013 remain unchanged and stream audit details is 0014. The migration upgrade test from dev’s exact 1–13 history passes. Merge compile mismatches in SSH audit failures and the CLI exec constructor were fixed; OpenAPI and the API client were regenerated from merged Rust types.
Author
Owner

Integration finding: real-engine checks passed through resource HTTP, but scripts/verify integration failed in cli_and_mcp_use_the_public_http_contract at crates/infraternal/tests/cli_mcp.rs:2006: expected exit code Some(0), received Some(7). The gate also emitted 10,009 ms slow-command markers for the Docker and Podman logs commands; stream exec RTTs were 166 ms (Docker) and 294 ms (Podman). I am tracing the failing command before recording a final status.

Integration finding: real-engine checks passed through resource HTTP, but `scripts/verify integration` failed in `cli_and_mcp_use_the_public_http_contract` at `crates/infraternal/tests/cli_mcp.rs:2006`: expected exit code `Some(0)`, received `Some(7)`. The gate also emitted 10,009 ms slow-command markers for the Docker and Podman `logs` commands; stream exec RTTs were 166 ms (Docker) and 294 ms (Podman). I am tracing the failing command before recording a final status.
Author
Owner

Root cause for the CLI integration failure: stderr reported Could not write command output: Broken pipe. read_cli_stdout_exact returned as soon as it had the expected cat bytes and dropped the child stdout reader, while observe_exec_stdin_eof_behavior continued waiting for EOF behavior. I am changing the test helper to keep draining stdout until the child exits, so it can observe the existing PTY EOF limitation without manufacturing a CLI output failure.

Root cause for the CLI integration failure: stderr reported `Could not write command output: Broken pipe`. `read_cli_stdout_exact` returned as soon as it had the expected `cat` bytes and dropped the child stdout reader, while `observe_exec_stdin_eof_behavior` continued waiting for EOF behavior. I am changing the test helper to keep draining stdout until the child exits, so it can observe the existing PTY EOF limitation without manufacturing a CLI output failure.
Author
Owner

The targeted compiled CLI/MCP integration test now passes after commit 9f07684, which keeps the test's stdout reader draining while it observes piped cat EOF: test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out. It runs against real Docker and rootless Podman. The CLI's PTY-based exec -i path reports STREAM_EXEC_STDIN_EOF=blocked_by_current_pty and is interrupted as the test expects; the separate TypeScript stream /bin/cat test proves byte-exact DATA-before-EOF on both engines.

The targeted compiled CLI/MCP integration test now passes after commit `9f07684`, which keeps the test's stdout reader draining while it observes piped `cat` EOF: `test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out`. It runs against real Docker and rootless Podman. The CLI's PTY-based `exec -i` path reports `STREAM_EXEC_STDIN_EOF=blocked_by_current_pty` and is interrupted as the test expects; the separate TypeScript stream `/bin/cat` test proves byte-exact DATA-before-EOF on both engines.
Author
Owner

Integration finding: a rerun passed Docker/Podman engine adapters and the bounded follow:false snapshot test, then the TypeScript stream E2E's unrelated tail:5 assertion got 4 records on Docker. The fixture currently emits another line every 50 ms, while the finite snapshot excludes records timestamped after OPEN; this can move a boundary-crossing line into the engine's five-record tail and leave four after filtering. I am stabilizing the real-engine fixture by emitting finite history and waiting until the runtime CLI sees it before opening the tail snapshot.

Integration finding: a rerun passed Docker/Podman engine adapters and the bounded `follow:false` snapshot test, then the TypeScript stream E2E's unrelated `tail:5` assertion got 4 records on Docker. The fixture currently emits another line every 50 ms, while the finite snapshot excludes records timestamped after OPEN; this can move a boundary-crossing line into the engine's five-record tail and leave four after filtering. I am stabilizing the real-engine fixture by emitting finite history and waiting until the runtime CLI sees it before opening the tail snapshot.
Author
Owner

The flaky tail fixture is now finite and waits until the real Docker/Podman logs APIs expose all eight initial records before the stream server starts. Targeted rerun passed: test typescript_client_streams_logs_and_exec_for_docker_and_podman ... ok; test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 4 filtered out; finished in 6.59s. This rerun covers both engines, the credit-blocked /bin/cat DATA-before-EOF assertion, and safe logs/exec audit details.

The flaky tail fixture is now finite and waits until the real Docker/Podman logs APIs expose all eight initial records before the stream server starts. Targeted rerun passed: `test typescript_client_streams_logs_and_exec_for_docker_and_podman ... ok`; `test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 4 filtered out; finished in 6.59s`. This rerun covers both engines, the credit-blocked `/bin/cat` DATA-before-EOF assertion, and safe logs/exec audit details.
Author
Owner

Verification result

Final verified head: db9d877301b8c0f1c2f9b68f93eaedb0a995c005. Merge commit: 53ccddda721ef6092b99736c6aaf2595d7fd33ce (integrated dev at a5fec35ee358542e471fd2ad9e019e84dd458bc0).

The unmerged 0012_stream_open_audit_details.sql was renumbered to 0014_stream_open_audit_details.sql; dev migrations 0012_ssh_hosts.sql and 0013_stream_session_creator.sql remain unchanged. The upgrade test starts from dev's exact versions 1–13, upgrades to 14, and verifies the pre-existing audit row gets details_json = '{}'.

  1. PASS — Docker and rootless Podman /bin/cat stream E2E holds back one byte of credit, verifies no DATA/EOF was sent, grants credit, then checks byte-exact output and DATA-before-one-EOF frame order.
  2. PASS — Real-engine follow:false snapshot test forces follow:true underneath on Docker and rootless Podman; it receives the three existing records and clean EOF/CLOSE within the five-second bound plus the test tolerance.
  3. PASS — Docker and Podman audit rows distinguish logs and exec. Exec details record argument count 2, basename echo, tty:false, and one env entry; assertions find no argv/env marker, environment key, or /tmp workdir in audit text.

The browser gate passed against the production release server, including the Ghostty exec terminal. The compiled CLI/MCP test passed against the real server and both engines; its PTY-based exec -i EOF observer reports STREAM_EXEC_STDIN_EOF=blocked_by_current_pty and interrupts as the test expects. The same test emitted 10,009 ms slow-command markers for Docker and Podman logs, while its assertions passed. The SSH contract and SSH product stream tests both passed against isolated real sshd.

scripts/verify fast output

PASS fast: migrations are append-only
PASS fast: JavaScript dependencies
PASS fast: build web app
PASS fast: cargo fmt --check
PASS fast: cargo clippy
PASS fast: cargo test
PASS fast: svelte-check
PASS fast: UI package svelte-check
PASS fast: stream package typecheck
PASS fast: Vitest
PASS fast: UI package Vitest
PASS fast: stream package Vitest
PASS fast: OpenAPI and TypeScript client drift
PASS fast: terminal TypeScript
PASS fast: terminal Vitest

scripts/verify integration output

PASS integration: JavaScript dependencies
PASS integration: build web app
PASS integration: build release binary
PASS integration: real HTTP behavior
PASS integration: Docker socket _ping
PASS integration: rootless Podman socket _ping
PASS integration: engine adapters against Docker and Podman
PASS integration: bounded follow:false log snapshots against Docker and rootless Podman
PASS integration: TypeScript stream client against Docker and Podman
PASS integration: stream authorization and CLOSE regressions against Docker
PASS integration: persistent PTY termination preempts blocked send
PASS integration: resource HTTP API against Docker and rootless Podman
PASS integration: compiled CLI and MCP against real server and engines
PASS integration: SSH contract against isolated real sshd
PASS integration: SSH product API and stream against isolated real sshd

scripts/verify browser output

PASS browser: JavaScript dependencies
PASS browser: install Chromium and system dependencies
PASS browser: terminal fixture Playwright
PASS browser: build web app
PASS browser: build release binary
METRIC browser log flood: {"recordsPerSecond":17883,"frames":109,"fps":60,"retained":10000,"bytes":210000,"dropped":79400,"renderedRows":28}
PASS browser: Playwright against release server
PASS browser: terminal production first-load budget

No push; issue remains open.

## Verification result Final verified head: `db9d877301b8c0f1c2f9b68f93eaedb0a995c005`. Merge commit: `53ccddda721ef6092b99736c6aaf2595d7fd33ce` (integrated `dev` at `a5fec35ee358542e471fd2ad9e019e84dd458bc0`). The unmerged `0012_stream_open_audit_details.sql` was renumbered to `0014_stream_open_audit_details.sql`; dev migrations `0012_ssh_hosts.sql` and `0013_stream_session_creator.sql` remain unchanged. The upgrade test starts from dev's exact versions 1–13, upgrades to 14, and verifies the pre-existing audit row gets `details_json = '{}'`. 1. **PASS** — Docker and rootless Podman `/bin/cat` stream E2E holds back one byte of credit, verifies no DATA/EOF was sent, grants credit, then checks byte-exact output and DATA-before-one-EOF frame order. 2. **PASS** — Real-engine `follow:false` snapshot test forces `follow:true` underneath on Docker and rootless Podman; it receives the three existing records and clean EOF/CLOSE within the five-second bound plus the test tolerance. 3. **PASS** — Docker and Podman audit rows distinguish logs and exec. Exec details record argument count 2, basename `echo`, `tty:false`, and one env entry; assertions find no argv/env marker, environment key, or `/tmp` workdir in audit text. The browser gate passed against the production release server, including the Ghostty exec terminal. The compiled CLI/MCP test passed against the real server and both engines; its PTY-based `exec -i` EOF observer reports `STREAM_EXEC_STDIN_EOF=blocked_by_current_pty` and interrupts as the test expects. The same test emitted 10,009 ms slow-command markers for Docker and Podman `logs`, while its assertions passed. The SSH contract and SSH product stream tests both passed against isolated real `sshd`. ### `scripts/verify fast` output ```text PASS fast: migrations are append-only PASS fast: JavaScript dependencies PASS fast: build web app PASS fast: cargo fmt --check PASS fast: cargo clippy PASS fast: cargo test PASS fast: svelte-check PASS fast: UI package svelte-check PASS fast: stream package typecheck PASS fast: Vitest PASS fast: UI package Vitest PASS fast: stream package Vitest PASS fast: OpenAPI and TypeScript client drift PASS fast: terminal TypeScript PASS fast: terminal Vitest ``` ### `scripts/verify integration` output ```text PASS integration: JavaScript dependencies PASS integration: build web app PASS integration: build release binary PASS integration: real HTTP behavior PASS integration: Docker socket _ping PASS integration: rootless Podman socket _ping PASS integration: engine adapters against Docker and Podman PASS integration: bounded follow:false log snapshots against Docker and rootless Podman PASS integration: TypeScript stream client against Docker and Podman PASS integration: stream authorization and CLOSE regressions against Docker PASS integration: persistent PTY termination preempts blocked send PASS integration: resource HTTP API against Docker and rootless Podman PASS integration: compiled CLI and MCP against real server and engines PASS integration: SSH contract against isolated real sshd PASS integration: SSH product API and stream against isolated real sshd ``` ### `scripts/verify browser` output ```text PASS browser: JavaScript dependencies PASS browser: install Chromium and system dependencies PASS browser: terminal fixture Playwright PASS browser: build web app PASS browser: build release binary METRIC browser log flood: {"recordsPerSecond":17883,"frames":109,"fps":60,"retained":10000,"bytes":210000,"dropped":79400,"renderedRows":28} PASS browser: Playwright against release server PASS browser: terminal production first-load budget ``` No push; issue remains open.
Author
Owner

Orchestrator: step 11d is merged to dev at 1337efd and pushed. Gates on the exact merge head: fast 15/15, integration 15/15. Migration renumbered to 0014. Follow-up: the CLI's exec -i should send the new stdin EOF (15c today observes blocked_by_current_pty).

Orchestrator: **step 11d is merged to `dev` at `1337efd`** and pushed. Gates on the exact merge head: fast 15/15, integration 15/15. Migration renumbered to `0014`. Follow-up: the CLI's `exec -i` should send the new stdin EOF (15c today observes `blocked_by_current_pty`).
Commenting is not possible because the repository is archived.
No labels
No milestone
No project
No assignees
1 participant
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/infraternal-v0#29
No description provided.