Skip to content

chore: move to Bun 1.4.4 and replace oxfmt with bun fmt - #61

Merged
ANT1OFF merged 3 commits into
mainfrom
feat/bun-1-4-4
Nov 1, 2026
Merged

ANT1OFF merged 3 commits into
mainfrom
feat/bun-1-4-4

Conversation

@ANT1OFF

@ANT1OFF ANT1OFF commented Nov 1, 2026 •

Copy link
Copy Markdown
Owner

Moves the repo to Bun 1.4.4 (the Mac runs it through ANT1OFF/nixconf#22), adds a one-day minimum release age, and replaces oxfmt with the built-in bun fmt.

  • Pins: .bun-version, packageManager and engines.bun are now 1.4.4. .node-version already says 26.12.0, which agrees with nixconf, so it is unchanged.
  • bunfig.toml: adds minimumReleaseAge = "1d". No minimumReleaseAgeExcludes, because nothing pinned needs one.
  • Formatter (own commit, c80ded7): fmt and fmt:check now run bun fmt --no-script. oxfmt is gone from the catalog and devDependencies, its $schema line is gone from .oxfmtrc.json, and the two comments naming it are updated.
  • Docs: docs/VERIFICATION.md says what the release age covers.
  • Left alone on purpose: .oxfmtrc\.json stays in tools/check/affected.mjs's GLOBAL list, because the file is still the formatter config, so a change to it still affects every workspace. The 1.4.3 strings in check-slot.test.mjs and local-checks.test.ts belong to synthetic fixtures that are consistent on their own.

What moved in bun.lock

Bump commit: no resolved version changed. The set of name@version values is identical before and after, and overrides, catalog, patchedDependencies and trustedDependencies are untouched.

  • This is the peer resolution change. The Babel 7 plugins that shadcn, @shadcn/registry, electron-vite, @tanstack/router-plugin and babel-dead-code-elimination pull in used to sit at the root, each with a private @babel/core@7 copy, because the root @babel/core is 8. They now sit under the parents that use them.
  • Entries go from 1418 to 1257 (234 removed, 73 added, 0 changed in place).
  • The lock's stale 1.2.2 workspace version also became 1.3.0.

Swap commit: removes oxfmt, its 21 platform bindings and tinypool, which only oxfmt used. Nothing else changes.

Risks from the 1.4.4 notes

Risk Result
Effect patch Applies from a cold cache with no warning; the patched lines are in the installed NodeChildProcessSpawner.js.
Install flags The only flags in the tree are --frozen-lockfile, and --prefer-offline --filter in pr-shots.mjs. No bun x, bunx or bun pm calls exist. The pr-shots.mjs combination ran with exit 0 on 1.4.4.
Globs apps/*, packages/* and --filter @gram/... contain no *(...) or {a..b} forms.
hasInstallScript Not in the lock yet, because Bun keeps existing entries; expect it in later lock changes. It is a key in the details object, and dependabot-core's record.rb only reads dependencies from that object. check-dependency-pins.mjs doesn't read bun.lock.
CI cache No change needed. The key hashes bun.lock and .bun-version, with no restore-keys, and this PR changes both.
"1d" on older Bun Bun 1.4.2 and 1.4.3 both reject the string (Expected number of seconds), so this value and the 1.4.4 pin must land together. Dependabot's Bun is 1.4.2, but its file fetcher takes only .npmrc, never bunfig.toml, so it isn't affected.
Dependabot on the new lock Bun 1.4.2 re-resolving this lock changed zero lines, so its PRs shouldn't undo the re-nesting.
Frozen installs vs. release age A frozen install doesn't re-check age: with a 3650d limit, a pin that limit blocks still installed. CI and rc.mjs can't fail on it. A plain bun install does refuse (blocked by minimum-release-age).

Checks run

All local on Bun 1.4.4 and Node 26.12.0, using synthetic state. No live-provider or packaged-app qualification.

  • bun install --frozen-lockfile from a fresh clone with an empty BUN_INSTALL_CACHE_DIR (on the bump commit): exit 0, no warnings, working tree clean.
  • bun install --frozen-lockfile on the final tree: no changes.
  • bun run check:scaffold, bun run check:docs, bun run lint, bun run typecheck: pass.
  • bun run fmt:check: 2858 files, 67 ms. bun run fmt wrote no diff, and --doctor reports all 2858 formatted natively.
  • Formatter probes: an unformatted file fails the check (exit 1), a CRLF file fails (exit 1), a 120-column line wraps at printWidth, and a file under an ignorePatterns path is skipped (exit 0).
  • bun run test tools/check tools/release-rc packages/themes: 15 files, 269 tests pass.
  • bun run build:bundles: pass, including third-party notices, which exercises the re-nested Babel packages.

The full bun run check, e2e and packaging are left to CI and the managed check, as this is a shared Mac.

Known limits

  • Reverting the formatter: the repo allows squash merges only, so the swap won't stay a separate commit on main. To revert it alone, use git revert on c80ded7 (kept by the PR's head ref) and regenerate bun.lock.
  • Regenerating from nothing: deleting bun.lock and resolving from scratch in a stripped copy didn't finish within 5 minutes, and I didn't find out why. The lock here comes from bun install over the existing file, which is also what keeps versions stable.
  • Not exercised: Windows, x64 Linux and a signed Mac build.

Dependencies or holds

#60 (feat/agent-deps-bump) also touches bun.lock and package.json. Neither PR has to merge first. Whichever lands second rebases and regenerates bun.lock; its bumps are not redone here.

@ANT1OFF
ANT1OFF merged commit 875cd76 into main Nov 1, 2026
22 checks passed
@ANT1OFF
ANT1OFF deleted the feat/bun-1-4-4 branch November 1, 2026 15:09
ANT1OFF added a commit that referenced this pull request Nov 1, 2026
…h set (#60)

Bumps the Claude Agent SDK, the Anthropic, MCP and AI SDKs, and the
Codex side, and takes the rest of #55's set so this replaces it. Every
version is at least 7 days old by npm publish time and pinned exactly.
Nothing from #55 was dropped, except `oxfmt`: #61 replaced it with `bun
fmt` on `main`.

**Live-provider behaviour was not qualified.** No real ChatGPT or Claude
account and no paid provider turn was used. Codex helpers and Dream now
accept only CLI 0.164.0 on account-free evidence (below), which was the
user's decision. `GRAM_LIVE_SUBSCRIPTIONS` has not been run against
0.164.0.

## Versions

| Package | From | To | Newer, skipped (under 7 days old) |
| --- | --- | --- | --- |
| `@anthropic-ai/claude-agent-sdk` | 0.3.302 | **0.3.310** |
0.3.311–0.3.316 |
| `@anthropic-ai/sdk` | 0.135.0 | **0.137.0** | none in 0.x; 1.0.0–1.3.0
are a major, out of scope |
| `@modelcontextprotocol/sdk` | 1.32.1 | **1.32.2** | none |
| `ai` | 7.0.143 | **7.0.153** | 7.0.154–7.0.159 |
| `@ai-sdk/openai-compatible` | 3.0.71 | **3.0.78** | 3.0.79–3.0.82 |
| Codex CLI (bindings and bundled runtime) | 0.162.1 | **0.164.0** |
0.165.0, 0.166.0 |
| `@tanstack/react-virtual` | 3.14.21 | **3.14.22** | 3.14.23, 3.14.24 |
| `@tanstack/react-query` | 5.104.4 | **5.104.5** | 5.105.0 |
| `@tanstack/react-router` | 1.170.45 | **1.170.46** | none |
| `@tanstack/router-plugin` | 1.168.47 | **1.168.48** | none |
| `streamdown` | 2.7.0 | **2.7.1** | none |
| `@babel/core` | 8.0.7 | **8.0.9** | 8.0.10 |
| `lucide-react` | 1.59.0 | **1.61.0** | 1.62.0–1.66.0 |
| `motion` | 14.3.0 | **14.6.0** | 14.7.0–14.9.0 |
| `oxlint` | 1.88.0 | **1.89.0** | 1.90.0 |
| `oxlint-tsgolint` | 7.0.2004 | **7.0.2005** | none |
| `shadcn` | 4.22.0 | **4.22.1** | 4.22.2, 4.23.0 |
| `vite` | 8.3.4 | **8.3.5** | none |
| `zustand` | 5.0.15 | **5.0.16** | none |

`ai` and `@ai-sdk/openai-compatible` land one patch past #55 because
those versions passed the 7-day line. `bun.lock` was regenerated with
pinned Bun 1.4.4 (#61 landed mid-PR) and `--minimum-release-age 604800`,
so transitive packages obey the rule too: a plain install pulled
`rolldown` 1.2.16, `postcss` 8.5.32 and `@babel/*` 8.0.10, all too new.
All 82 packages new to the lockfile against `main` were then checked
against npm publish times. The catalog keeps its keys and only the
values listed above change. Native overrides, the Effect patch,
`.bun-version` (beyond #61 on `main`), Node and Electron packages are
unchanged.

## What "the Codex side" is

There is no Codex npm dependency, and none was added. Gram pins the CLI
in `CODEX_CLI_VERSION` and commits generated bindings, so moving it
means:

- `packages/agents/src/codex/protocol`: regenerated with `codex
app-server generate-ts` from the real 0.164.0 in an empty `CODEX_HOME`
(24 files changed, 16 added).
- `codex-baseline.ts`: the managed runtime's npm packages for all four
targets. Integrity is computed from the downloaded tarballs and matches
the registry's.
- `CODEX_HELPER_VERSION` and `VERIFIED_CODEX_DREAM_VERSION`, the
exact-match gates, move to 0.164.0, with their messages, docs and
synthetic fixtures.

**User-visible effect:** Codex 0.162.x is now a release line below the
baseline, so its verdict goes from `supported` to `unsupported`, shown
in the error tone with an upgrade step, and helpers and Dream refuse it.
The verdict blocks no session, so ordinary Codex chats still run.
`codexVersionWarning` warns there too.

## Changelog notes that matter to Gram

**Codex 0.163.0 and 0.164.0**
- Protocol: additive for Gram; the adapter typechecks unchanged.
`thread/readState/changed` falls to the mapper's default. `sci` and
`sci_plus` plans and the `preModelRequest` and `postModelRequest` hook
events reach no Gram mapping. `ImageGenerationItem.result` is now
optional, and Gram doesn't read it.
- Resume now restores saved permissions, network grants and model
settings. Gram sends explicit thread settings on `thread/resume`, which
the changelog says are respected. Not exercised against a real saved
thread.
- Instant interrupt is on by default, and input admission closes while a
turn finishes recording. A steer in that window could be refused more
often than before; Gram surfaces a refused steer as an error, as it
already did. Not changed here.
- Failed compaction no longer leaves partial summaries, and incomplete
responses are terminal errors.
- **`stable_environment_tools` is `removed` in 0.164.0 and its "on"
behaviour is now permanent**, while `config/read` still echoes the
`false` pin, so that check proved nothing. With Gram's exact helper
params on `gpt-5.5`, 0.164.0 offered `apply_patch` and `view_image`,
which 0.162.1 did not. An independent review found this; I reproduced
it. The fix: the dead pin is gone and `view_image` is pinned off. No key
found removes `apply_patch` (`include_apply_patch_tool`,
`tools.apply_patch` and `apply_patch_tool_type` had no effect), so it
stays declared on that model, and the reviewer's fake-model call got "No
usable execution environment is available", wrote nothing and returned
no image data. `environments: []` is now the only control on it. Wording
that said environment tools are disabled now says helpers run with no
environment.
- `executor_capability_discovery_v2`, `multi_env_copy_file` and
`request_permissions_tool` are also pinned off. They are opt-in, but a
user's `config.toml` could enable them for helper threads.
- The bundled model catalog is identical between 0.162.1 and 0.164.0.
- Thread-ownership behaviour was measured at 0.162.1 and not
re-measured. Its comments and docs now say so rather than claiming
0.164.0. The fence stays conservative either way: only owning-child exit
clears it.

**Agent SDK 0.3.303–0.3.310 and Claude Code 2.1.303–2.1.310**
- `sdk.d.ts` adds no exported types and no message subtypes, and model
ids are unchanged. `CLAUDE_AGENT_SDK_VERSION` moves to 0.3.310, so the
baseline CLI is 2.1.310. The bundled darwin-arm64 CLI reports that
version.
- Permission and sandbox hardening arrives with no Gram change,
including: Bash `cd`-then-git prompts, `Edit` deny rules against
redirects, sandboxed startup files, `--mcp-config` write protection, a
disabled MCP server no longer reconnecting on a later prompt in SDK
sessions, and Windows PowerShell hooks no longer failing open.
- 0.3.305 stops `setMcpServers` servers being replaced by same-named
user servers, and 0.3.309 fixes `managedSettings` dropping
`disableAllHooks`. Gram neither calls `setMcpServers` nor passes
`managedSettings`, so neither fix reaches it.

**Worth a follow-up, not built here:** Codex `excludedThreadIds`,
durable thread read state, `thread/sample`, the `thread/fork` prediction
mode and `toolListMeta`. Agent SDK `wakeup_source` on
`command_lifecycle`, usage `recommendations`, and stable
`getContextUsage()` row ids.

## Test change

`resolve.test.ts` used `patch(CODEX, -1)` as "an older Codex", which at
x.y.0 gives `0.164.-1`, an unparsable version. Nothing weakened: the
Codex tests now use `minor(CODEX, -1)` and assert the stricter
`unsupported` verdict, and the `graceful` case is asserted through
Claude, whose patch number is not zero.

## Verification

| Command | Result |
| --- | --- |
| `bun run check:scaffold`, `check:docs`, `check-dependency-pins`,
`oxlint --deny-warnings`, `fmt:check`, `tsc -b` | pass |
| `vitest run packages/agents` plus server `agent-runtime`,
`agent-support`, `helper`, `launch-checks`, `gateway`,
`codex-dream-protocol` | 70 files passed, 9 skipped; 1169 tests passed,
25 skipped |
| `GRAM_LIVE_CODEX_RUNTIME=1 vitest run managed-codex.live.test.ts` |
Real 0.164.0 fetched from npm, verified, installed to a temp store and
handshaken in an empty home. No account. The Windows case is skipped. |
| `bun run --filter @gram/web build` | pass |
| `bun install --frozen-lockfile` (Bun 1.4.4) | pass |

Account-free helper probe: real app-servers with Gram's helper flags and
`thread/start` and `turn/start` params including `environments: []`, an
empty home, a fake key and a loopback Responses endpoint. `config/read`
reads back every pin as `false`, `web_search` disabled, `agents.enabled`
false and the probe's base URL. Tools offered:

| CLI, pins, model | Tools |
| --- | --- |
| 0.162.1, old pins, `gpt-5.5` | `request_user_input`, `get_goal`,
`create_goal`, `update_goal` |
| 0.164.0, old pins, `gpt-5.5` | the above + `apply_patch`, `view_image`
|
| 0.164.0, this PR, `gpt-5.5` | the above 4 + `apply_patch` |
| 0.164.0, this PR, `gpt-6-luna` | none |

CI can't catch this class of change: the fixtures are synthetic and no
test runs a real CLI's tool list.

Independent review: baseline hashes, target mapping and lockfile scope
were confirmed against the registry on `fe7fdc3`. The helper fix was
re-reviewed on `8f0c6fb`, and all 23 pins read back `false` on real
0.164.0. Later commits merge `main` (#61), reflow one note and tighten
one comment.

## Known limits

- Not tested: real ChatGPT auth and the built-in route, any real
provider turn, Dream on a real account, resume of a real saved thread,
Windows and Intel Mac runtime installs (their hashes are verified, the
binaries were not run).
- The probe used a loopback endpoint, not the helper's real base URL.
- `apply_patch` stays declared to the model on `gpt-5.5`, so a helper
turn can spend an extra provider round on a call that fails.
- Not fixed here, not caused by this bump: `helper.ts` sends
`model_reasoning_effort: null` when a route has no effort, and real
0.162.1 and 0.164.0 both reject that at `thread/start`
("reasoning_effort must not be empty"). A Codex helper call without an
effort fails on either version.
- Full `bun run check` and e2e were left to CI and the managed check,
per the shared-Mac rule.
- The bindings are generated from the darwin-arm64 CLI.
ANT1OFF added a commit that referenced this pull request Nov 1, 2026
## Why

Since #61, `bunfig.toml` has `minimumReleaseAge = "1d"`. Dependabot's
own Bun reads `bunfig.toml`
and cannot parse unit strings, so every Dependabot Updates run fails on
that file. Run
41319933398 failed all 18 dependencies with:

```
error: Expected number of seconds for minimumReleaseAge at .../bunfig.toml:4:21
Invalid Bunfig: failed to load bunfig
```

Bun 1.4.2 and 1.4.3 reject the string too.

## Change

`minimumReleaseAge = 86400` with a one-line comment. A plain number is
seconds in every Bun
version, so the policy stays one day. Nothing else in the file changed,
and no docs or tests
repeat `"1d"`. [docs/VERIFICATION.md](docs/VERIFICATION.md) describes
the policy as "a day" and
does not say Dependabot skips `bunfig.toml`, so it needed no edit.

## Verification

| Command | Result |
| --- | --- |
| `bun install --frozen-lockfile` (Bun 1.4.4) | passes; `bun.lock`
unchanged (only `bunfig.toml` in the diff) |
| Bun 1.4.3, `install --dry-run --ignore-scripts` on a copy with `"1d"`
| fails: `Expected number of seconds for minimumReleaseAge` / `Invalid
Bunfig: failed to load bunfig`, exit 1 |
| Bun 1.4.3, same on a copy with `86400` | passes, exit 0 |
| `bun run check:scaffold` | Scaffold boundaries pass (3767 text files
checked) |
| `bun run check:docs` | passed (35 tracked Markdown files) |
| `bun run fmt:check` | all matched files use the correct format |

The old-Bun copies held `package.json`, `bun.lock`, `patches/` and
`bunfig.toml`. They ran with
`--ignore-scripts` because the root `postinstall` needs the full source
tree.

## Known limits

- Dependabot cannot be triggered from here. Its next scheduled Monday
run is the real proof that
  the Bun it uses now parses the config.
- Bun 1.4.3 was checked by dry run only, not a full install.
- Root configuration runs every CI lane, so CI is the full check. I did
not run `bun run check`
  locally.
- No release is cut.
ANT1OFF added a commit that referenced this pull request Nov 2, 2026
## Why

The disk was nearly full and 66 Gram worktrees had piled up, mostly from
finished, merged and archived workers. The sweep had already freed most
of their `node_modules`, but it never removed them. Two causes showed up
on a real machine:

| Leftovers | Why the sweep kept them |
|---|---|
| ~40 archived workers whose PR **merged** (e.g. #60, #61, #98, #99) |
Once GitHub answers that an archived thread's PR merged, the publisher
drops that thread's state. `badgeFor` then returns `undefined`,
`workerPrBadges` gives `[undefined]`, `anyPrOpen` reads the unknown PR
as open, and `mergedAndDone` is false. So the 10-minute
`MERGED_GRACE_MS` never applied, and every merged archived worker waited
the 14-day `removeAfterDays`. All of the Gram 1.x leftovers were 7 days
old or less. |
| ~15 archived workers with **no PR of their own** (reviewers, research,
release cuts on a detached HEAD) | `remove` needs `hasPr`, so these were
only ever freed. A test named it a rule: "never removes a worker with no
PR". |

Not gaps, checked and left alone:
- `local-checks` is already pruned by `local-checks.ts` retention:
passes are kept 30 days and other checks 7, logs are capped at 256 MB,
and abandoned work folders are reclaimed after an hour. On this machine
it was 57 MB with `work/` empty.
- Worktrees from the 0.x app (gram-private) belong to another app's
store. The 1.x sweep never knew about them.

## What changes

- **Archived merged workers**: a parked thread completed on a merge
(`completedReason: "prMerged"`, `completedAt` set) that follows no extra
PRs and has no badge still open counts as merged-done. It goes after
`MERGED_GRACE_MS`. `checkWorktree` still asks GitHub about the bound PR
afresh and needs it merged or closed, the worktree clean, and HEAD on a
remote.
- **Archived workers with no PR**: a parked worker with no bound PR, no
badge and no extra PRs gets `remove` a day (`ARCHIVED_GRACE_MS`) after
its last activity, marked `withoutPr`. `sweepStep` removes it only when
it's clean, HEAD is on a remote, and GitHub answered that no PR is open
for its branch (`noPrOpen`). A failed lookup keeps it. Plain
(non-worker) threads with no PR still aren't removed, and neither are
unarchived workers.
- **A process working in the worktree keeps it.** Right before it frees
`node_modules` or removes a worktree, the sweep lists every process's
working folder: `lsof -d cwd` on macOS, `/proc/*/cwd` on Linux. If any
is inside the worktree, it does nothing (`inUse` / `freeUnlessInUse`).
If it can't tell (Windows, a failed scan), it also does nothing. Thread
state alone can't see an agent still running after an archive, a dev
server or an outside shell.
- The Settings › Storage toggle copy names the new rule.

Nothing else changed: removal still goes through `closeAtRest`, which
refuses a dirty tree, a busy thread or a running terminal. Threads held
by others, threads waiting on the user and orchestrators are still
skipped, and no branch is ever deleted.

## Independent review fixes (findings 1–5)

| # | Finding | Fix | Refusal test |
|---|---|---|---|
| 1 | An adopted worker in a user-made worktree outside `.worktrees` got
removed | Every thread, workers included, is swept only directly inside
the repo's own `.worktrees/` | Adopted worker outside, merged worker
elsewhere, nested folder and unknown repo are never due |
| 2 | `status.showUntrackedFiles=no` hid untracked files from the clean
check and from `git worktree remove` | `STRICT_STATUS_ARGS`
(`--untracked-files=all --ignore-submodules=none --ignored=no`) in the
sweep, the Storage probe and `worktreeCleanliness`; `git worktree
remove` runs with `-c status.showUntrackedFiles=all` | Real repo with
the config set: the sweep reads it dirty, `removeWorktreeDir` refuses,
the file survives |
| 3 | `Pr.status` returns `undefined` when `gh` isn't ready, which read
as "no PR open" | No-PR removal needs `gh pr list --state open` itself
to succeed and list none (by branch, or by commit when detached).
`Pr.status` no longer feeds it | Signed out, missing, timed out, garbage
or open PR: kept |
| 4 | Closing the thread deleted its pending wakes | A thread with a
wake isn't removed (checked when judged and again right before the
close); freeing still runs | No-PR and merged threads with a wake:
removal not due |
| 5 | The grace counted from last activity, not from the archive |
`Thread.parkedAt`, stamped by the store when an edit archives a thread
and dropped on unarchive; grace counts from max(activity, parkedAt).
Threads archived before this wait the full M days | Quiet 3 days,
archived 1 h ago: not due. Store test for the stamp |

Also from the review's lower-severity notes: a detached HEAD is kept
under `refs/gram/swept/<thread id>` before its worktree goes (if that
can't be written, the worktree stays), a free step scans processes once
instead of twice, and the Storage copy is fixed. For each finding,
putting the old line back makes its new test fail.

## Verification

- At 1a656a2: `bunx vitest run packages/contracts packages/git
packages/server/src/git packages/server/src/persistence
apps/web/src/settings`: 179 files, 1935 passed, 16 skipped. lint,
typecheck, fmt:check, check:scaffold pass.

- `bunx vitest run packages/server/src/git/`: 33 files, 326 passed, 13
skipped. `worktree-gc.test.ts` is 19/19.
- Live-process test: a real `sleep` runs inside a temp worktree's
`node_modules`. The sweep's free step leaves every file in place, and
frees the folder once the process exits. With the guard line taken out,
this test fails.
- A worker in the same state as `feat-voice-vocab` (open, cold between
turns, no PR, never completed) is asserted never due.

- `bunx vitest run packages/server/src/git/ apps/web/src/settings`: 76
files, 682 passed, 13 skipped
- The new tests run against the old `worktree-gc.ts` fail 6 of 15,
including "removes an archived merged worker after the grace, though its
badge was dropped". With the fix, 15/15 pass.
- `bun run typecheck`, `bun run lint`, `bun run fmt`, `bun run
check:scaffold` pass.
- Full `bun run check` is left to CI and the merge gate. The machine is
shared.

## Known limits

- A reviewer worktree on a detached HEAD whose PR branch was deleted and
pruned from the remote reads as unpushed and is kept. That's the safe
side. It didn't happen on this machine, so it isn't handled here.
- Ignored files (e.g. `.env.local`, notes in ignored folders) aren't
counted by any clean check and go with `git worktree remove`, as they
already did for the close dialog and Storage cleanup.
- The process guard sees only processes whose working folder is inside
the worktree, not ones holding files open there from elsewhere (an
editor, a `bun --cwd` run). An idle thread's live agent session or a
shell left at its prompt keeps its worktree until the session is
evicted.
- Removal closes the thread. A branch it had stays, and a detached HEAD
is kept under `refs/gram/swept/`. `restore_worktree` can't bring back a
worktree that had no branch.
- On Windows the process guard can't tell, so the sweep acts on nothing
there. No Windows releases are published.
- The process check, like the thread check, runs right before the step
and isn't held across the deletion (the existing "known and accepted
gap").
- Not live-qualified against a running app. Covered by unit tests and
git-backed tests with a stubbed `gh`.
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