Repository navigation
feat(lifecycle): identity-verified shared host helpers in config, up and down (HACK-1142) - #565
Conversation
…ig, up and down (HACK-1142) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ilure rollback and docs (HACK-1142) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
🛡️ Codex Security Review · Automatically triggeredSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b3d59484a3
ℹ️ 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".
|
Full-suite follow-up (local Linux x64, no C linker): I reran the 76 files that failed in |
…ls through process startup, skip env for disabled helpers (HACK-1142) Addresses Codex review on #565. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…env (HACK-1142) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8d97f19d90
ℹ️ 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".
🛡️ Codex Security Review · Automatically triggeredSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
…tarts (HACK-1142) Addresses Codex review on #565. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ing (HACK-1142) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review |
|
Codex Review: Didn't find any major issues. What shall we delve into next? Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
🛡️ Codex Security Review · Automatically triggeredSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
|
Merged |
…ail down on unreleased helpers (HACK-1142) Addresses the independent Codex Astra review of #565: - down marks the instance stopping before Compose shutdown; up checks the instance is still current right before containers start (new backend beforeStart hook) and after they start, and fails with E_LIFECYCLE_FAILED when a concurrent down released its helpers. - each instance records the helper identities it acquired; down releases that record plus the current config, so a removed or renamed entry is still released. - helper release failures now fail hack down and hack restart with a structured E_LIFECYCLE_FAILED result; the claim is kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…moved helper entries and failed releases (HACK-1142) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…and failing down (HACK-1142) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
New head:
Locally on |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b8779909fb
ℹ️ 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".
🛡️ Codex Security Review · Automatically triggeredSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
…elper release failures (HACK-1142) Addresses Codex review on #565. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
New head |
|
@codex review |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 30595cbc89
ℹ️ 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".
🛡️ Codex Security Review · Automatically triggeredSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
…reserves (HACK-1142) hack restart preserves the Compose runtime, so its down phase never shuts the containers down, but the synthetic exit code 0 let it release their helper holders: a sole holder's restart stopped the helper under running containers. The container outcome is now explicit (ContainerShutdown: stopped, unconfirmed or kept); a preserved runtime is "kept", which keeps the holders without reporting a failure, and the up phase reuses the helpers. e2e regression through the real hack restart: the helper keeps its PID, birth and claim. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ntainers (HACK-1142) A failing up.after hook (or a later startup check) ran the combined cleanup after a detached Compose up had started the containers, and rolled the helper holders back underneath them. The combined cleanup now exposes keepSharedHelpers; up and restart call it once detached containers run, after which cleanup stops only the lifecycle processes and keeps the holders for hack down. e2e regressions through the real CLI for restart (preserved runtime) and a failing up.after hook, moved into runRetainedHelperPhases. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…142) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…olders (HACK-1142) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
New head: The m5 reviewer's restart P2 and the two Codex threads from
Checks at
CI: |
|
@codex review |
🛡️ Codex Security Review · Automatically triggeredSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: dc030562ec
ℹ️ 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".
… have left services running (HACK-1142) docker compose up -d can start some services and then fail or time out; the failure path then rolled back the helper holders under the services it had started. After an unsuccessful start (up failure, restart repair failure, or an error after start), the helpers are now kept unless Compose confirms that no container of the project exists in a state that may run (probeComposeContainers: a failed ps is unknown, so kept). Regressions: e2e partial start through the real CLI (fake Compose fails after starting a service), restart repair ordering, and the retention decision for absent, present, unknown and failing probes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tainer may run (HACK-1142) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
New head: The m5 reviewer and Codex (on
Checks at
|
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Lines 6257 to 6259 in b05a663
If SIGINT/SIGTERM arrives during a detached Compose start after some containers have started, this handler invokes lifecycleCleanup directly while the combined cleanup's containersRunning flag is still false; keepSharedHelpers is only called after Compose returns successfully. With no retained lifecycle session, cleanup therefore releases newly added holders and can stop the last-held helper underneath those containers. Unlike the corrected nonzero-result path, this signal callback bypasses retainHelpersUnlessContainersAbsent entirely. Apply container-retention protection to signal cleanup as well, including the corresponding restart handler, before allowing helper rollback.
ℹ️ 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".
…lpers (HACK-1142) The SIGINT/SIGTERM cleanup installed for up and restart called the combined cleanup directly, so a signal while docker compose up was still starting services (after some had started) released the helper holders before any retention check. Once a container start has begun, the signal cleanup now keeps the helpers first (no probe: a start in flight can start services after any snapshot) and only stops the lifecycle processes. e2e regression: SIGTERM to a real hack up paused inside compose up keeps the helper and its claim until hack down; unit test for the handler. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rs (HACK-1142) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
🛡️ Codex Security Review · Automatically triggeredSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
|
New head: The m5 reviewer and Codex's review of Fixed in
Checks at
|
|
@codex review |
🛡️ Codex Security Review · Automatically triggeredSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
…shutdown generation (HACK-1142) A down publishes its shutdown generation and then its stopping operation record as two writes. A down interrupted between them (or whose record write failed) left the generation advanced but the up's operation record in place, so a superseded up's post-start rollback still released the last holder and stopped the helper under the containers it had started. Rollback now also requires the shutdown generation captured at start; otherwise it keeps the holders for the next down. Regression: an interrupted mark, then the post-start supersession check and rollback keep the helper and its claim; a later down releases it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d (HACK-1142) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
New head: Astra r13 (full review at Fixed in
Codex's review of Checks at
|
|
@codex review |
🛡️ Codex Security Review · Automatically triggeredSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
Refs HACK-1142 (WU29: ownership-safe shared host lifecycles).
What changes
The shared host helper contract from #343 (
src/lib/shared-host-lifecycle.ts) is now usable from projects:lifecycle.sharedHelpersinhack.config.json, parsed insrc/lib/project-shared-helpers.ts. Each entry hasname, argvcommand,port, exactly one ofupstream/upstreamEnv, and optionalidentityPath,cwd,env(names; the helper also getsPATHand nothing else),enabledandreadyTimeoutMs. An invalid entry is refused, not skipped:hack up,hack downandhack restartfail withE_LIFECYCLE_FAILEDbefore any hook, helper, lifecycle process or Compose command (a dedicatedsharedHelpersError; other parse errors still only warn on the Compose path). JSON schema updated insrc/templates.ts.hack up/hack restart(Compose and native):runLifecycleUpBeforeAndProcessesensures each helper afterup.beforehooks and beforelifecycle.processes. The holder is the instance's Compose project (<project>--<branch>). The claim dir is~/.hack/shared-helpers/<name>-<port>/(or$HACK_HOME), derived from helper and port only, so every branch and checkout shares one claim and lock.hack upruns of one instance hold a per-instance lock across their ensures and record their operation id. A rollback releases only while it is still the latest start, so it never revokes a newer run's holder.hack down's release takes the same lock, so up and down of one instance never split its helper set.readyresult gainsholderAdded, decided under the helper lock. SIGINT/SIGTERM handlers stay installed across the helper and lifecycle-process start. A signal aborts an in-flight helper start (the library stops a child it spawned), runs the cleanup for whatever started, then is re-raised. A disabled helper resolves none of its env.hack down: marks the instance stopping before Compose shutdown, then releases the instance's holder for every helper it acquired (recorded per instance at start) plus every configured one, so removing or renaming an entry does not strand a holder. A failed or unconfirmed release keeps the claim and failshack down/hack restartwithE_LIFECYCLE_FAILED(helper names indetail).hack upchecks the instance right before containers start (a newbeforeStartbackend hook betweenup --no-startandup -d, before an attachedup, at the end of native preparation, and before the restart repair start) and again after they start. A down during preparation is prevented (up failsE_LIFECYCLE_FAILED, no container starts). A down that completes after that check is detected after launch (detached up failsE_LIFECYCLE_FAILED; attached up only after Compose exits); the documented manual step ishack down --branch <name>, thenhack up. Shutdown is tracked as a per-instance generation that onlyhack downadvances. An up captures it when lifecycle preparation begins (beforeup.beforehooks), so a down supersedes every up whose preparation began before it, even after a later up succeeds (with possibly different helpers), and a down duringup.beforestops that up before any helper starts. A newer start without a down in between does not supersede an earlier one, and once it is current the earlier start's cleanup (on failure or SIGINT/SIGTERM) skips the lifecycle session and helper holders it may have adopted. When the instance lock is too busy to decide, the cleanup keeps them and warns (hack downstops them).Acceptance items
tests/project-shared-helpers.test.ts(parse, refusals,readProjectConfigparse error),tests/project-config-schema.test.tsup/down(andrestart, native up)shared-host-helpersdrives realhack up/down --branchsrc/daemon). Giving it start/stop authority over shared helpers is new live-session cleanup authority, which this task excludes. A read-only status view could be a follow-up.E_LIFECYCLE_FAILED/wrong-upstream, the value is absent from output, and the shared helper and its holders are unchanged.foreign-listener, survives and leaves no claim. Module test also covers a different helper kind.hack up --branchin parallel give one owned spawn and both holders.hack upwhile a never-ready helper retries; exit 143 within 5 s, the owned child is stopped and the claim removed.hack upleaves the helper recorded; the next up stops it at its deadline; a later up starts fresh.E_LIFECYCLE_FAILEDand never runsup -d. Module test for the stopping mark.hack downexits non-zero withE_LIFECYCLE_FAILEDand keeps the claim; module test keeps the acquired record for retry.helpers: undefinedand[]for an unreadable record and an unavailable lock.docs/lifecycle.mdstates "prevented during preparation, detected after launch" with the manual cleanup step.tests/project-restart-repair-guard.test.ts.runUnlessNewerStart.runUnlessNewerStartreturnsundecidableand keeps the session when the lock is unavailable; the rollback keeps holders (lock-unavailable) instead of throwing. e2e overlap variantsigterm-lock-held; module test with B successful and the lock held past the timeout.runLifecycleUpBeforeAndProcesses, beforeup.before, and carried into helper startup. e2e "a down while an up waits in up.before supersedes that up"; module test.nativeProjectDowngainsbeforeCleanup, run after the before hook and the ownership/finalization rechecks, immediately before graph cleanup; the mark lives there.tests/native-project-down.test.ts: a down refused by a finalization change, an ownership change or a failing hook leaves the shutdown generation and operation record unchanged; a committed down marks right beforegraph cleanup.tests/shared-helper-cleanup-combiner.test.ts.SharedHelperSessionOwnership: session decision/creation recorded under the instance lock; a creator's cleanup runs only while the epoch is its own. Module tests and e2e overlap variants.tests/project-lifecycle-controller-ownership.test.ts.starterAbortremoves the entry only after a confirmed stop; regression throughabort()with the kill still failing.finalize()is a locked recheck with conditional publication; a displaced start gives up its authority (LifecycleSessionDisplacedError). Controller regression (replacement after creation, before finalize; reclaimed by the real stop controller).MUX_COMMAND_TIMEOUT_MS); controller deadline on each mux call under the lock; unknown outcomes keep the entry and token. Hung-creation regression.createSessiontracked; cleanup and abort keep the record until it settles.runMuxCommandbounds every lifecycle and backend mux call with a subprocess timeout, so each call settles (the client is killed) before the lock is released; no late creation or stale continuation exists. Replacement retires only the inspected entry, by token. Regressions: timed-out creation (created / not created, nothing in flight afterwards) and a stalled replacement with an overlapping up. Timeout settlement is an assumption, narrowed per backend in round 11 (see that row).MuxCommandTimeoutError(presence reports unknown); lifecycle callers anddoctorhygiene fail closed; the project-meta display skips the backend.set-environment,new-window,list-panes,send-keysandzellij runare bounded too. SIGTERM-during-environment-setup regression.abort()returnsLIFECYCLE_SESSION_RETAINED;startLifecycleProcessesraisesLifecycleSessionRetainedError;releaseAfterFailedProcessStartskips the rollback. The regression goes through the realstartLifecycleProcessescatch (the second process fails, the kill fails).abort()killed by name without the lockabort()and signal cleanup take the lock; the operation cleanup runs insiderunIfOwned's lock (now always taken) and never re-acquires it. Regressions: a replacement overlapping the abort, and one overlapping the failed-creation cleanup (each stalls the kill's own token read).LifecycleSessionDisplacedError, nothing launched). Regressions for a displaced launch (zellij call log) and for environment setup overlapped by a replacement (tmux event order).stopSessionBeforeReleasingHelpers: down, restart and native retirement release only after the session stop succeeded; otherwise every holder is kept, a warning is shown and the command fails (E_LIFECYCLE_FAILED, helper names indetail). Unit regressions for both outcomes.lifecycle.singletonconfigssingletonkeeps port-only adoption; moving tosharedHelpersis opt-in.tests/models/tla/shared-helper-instance: session ownership across overlapping ups and a down;SessionSafe,Reclaimable,NoForeignLaunchandHelpersKeptForSessionover 7,466 states. The session decision, the locked creation transaction (revalidate, publish, create, environment, record, and the failure cleanup inside the lock), fenced process launches, the locked conditional finalize, and a down whose session stop fails are separate steps. Ten controls each fail with a named witness: no epoch check, no revalidation, unstopped epoch failure, stale finalize, abandoned creation, abandoned replacement, timeout read as absence, unlocked failure cleanup, unfenced launch, and release on a failed stop. Registered and mapped in the README anddocs/reference/v5-model-coverage.md. The claim protocol itself stays fixture-covered.hack logs/projects --detailsvisibilityNote: the Linear connector was not authorized in the session that built this, so the items above come from the task brief and the in-repo contract docs, not from the issue's Acceptance text. Please reconcile against the issue.
Negative controls (source mutation, then the guarding test fails)
holderAddedholderAddedalways truedowndoes not releaseupignores a failed helpertests/project-restart-repair-guard.test.tsrunUnlessNewerStartdirectly.tests/native-project-down.test.ts: both new teststests/shared-helper-cleanup-combiner.test.ts: "an undecidable process cleanup keeps the helper holders too"tests/project-lifecycle-controller-ownership.test.ts: "a start that decided to create refuses after an overlapping start created and finalized the session"tests/project-lifecycle-controller-ownership.test.ts: both epoch-record-failure teststests/project-lifecycle-controller-ownership.test.ts: "when that stop is not confirmed, the entry and token stay so the session is reclaimable"tests/project-lifecycle-controller-ownership.test.ts: "a start displaced after creating its session, before finalizing, leaves the replacement owned and reclaimable"tests/project-lifecycle-controller-ownership.test.ts: "a hung session creation releases the instance lock within its bound and stays reclaimable" (fails with "creation did not settle")tests/project-lifecycle-controller-ownership.test.ts: "a timed-out creation keeps its ownership record until the creation settles…"[])tests/mux-command-timeout.test.ts: both teststests/project-lifecycle-controller-ownership.test.ts: "a stalled replacement holds the lock until it settles…" (an abandoned call is still in flight)set-environmenttests/project-lifecycle-controller-ownership.test.ts: "SIGTERM during tmux environment setup settles within the mux bound"tests/project-lifecycle-controller-ownership.test.ts: "a startup whose second process fails and whose session stop is unconfirmed keeps the shared helpers (real startup catch)"abort()without the instance locktests/shared-helper-cleanup-combiner.test.ts: "down keeps every helper holder and reports them when the session stop failed or is unknown"M6 first slipped through: locally
bunon PATH is a wrapper, and SIGTERM to it tore down the whole process scope. The signal probe now signals the CLI process itself (process.execPath index.ts) and bounds the exit time.Validation (local, Linux x64)
bun test tests/project-shared-helpers.test.ts tests/project-restart-repair-guard.test.ts: 29 passbun test tests/shared-host-lifecycle.test.ts: 39 passbun test tests/project-config-schema.test.ts tests/project-config.test.ts: passbun tests/e2e/run.ts --only=shared-host-helpers: pass (~15 s; registered as a local-tier scenario, so it runs in the docker-e2e shards)bun run typecheck,bun run check,bun run docs:check: passbun run test(full suite, Linux host without a C linker; config compiler copied from a same-base build): 850 failures in 76 files, almost all native/macOS VM suites. Rerunning those 76 files one at a time gives 844 failures on both base31b497cfand head, identical file by file; the other 6 pass in isolation. None is introduced here.tests/project-shared-helpers.test.tsis added to the hostedlinux-process-lifetimejob.df72b183:bun run typecheck,bun run check,bun run docs:checkpass. The focused lifecycle, shared-helper, mux, native-down and project-meta tests pass (267 of 269). The two failures are innative-compose-adoption-local*; they fail the same way on this host at98978b5b, whose hosted CI was green, so they depend on this host and not on this change. Both e2e suites pass, run one at a time (shared-host-helpers,lifecycle-session-recovery).bun run test:modelspasses all models and controls.Not proven here: macOS host behavior (hosted macOS test shards will run the bun tests), installed binaries and releases.
Release intent:
feat(optional new config capability).🤖 Generated with Claude Code