Skip to content

CCY: optional, default-off launch-wrapper hook to supervise claude (auto-/compact & injection seam) #31

Description

@edmondscommerce

Summary

Add an optional, default-OFF hook to the CCY launch path that lets an
in-container wrapper command be prepended to the claude invocation. When the
hook is unset (the default), CCY behaves exactly as it does today — zero behaviour
change. When enabled, it allows claude to be run under a supervisor process
(a PTY wrapper) in the same namespace as claude.

This is the CCY-side integration seam for a capability being built in the
claude-code-hooks-daemon
repo (its Plan 00135): a thin PTY supervisor that can, opt-in, auto-issue a
whitelisted slash command (flagship: /compact at a custom, lower context-usage
threshold) into a live session. The daemon side owns the supervisor and all its
safety rails; this issue is only the launch-path seam so CCY can run it.

Why this shape (and not something bespoke)

  • Generic, unopinionated seam. CCY should not know what the wrapper is — it
    just prepends an operator-provided command. That keeps this repo decoupled from
    the daemon's release cadence and avoids baking in supervisor-specific logic.
  • In-container, in-namespace. The wrap must happen at entrypoint.sh's final
    exec (inside the container), NOT on the host around podman run. A host-side
    tmux/wrapper would sit across the PID-namespace boundary, so the wrapper could
    only see podman, not claude — it could not observe Claude's I/O or reap it.
    In-container exec claude-supervise -- claude … gives the supervisor Claude's
    own PTY and a direct waitpid liveness signal.
  • Default-off = fail-safe. Unset env var → today's exec "$@". No dormant
    behaviour, nothing to regress.

The change (two files)

1. files/var/local/claude-yolo/entrypoint.sh — the seam (line 266)

Today:

# Execute the command
exec "$@"

Proposed:

# Execute the command.
# Optional supervisor wrap (default OFF): if CCY_CLAUDE_WRAPPER is set, prepend it
# to the claude invocation. Unset => unchanged behaviour. The wrapper command must
# exist on PATH inside the image (see supervisor delivery below).
if [[ -n "${CCY_CLAUDE_WRAPPER:-}" ]]; then
    # Word-split the operator-provided wrapper into argv (e.g. "claude-supervise --")
    read -ra _ccy_wrapper <<< "$CCY_CLAUDE_WRAPPER"
    exec "${_ccy_wrapper[@]}" "$@"
fi
exec "$@"

Shellcheck-clean (array expansion, no unquoted word-split of "$@"). No error
hiding. Fail-fast preserved — if the wrapper command is missing, exec fails
loudly rather than silently falling back.

2. files/var/local/claude-yolo/claude-yolo — opt-in flag + env forward

  • Add a --supervise flag, mirroring the existing --headless handling
    (declare SUPERVISE_MODE=false near line 421; parse near line 492).

  • When --supervise is given (or a config var is set), export
    CCY_CLAUDE_WRAPPER (default e.g. claude-supervise --, overridable) and add
    it to the podman run -e list (near lines 2745–2766) so it reaches the
    container:

    -e "CCY_CLAUDE_WRAPPER=${CCY_CLAUDE_WRAPPER:-}" \

    With --supervise absent, CCY_CLAUDE_WRAPPER is empty and the entrypoint
    branch above is a no-op.

  • Document --supervise in the usage/help block (near line 155).

  • CCY_VERSION bump required (minor — new backward-compatible feature), per
    CLAUDE/ContainerRules.md. The pre-commit hook enforces this.

Supervisor delivery (dependency — NOT part of the first mergeable unit)

The wrapper command (claude-supervise) is being built in the hooks-daemon repo
(Plan 00135, ARCH-B). It must be present on PATH inside the CCY image before
--supervise does anything useful. Options to decide when it lands:

  1. Vendor into the imageDockerfile COPYs a self-contained
    claude-supervise (Python, stdlib-only) to a known path. Fully self-contained,
    no runtime cross-repo coupling; costs a periodic sync of the vendored copy.
  2. Install from a daemon releaseDockerfile fetches it during build.
    Single source of truth; adds a build-time network dependency.
  3. Resolve at runtime from the project's daemon venv — entrypoint locates the
    installed daemon's console script. Most decoupled, but fiddly PATH resolution.

Recommendation: (1) vendor for the first cut (matches this repo's
self-contained image philosophy), revisit (2) later. Either way this is a separate
change from the seam above and gated behind the same default-off flag.

Constraints respected

  • Optional & default-off — unset env var = today's behaviour, verifiably.
  • IaC only — all via entrypoint.sh + claude-yolo + Dockerfile, deployed
    by image rebuild on the host. No manual container edits.
  • Fail-fast — no || true, no error hiding; missing wrapper fails loudly.
  • Version-bumped — CCY_VERSION minor bump; REQUIRED_CONTAINER_VERSION
    bump only when the vendored supervisor lands (forces the rebuild).
  • QA./scripts/qa-all.bash (bash -n + shellcheck + semgrep) before commit.

Staged plan

  1. Seam only (this issue, mergeable alone): entrypoint wrapper branch +
    --supervise flag + env forward + version bump + help/docs. Ships dormant
    (default-off); no supervisor yet, so --supervise is a documented no-op-until-
    supervisor-present.
  2. Supervisor delivery: vendor claude-supervise into the image once the
    daemon side (Plan 00135 Slice 2) is ready; bump REQUIRED_CONTAINER_VERSION.
  3. Enable & dogfood: ccy --supervise, verify auto-/compact at threshold in
    a real session, with the supervisor in --dry-run first, then armed.

Open questions to confirm

  1. Flag name + trigger: ccy --supervise (recommended), a host_vars/config var,
    or both?
  2. Supervisor delivery: vendor (1), fetch-on-build (2), or venv-resolve (3)?
  3. Should step 1 (the dormant seam) merge on its own now, or wait and land the
    seam + supervisor together to avoid a merged-but-inert --supervise flag
    (YAGNI)?

Source design & audit: hooks-daemon Plan 00135 (plan-audit-fable-1.md,
Decision G — ARCH-B PTY supervisor, CCY-first).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions