Skip to content

feat(commands): dual-mode subcommands + mode banner + doctor report (PR 2/3)#152

Merged
jraicr merged 5 commits into
devfrom
feat/dual-mode-commands-banner
Jun 4, 2026
Merged

feat(commands): dual-mode subcommands + mode banner + doctor report (PR 2/3)#152
jraicr merged 5 commits into
devfrom
feat/dual-mode-commands-banner

Conversation

@jraicr

@jraicr jraicr commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Part of #149 — Phase 1 dual-mode network/socket containment, PR 2/3 (stacked on PR1 #151, merged to dev).

What this adds

Building on PR1's compose-core (network-neutral base + dood/contain overlays + resolve_run_mode), PR2 delivers the user-facing controls and the visible posture signal:

  • drydock default <dood|contain> — manage the global default sentinel (~/.config/drydock/default-dood). Idempotent.
  • drydock dood <proj> [--remove] / drydock contain <proj> [--remove] — per-project pins, mutually exclusive (pinning one clears the opposite). --remove on an unpinned project is a no-op, not an error. dood prints the INV-6 root-equivalent warning.
  • _emit_mode_banner — prints the active mode + the deciding reason at container creation only, never on attach.
  • M2 — drydock doctor now reports the active network/socket mode (via resolve_run_mode) in the COMPOSE OVERLAYS section, with the deciding reason. Without it, contained-by-default makes make shell-api / curl localhost:PORT fail with no visible cause.
  • Dispatch entries in bin/drydock + usage() rows.

Honest framing

Contained mode in Phase 1 removes the Docker socket and host networking only — egress stays open (no internal: true; the agent still reaches the internet). The banner and doctor wording state exactly that and make no egress-filtering claim. A domain-allowlist egress jail is Phase 2.

Notes for review

  • Banner is wired at six creation sites, not four. The design enumerated four (cmd_run nested, cmd_run 0-session, cmd_new, cmd_shell), but decision D6 (emit at creation, never attach) also covers the two ≥1-session TUI menu branches — "Start a new session" and "Stop all sessions and start fresh" — which create via _launch_new. All six emit at the call site (never inside _launch_new, to avoid double-emit) and zero attach paths emit.
  • INV-6: the dood warning restates that the Docker socket is root-equivalent on the host. The full INV-9 invariant text lands in PR3 (forward-referenced here).
  • Project names are sanitized before sentinel paths are computed, matching resolve_run_mode's lookup.

Tests & gates

  • scripts/test.sh: 1099/1099 passing (22 new tests covering subcommands, mutual exclusion, idempotency, arg-validation, banner wording/reason, and doctor reporting).
  • shellcheck, shfmt -d, scripts/lint-commits.sh: all clean (5 conventional commits).

Size

size:exception — 440 lines changed, 40 over the 400 budget. The implementation is 139 lines; the remaining 301 are tests (per-test isolated HOME fixtures). The feature slice is cohesive and small; the overage is test coverage.

@jraicr jraicr added type:feat Feature work size:exception Over 400 lines, pre-approved by maintainer labels Jun 4, 2026
@jraicr jraicr merged commit 5ff9758 into dev Jun 4, 2026
4 checks passed
@jraicr jraicr deleted the feat/dual-mode-commands-banner branch June 4, 2026 23:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:exception Over 400 lines, pre-approved by maintainer type:feat Feature work

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant