Grok worktree show stays alive after the creator exits

John Burns

grok worktree show printed Status: alive. The creator PID on that line was not a running process. grok sessions list exited 0 and printed No sessions found. The session id began with pager-. A search of the grok home found that id in the worktree registry and nowhere else.

That is the line that will mislead a wrapper. grok worktree create exited 0, wrote one path to stdout, and wrote nothing to stderr. The sessions page this binary installed on first launch says create does not start a session, and that the path is the only stdout, so a command substitution can change to that directory. Both of those sentences held. The registry row did not. Show still reported a session id, a creator PID, and a status of alive after the create process was gone.

The same command leaves a second trap when you name a branch. grok worktree list printed BRANCH as master for a worktree created with --ref master. git status -sb in that directory printed ## HEAD (no branch). There was no symbolic ref. A commit there does not advance master.

The path is not the row

The binary was Grok Build 1.0.67, build 2fe064390d07, on Linux x86_64, kernel 7.0.14-6-pve, on 10 November 2026. HOME pointed at a new directory. GROK_HOME was unset, so the grok home was the .grok directory under that home. The operator grok home was not the directory under test. No grok login was run. No prompt was sent. The operator worktree database timestamp was the same after the run as before it.

The repository was a new git checkout with one commit on master and one tracked file, seed.txt. From that checkout, grok worktree create labwt exited 0. Stdout was the worktree path and nothing else. Stderr was empty, zero bytes. That is the contract in the bundled sessions page: create prints the directory the session would have opened in, and that path is the only output on stdout. The help text says the shorter version. Create makes the worktree grok -w would make, without starting a session.

grok worktree show labwt then exited 0. The path and source repository in the real output were under the disposable home. The rest of the block was:

  ID:             labwt-2b0301e35694960c
  Type:           fork
  Creation Mode:  copy
  Git Ref:        HEAD
  HEAD:           a106e41e29e7
  Created:        2026-11-10 16:09:03 UTC
  Session ID:     pager-7b88d4832505
  Creator PID:    980750
  Status:         alive
  Label:          labwt
  Disk Usage:     8.0 KB

ps on PID 980750 found no process. Show, read again after that check, printed the same PID and the same alive. The status is not a probe of the process table. It is a column on the registry row, and create writes the row as alive when it finishes. The schema in worktrees.db defaults status to alive. A row that has not been marked otherwise will keep saying alive, including after the process that inserted it has exited.

grok worktree list --json is the form a script will parse. For that row, status was alive, session_id was pager-7b88d4832505, creator_pid was 980750, kind was fork, and creation_mode was copy. last_accessed_at was null. Nothing in the object says the creator has exited. If you gate the next step on status == "alive", create will always pass that gate on this binary, whether or not a Grok process is still in the directory.

The text list is what a person will read. After three creates, it was:

  ID                       TYPE STRATEGY REPO   LABEL   BRANCH               AGE        PATH
  dirtywt-9c0b2bd0e8c66607 fork copy     repo   dirtywt HEAD                 1s ago     ~/.grok/worktrees/lab-repo/dirtywt
  refwt-048b01979c527cd2   fork copy     repo   refwt   master               1s ago     ~/.grok/worktrees/lab-repo/refwt
  labwt-2b0301e35694960c   fork copy     repo   labwt   HEAD                 2s ago     ~/.grok/worktrees/lab-repo/labwt
  3 worktrees (3 fork)

TYPE is fork on every row. The schema default for kind is session. These rows are not that default. They are forks that never had a session. A query that looks for kind = 'session' to find live work will miss them, and the text list will still show them. A query that looks for status = 'alive' to find a running creator will include them, and no creator will be there.

The ages in that table are how long the row has existed, not how long a process has been running. Two seconds after create, the row is already a record of a finished command. Treat the age the same way you treat the status. Neither one is a heartbeat.

The session id is not a session

The sessions page says a session id is a UUIDv7 when Grok generates it, or an id the client passes with -s, and that sessions are stored under the sessions directory of the grok home. The id on this row is pager-7b88d4832505. That is not a UUID. It was not a file in the sessions directory. It was a field on the worktree row.

grok sessions list exited 0 and printed No sessions found. That is the list the sessions page tells you to use instead of reading session files. It does not know about the id show just printed. The id is stored so a later session could have been bound to the worktree. No later session was. Create’s own help says it does not start one. The row still carries a session-shaped id, and the prefix pager- is the tell. A UUID from -s, or an id that sessions list can print, is a session. This one is a placeholder written at create time.

The help for grok worktree show does not define Status or Session ID. The sessions page, in the paragraph that documents grok worktree create, does not mention either field. It says create does not start a session, that stdout is only the path, that the default copies uncommitted changes, and that --ref is a clean checkout of a branch, tag, or commit. Read the first of those as a statement about the process. It is not a promise that show will omit session fields. On this binary the process is gone and the fields remain.

The public changelog, read the same day, lists Grok Build 1.0.77 on 5 November 2026 as the newest release. This host was still on 1.0.67. The 1.0.77 notes do not say that show stops printing alive after create exits. An earlier changelog note says list and show mark a worktree whose folder is missing or whose source repository moved. That is a different condition. The folder in this run was present. The source repository had not moved. The creator PID was gone, and status stayed alive. This run did not install 1.0.77, and it does not say what that release prints.

Do not send the pager- id to grok -r or grok usage and expect a transcript. This run did not call those commands. It does say that sessions list is empty while show names an id, and that the sessions directory does not contain that id. If you need a session, start one. Create did not.

The branch column is the ref you asked for

Without --ref, the sessions page says the worktree starts from HEAD and copies uncommitted changes. With --ref, it says the worktree is a clean checkout of that branch, tag, or commit. The headless flag table says the same thing in fewer words: a clean checkout, no dirty overlay. Both content rules held. Neither one attached the worktree to the branch.

The checkout was dirty before the second and third creates. seed.txt had gained a second line, dirty. An untracked file, extra.txt, held extra. Then grok worktree create dirtywt and grok worktree create --ref master refwt each exited 0, and each wrote one path to stdout.

In the dirty worktree, seed.txt contained both lines and extra.txt was present. git status -sb printed:

## HEAD (no branch)
 M seed.txt
?? extra.txt

That is the overlay the sessions page describes. The changes are copied. They are not committed. The worktree is detached, the same way the first create was. git rev-parse --abbrev-ref HEAD printed HEAD. git symbolic-ref HEAD failed. The copy is a working tree, not a branch.

In the --ref master worktree, seed.txt was the committed line only. extra.txt was absent. git status -sb printed:

## HEAD (no branch)

There was no symbolic ref. The content matched master at a106e41e29e7. The checkout was not on master.

grok worktree list is where the wording diverges from git. The BRANCH column said master for refwt and HEAD for the other two. grok worktree show refwt printed Git Ref: master and the same HEAD commit. The JSON field was git_ref, set to master. That field is the ref you passed, or the string HEAD when you did not pass one. It is not the result of git symbolic-ref. A script that reads BRANCH or git_ref and then commits inside the worktree will create a commit that master does not contain. Git will accept the commit. The branch you named will not move.

The phrase “clean checkout of a branch” is easy to read as “this directory is on that branch.” On this binary it means the files match that commit, and the uncommitted overlay was not copied. The attachment is a separate fact, and create does not establish it. If the ref had been a tag or a raw commit, a detached HEAD would be the ordinary git result. --ref master was a branch name. The worktree was still detached. The list column did not say so.

HEAD in the branch column of the other two rows is the same kind of reprint. It is the ref create recorded, which was the literal HEAD it started from. It is not a branch named HEAD, and it is not a claim that git has a symbolic ref. git status -sb is the line that says whether a branch is checked out. The grok column is the line that says what you asked for.

Verify the row before you trust it

After grok worktree create, keep the path from stdout. Do not parse show to find the directory. Show is a registry dump, and on this run its path line was an absolute path under the disposable home. The path on stdout is the directory git knows about. If you are writing a script, require that stdout be exactly one line and that the exit code be 0. Extra lines would break the command substitution the sessions page recommends. This binary did not print extra lines.

Confirm the process with the process table, not with Status. If you saved the creator PID, ask ps about it. On this run the PID was already gone when show was read back. A missing process plus status alive is the normal result of create, not a stuck session. Do not poll show waiting for the status to change. It did not change between the first show and the second.

Confirm the session with grok sessions list, not with the session id on the worktree row. An id that starts with pager- and does not appear in that list is not a session you can resume. The sessions page stores real sessions under the sessions directory. This id was not there. An empty session list after create is the expected result, not a failure of the worktree.

Confirm the branch with git, in the worktree directory, before any commit. ## HEAD (no branch) means the BRANCH column is not a checked-out branch, even when that column says master. If the new commit has to land on a branch, create the branch from inside the worktree, or check the branch out, and then require git symbolic-ref HEAD to succeed. Create will not do that step for you on this binary. Checking git_ref in the JSON is how you learn what was requested. It is not how you learn what is checked out.

Confirm the overlay separately from the branch. Without --ref, uncommitted changes are copied and left uncommitted. With --ref, they are not copied. A missing extra.txt in the --ref worktree is the clean checkout the guide describes. It is not evidence that the worktree is attached to the named branch. A modified seed.txt in the default worktree is the overlay, still uncommitted, on a detached HEAD. Status was alive on the dirty row, the clean row, and the first row. It did not distinguish them.

The help for grok worktree rm says --force removes a worktree even when it is in use or has unsaved work. This run did not call rm. Do not pass --force because show says alive. That word was set when the row was inserted. Unsaved work is a different fact, and git status in the worktree is where it shows up. The dirty copy had a modified file and an untracked file. The --ref copy was clean. All three rows were alive.

Point HOME, or set GROK_HOME, at a disposable directory before you repeat the check. The first launch into an empty home writes the user guide quoted here. Read docs/user-guide/17-sessions.md in that home, the paragraph under Worktree Sessions that begins with grok worktree create. It is the sentence that is true about the process and silent about the row. Then read show, the process table, sessions list, and git status -sb. Those four disagree, on this binary, in a way the paragraph does not mention. The disagreement is the check.

References

  • Grok Build 1.0.67, build 2fe064390d07, Linux x86_64, kernel 7.0.14-6-pve, 10 November 2026. grok worktree create, grok worktree show, grok worktree list, and grok sessions list ran with HOME pointed at a new directory. No grok login was run. No prompt was sent. The operator grok home was not modified.
  • Bundled user guide docs/user-guide/17-sessions.md, written by that binary into the empty grok home on first launch. The Worktree Sessions paragraph says create does not start a session, stdout is only the path, the default copies uncommitted changes, and --ref is a clean checkout of a branch, tag, or commit. Session ids are described earlier as a UUIDv7 or a client id passed with -s, stored under the sessions directory.
  • Bundled user guide docs/user-guide/14-headless-mode.md, the --ref / --worktree-ref row: a clean checkout, no dirty overlay. GROK_HOME overrides the grok home. The default is ~/.grok.
  • grok worktree create --help and grok worktree show --help from the same binary. Create’s help says it does not start a session. Show’s help does not define status or session id. grok worktree rm --help describes --force for a worktree that is in use or has unsaved work. This run did not call rm.
  • Grok Build changelog, https://x.ai/build/changelog, read 10 November 2026. The newest listed release is 1.0.77, dated 5 November 2026. This host was not upgraded. Those notes do not say that show stops printing alive after create exits. An earlier note about marking a missing folder or a moved source repository describes a different condition than a creator PID that has already exited.
  • Grok Build source repository, https://github.com/xai-org/grok-build, read 10 November 2026. Its SOURCE_REV was 559751fdcec02d413e4c57c8832ab275e4f44980, which is not the installed build id 2fe064390d07. The behavior above is from the installed binary, not from that tree.