Skip to content

fix: normalize relative paths and add integration tests#6

Merged
donbeave merged 1 commit into
mainfrom
feature/relative-paths-tests
Apr 6, 2026
Merged

fix: normalize relative paths and add integration tests#6
donbeave merged 1 commit into
mainfrom
feature/relative-paths-tests

Conversation

@donbeave

@donbeave donbeave commented Apr 6, 2026

Copy link
Copy Markdown
Member

Summary

  • resolve_path now normalizes . and .. components so paths like ../sibling resolve to clean absolute paths (e.g. /a/c instead of /a/b/../c)
  • config mount add --src now resolves relative paths (was passing raw input directly)
  • Added 10 new tests: 7 unit tests for normalize_path/resolve_path/parse_mount_spec_resolved and 3 integration tests mirroring exact CLI scenarios (workspace add with relative workdir/mounts, workspace edit with relative mount)
  • Updated AGENTS.md with branching policy requiring feature branches for all new work

Test plan

  • All 152 tests pass (cargo test)
  • Manual verification: jackin workspace add ws --workdir . --mount ../sibling resolves to clean absolute paths
  • Manual verification: jackin workspace edit ws --mount relative-dir resolves correctly

🤖 Generated with Claude Code

resolve_path now normalizes path components so `../sibling` becomes a
clean absolute path instead of `/a/b/../sibling`. Also resolves relative
`--src` in `config mount add` and adds integration tests that mirror the
exact CLI scenarios (workspace add/edit with relative workdir and mounts).
Updates AGENTS.md with branching policy requiring feature branches.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@donbeave donbeave merged commit c9b06b4 into main Apr 6, 2026
1 check failed
@donbeave donbeave deleted the feature/relative-paths-tests branch April 6, 2026 09:35
donbeave added a commit that referenced this pull request Apr 20, 2026
- Move 9 TODO items from monolithic TODO.md into separate files in todo/
- Each file is a self-contained design doc with problem, options, and
  related source files for easy agent handoff
- Mark resolved security findings (#3, #4, #6, #7) in SECURITY_REVIEW_FINDINGS.md
- Update PROJECT_STRUCTURE.md with todo/ section and TESTING.md entry
- TODO.md becomes an index pointing to todo/ files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
donbeave added a commit that referenced this pull request Apr 20, 2026
…ts (#6)

resolve_path now normalizes path components so `../sibling` becomes a
clean absolute path instead of `/a/b/../sibling`. Also resolves relative
`--src` in `config mount add` and adds integration tests that mirror the
exact CLI scenarios (workspace add/edit with relative workdir and mounts).
Updates AGENTS.md with branching policy requiring feature branches.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
donbeave added a commit that referenced this pull request Apr 21, 2026
- Move 9 TODO items from monolithic TODO.md into separate files in todo/
- Each file is a self-contained design doc with problem, options, and
  related source files for easy agent handoff
- Mark resolved security findings (#3, #4, #6, #7) in SECURITY_REVIEW_FINDINGS.md
- Update PROJECT_STRUCTURE.md with todo/ section and TESTING.md entry
- TODO.md becomes an index pointing to todo/ files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
donbeave added a commit that referenced this pull request Apr 21, 2026
…ts (#6)

resolve_path now normalizes path components so `../sibling` becomes a
clean absolute path instead of `/a/b/../sibling`. Also resolves relative
`--src` in `config mount add` and adds integration tests that mirror the
exact CLI scenarios (workspace add/edit with relative workdir and mounts).
Updates AGENTS.md with branching policy requiring feature branches.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
donbeave added a commit that referenced this pull request Apr 21, 2026
- Move 9 TODO items from monolithic TODO.md into separate files in todo/
- Each file is a self-contained design doc with problem, options, and
  related source files for easy agent handoff
- Mark resolved security findings (#3, #4, #6, #7) in SECURITY_REVIEW_FINDINGS.md
- Update PROJECT_STRUCTURE.md with todo/ section and TESTING.md entry
- TODO.md becomes an index pointing to todo/ files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
donbeave added a commit that referenced this pull request Apr 21, 2026
…ts (#6)

resolve_path now normalizes path components so `../sibling` becomes a
clean absolute path instead of `/a/b/../sibling`. Also resolves relative
`--src` in `config mount add` and adds integration tests that mirror the
exact CLI scenarios (workspace add/edit with relative workdir and mounts).
Updates AGENTS.md with branching policy requiring feature branches.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
donbeave added a commit that referenced this pull request Apr 21, 2026
- Move 9 TODO items from monolithic TODO.md into separate files in todo/
- Each file is a self-contained design doc with problem, options, and
  related source files for easy agent handoff
- Mark resolved security findings (#3, #4, #6, #7) in SECURITY_REVIEW_FINDINGS.md
- Update PROJECT_STRUCTURE.md with todo/ section and TESTING.md entry
- TODO.md becomes an index pointing to todo/ files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
donbeave added a commit that referenced this pull request Apr 21, 2026
…ts (#6)

resolve_path now normalizes path components so `../sibling` becomes a
clean absolute path instead of `/a/b/../sibling`. Also resolves relative
`--src` in `config mount add` and adds integration tests that mirror the
exact CLI scenarios (workspace add/edit with relative workdir and mounts).
Updates AGENTS.md with branching policy requiring feature branches.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
donbeave added a commit that referenced this pull request Apr 21, 2026
- Move 9 TODO items from monolithic TODO.md into separate files in todo/
- Each file is a self-contained design doc with problem, options, and
  related source files for easy agent handoff
- Mark resolved security findings (#3, #4, #6, #7) in SECURITY_REVIEW_FINDINGS.md
- Update PROJECT_STRUCTURE.md with todo/ section and TESTING.md entry
- TODO.md becomes an index pointing to todo/ files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
donbeave added a commit that referenced this pull request Apr 21, 2026
…ts (#6)

resolve_path now normalizes path components so `../sibling` becomes a
clean absolute path instead of `/a/b/../sibling`. Also resolves relative
`--src` in `config mount add` and adds integration tests that mirror the
exact CLI scenarios (workspace add/edit with relative workdir and mounts).
Updates AGENTS.md with branching policy requiring feature branches.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
donbeave added a commit that referenced this pull request Apr 24, 2026
The single-file `input.rs` grew to ~4750 lines and combined top-level
dispatch, list/editor/prelude/confirm-delete stage handlers, save-flow
plumbing, mouse/geometry helpers, and a large test suite. Split it
into `input/` as a directory module:

- `input/mod.rs` — thin top-level dispatcher (`handle_key`, `handle_mouse`),
  `InputOutcome`, re-exports.
- `input/list.rs` — list-stage key/modal handling.
- `input/editor.rs` — editor-stage key/modal handling, tab dispatch,
  agent-allow toggles, agent-default selection.
- `input/save.rs` — `begin_editor_save`, `commit_editor_save`, save-flow
  helpers (`build_workspace_edit`, confirm-save line builders, etc.).
- `input/prelude.rs` — create-workspace wizard.
- `input/mouse.rs` — mouse events, seam drag, list hit-test, file-browser
  URL click.

Tests travel with the behavior they exercise. No runtime behavior
change — key bindings, mouse behavior, save semantics, and dispatch
order are all byte-identical.

Addresses finding #6 of the PR #166 current-branch review.

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Co-authored-by: Claude <noreply@anthropic.com>
donbeave added a commit that referenced this pull request Apr 24, 2026
`render.rs` grew to ~2350 lines and combined the top-level stage
switch, list chrome, editor tabs, modal dispatcher, footer
composition, and visual-regression tests. Split into `render/` as a
directory module:

- `render/mod.rs` — stage-switch entry point (`render`), header
  renderer, shared geometry helpers, cross-cutting re-exports.
- `render/list.rs` — list-stage body, toast, details/sentinel
  right-pane renderers.
- `render/editor.rs` — editor-stage body, tab-bar, per-tab renderers.
- `render/modal.rs` — modal dispatcher (`render_modal`),
  `modal_outer_rect` shared geometry helper.
- `render/footer.rs` — `FooterItem`, `render_footer`.

Tests travel with the behavior they exercise. No visual or layout
change — the operator sees pixel-identical output.

Addresses finding #6 of the PR #166 current-branch review.

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Co-authored-by: Claude <noreply@anthropic.com>
donbeave added a commit that referenced this pull request Apr 24, 2026
…odules

`file_browser.rs` grew to ~1800 lines and combined filesystem
scanning, sandbox policy, git-repo detection, git URL resolution,
keyboard navigation, a git-prompt state machine, mouse URL hit-test,
rendering, and a large test suite. Split into `file_browser/` as a
directory module:

- `file_browser/mod.rs` — module doc, shared palette, module
  declarations, cross-module re-exports.
- `file_browser/state.rs` — `FileBrowserState`, `FolderEntry`,
  sandbox helpers (`is_within_root`, `canonicalize_or_self`),
  listing helpers (`load_entries`, `has_git_dir`, `is_excluded`),
  navigation (`set_cwd`, `reload`, `navigate_up`).
- `file_browser/input.rs` — keyboard/mouse event handling
  (`handle_key`, `handle_enter`, `commit_or_reject`,
  `maybe_open_url_on_click`).
- `file_browser/render.rs` — `render`, row styling.
- `file_browser/git_prompt.rs` — `GitPromptFocus`,
  `handle_git_prompt_key`, `render_git_prompt`, `resolve_git_url`.

Tests travel with the behavior they exercise. No behavior change —
keyboard behavior, sandbox rules, mouse URL opening, and rendering
are all byte-identical.

Addresses finding #6 of the PR #166 current-branch review.

Co-authored-by: Claude <noreply@anthropic.com>
Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
donbeave added a commit that referenced this pull request Apr 24, 2026
The `input/mod.rs` `#[cfg(test)] mod tests` block held ~1800 lines
of test cases covering every stage. That concentration forced
future maintainers to navigate a large mixed-responsibility file
and surfaced in static analysis as a cluster of duplicate-binding
warnings localized to one place.

Disperse the tests so each submodule owns the tests for the
behavior it owns:

- list-stage tests -> input/list.rs
- editor-stage tests -> input/editor.rs
- save-flow tests -> input/save.rs
- create-wizard tests -> input/prelude.rs
- mouse tests stay in input/mouse.rs

input/mod.rs retains only the genuine cross-flow test that drives
multiple stages in one case (create-mode rename + save). Shared
helpers `key()` and `mount()` are lifted to a `test_support`
sibling module since they show up in 3+ submodules; submodule-
local helpers (setup_with_workspace, press_s, etc.) stay co-
located with their tests.

No behavior change. Test count unchanged (829 -> 829).

Addresses finding #6 of the second-pass PR #166 review.

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 7, 2026
- Move 9 TODO items from monolithic TODO.md into separate files in todo/
- Each file is a self-contained design doc with problem, options, and
  related source files for easy agent handoff
- Mark resolved security findings (#3, #4, #6, #7) in SECURITY_REVIEW_FINDINGS.md
- Update PROJECT_STRUCTURE.md with todo/ section and TESTING.md entry
- TODO.md becomes an index pointing to todo/ files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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
…ts (#6)

resolve_path now normalizes path components so `../sibling` becomes a
clean absolute path instead of `/a/b/../sibling`. Also resolves relative
`--src` in `config mount add` and adds integration tests that mirror the
exact CLI scenarios (workspace add/edit with relative workdir and mounts).
Updates AGENTS.md with branching policy requiring feature branches.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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
- Move 9 TODO items from monolithic TODO.md into separate files in todo/
- Each file is a self-contained design doc with problem, options, and
  related source files for easy agent handoff
- Mark resolved security findings (#3, #4, #6, #7) in SECURITY_REVIEW_FINDINGS.md
- Update PROJECT_STRUCTURE.md with todo/ section and TESTING.md entry
- TODO.md becomes an index pointing to todo/ files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

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
…ts (#6)

resolve_path now normalizes path components so `../sibling` becomes a
clean absolute path instead of `/a/b/../sibling`. Also resolves relative
`--src` in `config mount add` and adds integration tests that mirror the
exact CLI scenarios (workspace add/edit with relative workdir and mounts).
Updates AGENTS.md with branching policy requiring feature branches.

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
- Move 9 TODO items from monolithic TODO.md into separate files in todo/
- Each file is a self-contained design doc with problem, options, and
  related source files for easy agent handoff
- Mark resolved security findings (#3, #4, #6, #7) in SECURITY_REVIEW_FINDINGS.md
- Update PROJECT_STRUCTURE.md with todo/ section and TESTING.md entry
- TODO.md becomes an index pointing to todo/ files

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
…ts (#6)

resolve_path now normalizes path components so `../sibling` becomes a
clean absolute path instead of `/a/b/../sibling`. Also resolves relative
`--src` in `config mount add` and adds integration tests that mirror the
exact CLI scenarios (workspace add/edit with relative workdir and mounts).
Updates AGENTS.md with branching policy requiring feature branches.

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 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