Skip to content

fix: improve debug mode output and exit behavior#12

Merged
donbeave merged 5 commits into
mainfrom
fix/debug-mode-output-improvements
Apr 7, 2026
Merged

fix: improve debug mode output and exit behavior#12
donbeave merged 5 commits into
mainfrom
fix/debug-mode-output-improvements

Conversation

@donbeave

@donbeave donbeave commented Apr 7, 2026

Copy link
Copy Markdown
Member

Summary

  • Remove clear_screen on exit so Docker errors remain visible in the terminal
  • Skip intro/outro animations in debug mode (--debug implies --no-intro)
  • Fix spinner and [debug] log overlap during Docker-in-Docker wait
  • Add newline separator after interactive docker run to prevent cleanup logs from overlapping with Claude Code's TUI
  • Disable all clear_screen calls in debug mode via a global AtomicBool flag

Test plan

  • Run jackin load --debug and verify no screen clears happen
  • Run jackin load --debug and verify spinner + debug logs don't overlap during DinD wait
  • Run jackin load (no debug) and verify intro/outro animations still work
  • Run jackin load --debug with a failing container and verify the Docker error is visible

🤖 Generated with Claude Code

donbeave and others added 5 commits April 7, 2026 16:36
- Remove clear_screen on exit so Docker errors remain visible
- Skip intro/outro animations in debug mode (--debug implies --no-intro)
- Fix spinner and debug log overlap during DinD wait
- Add newline after interactive docker run to separate cleanup logs
- Disable all clear_screen calls in debug mode via global flag

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Speeds up CI runs by caching compiled dependencies between builds.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Bump actions/checkout v4 → v5 across all workflows
- Bump actions/upload-pages-artifact v3 → v4
- Add Swatinem/rust-cache to release test and build jobs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@donbeave donbeave merged commit 3d57d81 into main Apr 7, 2026
1 check passed
@donbeave donbeave deleted the fix/debug-mode-output-improvements branch April 7, 2026 09:44
donbeave added a commit that referenced this pull request Apr 20, 2026
…ovements

fix: improve debug mode output and exit behavior
donbeave added a commit that referenced this pull request Apr 21, 2026
…ovements

fix: improve debug mode output and exit behavior
donbeave added a commit that referenced this pull request Apr 21, 2026
fix: improve debug mode output and exit behavior
donbeave added a commit that referenced this pull request Apr 21, 2026
fix: improve debug mode output and exit behavior
donbeave added a commit that referenced this pull request Apr 21, 2026
fix: improve debug mode output and exit behavior
donbeave added a commit that referenced this pull request May 7, 2026
fix: improve debug mode output and exit behavior
Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Co-authored-by: Codex <codex@openai.com>
donbeave added a commit that referenced this pull request May 7, 2026
fix: improve debug mode output and exit behavior

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Co-authored-by: Codex <codex@openai.com>
donbeave added a commit that referenced this pull request May 7, 2026
- Remove clear_screen on exit so Docker errors remain visible
- Skip intro/outro animations in debug mode (--debug implies --no-intro)
- Fix spinner and debug log overlap during DinD wait
- Add newline after interactive docker run to separate cleanup logs
- Disable all clear_screen calls in debug mode via global flag

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Co-authored-by: Claude <noreply@anthropic.com>
donbeave added a commit that referenced this pull request May 7, 2026
Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Co-authored-by: Claude <noreply@anthropic.com>
donbeave added a commit that referenced this pull request May 7, 2026
Speeds up CI runs by caching compiled dependencies between builds.

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Co-authored-by: Claude <noreply@anthropic.com>
donbeave added a commit that referenced this pull request May 7, 2026
- Bump actions/checkout v4 → v5 across all workflows
- Bump actions/upload-pages-artifact v3 → v4
- Add Swatinem/rust-cache to release test and build jobs

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Co-authored-by: Claude <noreply@anthropic.com>
donbeave added a commit that referenced this pull request May 7, 2026
fix: improve debug mode output and exit behavior

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Co-authored-by: Codex <codex@openai.com>
donbeave added a commit that referenced this pull request May 23, 2026
Trust-but-verify pass on the 15-item /code-review max-effort report
that followed the parallel-agent capture commit. Splits cleanly from
the in-flight env-config refactor by touching only the host-side
session-inventory and snapshot-fanout paths plus the capsule socket
layer; parallel-agent territory (main.rs, daemon.rs, session.rs,
launch.rs, env_model.rs, derived_image.rs, entrypoint.sh) stays
untouched.

Findings addressed:

* `src/console/manager/state.rs` — fan-out worker panics now surface
  through the existing `debug_log` path: `h.join()` failures are
  downcast to a string and re-routed as `Err(anyhow!)` so the slot
  is no longer dropped by `filter_map(|h| h.join().ok())`. Snapshot
  fan-out is also bounded by `SNAPSHOT_FANOUT_CHUNK = 8` so a host
  with dozens of active containers does not spawn dozens of OS
  threads per 500 ms refresh tick; each chunk's wall-clock cost is
  still bounded by the slowest fetch in that chunk. The fan-out
  body moves into a free `fetch_snapshots_parallel` helper so
  `refresh_instances` stays under the clippy line-count gate.

* `src/runtime/attach.rs` — `JACKIN_STATUS_CMD` now gates on
  `test -S /jackin/run/jackin.sock` so the early-bring-up window
  (between container start and the daemon binding its socket, plus
  any pre-`setup-once` time) does not leak operator-visible stderr
  from a binary that exists but cannot serve yet. `test -S` exits
  silently with status 1 if the socket is absent; `||` short-
  circuits at the first failure only — no `|| true` masking the
  second command's errors once the socket exists. `parse_jackin_sessions`
  drops the strict `len() != expected` equality in favour of
  `take(expected)` so trailing footer lines or labels that
  `Display`-emit a non-`[` continuation line no longer flip the
  parse to Unavailable; the count check is now a lower-bound
  (`< expected`), which still catches real format drift but
  tolerates harmless decoration. The header search lifts a shared
  `parse_session_count` helper used by both `inspect_agent_sessions`
  here and `isolation::finalize::has_jackin_sessions`, replacing
  the duplicate parser that was warned about in finding #3.

* `src/isolation/finalize.rs` — `parse_session_count` is now
  `runtime::attach::parse_session_count`; the local copy is gone.
  One header parser, one definition of malformed, no silent
  divergence on edge cases.

* `crates/jackin-capsule/src/socket.rs` — accept-loop cap-rejection
  log now fires exactly once on the saturation transition + once on
  the recovery transition, with per-drop messages demoted to
  `cdebug!` so a flood attacker (the exact threat
  `MAX_CONCURRENT_CLIENTS` defends against) cannot drown the
  compact-tier log. Per-backoff line also demoted to `cdebug!` —
  the accept-error clog above already names the failure, so the
  1-line-per-failure compact-log invariant holds. `start_listener_at`
  splits behind a new `start_listener_at_with_limiter` test-only
  helper that returns the inner `Arc<Semaphore>`. The cap regression
  test now reads `limiter.available_permits()` directly instead of
  racing `rx.recv()` against a 300 ms wall-clock deadline —
  cap-sensitive, not timing-sensitive. Wall-clock `timeout()`
  windows widen to 2 s for the remaining positive assertions so a
  slow CI runner does not produce spurious failures.

* `crates/jackin-capsule/src/protocol/attach.rs` — new
  `hello_env_count_over_cap_is_rejected_by_decoder_with_full_payload`
  variant supplies a fully-populated payload of `MAX_HELLO_ENV + 1`
  real entries so the per-entry boundary is verified after the read
  loop, not just at the count declaration. The earlier test only
  exercised the front-of-loop guard; a regression that moved the
  cap check below the per-entry loop would slip past it silently.

Deferred (parallel-agent territory or out-of-scope for this pass):

* `crates/jackin-capsule/src/session.rs` kitty single-pop vs N-push
  asymmetry (finding #6) and `focus_swap_reset_*` test-name
  overpromises (finding #12) — `session.rs` is mid-rebase by the
  parallel agent.
* `crates/jackin-capsule/src/main.rs` `--focus` eprintln eaten by
  alt-screen swap (finding #8) — `main.rs` mid-rebase.
* `src/runtime/launch.rs` `fake_docker_for_clean_attached_exit`
  fixture ordering (finding #14) — `launch.rs` mid-rebase under
  the env-config migration.

Setup-once gating left untouched: `docker/runtime/entrypoint.sh`
already gates on `/jackin/state/hooks/setup-once.done` (line 78,
runs only if marker absent; line 88 writes the marker on success).
The new `test -S` socket gate on the host-side status query is the
orthogonal half of the "don't query during early bring-up" rule.

Co-authored-by: Claude <noreply@anthropic.com>
Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
donbeave added a commit that referenced this pull request May 23, 2026
Trust-but-verify pass on the 15-item /code-review max-effort report
that followed the parallel-agent capture commit. Splits cleanly from
the in-flight env-config refactor by touching only the host-side
session-inventory and snapshot-fanout paths plus the capsule socket
layer; parallel-agent territory (main.rs, daemon.rs, session.rs,
launch.rs, env_model.rs, derived_image.rs, entrypoint.sh) stays
untouched.

Findings addressed:

* `src/console/manager/state.rs` — fan-out worker panics now surface
  through the existing `debug_log` path: `h.join()` failures are
  downcast to a string and re-routed as `Err(anyhow!)` so the slot
  is no longer dropped by `filter_map(|h| h.join().ok())`. Snapshot
  fan-out is also bounded by `SNAPSHOT_FANOUT_CHUNK = 8` so a host
  with dozens of active containers does not spawn dozens of OS
  threads per 500 ms refresh tick; each chunk's wall-clock cost is
  still bounded by the slowest fetch in that chunk. The fan-out
  body moves into a free `fetch_snapshots_parallel` helper so
  `refresh_instances` stays under the clippy line-count gate.

* `src/runtime/attach.rs` — `JACKIN_STATUS_CMD` now gates on
  `test -S /jackin/run/jackin.sock` so the early-bring-up window
  (between container start and the daemon binding its socket, plus
  any pre-`setup-once` time) does not leak operator-visible stderr
  from a binary that exists but cannot serve yet. `test -S` exits
  silently with status 1 if the socket is absent; `||` short-
  circuits at the first failure only — no `|| true` masking the
  second command's errors once the socket exists. `parse_jackin_sessions`
  drops the strict `len() != expected` equality in favour of
  `take(expected)` so trailing footer lines or labels that
  `Display`-emit a non-`[` continuation line no longer flip the
  parse to Unavailable; the count check is now a lower-bound
  (`< expected`), which still catches real format drift but
  tolerates harmless decoration. The header search lifts a shared
  `parse_session_count` helper used by both `inspect_agent_sessions`
  here and `isolation::finalize::has_jackin_sessions`, replacing
  the duplicate parser that was warned about in finding #3.

* `src/isolation/finalize.rs` — `parse_session_count` is now
  `runtime::attach::parse_session_count`; the local copy is gone.
  One header parser, one definition of malformed, no silent
  divergence on edge cases.

* `crates/jackin-capsule/src/socket.rs` — accept-loop cap-rejection
  log now fires exactly once on the saturation transition + once on
  the recovery transition, with per-drop messages demoted to
  `cdebug!` so a flood attacker (the exact threat
  `MAX_CONCURRENT_CLIENTS` defends against) cannot drown the
  compact-tier log. Per-backoff line also demoted to `cdebug!` —
  the accept-error clog above already names the failure, so the
  1-line-per-failure compact-log invariant holds. `start_listener_at`
  splits behind a new `start_listener_at_with_limiter` test-only
  helper that returns the inner `Arc<Semaphore>`. The cap regression
  test now reads `limiter.available_permits()` directly instead of
  racing `rx.recv()` against a 300 ms wall-clock deadline —
  cap-sensitive, not timing-sensitive. Wall-clock `timeout()`
  windows widen to 2 s for the remaining positive assertions so a
  slow CI runner does not produce spurious failures.

* `crates/jackin-capsule/src/protocol/attach.rs` — new
  `hello_env_count_over_cap_is_rejected_by_decoder_with_full_payload`
  variant supplies a fully-populated payload of `MAX_HELLO_ENV + 1`
  real entries so the per-entry boundary is verified after the read
  loop, not just at the count declaration. The earlier test only
  exercised the front-of-loop guard; a regression that moved the
  cap check below the per-entry loop would slip past it silently.

Deferred (parallel-agent territory or out-of-scope for this pass):

* `crates/jackin-capsule/src/session.rs` kitty single-pop vs N-push
  asymmetry (finding #6) and `focus_swap_reset_*` test-name
  overpromises (finding #12) — `session.rs` is mid-rebase by the
  parallel agent.
* `crates/jackin-capsule/src/main.rs` `--focus` eprintln eaten by
  alt-screen swap (finding #8) — `main.rs` mid-rebase.
* `src/runtime/launch.rs` `fake_docker_for_clean_attached_exit`
  fixture ordering (finding #14) — `launch.rs` mid-rebase under
  the env-config migration.

Setup-once gating left untouched: `docker/runtime/entrypoint.sh`
already gates on `/jackin/state/hooks/setup-once.done` (line 78,
runs only if marker absent; line 88 writes the marker on success).
The new `test -S` socket gate on the host-side status query is the
orthogonal half of the "don't query during early bring-up" rule.

Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
donbeave added a commit that referenced this pull request May 23, 2026
Trust-but-verify pass on the 15-item /code-review max-effort report
that followed the parallel-agent capture commit. Splits cleanly from
the in-flight env-config refactor by touching only the host-side
session-inventory and snapshot-fanout paths plus the capsule socket
layer; parallel-agent territory (main.rs, daemon.rs, session.rs,
launch.rs, env_model.rs, derived_image.rs, entrypoint.sh) stays
untouched.

Findings addressed:

* `src/console/manager/state.rs` — fan-out worker panics now surface
  through the existing `debug_log` path: `h.join()` failures are
  downcast to a string and re-routed as `Err(anyhow!)` so the slot
  is no longer dropped by `filter_map(|h| h.join().ok())`. Snapshot
  fan-out is also bounded by `SNAPSHOT_FANOUT_CHUNK = 8` so a host
  with dozens of active containers does not spawn dozens of OS
  threads per 500 ms refresh tick; each chunk's wall-clock cost is
  still bounded by the slowest fetch in that chunk. The fan-out
  body moves into a free `fetch_snapshots_parallel` helper so
  `refresh_instances` stays under the clippy line-count gate.

* `src/runtime/attach.rs` — `JACKIN_STATUS_CMD` now gates on
  `test -S /jackin/run/jackin.sock` so the early-bring-up window
  (between container start and the daemon binding its socket, plus
  any pre-`setup-once` time) does not leak operator-visible stderr
  from a binary that exists but cannot serve yet. `test -S` exits
  silently with status 1 if the socket is absent; `||` short-
  circuits at the first failure only — no `|| true` masking the
  second command's errors once the socket exists. `parse_jackin_sessions`
  drops the strict `len() != expected` equality in favour of
  `take(expected)` so trailing footer lines or labels that
  `Display`-emit a non-`[` continuation line no longer flip the
  parse to Unavailable; the count check is now a lower-bound
  (`< expected`), which still catches real format drift but
  tolerates harmless decoration. The header search lifts a shared
  `parse_session_count` helper used by both `inspect_agent_sessions`
  here and `isolation::finalize::has_jackin_sessions`, replacing
  the duplicate parser that was warned about in finding #3.

* `src/isolation/finalize.rs` — `parse_session_count` is now
  `runtime::attach::parse_session_count`; the local copy is gone.
  One header parser, one definition of malformed, no silent
  divergence on edge cases.

* `crates/jackin-capsule/src/socket.rs` — accept-loop cap-rejection
  log now fires exactly once on the saturation transition + once on
  the recovery transition, with per-drop messages demoted to
  `cdebug!` so a flood attacker (the exact threat
  `MAX_CONCURRENT_CLIENTS` defends against) cannot drown the
  compact-tier log. Per-backoff line also demoted to `cdebug!` —
  the accept-error clog above already names the failure, so the
  1-line-per-failure compact-log invariant holds. `start_listener_at`
  splits behind a new `start_listener_at_with_limiter` test-only
  helper that returns the inner `Arc<Semaphore>`. The cap regression
  test now reads `limiter.available_permits()` directly instead of
  racing `rx.recv()` against a 300 ms wall-clock deadline —
  cap-sensitive, not timing-sensitive. Wall-clock `timeout()`
  windows widen to 2 s for the remaining positive assertions so a
  slow CI runner does not produce spurious failures.

* `crates/jackin-capsule/src/protocol/attach.rs` — new
  `hello_env_count_over_cap_is_rejected_by_decoder_with_full_payload`
  variant supplies a fully-populated payload of `MAX_HELLO_ENV + 1`
  real entries so the per-entry boundary is verified after the read
  loop, not just at the count declaration. The earlier test only
  exercised the front-of-loop guard; a regression that moved the
  cap check below the per-entry loop would slip past it silently.

Deferred (parallel-agent territory or out-of-scope for this pass):

* `crates/jackin-capsule/src/session.rs` kitty single-pop vs N-push
  asymmetry (finding #6) and `focus_swap_reset_*` test-name
  overpromises (finding #12) — `session.rs` is mid-rebase by the
  parallel agent.
* `crates/jackin-capsule/src/main.rs` `--focus` eprintln eaten by
  alt-screen swap (finding #8) — `main.rs` mid-rebase.
* `src/runtime/launch.rs` `fake_docker_for_clean_attached_exit`
  fixture ordering (finding #14) — `launch.rs` mid-rebase under
  the env-config migration.

Setup-once gating left untouched: `docker/runtime/entrypoint.sh`
already gates on `/jackin/state/hooks/setup-once.done` (line 78,
runs only if marker absent; line 88 writes the marker on success).
The new `test -S` socket gate on the host-side status query is the
orthogonal half of the "don't query during early bring-up" rule.

Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
donbeave added a commit that referenced this pull request May 23, 2026
Trust-but-verify pass on the 15-item /code-review max-effort report
that followed the parallel-agent capture commit. Splits cleanly from
the in-flight env-config refactor by touching only the host-side
session-inventory and snapshot-fanout paths plus the capsule socket
layer; parallel-agent territory (main.rs, daemon.rs, session.rs,
launch.rs, env_model.rs, derived_image.rs, entrypoint.sh) stays
untouched.

Findings addressed:

* `src/console/manager/state.rs` — fan-out worker panics now surface
  through the existing `debug_log` path: `h.join()` failures are
  downcast to a string and re-routed as `Err(anyhow!)` so the slot
  is no longer dropped by `filter_map(|h| h.join().ok())`. Snapshot
  fan-out is also bounded by `SNAPSHOT_FANOUT_CHUNK = 8` so a host
  with dozens of active containers does not spawn dozens of OS
  threads per 500 ms refresh tick; each chunk's wall-clock cost is
  still bounded by the slowest fetch in that chunk. The fan-out
  body moves into a free `fetch_snapshots_parallel` helper so
  `refresh_instances` stays under the clippy line-count gate.

* `src/runtime/attach.rs` — `JACKIN_STATUS_CMD` now gates on
  `test -S /jackin/run/jackin.sock` so the early-bring-up window
  (between container start and the daemon binding its socket, plus
  any pre-`setup-once` time) does not leak operator-visible stderr
  from a binary that exists but cannot serve yet. `test -S` exits
  silently with status 1 if the socket is absent; `||` short-
  circuits at the first failure only — no `|| true` masking the
  second command's errors once the socket exists. `parse_jackin_sessions`
  drops the strict `len() != expected` equality in favour of
  `take(expected)` so trailing footer lines or labels that
  `Display`-emit a non-`[` continuation line no longer flip the
  parse to Unavailable; the count check is now a lower-bound
  (`< expected`), which still catches real format drift but
  tolerates harmless decoration. The header search lifts a shared
  `parse_session_count` helper used by both `inspect_agent_sessions`
  here and `isolation::finalize::has_jackin_sessions`, replacing
  the duplicate parser that was warned about in finding #3.

* `src/isolation/finalize.rs` — `parse_session_count` is now
  `runtime::attach::parse_session_count`; the local copy is gone.
  One header parser, one definition of malformed, no silent
  divergence on edge cases.

* `crates/jackin-capsule/src/socket.rs` — accept-loop cap-rejection
  log now fires exactly once on the saturation transition + once on
  the recovery transition, with per-drop messages demoted to
  `cdebug!` so a flood attacker (the exact threat
  `MAX_CONCURRENT_CLIENTS` defends against) cannot drown the
  compact-tier log. Per-backoff line also demoted to `cdebug!` —
  the accept-error clog above already names the failure, so the
  1-line-per-failure compact-log invariant holds. `start_listener_at`
  splits behind a new `start_listener_at_with_limiter` test-only
  helper that returns the inner `Arc<Semaphore>`. The cap regression
  test now reads `limiter.available_permits()` directly instead of
  racing `rx.recv()` against a 300 ms wall-clock deadline —
  cap-sensitive, not timing-sensitive. Wall-clock `timeout()`
  windows widen to 2 s for the remaining positive assertions so a
  slow CI runner does not produce spurious failures.

* `crates/jackin-capsule/src/protocol/attach.rs` — new
  `hello_env_count_over_cap_is_rejected_by_decoder_with_full_payload`
  variant supplies a fully-populated payload of `MAX_HELLO_ENV + 1`
  real entries so the per-entry boundary is verified after the read
  loop, not just at the count declaration. The earlier test only
  exercised the front-of-loop guard; a regression that moved the
  cap check below the per-entry loop would slip past it silently.

Deferred (parallel-agent territory or out-of-scope for this pass):

* `crates/jackin-capsule/src/session.rs` kitty single-pop vs N-push
  asymmetry (finding #6) and `focus_swap_reset_*` test-name
  overpromises (finding #12) — `session.rs` is mid-rebase by the
  parallel agent.
* `crates/jackin-capsule/src/main.rs` `--focus` eprintln eaten by
  alt-screen swap (finding #8) — `main.rs` mid-rebase.
* `src/runtime/launch.rs` `fake_docker_for_clean_attached_exit`
  fixture ordering (finding #14) — `launch.rs` mid-rebase under
  the env-config migration.

Setup-once gating left untouched: `docker/runtime/entrypoint.sh`
already gates on `/jackin/state/hooks/setup-once.done` (line 78,
runs only if marker absent; line 88 writes the marker on success).
The new `test -S` socket gate on the host-side status query is the
orthogonal half of the "don't query during early bring-up" rule.

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Co-authored-by: Codex <codex@openai.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.

1 participant