Skip to content

feat(sdlc): hapax-dev — unified front door for visible claude/codex/agy sessions#3834

Merged
ryanklee merged 2 commits into
mainfrom
zeta/hapax-dev-unified-launcher-20260601
Jun 1, 2026
Merged

feat(sdlc): hapax-dev — unified front door for visible claude/codex/agy sessions#3834
ryanklee merged 2 commits into
mainfrom
zeta/hapax-dev-unified-launcher-20260601

Conversation

@ryanklee
Copy link
Copy Markdown
Collaborator

@ryanklee ryanklee commented Jun 1, 2026

hapax-dev — one unified front door for visible sessions

One command for visible operator sessions across runtimes:

hapax-dev claude   # visible claude session, auto non-conflicting identity
hapax-dev codex
hapax-dev agy

It picks a free, non-conflicting interactive identity (distinct from the
supervised headless greek fleet), guarantees a fresh HAPAX_SESSION_ID, refuses
collisions by construction, and dispatches to the existing per-platform spawner
without reimplementing launch logic.

cc-task: hapax-dev-unified-launcher-20260601
AuthorityCase: CASE-CROSS-RUNTIME-COMMS-001
Parent spec: coordination-reform-master-design-2026-05-30.md

What's in it

  • scripts/hapax-dev — launcher + ls / attach / help.
  • scripts/hapax-claude — additive, non-greek dev/dev<N> interactive pool
    (defaults workdir to $PWD; operator-driven ⇒ exempt from the cc-task binding
    that still governs the headless greek lanes; greek path unchanged).
  • tests/scripts/test_hapax_dev.py — 36 hermetic tests.
  • docs/runbooks/hapax-dev-unified-launcher.md — usage + install.

Identity pools (distinct from the supervised fleet)

  • claude → dev, dev2, … (a free greek role only when explicitly named)
  • codex → free cx-<color> (reserves cx-red / cx-violet)
  • agy → free antigrav / antigrav-N (runs agy --prompt-interactive)

Free-detection spans tmux and active claim files (incl. session-keyed)
and the headless heartbeat, so a headless lane holding a claim without a
tmux session is still seen as busy.

Acceptance criteria — verified

  • hapax-dev claude → visible claude session, fresh HAPAX_SESSION_ID, free interactive role distinct from the headless greek fleet; collision with a running reform lane refused.
  • hapax-dev codex / hapax-dev agy → visible sessions on free codex/agy identities (agy ⇒ agy --prompt-interactive).
  • Auto-selection picks the lowest free slot; explicit name honored; collision REFUSED with the attach hint (no double-launch).
  • --window / --detach behave; default attaches in the current terminal; no unsolicited GUI window.
  • -- <args> pass-through reaches the spawner; hapax-dev ls lists live + free; hapax-dev attach <name> works.
  • shellcheck + ruff clean; tests/scripts/test_hapax_dev.py 36 passed; existing hapax-claude tests (9) pass (no regression); install/symlink note added to docs.

Verification

shellcheck scripts/hapax-dev                       # clean
uv run ruff check/format tests/scripts/test_*      # clean
uv run pytest tests/scripts/test_hapax_dev.py -q   # 36 passed
uv run pytest tests/scripts/test_hapax_claude_*    # 9 passed (no regression)

Install is automatic on the next post-merge deploy (scripts/hapax-*
~/.local/bin/ symlink).

🤖 Generated with Claude Code

…ex/agy sessions

Adds scripts/hapax-dev, the operator's single launcher for visible sessions
across runtimes. It picks a free, non-conflicting interactive identity (distinct
from the supervised headless greek fleet), guarantees a fresh HAPAX_SESSION_ID,
refuses collisions by construction, and dispatches to the existing per-platform
spawner — without reimplementing launch logic.

- claude -> free `dev`/`dev2`/... pool (never the supervised greek lanes; a free
  greek role only when explicitly named)
- codex  -> free `cx-<color>` (reserves red/violet)
- agy    -> free `antigrav`/`antigrav-N` slot (agy --prompt-interactive)

Free-detection spans tmux + active claim files (incl session-keyed) + headless
heartbeat, so a headless lane holding a claim without a tmux session is still
seen as busy. Default visibility attaches in the current terminal (no
unsolicited window); --window / --detach / --cd / -- passthrough supported.
`hapax-dev ls` and `hapax-dev attach <name>` round out the surface.

hapax-claude gains an additive, non-greek `dev`/`dev<N>` interactive pool: it
defaults its worktree to $PWD and, being operator-driven, is exempt from the
mandatory cc-task binding that still governs the headless greek lanes. Greek
governance is unchanged (regression-tested).

Tests: tests/scripts/test_hapax_dev.py (36, hermetic via --dry-run + HAPAX_DEV_*
hooks and stub spawner/tmux). shellcheck-clean; ruff clean. Install is automatic
on post-merge deploy (scripts/hapax-* -> ~/.local/bin symlink); runbook added.

Task: hapax-dev-unified-launcher-20260601
AuthorityCase: CASE-CROSS-RUNTIME-COMMS-001

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 1, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR introduces hapax-dev, a unified command-line tool for launching and managing "visible operator sessions" across multiple runtimes (claude, codex, agy). It extends the existing hapax-claude spawner to support interactive dev pool roles, documents the feature thoroughly, and provides comprehensive tests covering identity resolution, liveness detection, collision handling, and per-platform dispatch.

Changes

Unified visible session launcher

Layer / File(s) Summary
Runbook specification and contract
docs/runbooks/hapax-dev-unified-launcher.md
Documents the unified launcher's purpose, identity pools per platform, auto-selection rules (tmux absence + claim file + heartbeat checks), command syntax (ls, attach, launch), flags (--window, --detach, --cd, --dry-run), collision behavior, installation via post-merge deploy symlink, and hermetic test strategy.
hapax-claude dev pool support
scripts/hapax-claude
Extends role parsing and validation to accept dev|dev[0-9]* interactive lanes, defaults worktree to invoking $PWD for dev roles, and exempts operator-driven dev lanes from mandatory cc-task binding while preserving task requirement for supervised greek roles.
hapax-dev core configuration and utilities
scripts/hapax-dev (lines 1–191)
Defines environment variables (claim/cache dirs, heartbeat age), interactive identity pools per platform, platform descriptor resolution, identity name validation rules, and liveness detection via tmux session presence, claim file inspection, and heartbeat age semantics.
hapax-dev command implementations and dispatch
scripts/hapax-dev (lines 193–383)
Implements cmd_ls() to display live/free identity table with claimed tasks, cmd_attach() to re-attach to running sessions, cmd_launch() with identity resolution, collision refusal (including attach hints and diagnostics), fresh HAPAX_SESSION_ID generation, spawner invocation with forwarded args, --dry-run plan printing, and optional window/foot terminal opening for agy; main dispatch routes subcommands.
Comprehensive test coverage
tests/scripts/test_hapax_dev.py
Tests help/usage, claude auto-selection via fake tmux probing, claim and heartbeat free-detection semantics, collision refusal with stderr and attach hints, explicit name validation and platform validation, codex/agy dispatch with passthrough args and window mode handling, visibility flags (attach/detach/window mutual exclusivity), session ID freshness and uniqueness, ls/attach command correctness, stubbed integration tests for hapax-claude dev-pool role acceptance and task-binding behavior, and spawn/attach seam test with spawner/tmux command stubbing.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • hapax-systems/hapax-council#3758: Main PR's scripts/hapax-dev liveness/free-slot probing and scripts/hapax-claude "missing cc-task binding" launch gating are directly built on the session-keyed claim-file/heartbeat scheme and role/effective-role behavior introduced in the agent-role.sh/cc-task-gate.sh/cc-claim changes of PR #3758.

Poem

🐰 A unified tunnel, at last!
One door for all the runtimes past,
Dev pools dance, identities flow free,
Fresh sessions bloom from tmux and trees,
Collision guards and attach commands sing. 🎭

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.76% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly summarizes the main change: adding a unified launcher script (hapax-dev) for visible sessions across multiple runtime platforms.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed PR description is comprehensive and well-structured, covering summary, case tracking, test plan, and detailed implementation details with verification results.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch zeta/hapax-dev-unified-launcher-20260601

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/hapax-dev`:
- Around line 266-267: The --cd) case should validate that the next positional
argument exists before running shift 2 to avoid a fatal shell error; update the
case handling around the --cd) pattern to check if "${2:-}" is non-empty (or
else emit the CLI error/usage and exit) and only then set workdir="${2}" and
shift 2; keep the --cd=*) branch unchanged (it already handles the inline form)
and ensure you reference the same workdir variable and error/usage path used
elsewhere in the script.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: af4c0249-8802-447e-b19e-6069b221d828

📥 Commits

Reviewing files that changed from the base of the PR and between 9436745 and 21d318a.

📒 Files selected for processing (4)
  • docs/runbooks/hapax-dev-unified-launcher.md
  • scripts/hapax-claude
  • scripts/hapax-dev
  • tests/scripts/test_hapax_dev.py

Comment thread scripts/hapax-dev
Comment on lines +266 to +267
--cd) workdir="${2:-}"; shift 2 ;;
--cd=*) workdir="${1#--cd=}"; shift ;;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard missing --cd value before shift 2.

--cd without an argument exits via shell shift failure under set -e, instead of returning your structured CLI error.

Proposed fix
-      --cd)      workdir="${2:-}"; shift 2 ;;
+      --cd)
+        [ $# -ge 2 ] && [ -n "${2:-}" ] || die "--cd requires DIR" 2
+        workdir="$2"
+        shift 2
+        ;;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
--cd) workdir="${2:-}"; shift 2 ;;
--cd=*) workdir="${1#--cd=}"; shift ;;
--cd)
[ $# -ge 2 ] && [ -n "${2:-}" ] || die "--cd requires DIR" 2
workdir="$2"
shift 2
;;
--cd=*) workdir="${1#--cd=}"; shift ;;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/hapax-dev` around lines 266 - 267, The --cd) case should validate
that the next positional argument exists before running shift 2 to avoid a fatal
shell error; update the case handling around the --cd) pattern to check if
"${2:-}" is non-empty (or else emit the CLI error/usage and exit) and only then
set workdir="${2}" and shift 2; keep the --cd=*) branch unchanged (it already
handles the inline form) and ensure you reference the same workdir variable and
error/usage path used elsewhere in the script.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 21d318aca6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/hapax-claude
IS_DEV_POOL=0
case "$ROLE" in
alpha|beta|gamma|delta|epsilon|zeta|eta|theta) ;;
dev|dev[0-9]*) IS_DEV_POOL=1 ;;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject nonnumeric dev roles

Because this is a shell case glob, dev[0-9]* matches any string starting with dev plus one digit (for example dev2foo), not just dev2, dev3, etc. In standalone invocations such as hapax-claude --role dev2foo --terminal tmux, that role is accepted as part of the interactive dev pool and then skips the cc-task binding exemption below, creating arbitrary ungoverned role names outside the documented dev|dev<N> pool.

Useful? React with 👍 / 👎.

Comment thread scripts/hapax-dev
[ -s "$CLAIM_DIR/cc-active-task-$name" ] && return 0
local f
for f in "$CLAIM_DIR/cc-active-task-$name-"*; do
[ -e "$f" ] && return 0
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor claim lease expiry before marking slots busy

When a session-keyed cc-active-task-<name>-<sid> file is left behind by a dead session, this marks the identity live solely because the file exists, without checking that the lease is non-empty and still within the HAPAX_CLAIM_LEASE_TTL_SECS expiry that cc-claim uses. In that stale-lease scenario hapax-dev skips or refuses the slot even though the task gate considers it free, so old claim files can unnecessarily exhaust the interactive pool until separate cleanup runs.

Useful? React with 👍 / 👎.

Comment thread scripts/hapax-dev
# 2. active claim file (non-empty) or any session-keyed claim variant
[ -s "$CLAIM_DIR/cc-active-task-$name" ] && return 0
local f
for f in "$CLAIM_DIR/cc-active-task-$name-"*; do
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid treating sibling agy claims as base-session claims

For identities that are prefixes of other valid identities, this glob also matches the sibling's legacy claim file; for example cc-active-task-antigrav-2 is treated as a session-keyed claim for antigrav. With antigrav-2 claimed, hapax-dev agy antigrav is refused and auto-selection skips the otherwise free base antigrav slot, so a busy numbered lane can incorrectly block a distinct unnumbered lane.

Useful? React with 👍 / 👎.

@ryanklee ryanklee added this pull request to the merge queue Jun 1, 2026
@ryanklee ryanklee removed this pull request from the merge queue due to a manual request Jun 1, 2026
…se-check run)

cc-task: hapax-dev-unified-launcher-20260601
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ryanklee ryanklee enabled auto-merge June 1, 2026 15:13
@ryanklee ryanklee disabled auto-merge June 1, 2026 15:15
@ryanklee ryanklee added this pull request to the merge queue Jun 1, 2026
Merged via the queue into main with commit dc984c3 Jun 1, 2026
31 of 32 checks passed
@ryanklee ryanklee deleted the zeta/hapax-dev-unified-launcher-20260601 branch June 1, 2026 16:11
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