You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(runtime): prevent polling and stale worktree cache buildup (#71)
## Summary
- stop injecting `CHOKIDAR_USEPOLLING=true` and `WATCHPACK_POLLING=true`
into every generated service while preserving explicit user-authored
values
- make implicit `hack down` recover a uniquely owned same-checkout
runtime after a linked-worktree branch rename, and fail safely when
ownership is ambiguous
- add confirmation-gated `hack down --prune-caches` cleanup for exact
Compose-owned disposable cache volumes
- support any framework or language through the top-level Compose volume
label `hack.cache.disposable: "true"`; retain the narrow built-in
`.next` destination rule for existing Next caches
- document the root cause, contracts, safety boundaries, and
intentionally omitted noisy doctor checks
## Root cause
Hack's Compose generator unconditionally forced Chokidar and Watchpack
polling on every discovered service, including non-Next services.
Separately, linked-worktree branch renames changed the inferred Compose
project name, so an implicit `hack down` could succeed against an empty
target while leaving the checkout's real stopped runtime and cache
volumes behind. Ordinary Compose down correctly preserves named volumes,
but Hack had no project-scoped lifecycle for disposable branch build
caches.
The observed OrbStack/Next incident combined those Hack behaviors with
project-authored amd64 emulation and large persistent `.next` volumes.
This PR fixes only the behavior Hack owns; it does not rewrite project
platform or healthcheck configuration.
## Safety boundaries
- watcher defaults are runtime-agnostic and use native container-runtime
file notifications; explicit service-level polling remains supported
- branch retargeting requires the exact canonical checkout path and
Compose project family
- zero owned runtimes keep the inferred target; one is retargeted;
multiple fail and require explicit `--branch`
- cache cleanup is local-only, opt-in, previewed/confirmation-gated, and
requires `--yes` for JSON/noninteractive use
- candidates must be named volumes observed on exact target containers
with matching Compose project/service labels
- `docker volume inspect` must independently prove the exact Compose
project and logical-volume labels
- a cache must either be mounted exclusively at `.next` destinations or
explicitly carry `hack.cache.disposable=true`
- unlabeled `.turbo`, Postgres, Redis, application-data, bind,
foreign-project, and sibling-checkout volumes are preserved
- deletion uses exact `docker volume rm <name>` calls only; there is no
`down -v`, force removal, name matching, or broad prune
Already-orphaned volumes with no surviving container are deliberately
left alone because Hack cannot prove their former checkout and mount
destination safely.
## Verification
- 70 focused tests / 304 assertions passed across Compose generation,
linked-worktree targeting, down orchestration, disposable-volume
verification, lifecycle startup regression coverage, generated agent
guidance, and CLI-reference drift
- real Docker-tier E2E creates a Compose-labeled `.turbo` cache plus an
adjacent durable volume, writes data to both, runs `hack down
--prune-caches --yes --json`, proves the cache is removed, and mounts
the durable volume read-only to prove its data survived
- `bun run typecheck`
- `bun run check`
- `bun x ultracite check` (543 files after the E2E addition)
- `bun run privacy:check`
- `bun index.ts setup sync --all-scopes --check`
- `git diff --check`
- hosted secret scan, runtime-image builds, macOS test/build job, and
Docker E2E job pass
Local Docker execution was intentionally skipped because OrbStack and
the user's projects remain stopped; the local E2E isolation canary
passed and the Docker scenario executed successfully in isolated hosted
CI. No Event Agent, MSP, sick.email, OrbStack, Docker Desktop, or
machine-wide service was started locally. The prior real OrbStack A/B
evidence supports native watching, while the exact cleanup workflow now
has real standard-Docker coverage.
## Release decision
Yes: this is a user-visible runtime correctness fix and the Conventional
Commit / PR title intentionally carries a `fix` release signal. This
repository does not currently use a separate changeset artifact for this
flow.
Copy file name to clipboardExpand all lines: .codex/skills/hack-cli/SKILL.md
+4-2Lines changed: 4 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -12,11 +12,11 @@ Use `hack` as the primary interface for local-first development.
12
12
13
13
## Integration freshness
14
14
15
-
- These instructions were generated by hack CLI v3.4.1; treat cached rules from another version as potentially stale.
15
+
- These instructions were generated by hack CLI v3.5.0; treat cached rules from another version as potentially stale.
16
16
- At session start, audit project and global integrations with `hack setup sync --all-scopes --check`.
17
17
- If anything is stale, missing, or deprecated, run `hack setup sync --all-scopes`, then reload the agent session so cached instructions are replaced.
18
18
- Never copy or hand-edit generated Hack rules to refresh them; update the CLI and run the sync command.
19
-
- Content revision: `902b04fe1469` (version alone is not a freshness guarantee).
19
+
- Content revision: `1319d93c89c3` (version alone is not a freshness guarantee).
20
20
21
21
## Product boundary
22
22
@@ -81,6 +81,7 @@ Use `hack` as the primary interface for local-first development.
81
81
- Secret key inherits from the primary checkout automatically through the shared git common dir; set `HACK_ENV_SECRET_KEY` for CI or detached environments.
82
82
-`hack up` in a linked worktree defaults to a branch instance named after the worktree's git branch; a detached linked worktree requires an explicit `--branch`, unless config `worktree.auto_branch=false` explicitly opts into the base instance.
83
83
- Before `hack up` or `hack restart` auto-targets a new branch instance, Hack warns when the same worktree already owns a non-terminal instance; pass `--branch <name>` to make the target explicit.
84
+
- Implicit `hack down` retargets a uniquely owned same-checkout runtime after a Git branch rename, including Created and stopped containers; when multiple runtimes belong to the checkout, pass `--branch <name>` explicitly.
84
85
-`hack doctor` flags divergent secret keys and dev_host collisions across checkouts.
85
86
86
87
## Advanced networking (extra_hosts + local proxies/tunnels)
@@ -131,6 +132,7 @@ Use `hack` as the primary interface for local-first development.
131
132
- Target only affected services with `hack up <service...> --detach`, `hack restart <service...>`, or `hack env apply --service <service>`; scoped operations skip project lifecycle hooks and implicit dependency startup.
132
133
- Use `hack env explain <KEY> --env <overlay> --service <service> --target <host|compose>` for redacted source, precedence, availability, and delivery diagnostics.
133
134
- Dependency installer services are detected generically by command or `hack.dependencies.bootstrap=true`; registry env references are preflighted before container mutation. Optional `hack.dependencies.cache-volume`, `hack.dependencies.lockfiles`, and `hack.dependencies.runtime-files` labels enable lockfile/runtime-keyed volumes shared across compatible worktrees.
135
+
-`hack down --prune-caches` can remove only confirmed Compose-owned named volumes mounted exclusively at `.next` destinations or explicitly labeled `hack.cache.disposable=true`; it is confirmation-gated, requires `--yes` for JSON/scripted runs, and never performs broad volume pruning.
134
136
135
137
## Workspaces (mux-managed, tmux-first by default)
Copy file name to clipboardExpand all lines: .cursor/rules/hack.mdc
+4-2Lines changed: 4 additions & 2 deletions
Original file line number
Diff line number
Diff line change
@@ -7,11 +7,11 @@ Prefer `hack` when shell access is available. Use MCP only when shell access is
7
7
8
8
## Integration freshness
9
9
10
-
- These instructions were generated by hack CLI v3.4.1; treat cached rules from another version as potentially stale.
10
+
- These instructions were generated by hack CLI v3.5.0; treat cached rules from another version as potentially stale.
11
11
- At session start, audit project and global integrations with `hack setup sync --all-scopes --check`.
12
12
- If anything is stale, missing, or deprecated, run `hack setup sync --all-scopes`, then reload the agent session so cached instructions are replaced.
13
13
- Never copy or hand-edit generated Hack rules to refresh them; update the CLI and run the sync command.
14
-
- Content revision: `902b04fe1469` (version alone is not a freshness guarantee).
14
+
- Content revision: `1319d93c89c3` (version alone is not a freshness guarantee).
15
15
16
16
## Product boundary
17
17
@@ -44,6 +44,7 @@ Prefer `hack` when shell access is available. Use MCP only when shell access is
44
44
- Secret key inherits from the primary checkout automatically through the shared git common dir; set `HACK_ENV_SECRET_KEY` for CI or detached environments.
45
45
- `hack up` in a linked worktree defaults to a branch instance named after the worktree's git branch; a detached linked worktree requires an explicit `--branch`, unless config `worktree.auto_branch=false` explicitly opts into the base instance.
46
46
- Before `hack up` or `hack restart` auto-targets a new branch instance, Hack warns when the same worktree already owns a non-terminal instance; pass `--branch <name>` to make the target explicit.
47
+
- Implicit `hack down` retargets a uniquely owned same-checkout runtime after a Git branch rename, including Created and stopped containers; when multiple runtimes belong to the checkout, pass `--branch <name>` explicitly.
47
48
- `hack doctor` flags divergent secret keys and dev_host collisions across checkouts.
48
49
49
50
## Standard workflow
@@ -78,6 +79,7 @@ Prefer `hack` when shell access is available. Use MCP only when shell access is
78
79
- Target only affected services with `hack up <service...> --detach`, `hack restart <service...>`, or `hack env apply --service <service>`; scoped operations skip project lifecycle hooks and implicit dependency startup.
79
80
- Use `hack env explain <KEY> --env <overlay> --service <service> --target <host|compose>` for redacted source, precedence, availability, and delivery diagnostics.
80
81
- Dependency installer services are detected generically by command or `hack.dependencies.bootstrap=true`; registry env references are preflighted before container mutation. Optional `hack.dependencies.cache-volume`, `hack.dependencies.lockfiles`, and `hack.dependencies.runtime-files` labels enable lockfile/runtime-keyed volumes shared across compatible worktrees.
82
+
- `hack down --prune-caches` can remove only confirmed Compose-owned named volumes mounted exclusively at `.next` destinations or explicitly labeled `hack.cache.disposable=true`; it is confirmation-gated, requires `--yes` for JSON/scripted runs, and never performs broad volume pruning.
- Secret key inherits from the primary checkout automatically through the shared git common dir; set `HACK_ENV_SECRET_KEY` for CI or detached environments.
272
272
-`hack up` in a linked worktree defaults to a branch instance named after the worktree's git branch; a detached linked worktree requires an explicit `--branch`, unless config `worktree.auto_branch=false` explicitly opts into the base instance.
273
273
- Before `hack up` or `hack restart` auto-targets a new branch instance, Hack warns when the same worktree already owns a non-terminal instance; pass `--branch <name>` to make the target explicit.
274
+
- Implicit `hack down` retargets a uniquely owned same-checkout runtime after a Git branch rename, including Created and stopped containers; when multiple runtimes belong to the checkout, pass `--branch <name>` explicitly.
274
275
-`hack doctor` flags divergent secret keys and dev_host collisions across checkouts.
275
276
276
277
Advanced networking (extra_hosts + local proxies/tunnels):
@@ -316,6 +317,7 @@ Lifecycle + startup:
316
317
- Target only affected services with `hack up <service...> --detach`, `hack restart <service...>`, or `hack env apply --service <service>`; scoped operations skip project lifecycle hooks and implicit dependency startup.
317
318
- Use `hack env explain <KEY> --env <overlay> --service <service> --target <host|compose>` for redacted source, precedence, availability, and delivery diagnostics.
318
319
- Dependency installer services are detected generically by command or `hack.dependencies.bootstrap=true`; registry env references are preflighted before container mutation. Optional `hack.dependencies.cache-volume`, `hack.dependencies.lockfiles`, and `hack.dependencies.runtime-files` labels enable lockfile/runtime-keyed volumes shared across compatible worktrees.
320
+
-`hack down --prune-caches` can remove only confirmed Compose-owned named volumes mounted exclusively at `.next` destinations or explicitly labeled `hack.cache.disposable=true`; it is confirmation-gated, requires `--yes` for JSON/scripted runs, and never performs broad volume pruning.
319
321
320
322
Workspaces (mux-managed, tmux-first by default):
321
323
- Picker: `hack session` for persistent project workspaces.
- Secret key inherits from the primary checkout automatically through the shared git common dir; set `HACK_ENV_SECRET_KEY` for CI or detached environments.
142
142
-`hack up` in a linked worktree defaults to a branch instance named after the worktree's git branch; a detached linked worktree requires an explicit `--branch`, unless config `worktree.auto_branch=false` explicitly opts into the base instance.
143
143
- Before `hack up` or `hack restart` auto-targets a new branch instance, Hack warns when the same worktree already owns a non-terminal instance; pass `--branch <name>` to make the target explicit.
144
+
- Implicit `hack down` retargets a uniquely owned same-checkout runtime after a Git branch rename, including Created and stopped containers; when multiple runtimes belong to the checkout, pass `--branch <name>` explicitly.
144
145
-`hack doctor` flags divergent secret keys and dev_host collisions across checkouts.
145
146
146
147
Advanced networking (extra_hosts + local proxies/tunnels):
@@ -186,6 +187,7 @@ Lifecycle + startup:
186
187
- Target only affected services with `hack up <service...> --detach`, `hack restart <service...>`, or `hack env apply --service <service>`; scoped operations skip project lifecycle hooks and implicit dependency startup.
187
188
- Use `hack env explain <KEY> --env <overlay> --service <service> --target <host|compose>` for redacted source, precedence, availability, and delivery diagnostics.
188
189
- Dependency installer services are detected generically by command or `hack.dependencies.bootstrap=true`; registry env references are preflighted before container mutation. Optional `hack.dependencies.cache-volume`, `hack.dependencies.lockfiles`, and `hack.dependencies.runtime-files` labels enable lockfile/runtime-keyed volumes shared across compatible worktrees.
190
+
-`hack down --prune-caches` can remove only confirmed Compose-owned named volumes mounted exclusively at `.next` destinations or explicitly labeled `hack.cache.disposable=true`; it is confirmation-gated, requires `--yes` for JSON/scripted runs, and never performs broad volume pruning.
189
191
190
192
Workspaces (mux-managed, tmux-first by default):
191
193
- Picker: `hack session` for persistent project workspaces.
0 commit comments