Repository navigation
fix(logs): report real branch names in hook-output JSON; fix tips recipes wt rejects - #4279
Conversation
The "Database per worktree" set-vars step passed three pairs to one `wt config state vars set`, which takes a single KEY=VALUE, and stored `db_url`, a key vars validation rejects since #2285. Split it into three calls chained with && and rename the key to `db-url`. "Monitor hook logs" used `wt config state logs get --hook=...`, a flag removed in #2161. Replace it with the jq filter over `wt config state logs --format=json` that the command's own help shows. Add a test that reads the set-vars step from the doc page and runs it through a wt alias, so a recipe wt rejects fails the suite.
worktrunk-bot
left a comment
There was a problem hiding this comment.
The replacement hook-log recipe is no longer scoped to the current branch. The removed wt config state logs get --hook=user:post-start:server resolved the current branch, with --branch as the override. wt config state logs --format=json lists hook_output for every branch's subtree under .git/wt/logs/, sorted newest first. Take a repo where several worktrees each run a server post-start hook: tail -f / wtlog server then follows whichever worktree wrote its log most recently, not the one the user is in. That is the usual setup for the per-worktree dev-server pattern this page describes.
A plain branch filter is harder than it looks, because .branch is the sanitized directory name. A name that isn't already filename-safe gets a hash suffix: feature/x is stored as feature-x-<hash>. So select(.branch == "$(git branch --show-current)") misses those branches, and a startswith on a tr / - form can also match a sibling such as feature-x2. The wt config state logs --help example (src/cli/config.rs, "Path to one hook log (e.g. the post-start server hook for the current branch)") has the same gap and claims current-branch scoping that its jq doesn't do.
A maintainer should choose between two options: document the recipe as picking the newest matching log across all branches, or add a branch filter to the JSON output. The second could be an unsanitized branch value or a current-branch --branch option, which would restore what #2161 dropped. I haven't pushed a fix because this changes the output format.
`wt config state logs --format=json` reported `branch` as the sanitized log directory, so `feature/x` read as `feature-x-<hash>` and no filter could match the current branch. The hook-log recipes fell back to `startswith` plus `head -1`, which picks the newest log from any worktree. Map each directory back through `sanitize_for_filename` over the local branches. `branch` is now the exact name, or null when no local branch writes there (deleted, detached, or shared by two branches). The help and tips recipes filter on `.branch == $branch` and `.name == "server"`. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…s its findings (#1433) #1203 moved the job of applying a bot-authored PR's findings into the review run, and removed the `tend-mention` hand-off. The skill still treated the review as the last step on a self-authored PR, though. The **Self-authored PRs** paragraph in Submit said "Submit as `COMMENT` when there are concerns", and it pointed at **Push fixes** only "after earlier rounds". The rule to apply the findings sat in step 9, after Monitor CI and thread resolution. On [max-sixty/worktrunk#4279](max-sixty/worktrunk#4279), the reviewer wrote that it would "post this as a comment since it's a self-authored PR", posted, and ended. It named two options, one of them a docs-only fix, and deferred both because the other needed a new flag. The pre-flight now decides who the author is and emits `author: self | bot | human` in place of `self_authored`. `self` is matched against the bot login, because the bot's own account is a plain user to GitHub. `bot` is GitHub's `is_bot` flag (Dependabot, renovate). The skill forks on that field among the step-1 snapshot fields that decide how much of the workflow runs. On `self` or `bot`, the run is the author: it posts the review, then applies the findings in one push. Three wording changes follow from the fork. The self-authored paragraph now continues into **Push fixes** instead of ending at the `COMMENT`. The two "offer to push a fix commit" lines now defer to **Push fixes**, which already branches on the author. Step 9 adds one sentence: an option that needs maintainer intent doesn't block one that doesn't. This failure is uncommon. Of 20 recent worktrunk bot PRs with a self-review, 16 got later commits, and the three I timed landed 17–36s after the review, from the review run itself. Two cold A/B scenarios on the worktrunk#4279 state pushed a fix in every run under both the old and new text (7/7 each). So this change rests on the structural argument rather than a measured effect. > _This was written by Claude Code on behalf of max-sixty_ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Compare the fields as one array instead of chaining `and`. The
extending page's `hook-log` alias matched `.branch` against
`{{ branch | sanitize_hash }}`, the on-disk form; `branch` is now the
real name, so it compares `{{ branch }}` directly.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
worktrunk-bot
left a comment
There was a problem hiding this comment.
The hook-log alias now splices {{ branch }} into a jq string literal inside a single-quoted shell program. Aliases render with POSIX shell escaping, so a branch name that needs quoting ends the single quote early. Git accepts a"b (git check-ref-format --branch 'a"b'), and that branch renders as ["'a"b'", $kind, $name], which is a shell syntax error. Before this change, sanitize_hash rewrote the ", so this is a regression. Pass the branch through --arg like the other values and leave the placeholders unquoted, so wt's escaping yields exactly one argument each. The fix is below, and I'll push it with the regenerated mirrors.
Splicing `{{ branch }}` into the single-quoted jq program broke on a
branch name that wt's shell escaping quotes, such as `a"b` or `it's`.
Each value now reaches jq as its own argument.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Bumps 0.79.0 → 0.80.0 and adds the `## 0.80.0` changelog section, cut from `12efb5159`. The bump is minor because the library API breaks (see Verification). ## Verification - `cargo run -- hook pre-merge --yes` on the cut tip: 4985 passed. - `nightly.yaml` on `12efb5159`, all jobs green including feature-powerset: https://github.com/max-sixty/worktrunk/actions/runs/36338233859 - `cargo semver-checks check-release -p worktrunk` reports five major-level lints, the removals and signature changes listed in the Internal entry. - Changelog: repeated verification passes against source until one came back clean. Fixed headlines name the condition a bug needed rather than its worst case, and near-zero-reach fixes are folded into two roll-up bullets, per #4295. - Data-loss surface review over `v0.79.0..HEAD` with independent finders. The one real finding, a nested stale detached worktree read as the enclosing repo's rebase so `wt step prune` could unregister it, was fixed in #4302. The review also found a squash regression in the backup path, fixed in #4303. The remaining candidates were adjudicated acceptable and signed off: #4228 unregistering stale worktrees whose directory remains (files stay on disk; the branch is deleted only if integrated), #4206 leaving a detached `HEAD` on Ctrl-C during a slow commit hook, #4251 allowing `--no-squash` merges with untracked files, #4219 dropping `--no-hooks` from the skill example, and #4279's `docker stop` recipe. #4252 closes a submodule data-loss path. > _This was written by Claude Code on behalf of max-sixty_ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Two recipes on the Tips & Patterns page fail when copied. This PR fixes both, adds a test that runs the database recipe straight from the page, and makes the hook-output JSON report real branch names so the log recipes can scope to the current branch.
set-varsstep passed threeKEY=VALUEpairs to onewt config state vars set, which accepts exactly one (error: unexpected argument 'port=…' found). Because the step failed,vars.containerandvars.portwere never set for thedbstep orpre-remove. The step now runs threevars setcalls chained with&&. The recipe also storeddb_url, and vars keys have rejected underscores since fix: reject underscore in vars keys (invalid git config variable name) #2285 (Git config variable names can't contain them). It's nowdb-url, in thevars getexample too. fix: use underscores in db_url var key examples #1848 had moved this key from hyphens to underscores because Jinja parsesvars.db-urlas subtraction. That doesn't apply here: the recipe reads the key only withwt config state vars get db-url, never in a template.wt config state logs get --hook=user:post-start:serverand thewtlogalias built on it fail withunexpected argument '--hook', because refactor(config-state): structured JSON for logs; drop --hook/--branch filters #2161 removed that flag. Its replacement, ajqfilter overwt config state logs --format=json, couldn't scope to the current branch. The JSONbranchfield was the sanitized log directory (feature/xbecamefeature-x-<hash>), so the help example usedstartswithandhead -1and tailed whichever worktree'sserverlog was newest.branch: the field now holds the real branch name. It maps each log directory back throughsanitize_for_filenameover the local branches, and isnullwhen no local branch writes there: a deleted branch, a detached HEAD, or a directory two branches share. The recipes in the help and on the page match[.branch, .source, .hook_type, .name]exactly. The extending page'shook-logalias compared.branchwith{{ branch | sanitize_hash }}, the on-disk form, and now passes{{ branch }}to jq with--arg, like its other values. Thewtlogalias becomes a shell function taking the hook name.test_doc_example_database_set_vars_step_runspulls theset-varsstep from the page's TOML block, runs it as awtalias withwtonPATH, and checks the storedport. It fails onmainwith the argument error above. After the first fix it also caught thedb_urlrejection.test_logs_get_json_branch_is_unsanitizedcovers the branch mapping, including the deleted and shared-directory cases. I ran the new recipe by hand from afeature/xworktree next to another/yworktree, and it tailed only thefeature/xlog.The
skills/andplugins/copies of the page are regenerated bytest_docs_are_in_sync.