feat(sdlc): hapax-dev — unified front door for visible claude/codex/agy sessions#3834
Conversation
…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>
📝 WalkthroughWalkthroughThis PR introduces ChangesUnified visible session launcher
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (4)
docs/runbooks/hapax-dev-unified-launcher.mdscripts/hapax-claudescripts/hapax-devtests/scripts/test_hapax_dev.py
| --cd) workdir="${2:-}"; shift 2 ;; | ||
| --cd=*) workdir="${1#--cd=}"; shift ;; |
There was a problem hiding this comment.
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.
| --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.
There was a problem hiding this comment.
💡 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".
| IS_DEV_POOL=0 | ||
| case "$ROLE" in | ||
| alpha|beta|gamma|delta|epsilon|zeta|eta|theta) ;; | ||
| dev|dev[0-9]*) IS_DEV_POOL=1 ;; |
There was a problem hiding this comment.
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 👍 / 👎.
| [ -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 |
There was a problem hiding this comment.
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 👍 / 👎.
| # 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 |
There was a problem hiding this comment.
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 👍 / 👎.
…se-check run) cc-task: hapax-dev-unified-launcher-20260601 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
hapax-dev — one unified front door for visible sessions
One command for visible operator sessions across runtimes:
It picks a free, non-conflicting interactive identity (distinct from the
supervised headless greek fleet), guarantees a fresh
HAPAX_SESSION_ID, refusescollisions 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.mdWhat's in it
scripts/hapax-dev— launcher +ls/attach/help.scripts/hapax-claude— additive, non-greekdev/dev<N>interactive pool(defaults workdir to
$PWD; operator-driven ⇒ exempt from the cc-task bindingthat 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)
dev,dev2, … (a free greek role only when explicitly named)cx-<color>(reservescx-red/cx-violet)antigrav/antigrav-N(runsagy --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, freshHAPAX_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).--window/--detachbehave; default attaches in the current terminal; no unsolicited GUI window.-- <args>pass-through reaches the spawner;hapax-dev lslists live + free;hapax-dev attach <name>works.tests/scripts/test_hapax_dev.py36 passed; existinghapax-claudetests (9) pass (no regression); install/symlink note added to docs.Verification
Install is automatic on the next post-merge deploy (
scripts/hapax-*→~/.local/bin/symlink).🤖 Generated with Claude Code