Commit Graph

4873 Commits

Author SHA1 Message Date
Maximilian Roos 0d3ce4b14c chore: use native Codex Cloud environment 2026-08-17 04:53:07 -07:00
dependabot[bot] 6ccdae8a53 chore: bump the patch group with 3 updates (#3830)
Bumps the patch group with 3 updates:
[tree-sitter-highlight](https://github.com/tree-sitter/tree-sitter),
[skim](https://github.com/skim-rs/skim) and
[jsonschema](https://github.com/Stranger6667/jsonschema).

Updates `tree-sitter-highlight` from 0.26.11 to 0.26.12
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/tree-sitter/tree-sitter/releases">tree-sitter-highlight's
releases</a>.</em></p>
<blockquote>
<h2>v0.26.12</h2>
<h2>What's Changed</h2>
<ul>
<li>fix(generate): fold case-insensitive patterns ourselves by <a
href="https://github.com/WillLillis"><code>@​WillLillis</code></a> in <a
href="https://redirect.github.com/tree-sitter/tree-sitter/pull/5761">tree-sitter/tree-sitter#5761</a></li>
<li>fix(query): keep the trailing anchor when a zero-matched quantifier
is anchored on both sides by <a
href="https://github.com/tree-sitter-ci-bot"><code>@​tree-sitter-ci-bot</code></a>[bot]
in <a
href="https://redirect.github.com/tree-sitter/tree-sitter/pull/5764">tree-sitter/tree-sitter#5764</a></li>
<li>fix(templates): add C source files to Python sdist by <a
href="https://github.com/WillLillis"><code>@​WillLillis</code></a> in <a
href="https://redirect.github.com/tree-sitter/tree-sitter/pull/5765">tree-sitter/tree-sitter#5765</a></li>
<li>fix(init): don't lowercase repository url by <a
href="https://github.com/tree-sitter-ci-bot"><code>@​tree-sitter-ci-bot</code></a>[bot]
in <a
href="https://redirect.github.com/tree-sitter/tree-sitter/pull/5768">tree-sitter/tree-sitter#5768</a></li>
<li>fix(highlight): use <code>std::sync::OnceCell</code> for various
loader fields by <a
href="https://github.com/WillLillis"><code>@​WillLillis</code></a> in <a
href="https://redirect.github.com/tree-sitter/tree-sitter/pull/5785">tree-sitter/tree-sitter#5785</a></li>
<li>fix(parser): restart recovery for invalid tokens in the error state
by <a
href="https://github.com/tree-sitter-ci-bot"><code>@​tree-sitter-ci-bot</code></a>[bot]
in <a
href="https://redirect.github.com/tree-sitter/tree-sitter/pull/5792">tree-sitter/tree-sitter#5792</a></li>
<li>Fix associativity resolution with mixed shift precedences by <a
href="https://github.com/tree-sitter-ci-bot"><code>@​tree-sitter-ci-bot</code></a>[bot]
in <a
href="https://redirect.github.com/tree-sitter/tree-sitter/pull/5796">tree-sitter/tree-sitter#5796</a></li>
<li>fix(parser): fix parsing bug repeat operator by <a
href="https://github.com/tree-sitter-ci-bot"><code>@​tree-sitter-ci-bot</code></a>[bot]
in <a
href="https://redirect.github.com/tree-sitter/tree-sitter/pull/5829">tree-sitter/tree-sitter#5829</a></li>
<li>release v0.26.12 by <a
href="https://github.com/clason"><code>@​clason</code></a> in <a
href="https://redirect.github.com/tree-sitter/tree-sitter/pull/5831">tree-sitter/tree-sitter#5831</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/tree-sitter/tree-sitter/compare/v0.26.11...v0.26.12">https://github.com/tree-sitter/tree-sitter/compare/v0.26.11...v0.26.12</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/tree-sitter/tree-sitter/commit/808e4b1fc06e269a107c4bd8bd936cc6fde18b00"><code>808e4b1</code></a>
release v0.26.12</li>
<li><a
href="https://github.com/tree-sitter/tree-sitter/commit/7566ffacb7a11e859cf1800f15209b021f53fb53"><code>7566ffa</code></a>
fix(lib): continue search for later named siblings in</li>
<li><a
href="https://github.com/tree-sitter/tree-sitter/commit/4b7e2c956c8653947385394b644771da2ff031b4"><code>4b7e2c9</code></a>
fix(generate): honor right associativity despite a lower-precedence
shift</li>
<li><a
href="https://github.com/tree-sitter/tree-sitter/commit/3ee7c639dee4a0fd02cd0fa0a0146d05d29063a6"><code>3ee7c63</code></a>
fix(parser): restart recovery for invalid tokens in the error state</li>
<li><a
href="https://github.com/tree-sitter/tree-sitter/commit/fd0ccd65a597ffdaf9cd4c199c47564ef4f5c709"><code>fd0ccd6</code></a>
fix(highlight): use <code>std::sync::OnceCell</code> for various loader
fields</li>
<li><a
href="https://github.com/tree-sitter/tree-sitter/commit/8df22e6f0e878d683605eb8200d2c2861920e7d8"><code>8df22e6</code></a>
fix(init): don't lowercase repository url</li>
<li><a
href="https://github.com/tree-sitter/tree-sitter/commit/c02f32485a76063b17a95c24b1c3758489d459d3"><code>c02f324</code></a>
fix(cli): <code>init --update</code> should not update
<code>setup.py</code> after replacing it (<a
href="https://redirect.github.com/tree-sitter/tree-sitter/issues/5">#5</a>...</li>
<li><a
href="https://github.com/tree-sitter/tree-sitter/commit/f9b9b390756c7ce9013542eb90e727f1785d6962"><code>f9b9b39</code></a>
fix(templates): replace deprecated method in Package.swift</li>
<li><a
href="https://github.com/tree-sitter/tree-sitter/commit/09384230b4d0d1deda7edf33d3c7e342cd874b0e"><code>0938423</code></a>
fix(templates): add C source files to Python sdist</li>
<li><a
href="https://github.com/tree-sitter/tree-sitter/commit/5ccdbb6b84b689350e47e1c53f5525307f5585d2"><code>5ccdbb6</code></a>
fix(query): transfer a leading boundary anchor across a zero-matched
quantifier</li>
<li>Additional commits viewable in <a
href="https://github.com/tree-sitter/tree-sitter/compare/v0.26.11...v0.26.12">compare
view</a></li>
</ul>
</details>
<br />

Updates `skim` from 5.6.1 to 5.6.4
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/skim-rs/skim/releases">skim's
releases</a>.</em></p>
<blockquote>
<h2>5.6.4 - 2026-08-10</h2>
<h2>Release Notes</h2>
<h3>🐛 Bug Fixes</h3>
<ul>
<li><em>(image)</em> Detect picker from tty to support protocol
detection with piped input</li>
<li>Platform-dependant timevals</li>
<li>Different suseconds and time per platform</li>
<li>Avoid truncation by casting up instead of down</li>
</ul>
<h2>Install skim 5.6.4</h2>
<h3>Install prebuilt binaries via shell script</h3>
<pre lang="sh"><code>curl --proto '=https' --tlsv1.2 -LsSf
https://github.com/skim-rs/skim/releases/download/v5.6.4/skim-installer.sh
| sh
</code></pre>
<h3>Install prebuilt binaries via powershell script</h3>
<pre lang="sh"><code>powershell -ExecutionPolicy Bypass -c &quot;irm
https://github.com/skim-rs/skim/releases/download/v5.6.4/skim-installer.ps1
| iex&quot;
</code></pre>
<h2>Download skim 5.6.4</h2>
<table>
<thead>
<tr>
<th>File</th>
<th>Platform</th>
<th>Checksum</th>
</tr>
</thead>
<tbody>
<tr>
<td><a
href="https://github.com/skim-rs/skim/releases/download/v5.6.4/skim-aarch64-apple-darwin.tar.xz">skim-aarch64-apple-darwin.tar.xz</a></td>
<td>Apple Silicon macOS</td>
<td><a
href="https://github.com/skim-rs/skim/releases/download/v5.6.4/skim-aarch64-apple-darwin.tar.xz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://github.com/skim-rs/skim/releases/download/v5.6.4/skim-x86_64-apple-darwin.tar.xz">skim-x86_64-apple-darwin.tar.xz</a></td>
<td>Intel macOS</td>
<td><a
href="https://github.com/skim-rs/skim/releases/download/v5.6.4/skim-x86_64-apple-darwin.tar.xz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://github.com/skim-rs/skim/releases/download/v5.6.4/skim-x86_64-pc-windows-msvc.zip">skim-x86_64-pc-windows-msvc.zip</a></td>
<td>x64 Windows</td>
<td><a
href="https://github.com/skim-rs/skim/releases/download/v5.6.4/skim-x86_64-pc-windows-msvc.zip.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://github.com/skim-rs/skim/releases/download/v5.6.4/skim-x86_64-pc-windows-msvc.msi">skim-x86_64-pc-windows-msvc.msi</a></td>
<td>x64 Windows</td>
<td><a
href="https://github.com/skim-rs/skim/releases/download/v5.6.4/skim-x86_64-pc-windows-msvc.msi.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://github.com/skim-rs/skim/releases/download/v5.6.4/skim-aarch64-unknown-linux-gnu.tar.xz">skim-aarch64-unknown-linux-gnu.tar.xz</a></td>
<td>ARM64 Linux</td>
<td><a
href="https://github.com/skim-rs/skim/releases/download/v5.6.4/skim-aarch64-unknown-linux-gnu.tar.xz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://github.com/skim-rs/skim/releases/download/v5.6.4/skim-x86_64-unknown-linux-gnu.tar.xz">skim-x86_64-unknown-linux-gnu.tar.xz</a></td>
<td>x64 Linux</td>
<td><a
href="https://github.com/skim-rs/skim/releases/download/v5.6.4/skim-x86_64-unknown-linux-gnu.tar.xz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://github.com/skim-rs/skim/releases/download/v5.6.4/skim-aarch64-unknown-linux-musl.tar.xz">skim-aarch64-unknown-linux-musl.tar.xz</a></td>
<td>ARM64 MUSL Linux</td>
<td><a
href="https://github.com/skim-rs/skim/releases/download/v5.6.4/skim-aarch64-unknown-linux-musl.tar.xz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://github.com/skim-rs/skim/releases/download/v5.6.4/skim-x86_64-unknown-linux-musl.tar.xz">skim-x86_64-unknown-linux-musl.tar.xz</a></td>
<td>x64 MUSL Linux</td>
<td><a
href="https://github.com/skim-rs/skim/releases/download/v5.6.4/skim-x86_64-unknown-linux-musl.tar.xz.sha256">checksum</a></td>
</tr>
</tbody>
</table>
<h2>5.6.3 - 2026-08-07</h2>
<h2>Release Notes</h2>
<h3>🐛 Bug Fixes</h3>
<ul>
<li>Advance the input cursor by byte length in insert_str (<a
href="https://redirect.github.com/skim-rs/skim/issues/1151">#1151</a>)</li>
</ul>
<h3>New Contributors</h3>
<ul>
<li><a href="https://github.com/vimsucks"><code>@​vimsucks</code></a>
made their first contribution in <a
href="https://redirect.github.com/skim-rs/skim/pull/1151">#1151</a></li>
</ul>
<h2>Install skim 5.6.3</h2>
<h3>Install prebuilt binaries via shell script</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/skim-rs/skim/blob/master/CHANGELOG.md">skim's
changelog</a>.</em></p>
<blockquote>
<h2>[5.6.4] - 2026-08-10</h2>
<h3>🐛 Bug Fixes</h3>
<ul>
<li><em>(image)</em> Detect picker from tty to support protocol
detection with piped input</li>
<li>Platform-dependant timevals</li>
<li>Different suseconds and time per platform</li>
<li>Avoid truncation by casting up instead of down</li>
</ul>
<h2>[5.6.3] - 2026-08-07</h2>
<h3>🐛 Bug Fixes</h3>
<ul>
<li>Advance the input cursor by byte length in insert_str (<a
href="https://redirect.github.com/skim-rs/skim/issues/1151">#1151</a>)</li>
</ul>
<h3>New Contributors</h3>
<ul>
<li><a href="https://github.com/vimsucks"><code>@​vimsucks</code></a>
made their first contribution in <a
href="https://redirect.github.com/skim-rs/skim/pull/1151">#1151</a></li>
</ul>
<h2>[5.6.2] - 2026-08-07</h2>
<h3>🐛 Bug Fixes</h3>
<ul>
<li>Assymetry in char_equal causing panic with some unicode
characters</li>
<li>Reorder batches in <code>--tac</code> mode (<a
href="https://redirect.github.com/skim-rs/skim/issues/1150">#1150</a>)</li>
<li>Match double-width roman characters (closes <a
href="https://redirect.github.com/skim-rs/skim/issues/1149">#1149</a>)</li>
</ul>
<h3>📚 Documentation</h3>
<ul>
<li>Preview command runs with sh/cmd, not SHELL</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/skim-rs/skim/commit/01be03f0d5821b48ae0311c41d02e9330a2571d8"><code>01be03f</code></a>
release: v5.6.4 (<a
href="https://redirect.github.com/skim-rs/skim/issues/1153">#1153</a>)</li>
<li><a
href="https://github.com/skim-rs/skim/commit/733aaeb14848969a0f55e06fac11a75d0c41b989"><code>733aaeb</code></a>
fix: avoid truncation by casting up instead of down</li>
<li><a
href="https://github.com/skim-rs/skim/commit/f6a302390c45875a01a7a51ead5286b8b9dafb5c"><code>f6a3023</code></a>
fix: different suseconds and time per platform</li>
<li><a
href="https://github.com/skim-rs/skim/commit/5ff1bda145b63b00509a2f75f9def6cb3982f04e"><code>5ff1bda</code></a>
fix: platform-dependant timevals</li>
<li><a
href="https://github.com/skim-rs/skim/commit/070d0849b253938ac0d7ff9394b2ec21bda9d884"><code>070d084</code></a>
fix(image): detect picker from tty to support protocol detection with
piped i...</li>
<li><a
href="https://github.com/skim-rs/skim/commit/5fc3b0c1a0cb1f42cfe9b9edab13b719d74e1bbd"><code>5fc3b0c</code></a>
release: v5.6.3 (<a
href="https://redirect.github.com/skim-rs/skim/issues/1152">#1152</a>)</li>
<li><a
href="https://github.com/skim-rs/skim/commit/a5bd0fefa6308739f36a635b0854cab22b3dfad7"><code>a5bd0fe</code></a>
fix: advance the input cursor by byte length in insert_str (<a
href="https://redirect.github.com/skim-rs/skim/issues/1151">#1151</a>)</li>
<li><a
href="https://github.com/skim-rs/skim/commit/f3c03000b35e1f7c20334ba92bfe54f2350d76bf"><code>f3c0300</code></a>
release: v5.6.2 (<a
href="https://redirect.github.com/skim-rs/skim/issues/1145">#1145</a>)</li>
<li><a
href="https://github.com/skim-rs/skim/commit/1dead2b3c670e0b8222a3a2ea624cacb6f10cec7"><code>1dead2b</code></a>
fix: match double-width roman characters (closes <a
href="https://redirect.github.com/skim-rs/skim/issues/1149">#1149</a>)</li>
<li><a
href="https://github.com/skim-rs/skim/commit/b17e93890aa0d393236b9158f6c197b0eb5dce11"><code>b17e938</code></a>
fix: reorder batches in <code>--tac</code> mode (<a
href="https://redirect.github.com/skim-rs/skim/issues/1150">#1150</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/skim-rs/skim/compare/v5.6.1...v5.6.4">compare
view</a></li>
</ul>
</details>
<br />

Updates `jsonschema` from 0.49.6 to 0.49.9
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/Stranger6667/jsonschema/releases">jsonschema's
releases</a>.</em></p>
<blockquote>
<h2>[Python] Release 0.49.9</h2>
<h3>Added</h3>
<ul>
<li>Canonicalization of a <code>oneOf</code> whose branches name object
targets a required constant tells apart, which degrades to a union.</li>
</ul>
<h3>Changed</h3>
<ul>
<li><code>CanonicalSchema.to_json_schema</code> on a definition emits
only the definitions that one names, not the whole document's.</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>Canonicalization running without end on a conjunction over unions;
past a ceiling on the meets it takes, the document stays unmodeled.</li>
</ul>
<h2>[Ruby] Release 0.49.9</h2>
<h3>Added</h3>
<ul>
<li>Canonicalization of a <code>oneOf</code> whose branches name object
targets a required constant tells apart, which degrades to a union.</li>
</ul>
<h3>Changed</h3>
<ul>
<li><code>CanonicalSchema#to_json_schema</code> on a definition emits
only the definitions that one names, not the whole document's.</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>Canonicalization running without end on a conjunction over unions;
past a ceiling on the meets it takes, the document stays unmodeled.</li>
</ul>
<h2>[Rust] Release 0.49.9</h2>
<h3>Added</h3>
<ul>
<li>Canonicalization of a <code>oneOf</code> whose branches name object
targets a required constant tells apart, which degrades to a union.</li>
</ul>
<h3>Changed</h3>
<ul>
<li><code>CanonicalSchema::to_json_schema</code> on a definition emits
only the definitions that one names, not the whole document's.</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>Canonicalization running without end on a conjunction over unions;
past a ceiling on the meets it takes, the document stays unmodeled.</li>
</ul>
<h2>[Python] Release 0.49.8</h2>
<h3>Performance</h3>
<ul>
<li>Up to 380x faster canonicalization of a <code>oneOf</code> whose
overlapping branches carry many properties, which no longer removes
shared regions the exactly-one spelling discards.</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>Draft detection treating the version-less
<code>http://json-schema.org/schema</code> meta-schema URI as a custom
dialect, where it names the current draft.</li>
<li>A <code>patternProperties</code> entry matching every key leaving
<code>additionalProperties: false</code> spelled as a key constraint,
where it forbids nothing.</li>
</ul>
<h2>[Ruby] Release 0.49.8</h2>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/Stranger6667/jsonschema/blob/master/CHANGELOG.md">jsonschema's
changelog</a>.</em></p>
<blockquote>
<h2>[0.49.9] - 2026-08-09</h2>
<h3>Added</h3>
<ul>
<li>Canonicalization of a <code>oneOf</code> whose branches name object
targets a required constant tells apart, which degrades to a union.</li>
</ul>
<h3>Changed</h3>
<ul>
<li><code>CanonicalSchema::to_json_schema</code> on a definition emits
only the definitions that one names, not the whole document's.</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>Canonicalization running without end on a conjunction over unions;
past a ceiling on the meets it takes, the document stays unmodeled.</li>
</ul>
<h2>[0.49.8] - 2026-08-08</h2>
<h3>Performance</h3>
<ul>
<li>Up to 380x faster canonicalization of a <code>oneOf</code> whose
overlapping branches carry many properties, which no longer removes
shared regions the exactly-one spelling discards.</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>Draft detection treating the version-less
<code>http://json-schema.org/schema</code> meta-schema URI as a custom
dialect, where it names the current draft.</li>
<li>A <code>patternProperties</code> entry matching every key leaving
<code>additionalProperties: false</code> spelled as a key constraint,
where it forbids nothing.</li>
<li><code>unevaluatedProperties</code>/<code>unevaluatedItems</code> set
to <code>true</code> forcing the whole document to stay unmodeled beside
an unresolved applicator, where the keyword rejects nothing regardless
of the evaluated set.</li>
<li><code>unevaluatedProperties</code>/<code>unevaluatedItems</code>
beside a <code>$ref</code> forcing the whole document to stay unmodeled,
where the reference's own evaluated properties or items are known once
it is resolved.</li>
</ul>
<h2>[0.49.7] - 2026-08-07</h2>
<h3>Added</h3>
<ul>
<li><code>CanonicalizeOptions::with_retriever</code> to fetch external
resources absent from the registry.</li>
<li><code>CanonicalizeOptions::with_base_uri</code> to resolve relative
references in the root schema.</li>
<li>Canonicalization of a <code>oneOf</code> whose branches name
disjoint targets, which degrades to a union.</li>
<li>Canonicalization of a vacuous <code>patternProperties</code> entry
beside schema-valued <code>additionalProperties</code>, where matching
keys escape its value constraint.</li>
<li>Canonicalization of a Draft 4 closed pattern map with a reference
nested under a property.</li>
<li>Canonicalization of Draft 4 closed pattern maps that meet through an
applicator.</li>
<li>The complement of a reference back to a target already being
negated, which stays symbolic instead of declining.</li>
<li><code>CanonicalSchema::definition</code> resolving <code>#</code> to
the document the handle was read against.</li>
</ul>
<h3>Performance</h3>
<ul>
<li>Up to 150x faster canonicalization of <code>not</code> over a union
of many branches.</li>
<li>2% faster emission of canonical schemas, which no longer formats
copied string values.</li>
<li>5% faster emission of canonical schemas, which no longer formats
object keys while copying them.</li>
<li>Up to 12% faster canonicalization of schemas that repeatedly
intersect the same branches.</li>
<li>13% faster canonicalization of a non-dynamic OpenAPI document, which
no longer scans every schema object for dynamic references.</li>
<li>Faster canonicalization of schemas with local <code>$defs</code>
references, which avoid decoding unescaped definition names.</li>
<li>23% faster canonicalization of an object whose keys a finite
property-name set spells.</li>
<li>3% faster intersection of schemas where one side constrains
nothing.</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/Stranger6667/jsonschema/commit/07484f9164fa555be12fe5f7d3a41c7e0c0ce754"><code>07484f9</code></a>
chore(ruby): Release 0.49.9</li>
<li><a
href="https://github.com/Stranger6667/jsonschema/commit/4aba49fb8b5115eca2b7a7b59e179cf7c743e6d6"><code>4aba49f</code></a>
chore(python): Release 0.49.9</li>
<li><a
href="https://github.com/Stranger6667/jsonschema/commit/4ad172231ae487511bfc4cf3199d7edab328923b"><code>4ad1722</code></a>
chore(rust): Release 0.49.9</li>
<li><a
href="https://github.com/Stranger6667/jsonschema/commit/0f114eed7329bbb19b4985a4cf4b450039872d60"><code>0f114ee</code></a>
fix: Canonicalization running without end on a conjunction over unions;
past ...</li>
<li><a
href="https://github.com/Stranger6667/jsonschema/commit/8d4d60caa027a346e1759bbd90c656ebded8db95"><code>8d4d60c</code></a>
chore: <code>CanonicalSchema::to_json_schema</code> on a definition
emits only the defin...</li>
<li><a
href="https://github.com/Stranger6667/jsonschema/commit/bed45535e954d60dbed2fd3628604eacc07ab2bf"><code>bed4553</code></a>
feat: Canonicalization of a <code>oneOf</code> whose branches name
object targets a requ...</li>
<li><a
href="https://github.com/Stranger6667/jsonschema/commit/0c310f43d71e1ebf6b698e641cf572ad3047cd8b"><code>0c310f4</code></a>
chore(ruby): Release 0.49.8</li>
<li><a
href="https://github.com/Stranger6667/jsonschema/commit/bd6836d60e96d6265288f5d067d2e16a0262fd41"><code>bd6836d</code></a>
chore(python): Release 0.49.8</li>
<li><a
href="https://github.com/Stranger6667/jsonschema/commit/71de15619cdaa1bee84b58ce89f2e29ebe0707a5"><code>71de156</code></a>
chore(rust): Release 0.49.8</li>
<li><a
href="https://github.com/Stranger6667/jsonschema/commit/a90819b37e4371ecf0b422e22318964a7454107d"><code>a90819b</code></a>
feat: Extend support for <code>unevaluated*</code> keywords in
canonicalization</li>
<li>Additional commits viewable in <a
href="https://github.com/Stranger6667/jsonschema/compare/ruby-v0.49.6...ruby-v0.49.9">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-17 02:12:44 -07:00
Worktrunk Bot aa4e527f35 refactor(git): drop Branch::push_remote, unused since the @{push} fix (#3833)
Nightly sweep finding. `Branch::push_remote()` has had no callers —
production or test — since
[#769](https://github.com/max-sixty/worktrunk/pull/769) (Jan 2026),
which replaced its only caller in the CI-status path with
`push_remote_url()`. This drops it, and fixes a stale doc comment that
named it.

## Why it went unnoticed

It's `pub` on a public lib type, so rustc's `dead_code` lint never fires
on it. `git log -S` puts the last caller's removal in `c4f1c0730`
(#769): `gh pr checkout` sets `branch.<name>.pushremote` to a URL rather
than a remote name, and `@{push}` — which is what `push_remote()`
resolves through — fails in that case. `push_remote_url()` uses
`%(push:remotename)` instead, which handles both.

So the method isn't merely unused; its docstring still advertises the
`@{push}` resolution chain that #769 established is wrong for the case
the codebase actually hits, which makes it read as a live alternative to
the function that superseded it.

## The stale comment

`setup_push_tracking` in `tests/integration_tests/default_branch.rs` was
documented as existing so `branch.push_remote()` and `github_push_url()`
work. The first is what this PR deletes; the second has never existed
anywhere in the tree (`grep` finds that comment as its only occurrence).
Its four call sites all call `push_remote_url()`, so the comment now
names that.

## Verification

No regression test accompanies this — there's no behavior to pin, since
the deleted method had no callers to change the behavior of. The proof
is negative and the compiler carries it: `cargo build --all-targets` and
`cargo clippy --all-targets -- -D warnings` both pass, which they could
not if any call site remained.

Also ran the suites covering the touched area: `cargo test --test
integration default_branch` (63 passed) and `cargo test --lib
git::repository` (178 passed).

<details><summary>Confirming there are no callers</summary>

Every mention of the bare identifier in the tree before this change:

```
src/git/repository/branch.rs:151:    pub fn push_remote(&self) -> Option<String> {          # the definition
src/git/repository/branch.rs:186:                let push_remote = self                    # local var in push_remote_url
src/git/repository/branch.rs:197:                if push_remote.contains("://") ...        # same local
src/git/repository/branch.rs:198:                    Some(push_remote)                     # same local
src/git/repository/branch.rs:200:                    self.repo.effective_remote_url(...)   # same local
tests/integration_tests/default_branch.rs:439:  /// ... `branch.push_remote()` ...            # the stale comment
```

The lines in `push_remote_url` are a local binding of the same name, not
calls. `switch.rs:808` writes the `branch.<name>.pushRemote` git-config
key and is unrelated.

</details>

---------

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
2026-08-17 01:48:38 -07:00
dependabot[bot] 93624131e3 chore: bump petname-macros from 3.1.0 to 3.2.0 (#3832)
Bumps [petname-macros](https://github.com/allenap/rust-petname) from
3.1.0 to 3.2.0.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/allenap/rust-petname/commit/a80733a5b317dcb0b929a7009490927a0fb190eb"><code>a80733a</code></a>
Remove the VS Code settings</li>
<li><a
href="https://github.com/allenap/rust-petname/commit/8cafcdbabfb0e8c014bd0dbf53272f709c2f4fc9"><code>8cafcdb</code></a>
Anchor the exclude patterns to the crate root</li>
<li><a
href="https://github.com/allenap/rust-petname/commit/eacea6de5794fdbc059838dd3b3734a1e49e600c"><code>eacea6d</code></a>
Bump version to 3.2.0</li>
<li><a
href="https://github.com/allenap/rust-petname/commit/fb7268407f4757580739daa88ae45fb277cb4dc0"><code>fb72684</code></a>
Trim the published crate, and check that it is complete</li>
<li><a
href="https://github.com/allenap/rust-petname/commit/1eaf3ab966d480932862e2627af330f46fe67861"><code>1eaf3ab</code></a>
Drop the upgrade notes for additive releases</li>
<li><a
href="https://github.com/allenap/rust-petname/commit/36c1fdb1c0850b40f5f1334b67699a914b63e70e"><code>36c1fdb</code></a>
Flag the Luxembourgish word lists as awaiting review</li>
<li><a
href="https://github.com/allenap/rust-petname/commit/6a21770d3df6c67cbcfc4e66abf1cfa16d02da6c"><code>6a21770</code></a>
Merge pull request <a
href="https://redirect.github.com/allenap/rust-petname/issues/139">#139</a>
from allenap/maintenance</li>
<li><a
href="https://github.com/allenap/rust-petname/commit/e8456aab57971a31de80656b8e073fcc1e60477e"><code>e8456aa</code></a>
Upgrade syn to 3</li>
<li><a
href="https://github.com/allenap/rust-petname/commit/40f166eeca33bfa3247443c688cb4537b6712ec2"><code>40f166e</code></a>
Record the commit message wrapping convention</li>
<li><a
href="https://github.com/allenap/rust-petname/commit/eae215bced99a41e90b05719b2cda108ed04d05d"><code>eae215b</code></a>
Merge pull request <a
href="https://redirect.github.com/allenap/rust-petname/issues/138">#138</a>
from allenap/maintenance</li>
<li>Additional commits viewable in <a
href="https://github.com/allenap/rust-petname/compare/v3.1.0...v3.2.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=petname-macros&package-manager=cargo&previous-version=3.1.0&new-version=3.2.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-17 01:48:36 -07:00
dependabot[bot] 4011ee4e9c chore: bump minijinja from 2.23.0 to 2.24.0 (#3831)
Bumps [minijinja](https://github.com/mitsuhiko/minijinja) from 2.23.0 to
2.24.0.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/mitsuhiko/minijinja/blob/main/CHANGELOG.md">minijinja's
changelog</a>.</em></p>
<blockquote>
<h2>2.24.0</h2>
<ul>
<li>Added the <code>wordwrap</code> filter to the Python bindings. <a
href="https://redirect.github.com/mitsuhiko/minijinja/issues/885">#885</a></li>
<li>Fixed conditional expressions in keyword argument values for Jinja2
compatibility in Rust and Go. <a
href="https://redirect.github.com/mitsuhiko/minijinja/issues/921">#921</a></li>
<li>Fixed <code>context!</code> sorting keys when the
<code>preserve_order</code> feature is enabled. <a
href="https://redirect.github.com/mitsuhiko/minijinja/issues/920">#920</a></li>
<li>Limited string repetition to 100 MB in Rust and Go to prevent
excessive memory allocations.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/mitsuhiko/minijinja/commit/0ca749f7ba507514fa6b052c74130ae6ae472e03"><code>0ca749f</code></a>
chore(release): 2.24.0</li>
<li><a
href="https://github.com/mitsuhiko/minijinja/commit/0f2989add87d1903247c3977a72ff1f00cf21e07"><code>0f2989a</code></a>
feat(python): expose wordwrap filter</li>
<li><a
href="https://github.com/mitsuhiko/minijinja/commit/3eac8fabe29fc2432622dd6e8b43c1e7adb0f44c"><code>3eac8fa</code></a>
docs(changelog): document context key ordering fix</li>
<li><a
href="https://github.com/mitsuhiko/minijinja/commit/c867352c5b370f6218e7b273c1f17479a6d5d670"><code>c867352</code></a>
fix(macros): preserve context key order</li>
<li><a
href="https://github.com/mitsuhiko/minijinja/commit/a9e1813d3c6489a63532a340b4bb3afdea0bf755"><code>a9e1813</code></a>
fix(parser): allow conditionals in keyword arguments</li>
<li><a
href="https://github.com/mitsuhiko/minijinja/commit/c6fa683e99e2f190b60a38060607f7b9900e9012"><code>c6fa683</code></a>
fix(value): limit repeated strings to 100 MB</li>
<li>See full diff in <a
href="https://github.com/mitsuhiko/minijinja/compare/minijinja-go/v2.23.0...minijinja-go/v2.24.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=minijinja&package-manager=cargo&previous-version=2.23.0&new-version=2.24.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-17 01:48:33 -07:00
dependabot[bot] 61d80b3ab8 chore: bump clechasseur/rs-cargo from 5.0.7 to 5.0.8 (#3829)
Bumps [clechasseur/rs-cargo](https://github.com/clechasseur/rs-cargo)
from 5.0.7 to 5.0.8.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/clechasseur/rs-cargo/releases">clechasseur/rs-cargo's
releases</a>.</em></p>
<blockquote>
<h2>v5.0.8</h2>
<p>New patch release with updated dependencies to fix some
vulnerabilities.</p>
<h2>What's Changed</h2>
<ul>
<li>chore(deps): bump undici from 6.27.0 to 6.28.0 in the npm_and_yarn
group across 1 directory by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/clechasseur/rs-cargo/pull/435">clechasseur/rs-cargo#435</a></li>
<li>chore(deps): bump the npm_and_yarn group across 1 directory with 1
update by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/clechasseur/rs-cargo/pull/436">clechasseur/rs-cargo#436</a></li>
<li>fix: <code>npm update</code> to get fixes, update
<code>@clechasseur/rs-actions-core</code> to 8.0.4, bump version to
5.0.8 by <a
href="https://github.com/clechasseur"><code>@​clechasseur</code></a> in
<a
href="https://redirect.github.com/clechasseur/rs-cargo/pull/437">clechasseur/rs-cargo#437</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/clechasseur/rs-cargo/compare/v5.0.7...v5.0.8">https://github.com/clechasseur/rs-cargo/compare/v5.0.7...v5.0.8</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/clechasseur/rs-cargo/commit/9a5c570d3347f8dee3916c8871f3ffaf38909956"><code>9a5c570</code></a>
fix: <code>npm update</code> to get fixes, update
<code>@clechasseur/rs-actions-core</code> to 8.0....</li>
<li><a
href="https://github.com/clechasseur/rs-cargo/commit/24c8a774d7e015322005aaca3336016bdc670085"><code>24c8a77</code></a>
chore(deps): bump the npm_and_yarn group across 1 directory with 1
update (<a
href="https://redirect.github.com/clechasseur/rs-cargo/issues/436">#436</a>)</li>
<li><a
href="https://github.com/clechasseur/rs-cargo/commit/b2652e1335c7ec927c51a006790e235ad741e1a7"><code>b2652e1</code></a>
chore(deps): bump undici in the npm_and_yarn group across 1 directory
(<a
href="https://redirect.github.com/clechasseur/rs-cargo/issues/435">#435</a>)</li>
<li><a
href="https://github.com/clechasseur/rs-cargo/commit/44bc6e9a0a8b85197cd377ad859fac1e3e9408bd"><code>44bc6e9</code></a>
chore(deps): update dependency rollup to ^4.62.4 (<a
href="https://redirect.github.com/clechasseur/rs-cargo/issues/432">#432</a>)</li>
<li><a
href="https://github.com/clechasseur/rs-cargo/commit/e914260698d7251b9589c737040ea88189e1d07e"><code>e914260</code></a>
chore(deps): update dependency oxlint to ^1.77.0 (<a
href="https://redirect.github.com/clechasseur/rs-cargo/issues/434">#434</a>)</li>
<li><a
href="https://github.com/clechasseur/rs-cargo/commit/952abcd19408ed276d2cb062ba1e680b5d564a1e"><code>952abcd</code></a>
chore(deps): update actions/checkout action to v7.0.1 (<a
href="https://redirect.github.com/clechasseur/rs-cargo/issues/431">#431</a>)</li>
<li><a
href="https://github.com/clechasseur/rs-cargo/commit/ebc623c7b9c4498bbb97ab84d0d7ef333f5645e0"><code>ebc623c</code></a>
chore(deps): update dependency ts-jest to ^29.4.12 (<a
href="https://redirect.github.com/clechasseur/rs-cargo/issues/428">#428</a>)</li>
<li><a
href="https://github.com/clechasseur/rs-cargo/commit/260bce3fe16b97604f986f900f052ddf2996f71c"><code>260bce3</code></a>
chore(deps): update dependency oxlint to ^1.75.0 (<a
href="https://redirect.github.com/clechasseur/rs-cargo/issues/430">#430</a>)</li>
<li>See full diff in <a
href="https://github.com/clechasseur/rs-cargo/compare/v5.0.7...v5.0.8">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=clechasseur/rs-cargo&package-manager=github_actions&previous-version=5.0.7&new-version=5.0.8)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-17 01:48:31 -07:00
dependabot[bot] 9c13f6e7d4 chore: bump taiki-e/install-action from 2.85.11 to 2.85.13 (#3828)
Bumps
[taiki-e/install-action](https://github.com/taiki-e/install-action) from
2.85.11 to 2.85.13.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/taiki-e/install-action/releases">taiki-e/install-action's
releases</a>.</em></p>
<blockquote>
<h2>2.85.13</h2>
<ul>
<li>
<p>Update <code>tombi@latest</code> to 1.3.3.</p>
</li>
<li>
<p>Update <code>mise@latest</code> to 2026.8.5.</p>
</li>
<li>
<p>Update <code>kingfisher@latest</code> to 1.113.0.</p>
</li>
<li>
<p>Update <code>cargo-shear@latest</code> to 1.13.4.</p>
</li>
<li>
<p>Update <code>bpf-linker@latest</code> to 0.11.0.</p>
</li>
</ul>
<h2>2.85.12</h2>
<ul>
<li>
<p>Update <code>zola@latest</code> to 0.23.3.</p>
</li>
<li>
<p>Update <code>wasm-tools@latest</code> to 1.256.0.</p>
</li>
<li>
<p>Update <code>tombi@latest</code> to 1.2.10.</p>
</li>
<li>
<p>Update <code>syft@latest</code> to 1.51.0.</p>
</li>
<li>
<p>Update <code>prek@latest</code> to 0.4.13.</p>
</li>
<li>
<p>Update <code>mise@latest</code> to 2026.8.4.</p>
</li>
<li>
<p>Update <code>editorconfig-checker@latest</code> to 3.11.1.</p>
</li>
<li>
<p>Update <code>cargo-tarpaulin@latest</code> to 0.37.1.</p>
</li>
<li>
<p>Update <code>cargo-rdme@latest</code> to 2.2.1.</p>
</li>
<li>
<p>Update <code>biome@latest</code> to 2.5.8.</p>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md">taiki-e/install-action's
changelog</a>.</em></p>
<blockquote>
<h2>[2.85.13] - 2026-08-13</h2>
<ul>
<li>
<p>Update <code>tombi@latest</code> to 1.3.3.</p>
</li>
<li>
<p>Update <code>mise@latest</code> to 2026.8.5.</p>
</li>
<li>
<p>Update <code>kingfisher@latest</code> to 1.113.0.</p>
</li>
<li>
<p>Update <code>cargo-shear@latest</code> to 1.13.4.</p>
</li>
<li>
<p>Update <code>bpf-linker@latest</code> to 0.11.0.</p>
</li>
</ul>
<h2>[2.85.12] - 2026-08-12</h2>
<ul>
<li>
<p>Update <code>zola@latest</code> to 0.23.3.</p>
</li>
<li>
<p>Update <code>wasm-tools@latest</code> to 1.256.0.</p>
</li>
<li>
<p>Update <code>tombi@latest</code> to 1.2.10.</p>
</li>
<li>
<p>Update <code>syft@latest</code> to 1.51.0.</p>
</li>
<li>
<p>Update <code>prek@latest</code> to 0.4.13.</p>
</li>
<li>
<p>Update <code>mise@latest</code> to 2026.8.4.</p>
</li>
<li>
<p>Update <code>editorconfig-checker@latest</code> to 3.11.1.</p>
</li>
<li>
<p>Update <code>cargo-tarpaulin@latest</code> to 0.37.1.</p>
</li>
<li>
<p>Update <code>cargo-rdme@latest</code> to 2.2.1.</p>
</li>
<li>
<p>Update <code>biome@latest</code> to 2.5.8.</p>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/taiki-e/install-action/commit/82cd3e7658a6f96c86c0234aeeda1748937cb0a1"><code>82cd3e7</code></a>
Release 2.85.13</li>
<li><a
href="https://github.com/taiki-e/install-action/commit/4dd6c67d0ecd1fab76c6cecc5931e1239cc16add"><code>4dd6c67</code></a>
Update <code>tombi@latest</code> to 1.3.3</li>
<li><a
href="https://github.com/taiki-e/install-action/commit/91cb1bb28383045bb69f87d336ede6db3c61927b"><code>91cb1bb</code></a>
Update <code>mise@latest</code> to 2026.8.5</li>
<li><a
href="https://github.com/taiki-e/install-action/commit/83d968e89df664219ce13abb14808207a131cc64"><code>83d968e</code></a>
Update <code>kingfisher@latest</code> to 1.113.0</li>
<li><a
href="https://github.com/taiki-e/install-action/commit/f7ab7f5d0a3e5a8120f10f39cfc442b2f40642b0"><code>f7ab7f5</code></a>
Update cargo-xwin manifest</li>
<li><a
href="https://github.com/taiki-e/install-action/commit/3faddd9e3002e0d2922192f5808a78c4118eb58c"><code>3faddd9</code></a>
Update <code>cargo-shear@latest</code> to 1.13.4</li>
<li><a
href="https://github.com/taiki-e/install-action/commit/b4cb4b238691473c8bf1425b4d38120d5058dfc2"><code>b4cb4b2</code></a>
Update <code>bpf-linker@latest</code> to 0.11.0</li>
<li><a
href="https://github.com/taiki-e/install-action/commit/b20dedce73af6905cdc30d6611090c9b67557c8d"><code>b20dedc</code></a>
Release 2.85.12</li>
<li><a
href="https://github.com/taiki-e/install-action/commit/952d13c8bb10d7e37f96fa2c8131338550a62663"><code>952d13c</code></a>
ci: Skip cargo-rdme on x86_64 macOS</li>
<li><a
href="https://github.com/taiki-e/install-action/commit/c8724e7258d0b8f8188a94aec0c00ae9da81edd7"><code>c8724e7</code></a>
Update <code>zola@latest</code> to 0.23.3</li>
<li>Additional commits viewable in <a
href="https://github.com/taiki-e/install-action/compare/v2.85.11...v2.85.13">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=taiki-e/install-action&package-manager=github_actions&previous-version=2.85.11&new-version=2.85.13)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-17 01:48:28 -07:00
Worktrunk Bot 1b278042de chore(ci): weekly renovation 2026-08-16 (#3826)
## Summary

Weekly CI renovation check found the following updates:

- `worktrunk`: 0.72.0 → 0.74.0 (MSRV 1.96, compatible with our 1.96.0) —
`ci.yaml` ×2, `nightly.yaml`
- `nushell`: 0.114.1 → 0.115.0 — `nightly.yaml`, `benchmarks.yaml`,
`coverage.yaml`, `actions/test-setup`, and
`scripts/codex-cloud/Taskfile.yaml`
- `pre-commit`: 4.6.1 → 4.6.2 — `scripts/codex-cloud/Taskfile.yaml`
- `PowerShell`: 7.6.4 → 7.6.5 — `scripts/codex-cloud/Taskfile.yaml` and
the root `Taskfile.yaml`'s `setup-web` task

The Codex Cloud archive checksums were recomputed from the new upstream
tarballs, and the resulting `Taskfile.yaml` digest (`f14dbc89…`) is
copied into both README launcher commands.

The `setup-web` PowerShell pin came in as a follow-up commit: the
initial sweep only grepped `.rs`/`.md`/`.toml` for stale versions, so
the root `Taskfile.yaml`'s `PWSH_VERSION="7.6.4"` was missed. Nothing
tests the two PowerShell pins against each other, so that one drifts
silently — worth a note for future renovation runs. The
`powershell_7.6.5-1.deb_amd64.deb` asset the `setup-web` branch
downloads is present in the v7.6.5 release.

## Already up to date

- Rust stable is 1.97.1, so MSRV and toolchain stay at 1.96 (latest
stable − 1) — `Cargo.toml`, `tests/helpers/wt-perf/Cargo.toml`,
`rust-toolchain.toml` need no change, and `flake.lock` is untouched.
- `cargo-insta` 1.48.0, `cargo-nextest` 0.9.143, `cargo-llvm-cov` 0.8.7,
`cargo-msrv` 0.19.3, `cargo-affected` 0.4.0, `cargo-udeps` 0.1.61,
`lychee` 0.24.2
- Task 3.52.0 (mise, Codex Cloud)
- Runner images: ubuntu-24.04, macos-15, windows-2022

## Held back: zola 0.22.1 → 0.23.3

Not bumped. Zola 0.23.0 shipped [Tera2 +
refactoring](https://github.com/getzola/zola/pull/3105), which is a
templating-engine swap rather than a routine release. Building `docs/`
with the 0.23.3 binary fails at the first line of `templates/base.html`:

```
ERROR error: Unknown tag
 --> base.html:1:4
  |
1 | {% import "macros.html" as macros %}
  |    ^^^^^^
```

`templates/base.html` and `templates/macros.html` are the two files that
use the `import`/`macro` pair, so the migration looks small, but it is
template work with its own review rather than a pin bump — kept out of
this PR so the rest can land. Raised separately.

<details><summary>Verification</summary>

- Every version above was read from the upstream source of truth:
`crates.io` for the cargo tools, `nushell/nushell` and
`PowerShell/PowerShell` releases, PyPI for pre-commit, and
`static.rust-lang.org/dist/channel-rust-stable.toml` for Rust stable
(1.97.1).
- Checksums were computed from the downloaded archives and the extracted
binaries were run (`nu --version` → `0.115.0`); the archive layouts
(`nu-<ver>-x86_64-unknown-linux-gnu/nu`, top-level `pwsh`) are
unchanged, so the `install_binary` paths still resolve.
- All six edited YAML files parse.
- The nushell bump was exercised against the shell-integration suite:
`cargo test --features shell-integration-tests --test integration --
nushell` with 0.115.0 on `PATH`. 13 of 14 pass;
`test_nushell_install_target_is_a_vendor_autoload_dir` fails — but it
fails identically on the currently-pinned 0.114.1, and passes on *both*
versions when run alone. It is a pre-existing shared-state race in the
sandbox, not a regression from this bump: the test asserts against the
real user `$nu.vendor-autoload-dirs` entry rather than one under its
temp `HOME` (nu resolves the home dir from the passwd database, so the
test's `HOME` override does not move it), and a sibling uninstall test
in the same filter removes `wt.nu` from that shared directory. Noted
rather than fixed here — it is unrelated to the pins.
- The zola failure above was reproduced with the official 0.23.3
`x86_64-unknown-linux-gnu` release binary against this repo's `docs/`.

</details>

---------

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
2026-08-17 01:48:26 -07:00
Worktrunk Bot 350ad764ef skills(running-tend): fix the statusline cache-check recipe for Linux (#3825)
The `running-tend` skill's weekly statusline cache-check recipe can't
run as written: its `sed -i ''` is BSD syntax, and the weekly job runs
on `ubuntu-24.04`. GNU sed reads the `''` as the script and the
substitution expression as a filename, so the step exits 2 and the input
file keeps its `REPLACE_WITH_CWD` placeholder — a session that pushed
past the error would then profile a statusline render against a
nonexistent working directory. Building the JSON with `jq -n --arg`
drops the placeholder dance entirely and can't be corrupted by a path
containing a quote.

The same block also invoked `wt list statusline --claude-code`, which
has been deprecated in favour of `--format=claude-code` since #2436 and
warns on every use.

<details><summary>Verification</summary>

GNU sed 4.9 on this runner, reproducing the failure:

```
$ sed -i '' "s|REPLACE_WITH_CWD|/home/x|" sedtest.txt
sed: can't read s|REPLACE_WITH_CWD|/home/x|: No such file or directory
exit=2
```

The corrected recipe, run end to end against a debug build of this
checkout:

```
$ jq -n --arg cwd "$PWD" '{hook_event_name:"Status", workspace:{current_dir:$cwd}, model:{display_name:"Opus"}, context_window:{used_percentage:42.0}}' > /tmp/statusline-input.json
$ wt -vv list statusline --format=claude-code < /tmp/statusline-input.json > /dev/null
exit=0
$ wt config state logs profile --format=json | jq .cache
{"same_context_duplicates": [...], "same_context_extra_calls": 2, ...}
```

The field names match what `StatuslineInput` reads —
`/workspace/current_dir`, `/model/display_name`,
`/context_window/used_percentage` in `src/commands/statusline.rs`.

Deprecation warning on the old flag:

```
$ wt list statusline --claude-code < /tmp/statusline-input.json
▲ --claude-code is deprecated; use --format=claude-code instead
```

No regression test: the change is to a skill markdown file, which has no
test harness in this repo. The recipe itself was run end to end instead,
as above.

</details>

Found by the nightly survey (`.claude/skills/` is not on the rotation,
but the statusline recipe surfaced while checking `wt list statusline`
doc accuracy in `skills/worktrunk/reference/claude-code.md`).

---------

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
2026-08-17 01:48:23 -07:00
Worktrunk Bot 1e2e05cfb4 chore: update tend workflows (0.1.17 → 0.1.18) (#3824)
Automated nightly regeneration of tend's workflow files, picking up tend
0.1.18.

**tend version:** 0.1.17 → 0.1.18

Notable changes:

- `tend-mention`: both halves of the 👀 reaction now live in the `handle`
job. Previously `verify` added the eyes and `handle` removed them, so a
burst of mentions on one thread could cancel a queued `handle` — which
allocates no runner and runs no steps, `always()` included — leaving the
reaction stranded (max-sixty/tend#990).
- `tend-review` / `tend-triage`: the eyes-removal lookup now paginates
(`--paginate`, `per_page=100`). A thread with more than 30 reactions
could push the bot's own eyes off the first page, so the removal
silently found nothing (max-sixty/tend#990).
- `tend check`: the secret audit now reports only org secrets this repo
can actually read, instead of every org secret (max-sixty/tend#994).
- `running-in-ci` skill: notes that fork PR reviews reach no successor
session, and scopes PR-description claims to the merge base
(max-sixty/tend#985, max-sixty/tend#992).

Full comparison:
https://github.com/max-sixty/tend/compare/0.1.17...0.1.18

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
2026-08-17 01:48:21 -07:00
Maximilian Roos 18b408ce98 Add shared Codex Cloud environment setup (#3810)
## Summary

- add a repository-owned Codex Cloud Taskfile, exposed through root
setup and maintenance tasks
- share setup and maintenance preparation in one task instead of two
scripts
- document concise, checksum-gated environment commands
- preserve the proven UID 1000, `tini`, pinned-tool, and retry behavior
- keep tool pins, archive checksums, Task version, and launcher digests
synchronized by test and maintenance guidance

## Why

Worktrunk's full suite needs dependencies and process/permission
semantics beyond the stock universal image. The working configuration
previously lived only in one saved environment, where other contributors
could neither review nor reuse it.

The dedicated Taskfile sits beside the project Taskfile without making
unrelated task edits invalidate the Cloud environment hash. Root wrapper
tasks launch it as a new Task process so its repository-relative paths
retain their own Taskfile context.

## Security

Codex checks out the task branch before setup or maintenance. Each saved
launcher verifies the dedicated Taskfile's fixed SHA-256 digest before
executing it as root. `MISE_NO_CONFIG=1` also prevents branch-controlled
mise configuration from running before the verified Taskfile. Approved
changes require updating the digest in environment settings, which
invalidates the cache.

Repository-sensitive Rustup, pre-commit, and Cargo work runs as the
image's UID 1000 `ubuntu` user; root is limited to the verified system
and ownership preparation.

## Validation

- root and direct Taskfile discovery expose `setup-codex` and
`maintain-codex`
- YAML parsing and extracted Bash syntax pass
- warning-level ShellCheck passes
- applicable pre-commit hooks pass
- positive and negative checksum checks pass
- the launcher-sync integration test passes
- independent adversarial, abstraction-level, and current-head code
reviews are clean
- exact Taskfile validation passed in Codex Cloud task
`task_e_6a7e53a87e908325bf50ea3413ed521c`
  - setup and maintenance launchers passed
  - root and direct Task discovery passed
  - Cargo identity probe returned UID 1000
  - `cargo run -- hook pre-merge --yes` exited 0
  - 4,601/4,601 tests passed; all gate components passed
- HEAD remained `288953dcb18466f64b8e5355192ae297f4862240` and the
checkout remained clean
- final-head Cloud task `task_e_6a8089197ecc8325afd723d41beb5c50`
reached `READY` with no diff after about 38 minutes on
`dab4a5dcd5f343c3e5ea0a1183e9fcc5d8271a08`; its transcript was
unavailable, so no finer-grained result is claimed
- all 18 applicable final-head checks pass on Linux, macOS, and Windows,
including coverage, `codecov/patch`, and current-head tend review

> _This was written by Codex on behalf of @max-sixty_
2026-08-15 09:22:15 -07:00
Worktrunk Bot 246c6bd919 fix(list): keep [list] columns out of the --format json plan (#3812)
Closes the `[list] columns` half of #3787, per the call in [this
comment](https://github.com/max-sixty/worktrunk/issues/3787#issuecomment-5273942067):
JSON always emits the same shape, and `list.columns` only affects the
actual columns.

Before, `--format json` planned `all_columns` (source `Default`)
*unioned* with the selection's forced-on columns, so the selection
reached JSON in one direction only — it couldn't narrow the emitted
fields, but a listed `ci` did force the forge fetch on without `--full`.
That made a presentation setting decide whether a machine-readable call
talks to GitHub, which is the thing the Neovim plugin in #3787 had to
pin `--config-set 'list.columns=[…]'` against. Now the JSON branch plans
`all_columns` alone; `--full` is the only switch for the gated data, and
it's the one a caller controls.

The table and the `wt switch` picker are untouched — a listed `ci` still
renders the CI column without `--full`, and the picker still unions the
selection in so its table matches `wt list`'s.

Only `ci` and `summary` are affected: every other column is ungated, so
`full_plan()` already covered them, and custom columns require no
background task.

**For the release note — this changes schema 1 too.** A caller with
`[list] columns = […, "ci"]` and no `--full` used to get the `ci` object
in schema-1 JSON and now won't; schema 1 has no `collected` envelope to
say why. The schema-1 `ci` row already documented `` `--full` only ``,
so the docs get *more* accurate, but the observable output changes for
anyone who was relying on the forcing path. Schema 2 reports the same
narrowing through `collected.ci`.

Docs updated in `after_long_help` (the `[list] columns` section plus the
schema-2 `pr`, `summary`, and `checks` rows — `summary` now names
`--full` alongside `[list] summary = true`, and `checks` names the
`--full` gate it shares with `pr`), with the generated mirrors,
`dev/config.example.toml`, and the `--help` snapshots regenerated. The
`CLAUDE.md` network inventory and the `collect` planning comment now
record the exemption too.

<details><summary>Test</summary>

`test_list_json_columns_selection_does_not_force_ci` in
`tests/integration_tests/list_config.rs` asserts schema 2's
`collected.ci` across three configs: unset (false), `columns =
["branch", "ci"]` without `--full` (false — the regression this fixes),
and the same with `--full` (true). `collected` records what the plan
requested rather than what a fetch returned, so the test needs no forge
and no `gh` on PATH. It sits next to
`test_list_json_ignores_columns_selection`, which owns the narrowing
direction, and `test_list_config_listed_column_overrides_full_gate`,
which owns the table's forcing behaviour and still passes unchanged.

Ran locally: full `cargo test --test integration` and `cargo test --lib
--bins`, plus `cargo clippy --all-targets` and `cargo fmt --check`. One
unrelated failure,
`test_copy_ignored_preserves_file_executable_permissions`, is a umask
artifact of this sandbox (expects `0644`, the runner's `umask 002`
produces `0664`); it touches no code in this diff.

The docs-row follow-up in df5c238 re-ran `cargo test --test integration
-- test_help test_docs_are_in_sync` (48 passed) and `cargo fmt --check`.

</details>

---------

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
2026-08-15 08:00:04 -07:00
Worktrunk Bot 06a0315efb chore(clawpatch): file the FailureStrategy invariant against the code that enforces it (#3823)
Nightly survey finding. The signal-handling clawpatch spec lists
`src/commands/command_approval.rs` as a context file with the reason
"FailureStrategy::Warn must not swallow signal-derived errors" — but
that file contains no reference to `FailureStrategy`, signals, or
interrupts. `grep -n "signal\|Interrupt\|FailureStrategy"
src/commands/command_approval.rs` returns nothing, and `git log -S
FailureStrategy -- src/commands/command_approval.rs` is empty, so the
entry has been wrong since the spec was added in #2859 rather than
having drifted.

`FailureStrategy` is declared and consumed entirely in
`src/commands/command_executor.rs`, which the spec already lists as an
owned file. So this drops the misfiled context entry and folds its claim
— the one detail the owned-file reason didn't carry, that the interrupt
check runs *before* any `FailureStrategy` branch — into that entry,
matching how CLAUDE.md → Signal Handling states it. Nothing is lost from
the spec; it just points at the file that implements the invariant.

I checked the rest of the specs the same way: every `path` in
`entrypoints` / `ownedFiles` / `contextFiles` / `tests` across all 14
`.config/clawpatch/features/*.json` resolves, every
`entrypoints[].symbol` appears in its named file, and this was the only
reason string naming a symbol its file doesn't contain.

No regression test — the change is metadata prose in a file no test
reads, and the mismatch isn't expressible as an assertion without
inventing a schema check for `reason` strings, which would be a larger
and more speculative change than the fix.

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
2026-08-15 07:55:44 -07:00
Worktrunk Bot e3b3482fd0 chore: update tend workflows (0.1.15 → 0.1.17) (#3822)
Automated nightly regeneration of tend's workflow files.

**tend version:** 0.1.15 → 0.1.17

**Notable changes**

- 👀 reactions now mark a session in flight: the bot reacts when an issue
or PR is opened, and the reaction comes off when the session ends
(max-sixty/tend#974, max-sixty/tend#979).
- Mention gating was reworked — coarser pre-check gates, tested poll
scripts, and an anchor-based run window replace the hand-rolled matching
(max-sixty/tend#965, max-sixty/tend#971).
- Self-initiated fixes are now gated on cost as well as evidence, so the
bot doesn't open a PR whose value doesn't justify the session
(max-sixty/tend#960).
- `tend check` reads the bot's own bypass verdict on repos where the
actor list is withheld, instead of reporting a false FAIL
(max-sixty/tend#976).
- Weekly no longer trusts a re-anchored approval on a rebased dependency
PR (max-sixty/tend#890), and review-reviewers is paused as a scheduled
sweep, kept as a manual spot-check (max-sixty/tend#966).

Compare: https://github.com/max-sixty/tend/compare/0.1.15...0.1.17

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
2026-08-15 07:55:41 -07:00
Maximilian Roos 2d4b6d8ac1 docs(shell): state that install owns the wrapper path it writes (#3821)
`wt config shell install` replaces an existing `functions/{cmd}.fish`,
`completions/{cmd}.fish`, or `vendor/autoload/{cmd}.nu` whole. That is
the design — the path is named after the command, so it names the file
worktrunk owns — but neither the write site nor the FAQ's file inventory
said so, which leaves the overwrite reading as a missing ownership
check.

`is_worktrunk_managed_content` already carries the rule, for the
uninstall side that has to recognize a file without knowing its name. So
`configure_wrapper_file` states it in a clause and points there rather
than keeping a second copy. The FAQ sentence adds the contrast: rc files
hold the rest of a shell's setup, so install only appends to those.

No behavior change, and no ownership check added.

> _This was written by Claude Code on behalf of max-sixty_

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 17:15:46 -07:00
Maximilian Roos 1e0ca1ce66 Release v0.74.0 (#3820)
Cuts 0.74.0. Minor bump: `cargo semver-checks` reports two breaking
library changes from #3808 (`GitError::WorktreePathNotOurs` gained a
field, `WorkingTree::ensure_belongs_to_repo` was renamed), and patch is
disallowed pre-1.0 with semver breakage.

Alongside the release, one fix the release's data-loss review turned up.

## The fix

`wt step promote` stages a worktree's gitignored files through
`<git-common-dir>/wt/staging/promote` and moves them back after the
branch exchange. When a worktree and the git dir sit on different
filesystems, `fs::rename` fails with EXDEV and `copy_and_remove` copies
then unconditionally deletes the source.

#3744 made the copy tolerate a source that vanishes mid-walk — correct
for `wt step copy-ignored`, which never deletes a source, and wrong
here: an incomplete copy reported `Ok` immediately before the delete, so
a gitignored file a concurrent build removed and rewrote was destroyed
rather than moved. Before #3744 the copy errored and the source
survived, so this was a regression introduced in this release window and
caught before it shipped.

`copy_dir_recursive` now returns how many of the entries the walk
collected to copy were not copied. `copy_and_remove` refuses on a
non-zero count and leaves the source in place; `copy-ignored` names the
count and drops it, with `#[must_use]` so a future third caller decides
rather than inheriting the old bug.

A non-regular file is dropped at classification rather than counted, per
@worktrunk-bot's review: a socket carries no content the destination can
be short of, and refusing over one lands worst where it is least
recoverable — `distribute_staged` runs *after* `exchange_branches`, so a
socket that reached staging by same-filesystem rename would kill the
promote with the branches already swapped and the staged files behind a
`check_leftover_staging` refusal whose remedy deletes them. Both
directions are pinned by tests.

## Release gates

- Local `wt hook pre-merge --yes`: 4649 tests, lints, doctests, rustdoc
under `-Dwarnings`.
- `nightly.yaml` green twice on this branch (full 3-OS matrix,
feature-powerset, release-target, nix-flake, minimal-versions), and
green on the cut-from tip 92dfb686b.
- Data-loss surface review over `v0.73.0..HEAD` with four independent
finders (behavioral, blast-radius, shipped-automation, keyword).
Thirteen candidates: one real, fixed here; twelve adjudicated acceptable
and signed off.
- Every changelog entry verified against its diff, with attributions and
links checked.

## Known-red check

`codecov/patch` fails on the three `skipped += 1;` counters in
`src/copy.rs`. Each sits inside a pre-existing `ErrorKind::NotFound` arm
that was already uncovered at the base commit — the concurrent-rewrite
races (`read_dir` vanished, `entry.file_type()` vanished,
`set_permissions` vanished), none with a deterministic trigger.
`codecov/project` passes. Merging over it is approved.

> _This was written by Claude Code on behalf of max-sixty_

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
v0.74.0
2026-08-14 09:44:27 -07:00
Maximilian Roos 92dfb686bb feat(approvals): let wt config approvals add --yes record approvals without a TTY (#3819)
`wt config approvals add` refused every non-interactive run — even with
`--yes`, whose hint then suggested the flag already passed — so there
was no way to pre-approve a project's commands unattended. An
orchestrator (tend's Codex Cloud container was the motivating case) had
to hand-write `approvals.toml` from `wt config approvals list
--format=json` output, a third-party reimplementation of `add` that
breaks whenever the schema changes. The `wt config approvals` docs
already promised "`--yes` to bypass prompts in CI" and described `stale`
entries as "what `--yes` would silently re-approve"; behavior now
matches them.

The two `--yes` meanings stay distinct: on a command that runs project
commands it grants consent for that run alone and records nothing
(unchanged), while on `add` — whose product is the record — it lists
what it trusts and writes it. `add` no longer routes through
`approve_command_batch` (the execution gate) for this: it prompts or
announces, then saves itself, which also makes a failed `approvals.toml`
write fail the command instead of warning behind a `✓ saved` line and
exit 0 — an orchestrator reading only the exit code would otherwise walk
into the prompt it just paid to avoid.

The non-interactive hint's pre-approval suggestion now carries `--yes`
(`run wt config approvals add --yes`), since a hint reached in CI must
name a command that runs there. Per the existing `list --format=json`
docs, `add --yes` re-approves templates edited since an earlier approval
without comment; the `add` help now says so and points at the `stale`
field for reading them first, and the worktrunk skill's escalation rule
tells agents not to reach for it on a user's behalf.

> _This was written by Claude Code on behalf of max-sixty_
2026-08-14 02:39:29 -07:00
Caleb Cox aa9d8c43df feat: add remote_repo variable (#3745)
Add a `remote_repo` variable that returns the repo name from the remote
URL. Unlike `repo`, it stays consistent even if the clone was renamed.

Feel free to reject, or suggest other names for the variable. But this
change would improve my workflow. I hope you don't mind my submitting a
PR before opening an issue. Thanks for an amazing developer tool!

AI Disclosure 🤖: I used Claude Code to generate the changes, but
reviewed every line and made adjustments.

---------

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
2026-08-14 00:48:54 -07:00
Anders 77feeeaef0 fix(copy): skip source files that vanish mid-copy instead of aborting (#3744)
Fixes #3743.

## Problem

`copy_leaf` propagates `NotFound` from
`symlink_metadata`/`read_link`/`reflink_or_copy` as a hard error.
`copy_dir_recursive` collects leaves in a walk phase, then copies them
in a parallel `try_for_each` — so a single source file that disappears
between those two phases (e.g. a concurrent build rewriting `target/`)
aborts the *entire* batch, even though most other files copy
successfully.

## Fix

Treat `NotFound` on the source the same way the existing `AlreadyExists`
case (destination) is already handled: skip the leaf and return
`Ok(None)` instead of erroring.

## Testing

- Added `test_copy_leaf_skips_vanished_source` (unit test, deterministic
— no race needed).
- `cargo test --lib copy::` — 3 passed.
- `cargo test step_copy_ignored` (full integration suite) — 56 passed.
- `cargo clippy --lib -- -D warnings` — clean.

---------

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
2026-08-14 00:27:11 -07:00
Worktrunk Bot 96c6c846f7 fix(shell): register completions under the --cmd name, not clap's (#3817)
## Problem

`wt config shell init <shell> --cmd <name>` renames the shell wrapper
and its lazy completion loader, but the registration that loader evals
comes from clap, which derives every identifier in it from its own
compile-time `Command` name (`wt`) — not from `argv[0]` and not from
`--cmd`. The two halves never agreed:

```console
$ wt config shell init zsh --cmd wot | grep _clap
        if ! (( $+functions[_clap_dynamic_completer_wot] )); then
        _clap_dynamic_completer_wot "$@"

$ COMPLETE=zsh wt | grep -oE '_clap_dynamic_completer_[a-z_]*' | sort -u
_clap_dynamic_completer_wt
```

Nothing completed, and because the guard never became true the
completion script was regenerated and re-evaluated on *every* TAB. Same
shape in bash (`_clap_complete_*`); PowerShell emitted
`Register-ArgumentCompleter -Native -CommandName wt`, so the `--cmd`
name was never registered at all. The documented `--cmd=git-wt` case
(the Windows Terminal conflict) was broken too — including for a binary
genuinely installed under that name, since clap's name comes from the
declaration rather than `argv[0]`.

There is a second, sharper edge: zsh's registration ends with `compdef
<completer> <cmd>`, so the first TAB on `wot` also bound worktrunk's
completer to plain `wt` — handing completions to the *other* `wt` that
`--cmd` exists to step around.

fish and nushell were unaffected. Both register a completer that shells
out to the binary rather than depending on a clap-emitted identifier, so
the reporter's "unverified" row for fish is a pass.

## Solution

The bash, zsh, and PowerShell loaders now pass the name they bind in
`WORKTRUNK_COMPLETE_NAME`, and `registration_name()` in
`src/completion.rs` emits the registration under that name (validated
through the same `validate_shell_command_name` guard `--cmd` uses, since
the value lands verbatim in generated shell code). The fallback is
`binary_name()`, which covers a binary installed as `git-wt` and invoked
directly. The templates apply clap's own `-` → `_` escaping to the
function they call, so `--cmd git-wt` guards on `_clap_complete_git_wt`
rather than the invalid `_clap_complete_git-wt`.

That fixes all four shells and the stray `compdef` in one place, rather
than pinning the templates to clap's internal naming:

```console
$ WORKTRUNK_COMPLETE_NAME=wot COMPLETE=zsh wt | grep -oE '_clap_dynamic_completer_[a-z_]*|compdef .*' | sort -u
_clap_dynamic_completer_wot
compdef _clap_dynamic_completer_wot wot
```

## Testing

Two reproduction tests in `tests/integration_tests/completion.rs`, both
failing before the change:

- `test_init_custom_cmd_defines_clap_completer_in_bash` drives the whole
chain through a real bash — generate the init script with `--cmd`, call
the loader it defines, then assert clap's completer function exists
afterwards. Printed `MISSING` before, `DEFINED` after. Cases for `wot`
and `git-wt`.
- `test_completion_registration_uses_shell_integration_cmd_name` covers
zsh and PowerShell, which CI can't drive: the identifier the init script
references must be the one the registration defines, and the `compdef` /
`-CommandName` target must be the `--cmd` name.

`cargo test --lib --bins` and `cargo test --test integration` are
otherwise green (one unrelated failure locally,
`test_copy_ignored_preserves_file_executable_permissions`, from this
sandbox's `umask 0002`), and `cargo clippy --all-targets --all-features`
/ `cargo fmt --check` are clean.

---
Closes #3816 — automated triage

---------

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
2026-08-13 16:44:02 -07:00
Maximilian Roos 9185eaa836 test(expansion): pin minijinja's None for a JSON null
minijinja 2.22 changed none and bool rendering to Jinja2's spelling
together (mitsuhiko/minijinja#913), and #3795 pinned only the bool half.
A JSON null stored in `worktrunk.state.<branch>.vars.<key>` reaches the
template as a real none, so `{{ vars.config.note }}` now renders `None`
— user-visible wherever a template reads a vars value, and until now
nothing held it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 15:56:11 -07:00
dependabot[bot] 3ecb5d9852 chore: bump minijinja from 2.21.0 to 2.23.0 (#3795)
Bumps [minijinja](https://github.com/mitsuhiko/minijinja) from 2.21.0 to
2.23.0.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/mitsuhiko/minijinja/blob/main/CHANGELOG.md">minijinja's
changelog</a>.</em></p>
<blockquote>
<h2>2.23.0</h2>
<ul>
<li>Fixed Unicode identifiers in templates rendered through the Python
bindings.</li>
</ul>
<h2>2.22.0</h2>
<ul>
<li>Changed rendering of none and boolean values to <code>None</code>,
<code>True</code>, and <code>False</code> for Jinja2 compatibility in
Rust and Go. <a
href="https://redirect.github.com/mitsuhiko/minijinja/issues/913">#913</a></li>
<li>Added <code>StringInput</code> for custom Rust filters and functions
that transform strings while preserving safety provenance.</li>
<li>Fixed safety handling in string-transforming and composing filters
to preserve safe strings and escape unsafe fragments in Rust and
Go.</li>
<li>Fixed the <code>split</code> filter to return a sequence, enabling
negative indexing and slicing in Rust and Go. <a
href="https://redirect.github.com/mitsuhiko/minijinja/issues/909">#909</a></li>
<li>Fixed Python-compatible dict methods being shadowed by same-named
map keys. <a
href="https://redirect.github.com/mitsuhiko/minijinja/issues/903">#903</a></li>
<li>Fixed loop-local assignments leaking into subsequent iterations in
Rust and Go. <a
href="https://redirect.github.com/mitsuhiko/minijinja/issues/912">#912</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/mitsuhiko/minijinja/commit/19af7d4afbd7ea529c4251c29483dce2179f3130"><code>19af7d4</code></a>
chore(release): 2.23.0</li>
<li><a
href="https://github.com/mitsuhiko/minijinja/commit/7f63616efcc76e326d07a7f923a5314b948010a4"><code>7f63616</code></a>
docs(changelog): document Python Unicode identifiers</li>
<li><a
href="https://github.com/mitsuhiko/minijinja/commit/179652f1393cd1b4c46e10e55896a5713cf15eb6"><code>179652f</code></a>
ref: Remove CLAUDE.md</li>
<li><a
href="https://github.com/mitsuhiko/minijinja/commit/52b1cc66b290cef1fb58b00e2fa277973bfc5f82"><code>52b1cc6</code></a>
ref: Move claude stuff</li>
<li><a
href="https://github.com/mitsuhiko/minijinja/commit/0d4c6f49a589e35da50f50dd024d316d21c35427"><code>0d4c6f4</code></a>
ref: Remove broken symlinks</li>
<li><a
href="https://github.com/mitsuhiko/minijinja/commit/be3c6f58af1c8b18b52513fc866afe823073883b"><code>be3c6f5</code></a>
fix(python): enable Unicode identifiers</li>
<li><a
href="https://github.com/mitsuhiko/minijinja/commit/d5bf25e588f219272c84ea1825181e402696134f"><code>d5bf25e</code></a>
chore(release): 2.22.0</li>
<li><a
href="https://github.com/mitsuhiko/minijinja/commit/cdbd4a98c485ad81a92c666efe7ef3327de61cb6"><code>cdbd4a9</code></a>
docs(changelog): document unreleased filter fixes</li>
<li><a
href="https://github.com/mitsuhiko/minijinja/commit/d715d56328169e41bed04025830ddcb1e4c82e84"><code>d715d56</code></a>
fix: doc link</li>
<li><a
href="https://github.com/mitsuhiko/minijinja/commit/cd62d8c1fe7a15eac2e910a87a7b0b1e3d45f8cd"><code>cd62d8c</code></a>
fix(filters): preserve safety in string transformations</li>
<li>Additional commits viewable in <a
href="https://github.com/mitsuhiko/minijinja/compare/minijinja-go/v2.21.0...minijinja-go/v2.23.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=minijinja&package-manager=cargo&previous-version=2.21.0&new-version=2.23.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
2026-08-13 13:20:53 -07:00
Maximilian Roos 0a55dcd917 test(remove): pin the ownership gate's refusal of an emptied worktree directory (#3815)
Removal's ownership gate (`ensure_holds_this_worktree`, #3808) reads
`<dir>/.git` and stops there, so a directory that has lost that entry
holds no worktree of ours and is refused. Nothing named that: `wt
remove` reaches the same refusal upstream at the `prunable` check, and
the gate's own tests cover the sibling-worktree and foreign-repo cases
instead.

The case bites where the emptied directory sits inside a repository — a
worktree nested in another, or any worktree under a git-managed `~`.
`git rev-parse --git-dir` walks up from it and answers with the
enclosing repository's git dir, which *is* the common dir, so a gate
resolving that way reads the directory as the main worktree and accepts
it:

```console
$ git -C repo worktree add nested -b nested && rm repo/nested/.git
$ git -C repo/nested rev-parse --git-dir
/…/repo/.git
```

So the test nests, and asserts that premise before asserting the refusal
— otherwise it would go vacuous if the nesting stopped producing that
resolution. In the flat sibling layout the walk-up finds no repository
at all and resolution simply fails, which is why a sibling-shaped test
would pin nothing.

Confirmed discriminating: with resolution swapped back to the
`GIT_DIRS`-cached `git_dir()`, it fails while
`ensure_holds_this_worktree_accepts_both_worktree_shapes` still passes.

> _This was written by Claude Code on behalf of max-sixty_
2026-08-13 09:16:54 -07:00
Worktrunk Bot bdce107d91 fix(config): rank env vars and --config-set above project entries (#3790)
Fixes #3788.

Layer and specificity were separate steps. `load_with_warnings`
flattened system config → user config → `WORKTRUNK_*` env vars →
`--config-set` into one document, and the accessors then resolved
specificity on that document, so a `[projects."<id>"]` entry answered
for the global key of the same name whichever layer set it.
`WORKTRUNK_WORKTREE_PATH` could therefore not override a project's
`worktree-path`, and a global `--config-set` hit the same wall.

Per @max-sixty in the issue thread — "env vars should indeed take
precedence over the user project config, we should fix this throughout"
— the two invocation layers now cross the axes: they're typed for one
run, so they outrank a project entry as well as the global key. Load
applies them at both scopes (`apply_invocation_layer_over_projects`, the
last step before `finalize`): whatever the layer set is dropped from
every project entry, leaving the global key it also set to answer for
it.

Two kinds of key are held back:

- **Keys the layer restates under `projects."<name>"`** — `--config-set
'projects."github.com/owner/repo".worktree-path = …'` is both the
highest layer *and* the most specific key, so it still wins over the
same layer's global key.
- **Composing keys** — hooks, aliases, and `step.copy-ignored.exclude` —
whose project-scoped values append to the global ones rather than
replacing them. Both already apply, so an env-set hook was never
outranked, and dropping the project's copy would silently stop it
running. Hook names come from `HooksConfig`'s schema, so a new hook
can't be forgotten.

Two sections have to go as a unit rather than leaf by leaf.
`[commit.generation]`'s mutually exclusive pairs: `template` and
`template-file` clear one another in `merge_with` *and* are rejected
together by `validate`, so overriding either has to displace both at
project scope — otherwise the project's partner would still win the
merge. `exclusive_sibling` names those pairs. And
`[list.custom-columns]`, which `ListConfig::merge_with` extends per
whole column, so a partial removal leaves the project's column replacing
the global one anyway — and `ListColumnConfig::template` is required, so
it can also strand a column that no longer deserializes.
`is_atomic_section` names that table.

Both are enumerations, so the pass degrades as a unit behind them: the
removals land on a candidate, kept only if it still deserializes and
validates. That is the guarantee the env and `--config-set` layers
already have, and without it the next required field would answer a
stranded leaf with `UserConfig::default()` — costing the user their
whole config for that invocation rather than one project entry's
precedence.

The precedence table now reads:

| Source of `worktree-path` | Loses to |
|---|---|
| `--config-set 'worktree-path = …'` | — |
| `WORKTRUNK_WORKTREE_PATH` | `--config-set` |
| `[projects."github.com/owner/repo"]` in a config file | either
invocation layer |
| global `worktree-path` in a config file | all of the above |

## Docs

The help text had no precedence section at all — the gap that made this
read as a bug — so this adds one under **Environment variables**, plus a
pointer from **User project-specific settings**. That supersedes #3789,
which documented the old behavior; I'll close it in favour of this.

## Testing

Nine unit tests in `src/config/user/tests.rs` cover the table-level rule
(both layers, pattern entries, restated project-scoped overrides,
untouched sibling keys, composing keys, the exclusive pair, the atomic
custom column, a rolled-back layer, and the no-override no-op), and
`test_switch_create_invocation_layers_outrank_project_worktree_path`
proves it end-to-end — a real process is the only thing that reads
`WORKTRUNK_WORKTREE_PATH` off the environment. That test keeps a control
showing the project entry still beats the config file's own global key,
so it can't pass by project entries having stopped applying.

The reproduction from the issue now lands where it says it should:

```console
$ WORKTRUNK_CONFIG_PATH="$tmp/wt.toml" WORKTRUNK_WORKTREE_PATH="$tmp/from-environment" \
    wt switch --create feature --no-cd --no-hooks --yes --format=json
{"action":"created","branch":"feature","path":"/tmp/tmp.jQiTAuNPFd/from-environment",…}
```

<details><summary>Local suite</summary>

`cargo test --lib --bins` and `cargo test --test integration` are green
apart from `test_copy_ignored_preserves_file_executable_permissions`,
which fails in this sandbox because its umask is `0002` (file created
`0664`, test expects `0644`) — unrelated to this change and not
reproducible on a `0022` runner. `cargo fmt --check` and `cargo clippy
--all-targets --all-features` are clean.

</details>

---------

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
Co-authored-by: Maximilian Roos <m@maxroos.com>
2026-08-13 09:06:00 -07:00
Worktrunk Bot 049f4c9d6a fix(url): scope the ssh:// userinfo search to the authority (#3813)
Found in the nightly survey of `src/git/url.rs`.

`GitRemoteUrl::parse`'s `ssh://` branch looked for the userinfo boundary
with `rest.split('@').next_back()` — the *whole* remainder, path
included — while the scheme branch (`authority_host` on everything
before the first `/`) and the SCP-style branch ("Split the authority
before looking for `@` so `@` remains valid in the path") both scope
that search to the authority. `@` is legal in a path, so on the `ssh://`
branch a later path segment could pose as the authority:

```
ssh://git@attacker.example/owner/repo@github.com/org/repo.git
  → host "github.com", project_identifier "github.com/org/repo"
```

git connects to `attacker.example`.
[`project_identifier()`](https://github.com/max-sixty/worktrunk/blob/7b0d5e93694080dc59da35d506e8801c4a41bd87/src/git/url.rs#L234)
is what `Approvals` keys a project's approved commands on, so a remote
crafted this way borrows the approvals granted to the repo it names —
the same host-impersonation the file already guards against for
`https://`/`http://`/`git://`/SCP in
`scheme_authorities_use_the_network_host_after_userinfo`. The `ssh://`
branch was the one that never got the fix.

The change splits the authority off at the first `/` and reuses
`authority_host`, so all four forms now resolve the host the same way.
Two consequences:

- **The spoof is closed** — the host is the authority git dials,
whatever the path contains.
- **`@` in a namespace parses instead of being rejected** —
`ssh://git@host.com/org@company/repo.git` now yields owner
`org@company`, matching what `https://host.com/org@company/repo.git`
already did. `test_adversarial_ssh_at_in_path` asserted the rejection as
"ambiguous parsing"; scoping the search to the authority removes the
ambiguity, so that test now asserts the parse.

The last-`@`-in-the-authority rule is unchanged and still pinned by
`test_adversarial_ssh_user_injection`:
`ssh://git@legitimate.com@attacker.com/…` is `attacker.com`, which is
the host the connection goes to.

<details><summary>Verification</summary>

`test_adversarial_ssh_host_spoofed_from_path` is the regression test.
Against the pre-fix parser it fails with the spoofed host:

```
---- git::url::tests::test_adversarial_ssh_host_spoofed_from_path stdout ----
assertion `left == right` failed: the host is the authority git connects to, not a later path segment
  left: "github.com"
 right: "attacker.example"
```

`cargo test --lib --no-default-features` passes (1449 tests), including
the port-stripping, empty-host, nested-group, and Azure DevOps cases
that go through the same branch, and the `ci_platform`
host-classification tests. `cargo clippy --lib --no-default-features` is
clean. The full suite and the platform matrix run in CI.

</details>

---------

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
2026-08-13 05:46:40 -07:00
Worktrunk Bot c31e827cba chore: update tend workflows (0.1.14 → 0.1.15) (#3814)
Automated nightly regeneration of tend's workflow files.

**tend version:** 0.1.14 → 0.1.15

**Notable changes**

- **Rate-limit handling**: a maintainer can approve past the spike limit
(max-sixty/tend#874), and `tend-mention` now skips comments on
`tend-rate-limit`-labelled issues as well as `tend-outage` ones — the
same self-trigger loop guard, widened to every issue tend files about
its own health.
- **Review flow**: a push is queued behind an examined-HEAD gate instead
of cancelling the in-flight review (max-sixty/tend#903), the review is
submitted before a fix is pushed (max-sixty/tend#834), a force-push
triggers a re-review rather than trusting the re-anchored SHA
(max-sixty/tend#884), and the `/code-review` second pass is
unconditional (max-sixty/tend#937).
- **CI monitoring**: both the check poll and the `gh run rerun --failed`
poll now end terminally when the cap is hit instead of reading as done
(max-sixty/tend#876, max-sixty/tend#951), and a failed GitHub-status
probe no longer reads as "no incident" (max-sixty/tend#913).
- **Nightly**: conflicted bot PRs are test-merged locally instead of
filtered on the lazy `mergeable` field (max-sixty/tend#898), and mention
runs skip the bot's own review and a third party's content-free approval
(max-sixty/tend#916, max-sixty/tend#955).
- **`tend check`**: an unreadable ruleset bypass list is reported as
unknown rather than ungated (max-sixty/tend#825), environment names are
read one per line and addressed encoded (max-sixty/tend#879), and
`credential-environments` no longer points at the setting it rejects
(max-sixty/tend#900) — this repo currently `SKIP`s that check, so its
wording should improve here.

Full comparison:
https://github.com/max-sixty/tend/compare/0.1.14...0.1.15

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
2026-08-13 05:41:10 -07:00
Maximilian Roos 7aba380f0c fix(remove): gate removal on the registration, not the repository (#3808)
`wt remove --force` deleted a live worktree of this same repository,
uncommitted work included, whenever that worktree had been moved onto
another worktree's registered path.

The guard added in #3785 asks which *repository* the occupant answers
to: a linked worktree's git dir sits under `<common>/worktrees/`, the
main worktree's *is* the common dir, anything else is someone else's. A
sibling worktree moved onto the path satisfies that — its git dir sits
under `<common>/worktrees/` like any worktree of this repo — so it
passed, and the fast path renamed the directory into trash and handed
the `rm -rf` to a detached process. It is not prunable either: its
gitdir file points at a location that exists, so the `is_prunable` arm
from the same PR doesn't catch it.

Git's own validation is one level finer. `validate_worktree` requires
the directory to point back at *this registration*, and refuses this
removal with `--force`:

```console
$ git -C repo worktree remove --force ../repo.feature
fatal: validation failed, cannot remove working tree:
  '.../repo.feature' does not point back to '.git/worktrees/repo.feature'
```

<details>
<summary>Reproducer, verified against a build of main</summary>

The occupant has to be *moved* onto the path rather than created there —
`git worktree add` refuses a registered path, which is what leaves a
plain `mv` as the way this state arises.

```console
$ git -C repo worktree add ../repo.feature -b feature
$ git -C repo worktree add ../repo.bar -b bar
$ rm -rf ../repo.feature && mv ../repo.bar ../repo.feature
$ echo PRECIOUS > ../repo.feature/precious.txt
$ wt remove --force --yes feature
◎ Removing feature worktree (--force) & branch in background (same commit as main, _)
$ ls ../repo.feature
ls: ../repo.feature: No such file or directory
```

</details>

## The fix

The gate is now git's comparison at git's granularity: the directory's
`.git` must name *this* registration, and that registration's `gitdir`
file must name the directory back. Repository-level ownership stays as
the weaker half of the conjunction — it is what rejects a `.git` file
pointing at another repository — and the main worktree is the same test
where there is no registration to point back at.

`ensure_belongs_to_repo` becomes `ensure_holds_this_worktree`, since it
no longer merely asks about repository membership.

Resolution moves to `Repository::git_dir_at`, the fs-only resolver the
`wt list` prewarm already used (`derive_worktree_git_dir`), generalized
to answer for a directory rather than for a known worktree of this repo:
its main-worktree branch returned `git_common_dir()` on trust, and now
canonicalizes the `.git` it actually found. It also never walks up to a
parent, which is what git reads too — `git rev-parse --git-dir` in an
emptied worktree can resolve the *enclosing* repository.

That settles a second thing the old docstring got wrong. It claimed the
plan→rename window was "narrower than `ensure_clean`'s"; in fact
`ensure_clean` re-runs `git status` while this gate answered from
`GIT_DIRS`, memoized process-wide, so the second call was vacuous and
the window — which contains the approval prompt and the `pre-remove`
hook — was unguarded. `git_dir_at` reads the filesystem on every call,
so the check at the rename now re-decides.

The refusal was `Directory @ … is not this repository's worktree`, which
is false in the sibling case: it *is* one of this repository's
worktrees, just not the one registered there. Its hint didn't fit either
— "move the directory aside, then run `git worktree prune`" is a
repo-wide prune, and with a sibling moved aside *both* registrations are
prunable, so following it clears both and leaves a live checkout that
has stopped being a worktree:

```console
$ mv ../repo.feature ../repo.aside && git worktree prune -v
Removing worktrees/repo.feature: gitdir file points to non-existent location
Removing worktrees/repo.other: gitdir file points to non-existent location
$ git -C ../repo.aside status
fatal: not a git repository: (null)
```

So the error carries where the occupant's own registration records it,
and each case gets the remedy that fits. Moving it back to that path
leaves prune with only the stale entry to clear:

```console
$ wt remove --force --yes feature
✗ Directory @ ../repo.feature does not hold the worktree registered there
↳ Removing it could destroy the worktree registered @ ../repo.other; move the directory back there, then run git worktree prune
```

That path is read through `canonicalize_with_parents`, because a
relative `gitdir` entry resolves against `<common>/worktrees/<id>` and
would otherwise reach the hint with the `..` chain still in it — and
plain canonicalization can't normalize a directory that no longer
exists, which is the case the arm is reached for. Normalizing there also
makes `crate::path::paths_match`, the crate's canonicalizing comparison
over that same helper, the right test for the gate, so there is no
second comparison beside it.

The gate's fail-closed behavior now rests on that helper resolving `..`
through the filesystem rather than collapsing it lexically — across a
symlink the two readings name different directories — so `src/path.rs`
records the constraint where a lexical rewrite would otherwise read as a
tidy-up.

The FAQ's "What can Worktrunk delete?" paragraph carried the same "a
*different* repository" framing and is corrected.

## Scope

Pre-existing, and 0.73.0 already narrowed it — 0.72.0 had no ownership
check at all and deleted foreign clones too. The guard has two call
sites (`prepare_worktree_removal` at planning, `stage_worktree_removal`
at the rename), so this reaches `wt merge --remove`, `wt step prune`,
and picker removal, not only `wt remove`.

One incidental tightening: `wt remove <bare-repo-path>` previously
passed the guard (a bare root's git dir *is* the common dir) and was
stopped only by the dirty check, which `--force` skips. It now refuses
at the guard.

<details>
<summary>One residual, left alone</summary>

`git worktree repair <path>` after the `mv` produces a *double
registration*: both `worktrees/repo.bar/gitdir` and
`worktrees/repo.feature/gitdir` come to record the same path, and `git
worktree list` reports two worktrees there. In that state the new gate
accepts the removal — the occupant does point at the `feature`
registration, and that registration does point back — while git refuses,
because its path→worktree lookup happens to match the `bar` entry first.
Closing it means knowing the registration id at the gate, or scanning
every `worktrees/*/gitdir` for duplicate claims. Unchanged by this PR,
and reachable only via `mv` followed by `repair`.

</details>

## Testing

Five new tests, each confirmed to fail with the line it covers reverted
and to leave the others passing. Three drive the binary:

- **the sibling case** — follows #3785's data-safety model: asserts the
filesystem afterwards, not just the exit code, since removal stages by
rename and deletes in a detached process. Snapshots the refusal, so the
hint and the path it names are pinned. Fails with the pointer-back
conjunct removed, while the foreign-repo test still passes without it —
the two cover different halves.
- **the re-check at the rename** — a `pre-remove` hook repoints the
worktree's `.git` at a sibling's registration after planning has already
cleared it, which is what makes the second gate's freshness observable.
Fails when resolution routes back through the `GIT_DIRS`-cached
`git_dir()`.
- **a relative `gitdir` entry** — removal succeeds, and git reads the
rewritten entry back, which is what makes it the form git itself writes.
Rewriting the entry rather than setting `worktree.useRelativePaths`
keeps the test independent of the git version that introduced the
option.

Two sit at the gate, where the CLI can't reach:

- **both worktree shapes are accepted** — including the main worktree,
whose git dir *is* the common dir. `wt remove` rejects the main worktree
well upstream of this gate and a bare repository's worktrees are all
linked, so nothing through the CLI would notice that arm inverting.
- **the refusal names a normalized path** — asserted against
`Diagnostic::render`, since the path is in the hint and `Display`
carries only the title.

Local gate green: 4607 tests, lints, doctests, rustdoc under
`-Dwarnings`.

> _This was written by Claude Code on behalf of max-sixty_

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 03:59:58 -07:00
Worktrunk Bot 667c6efaf0 docs(switch): note that Alt-x never forces (#3811)
Requested by @max-sixty in
[#3809](https://github.com/max-sixty/worktrunk/issues/3809#issuecomment-5273484502)
— a couple of words clarifying that `Alt-x`'s removal is safe-only.

The keybinding table read `Remove selected worktree/branch`, which
doesn't say the removal never forces; that's what sent the reporter
looking for a force-remove that isn't there. The picker hardcodes the
safe path —
[`prepare_removal`](https://github.com/max-sixty/worktrunk/blob/7a2a3e003e7eed138ff5f2dcd2296f6bbd8e86d4/src/commands/picker/mod.rs#L323-L330)
passes `BranchDeletionMode::SafeDelete` and `force_worktree: false`.

Deliberately scoped to the table cell, per the "(only)" in the request.
The bigger question — whether `Alt-x` should ever pass `-D` — is still
open on the issue and isn't touched here.

Primary source is `after_long_help` in `src/cli/mod.rs`; the three
mirrors and the `--help` snapshot are regenerated.

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
2026-08-12 16:19:20 -07:00
Maximilian Roos 7a2a3e003e Release v0.73.0 (#3804)
Version bump, Cargo.lock, and the 0.73.0 changelog section.
v0.73.0
2026-08-12 11:07:17 -07:00
Maximilian Roos 0cb3cc1ef7 test(remove): cover the reap guard's spared branch deterministically (#3805)
The controlling-terminal guard's sparing branch had no CI coverage.
`test_remove_reap_kills_process` branches on the suite's own terminal —
none in CI, so CI only ever exercised the kill side, and the branch that
protects an interactive process from `--reap` ran only on dev boxes.

`test_remove_reap_spares_terminal_process` gives the child its own PTY
instead of reading the suite's, so the spared branch runs everywhere.
`portable_pty` makes the child a session leader with that PTY as its
controlling terminal, which is exactly the property the guard reads. A
spared process and an undiscovered one produce identical output ("No
processes to reap"), so the test proves discovery sees the child first,
then pins the guard's verdict in-process, then asserts the child
survives removal.

The reap phase prints before `handle_remove_output` runs the removal, so
both tests now wait for the worktree to actually disappear — without it,
a `wt` that gave up after the reap phase would still satisfy every
output assertion. Both presence polls also move onto `wait_for`, the
existing presence-poll helper, instead of hand-rolled deadline loops.

`tests/CLAUDE.md` picks up two entries this work surfaced. The first is
the slow-timeout triage signature: a kill at the 180s bound is a
duration symptom with two causes, and the durations around it tell CPU
starvation (a sibling worktree's build) from a blocked call inside the
test. It records `threads-required` as starvation's only lever and why
it stays unset. The second distinguishes a bounded poll on one
identified `ErrorKind` from a retry, so `pin_test_binary` and
`forward_with_etxtbsy_retry` don't read as violations of the No Retries
doctrine — the opening sentence is narrowed to the re-run it actually
bans.

> _This was written by Claude Code on behalf of max-sixty_

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-12 10:19:33 -07:00
dependabot[bot] ab76101b0b chore: bump taiki-e/install-action from 2.85.10 to 2.85.11 (#3801)
Bumps
[taiki-e/install-action](https://github.com/taiki-e/install-action) from
2.85.10 to 2.85.11.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/taiki-e/install-action/releases">taiki-e/install-action's
releases</a>.</em></p>
<blockquote>
<h2>2.85.11</h2>
<ul>
<li>
<p>Update <code>zola@latest</code> to 0.23.2.</p>
</li>
<li>
<p>Update <code>wasm-bindgen@latest</code> to 0.2.127.</p>
</li>
<li>
<p>Update <code>uv@latest</code> to 0.12.3.</p>
</li>
<li>
<p>Update <code>osv-scanner@latest</code> to 2.5.0.</p>
</li>
<li>
<p>Update <code>mise@latest</code> to 2026.8.3.</p>
</li>
<li>
<p>Update <code>kingfisher@latest</code> to 1.112.0.</p>
</li>
<li>
<p>Update <code>editorconfig-checker@latest</code> to 3.10.0.</p>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md">taiki-e/install-action's
changelog</a>.</em></p>
<blockquote>
<h2>[2.85.11] - 2026-08-09</h2>
<ul>
<li>
<p>Update <code>zola@latest</code> to 0.23.2.</p>
</li>
<li>
<p>Update <code>wasm-bindgen@latest</code> to 0.2.127.</p>
</li>
<li>
<p>Update <code>uv@latest</code> to 0.12.3.</p>
</li>
<li>
<p>Update <code>osv-scanner@latest</code> to 2.5.0.</p>
</li>
<li>
<p>Update <code>mise@latest</code> to 2026.8.3.</p>
</li>
<li>
<p>Update <code>kingfisher@latest</code> to 1.112.0.</p>
</li>
<li>
<p>Update <code>editorconfig-checker@latest</code> to 3.10.0.</p>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/taiki-e/install-action/commit/7f4eb899022d8fe70b20c4f3de697aa85c309026"><code>7f4eb89</code></a>
Release 2.85.11</li>
<li><a
href="https://github.com/taiki-e/install-action/commit/c17da6245a7fe549cefa05b31e3614ce0b16c2af"><code>c17da62</code></a>
Update <code>zola@latest</code> to 0.23.2</li>
<li><a
href="https://github.com/taiki-e/install-action/commit/97e8291c2ba440a7119c03ebe3ed1e584a1f9b7f"><code>97e8291</code></a>
Update <code>wasm-bindgen@latest</code> to 0.2.127</li>
<li><a
href="https://github.com/taiki-e/install-action/commit/91f9e5c61a2dc7936a8f404d01b7c1551b9c581a"><code>91f9e5c</code></a>
Update <code>uv@latest</code> to 0.12.3</li>
<li><a
href="https://github.com/taiki-e/install-action/commit/bd0cb00440414f36db2f79bca8e27971bb63b2a3"><code>bd0cb00</code></a>
Update <code>osv-scanner@latest</code> to 2.5.0</li>
<li><a
href="https://github.com/taiki-e/install-action/commit/4def957aa80c252e2d4c61387c6a429d377907d1"><code>4def957</code></a>
Update <code>mise@latest</code> to 2026.8.3</li>
<li><a
href="https://github.com/taiki-e/install-action/commit/3d149dc3890afe4774d158d353b5a3e55fe90f60"><code>3d149dc</code></a>
Update <code>kingfisher@latest</code> to 1.112.0</li>
<li><a
href="https://github.com/taiki-e/install-action/commit/20d4381a5cf6917520fde30f9ff52387410bfb6e"><code>20d4381</code></a>
Update <code>editorconfig-checker@latest</code> to 3.10.0</li>
<li><a
href="https://github.com/taiki-e/install-action/commit/583939ec0c8ee9c523cc60342d3d979ce6730f38"><code>583939e</code></a>
codegen: Ignore clippy::assert_is_empty lint</li>
<li>See full diff in <a
href="https://github.com/taiki-e/install-action/compare/v2.85.10...v2.85.11">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=taiki-e/install-action&package-manager=github_actions&previous-version=2.85.10&new-version=2.85.11)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-12 09:40:35 -07:00
Worktrunk Bot 1636b78ddf fix(gitlab): forward glab's verdict when the project lookup fails (#3799) 2026-08-12 05:39:40 -07:00
Worktrunk Bot b688e8142b test(docs): drop the command-page sync test its docs file outlived (#3802) 2026-08-12 05:37:04 -07:00
Worktrunk Bot 91b7beff57 skills(writing-user-outputs): document the stderr half of the anstream macro rule (#3797)
Skill drift. `writing-user-outputs` is the thing loaded before editing
anything that produces user-visible strings, and its "Printing output"
section states only the stdout half of a two-sided rule — which
`println!` is in scope, and the `BrokenPipe` panic that turns on it. The
stderr half is absent, even though ec0f3a344 (#3771) made it structural:
`check_stderr_macros_come_from_styling` now requires every bare
`eprint!` / `eprintln!` under `src/` to resolve to anstream's.

The gap is exactly the mistake #3771 fixed in five files. A developer
following the skill's own example import — `use
worktrunk::styling::{eprintln, println, stderr};` — and then reaching
for `eprint!` mid-block gets std's macro, which keeps ANSI on a
redirected stderr where the `eprintln!` two lines up drops it. The skill
never names `eprint!` at all, and #3771's commit message notes the suite
cannot catch this: `CLICOLOR_FORCE=1` forces color on both printers, so
every snapshot agrees whichever macro is in scope.

One paragraph, placed directly after the `println!` one it mirrors: the
stripping asymmetry and its user-visible symptom, that `eprint!` is the
half that slips and why, the two ways to satisfy the rule (import, or
qualify as `styling::eprintln!(…)`), the guard test that enforces it,
and that `STD_STDERR_ALLOWED_PATHS` exempts whole files rather than
calls.

No regression test — the change is skill prose. The behavior it
describes is already pinned by `check_stderr_macros_come_from_styling`
and `test_stderr_narration_strips_ansi_when_piped`; what was missing is
that a developer reads the rule before tripping the guard rather than
after.

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
2026-08-11 15:55:17 -07:00
Worktrunk Bot e243472d9a docs(remove): correct the detached-worktree case in the removal planner (#3796)
Comment-only. Two adjacent comments in `prepare_worktree_removal`'s
`WorktreePath` arm describe a routing that cf822f75c ("guard a
registered path that no longer holds its worktree", #3785) changed
underneath them.

The branch-only cleanup's comment still ends with *"A detached worktree
has no branch to fall back to, so it proceeds and surfaces the removal
error."* It no longer proceeds. A detached worktree whose directory is
gone fails the first arm's `wt.branch.as_deref()` test, and git reports
exactly that registration as `prunable` — verified against git directly:

```
$ git worktree add --detach ../det HEAD && rm -rf ../det && git worktree list --porcelain
worktree /tmp/gt/det
HEAD 7c8964f04dc09ce2dff86351d5b63906ac4de41e
detached
prunable gitdir file points to non-existent location
```

So it lands in the `else if wt.is_prunable()` arm added by that commit
and is refused at planning with `WorktreeMissing` plus the `git worktree
prune` hint. Behavior is unchanged by this PR; the old path also ended
in an error, just git's raw one.

That second arm's own comment has the mirror-image gap: it opens
*"Registered, directory present"*, which names only the
deleted-and-recreated shape, while the detached-and-absent shape reaches
it too. It also says the cleanup above "needs the directory gone" when
that arm wants a branch *and* an absent directory — the missing half is
precisely why the detached case falls through.

Both comments now name the two shapes the arm catches and what each
cleanup actually requires.

No regression test: the change is comments, and the routing it describes
is already pinned by the prunable-registration tests that landed with
#3785.

Deliberately narrow. The asymmetry it exposes — a stale branch-carrying
registration is cleaned up by `wt remove` while a stale detached one is
refused — overlaps #3769 and #3791, so it is left to those rather than
folded in here.

---------

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
2026-08-11 15:55:14 -07:00
dependabot[bot] 6466b91673 chore: bump the patch group with 6 updates (#3794)
Bumps the patch group with 6 updates:

| Package | From | To |
| --- | --- | --- |
| [clap_complete](https://github.com/clap-rs/clap) | `4.6.8` | `4.6.9` |
| [ignore](https://github.com/BurntSushi/ripgrep) | `0.4.31` | `0.4.33`
|
| [open](https://github.com/Byron/open-rs) | `5.4.0` | `5.4.1` |
| [similar](https://github.com/mitsuhiko/similar) | `3.1.1` | `3.1.2` |
| [jsonschema](https://github.com/Stranger6667/jsonschema) | `0.49.5` |
`0.49.6` |
| [vergen-gitcl](https://github.com/rustyhorde/vergen) | `10.0.1` |
`10.0.2` |

Updates `clap_complete` from 4.6.8 to 4.6.9
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/clap-rs/clap/commit/715b812f3b72ebca1d5ce257e0495e1e76aea56f"><code>715b812</code></a>
chore: Release</li>
<li><a
href="https://github.com/clap-rs/clap/commit/89e6e9b59c6209c5d9817c7d113ad7ad7f6d6907"><code>89e6e9b</code></a>
docs: Update changelog</li>
<li><a
href="https://github.com/clap-rs/clap/commit/3918cb4db643ffdfb3c8206d241d70acf6d5c5de"><code>3918cb4</code></a>
Merge pull request <a
href="https://redirect.github.com/clap-rs/clap/issues/6468">#6468</a>
from sjh9714/fix-bash-posix-fn-name</li>
<li><a
href="https://github.com/clap-rs/clap/commit/526c81906357cae595259d46cea2fd4675489331"><code>526c819</code></a>
Merge pull request <a
href="https://redirect.github.com/clap-rs/clap/issues/6469">#6469</a>
from latent-9/docs/fix-derive-reference-link</li>
<li><a
href="https://github.com/clap-rs/clap/commit/b2bd685a8e809df2049f5cf6777641d8f86aeef6"><code>b2bd685</code></a>
docs(clap_derive): Fix derive reference link</li>
<li><a
href="https://github.com/clap-rs/clap/commit/ecee8a4f862b1b97ce2c7db5bf806cfe394a55f6"><code>ecee8a4</code></a>
fix(complete): Name the function after fn_name</li>
<li><a
href="https://github.com/clap-rs/clap/commit/6dd2e5a0802d84226afd63c427fc09940fa837d4"><code>6dd2e5a</code></a>
test(complete): Show POSIX bash will not source</li>
<li><a
href="https://github.com/clap-rs/clap/commit/4684d7abc545cef1d78708864cfe8c7668ed49c1"><code>4684d7a</code></a>
chore: Release</li>
<li><a
href="https://github.com/clap-rs/clap/commit/51d50ad3ae341f987f26115d6a6719cf76cc78f4"><code>51d50ad</code></a>
docs: Update changelog</li>
<li><a
href="https://github.com/clap-rs/clap/commit/3bc36222053a49936ea67e8616d1584a75c811b0"><code>3bc3622</code></a>
Merge pull request <a
href="https://redirect.github.com/clap-rs/clap/issues/6457">#6457</a>
from bl4ck4t/master</li>
<li>Additional commits viewable in <a
href="https://github.com/clap-rs/clap/compare/clap_complete-v4.6.8...clap_complete-v4.6.9">compare
view</a></li>
</ul>
</details>
<br />

Updates `ignore` from 0.4.31 to 0.4.33
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/BurntSushi/ripgrep/commit/3fce3b5bb0236da2df6d99672afb8a719642eca7"><code>3fce3b5</code></a>
ignore-0.4.33</li>
<li><a
href="https://github.com/BurntSushi/ripgrep/commit/5055264ae021b5d0e01e55f723622d638ea877f0"><code>5055264</code></a>
globset-0.4.20</li>
<li><a
href="https://github.com/BurntSushi/ripgrep/commit/020687a77d13146923333f0beb274eeabd54a270"><code>020687a</code></a>
ignore,globset: increase pool capacity</li>
<li><a
href="https://github.com/BurntSushi/ripgrep/commit/5ed408e17eccfc59bcec48f584feece902873e54"><code>5ed408e</code></a>
ignore-0.4.32</li>
<li><a
href="https://github.com/BurntSushi/ripgrep/commit/435f59fc4b43af3ab32f34d53fa34978f393fe52"><code>435f59f</code></a>
ignore: skip loading unreachable ignore files</li>
<li><a
href="https://github.com/BurntSushi/ripgrep/commit/f9c05a949d1a0dc8e16dee28ca9605d38611faeb"><code>f9c05a9</code></a>
index: remove incorrect README</li>
<li><a
href="https://github.com/BurntSushi/ripgrep/commit/8372866810a1f2a647d11d7780984d4402a5c1e9"><code>8372866</code></a>
index: add some initial indexing scaffolding</li>
<li><a
href="https://github.com/BurntSushi/ripgrep/commit/d99ac34406f82c03c3938c222f1786495c845034"><code>d99ac34</code></a>
core: add <code>index</code> module</li>
<li><a
href="https://github.com/BurntSushi/ripgrep/commit/2ed0c006fee4c441add774a711d9122a88477619"><code>2ed0c00</code></a>
flags: disable many flags when indexing is enabled</li>
<li>See full diff in <a
href="https://github.com/BurntSushi/ripgrep/compare/ignore-0.4.31...ignore-0.4.33">compare
view</a></li>
</ul>
</details>
<br />

Updates `open` from 5.4.0 to 5.4.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/Byron/open-rs/releases">open's
releases</a>.</em></p>
<blockquote>
<h2>v5.4.1</h2>
<h3>Bug Fixes</h3>
<ul>
<li>
<p>Forward WSL targets to PowerShell to make <code>open</code> actually
work there</p>
<!-- raw HTML omitted -->
<p>Opening a URL from WSL failed because the Windows process did not
receive
OPEN_RS_TARGET, even though it was present in the Linux command
environment.
The failure reproduces with cargo run -- <a
href="https://google.com">https://google.com</a> on Ubuntu under
WSL, where Start-Process receives a null FilePath and exits
unsuccessfully.</p>
<p>Add OPEN_RS_TARGET to WSLENV so WSL interop forwards the target into
the
PowerShell environment. Preserve existing WSLENV entries and their
flags,
while keeping the PowerShell command fixed so targets remain data rather
than
shell code.</p>
<p>Validated with focused WSL tests, the all-features test suite,
rustfmt,
Clippy with warnings denied, and an end-to-end cargo run from WSL.</p>
</li>
</ul>
<h3>Commit Statistics</h3>
<ul>
<li>1 commit contributed to the release.</li>
<li>24 days passed between releases.</li>
<li>1 commit was understood as <a
href="https://www.conventionalcommits.org">conventional</a>.</li>
<li>1 unique issue was worked on: <a
href="https://redirect.github.com/Byron/open-rs/issues/128">#128</a></li>
</ul>
<h3>Commit Details</h3>
<!-- raw HTML omitted -->
<!-- raw HTML omitted -->
<ul>
<li><strong><a
href="https://redirect.github.com/Byron/open-rs/issues/128">#128</a></strong>
<ul>
<li>Forward WSL targets to PowerShell to make <code>open</code> actually
work there (96fa673)</li>
</ul>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/Byron/open-rs/blob/main/changelog.md">open's
changelog</a>.</em></p>
<blockquote>
<h2>5.4.1 (2026-08-05)</h2>
<h3>Bug Fixes</h3>
<ul>
<li>
<p><!-- raw HTML omitted --> Forward WSL targets to PowerShell to make
<code>open</code> actually work there</p>
<!-- raw HTML omitted -->
<p>Opening a URL from WSL failed because the Windows process did not
receive
OPEN_RS_TARGET, even though it was present in the Linux command
environment.
The failure reproduces with cargo run -- <a
href="https://google.com">https://google.com</a> on Ubuntu under
WSL, where Start-Process receives a null FilePath and exits
unsuccessfully.</p>
<p>Add OPEN_RS_TARGET to WSLENV so WSL interop forwards the target into
the
PowerShell environment. Preserve existing WSLENV entries and their
flags,
while keeping the PowerShell command fixed so targets remain data rather
than
shell code.</p>
<p>Validated with focused WSL tests, the all-features test suite,
rustfmt,
Clippy with warnings denied, and an end-to-end cargo run from WSL.</p>
</li>
</ul>
<h3>Commit Statistics</h3>
<!-- raw HTML omitted -->
<ul>
<li>1 commit contributed to the release.</li>
<li>24 days passed between releases.</li>
<li>1 commit was understood as <a
href="https://www.conventionalcommits.org">conventional</a>.</li>
<li>1 unique issue was worked on: <a
href="https://redirect.github.com/Byron/open-rs/issues/128">#128</a></li>
</ul>
<h3>Commit Details</h3>
<!-- raw HTML omitted -->
<!-- raw HTML omitted -->
<ul>
<li><strong><a
href="https://redirect.github.com/Byron/open-rs/issues/128">#128</a></strong>
<ul>
<li>Forward WSL targets to PowerShell to make <code>open</code> actually
work there (<a
href="https://github.com/Byron/open-rs/commit/96fa673a6a152d193c210ff77766270192b42d16"><code>96fa673</code></a>)</li>
</ul>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/Byron/open-rs/commit/e548a4e9180ef8ca7e6b605f3f248fee534a03d1"><code>e548a4e</code></a>
Release open v5.4.1</li>
<li><a
href="https://github.com/Byron/open-rs/commit/96fa673a6a152d193c210ff77766270192b42d16"><code>96fa673</code></a>
fix: Forward WSL targets to PowerShell to make <code>open</code>
actually work there (<a
href="https://redirect.github.com/Byron/open-rs/issues/128">#128</a>)</li>
<li>See full diff in <a
href="https://github.com/Byron/open-rs/compare/v5.4.0...v5.4.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `similar` from 3.1.1 to 3.1.2
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/mitsuhiko/similar/blob/main/CHANGELOG.md">similar's
changelog</a>.</em></p>
<blockquote>
<h2>3.1.2</h2>
<ul>
<li>Fixed <code>Algorithm::Lcs</code> deadline fallback emitting edits
twice, which produced
out-of-bounds <code>DiffOp</code> ranges and panics when consuming
timed-out diffs. <a
href="https://redirect.github.com/mitsuhiko/similar/issues/97">#97</a></li>
<li>Fixed <code>Algorithm::Lcs</code> reporting identical subranges with
zero-based indices
instead of preserving the supplied range offsets. <a
href="https://redirect.github.com/mitsuhiko/similar/issues/98">#98</a></li>
<li>Fixed <code>Algorithm::Lcs</code> and <code>Algorithm::Hunt</code>
emitting zero-length delete
operations when both inputs are empty. <a
href="https://redirect.github.com/mitsuhiko/similar/issues/99">#99</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/mitsuhiko/similar/commit/71f0a30e449f857aec934b839cc4c9ca5b21eb26"><code>71f0a30</code></a>
chore(release): prepare 3.1.2</li>
<li><a
href="https://github.com/mitsuhiko/similar/commit/a0aa51e37a31a15898371430a6c03386b111303d"><code>a0aa51e</code></a>
docs(changelog): document recent fixes</li>
<li><a
href="https://github.com/mitsuhiko/similar/commit/51195348b1c7f0bf46d79970294ee84a802e1500"><code>5119534</code></a>
fix(algorithms): omit operations for empty inputs</li>
<li><a
href="https://github.com/mitsuhiko/similar/commit/043c9071ecdd328c9a6e8717f8765dda0c3982eb"><code>043c907</code></a>
fix(lcs): preserve indices for equal subranges</li>
<li><a
href="https://github.com/mitsuhiko/similar/commit/cfad604ea2f213c6816095edb94e49c81768a1d8"><code>cfad604</code></a>
fix(lcs): avoid duplicate deadline fallback edits</li>
<li>See full diff in <a
href="https://github.com/mitsuhiko/similar/compare/3.1.1...3.1.2">compare
view</a></li>
</ul>
</details>
<br />

Updates `jsonschema` from 0.49.5 to 0.49.6
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/Stranger6667/jsonschema/releases">jsonschema's
releases</a>.</em></p>
<blockquote>
<h2>[Python] Release 0.49.6</h2>
<h3>Added</h3>
<ul>
<li>Canonicalization of a negated <code>uniqueItems</code>, where the
complement demands a repeated element under the length floor two
elements imply.</li>
<li>Canonicalization of a negated array tuple, where each position's
complement stands under the length that reaches it.</li>
<li>Canonicalization of <code>contains</code> demands sharing no value,
where their counts add up into a length floor.</li>
<li><code>CanonicalSchema.negate</code> through references, where the
complement of the resolved target takes the reference's place.</li>
<li>Canonicalization of negated <code>propertyNames</code> and
<code>additionalProperties</code>, where the complement spells the
violating-key demand instead of a <code>not</code> residual.</li>
<li>Canonicalization of negated <code>additionalProperties</code> under
Draft 4, where the violating-key demand spells the closed property
map.</li>
<li>Canonicalization of <code>not</code> a reference, where the
complement of the resolved target takes the pointer's place.</li>
<li>Canonicalization of a negated <code>oneOf</code>, where the
complement spells the values no branch admits beside the values two
branches share.</li>
<li>Canonicalization of negated <code>items</code> under Draft 4, where
the violating-element demand spells the barred element schema.</li>
</ul>
<h3>Changed</h3>
<ul>
<li><code>ArrayView</code> reports distinctness in three states, so an
array demanding a repeated element reads apart from one demanding
distinct elements.</li>
</ul>
<h3>Fixed</h3>
<ul>
<li><code>CanonicalSchema.negate</code> keeping a root self-reference in
the complement, where it points at the complement instead of the
source.</li>
<li>An <code>additionalItems</code> that tails no tuple keeping the
whole document unmodeled under Draft 2020-12.</li>
</ul>
<h2>[Ruby] Release 0.49.6</h2>
<h3>Added</h3>
<ul>
<li>Canonicalization of a negated <code>uniqueItems</code>, where the
complement demands a repeated element under the length floor two
elements imply.</li>
<li>Canonicalization of a negated array tuple, where each position's
complement stands under the length that reaches it.</li>
<li>Canonicalization of <code>contains</code> demands sharing no value,
where their counts add up into a length floor.</li>
<li><code>CanonicalSchema#negate</code> through references, where the
complement of the resolved target takes the reference's place.</li>
<li>Canonicalization of negated <code>propertyNames</code> and
<code>additionalProperties</code>, where the complement spells the
violating-key demand instead of a <code>not</code> residual.</li>
<li>Canonicalization of negated <code>additionalProperties</code> under
Draft 4, where the violating-key demand spells the closed property
map.</li>
<li>Canonicalization of <code>not</code> a reference, where the
complement of the resolved target takes the pointer's place.</li>
<li>Canonicalization of a negated <code>oneOf</code>, where the
complement spells the values no branch admits beside the values two
branches share.</li>
<li>Canonicalization of negated <code>items</code> under Draft 4, where
the violating-element demand spells the barred element schema.</li>
</ul>
<h3>Changed</h3>
<ul>
<li><code>ArrayView</code> reports distinctness in three states, so an
array demanding a repeated element reads apart from one demanding
distinct elements.</li>
</ul>
<h3>Fixed</h3>
<ul>
<li><code>CanonicalSchema#negate</code> keeping a root self-reference in
the complement, where it points at the complement instead of the
source.</li>
<li>An <code>additionalItems</code> that tails no tuple keeping the
whole document unmodeled under Draft 2020-12.</li>
</ul>
<h2>[Rust] Release 0.49.6</h2>
<h3>Added</h3>
<ul>
<li>Canonicalization of a negated <code>uniqueItems</code>, where the
complement demands a repeated element under the length floor two
elements imply.</li>
<li>Canonicalization of a negated array tuple, where each position's
complement stands under the length that reaches it.</li>
<li>Canonicalization of <code>contains</code> demands sharing no value,
where their counts add up into a length floor.</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/Stranger6667/jsonschema/blob/master/CHANGELOG.md">jsonschema's
changelog</a>.</em></p>
<blockquote>
<h2>[0.49.6] - 2026-08-06</h2>
<h3>Added</h3>
<ul>
<li>Canonicalization of a negated <code>uniqueItems</code>, where the
complement demands a repeated element under the length floor two
elements imply.</li>
<li>Canonicalization of a negated array tuple, where each position's
complement stands under the length that reaches it.</li>
<li>Canonicalization of <code>contains</code> demands sharing no value,
where their counts add up into a length floor.</li>
<li><code>CanonicalSchema::negate</code> through references, where the
complement of the resolved target takes the reference's place.</li>
<li>Canonicalization of negated <code>propertyNames</code> and
<code>additionalProperties</code>, where the complement spells the
violating-key demand instead of a <code>not</code> residual.</li>
<li>Canonicalization of negated <code>additionalProperties</code> under
Draft 4, where the violating-key demand spells the closed property
map.</li>
<li>Canonicalization of <code>not</code> a reference, where the
complement of the resolved target takes the pointer's place.</li>
<li>Canonicalization of a negated <code>oneOf</code>, where the
complement spells the values no branch admits beside the values two
branches share.</li>
<li>Canonicalization of negated <code>items</code> under Draft 4, where
the violating-element demand spells the barred element schema.</li>
</ul>
<h3>Changed</h3>
<ul>
<li><code>ArrayView</code> reports distinctness in three states, so an
array demanding a repeated element reads apart from one demanding
distinct elements.</li>
</ul>
<h3>Fixed</h3>
<ul>
<li><code>CanonicalSchema::negate</code> keeping a root self-reference
in the complement, where it points at the complement instead of the
source.</li>
<li>An <code>additionalItems</code> that tails no tuple keeping the
whole document unmodeled under Draft 2020-12.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/Stranger6667/jsonschema/commit/7888cb4bdc40fa07dd908a06802323d0f4ad134f"><code>7888cb4</code></a>
chore(ruby): Release 0.49.6</li>
<li><a
href="https://github.com/Stranger6667/jsonschema/commit/d57a7e0b176923ebc9e6b05f39b5a6ca66709420"><code>d57a7e0</code></a>
chore(python): Release 0.49.6</li>
<li><a
href="https://github.com/Stranger6667/jsonschema/commit/eaeb5b3ece16ec7cabca85bdf98c97601bcc1983"><code>eaeb5b3</code></a>
chore(rust): Release 0.49.6</li>
<li><a
href="https://github.com/Stranger6667/jsonschema/commit/d87ab9b5f95d1bd73f9147a339b0f856c12150bc"><code>d87ab9b</code></a>
fix: An <code>additionalItems</code> that tails no tuple keeping the
whole document unmo...</li>
<li><a
href="https://github.com/Stranger6667/jsonschema/commit/b31c40474d01f83881479f20806b8254b4250311"><code>b31c404</code></a>
feat: Canonicalization of a negated <code>uniqueItems</code>, where the
complement deman...</li>
<li><a
href="https://github.com/Stranger6667/jsonschema/commit/cbcf7553ded29cc7e1e47acd4637a8b7069c5318"><code>cbcf755</code></a>
build(deps): bump crates/jsonschema/tests/suite</li>
<li><a
href="https://github.com/Stranger6667/jsonschema/commit/d89a297b61a70cf040ac81febab580bffa8caea1"><code>d89a297</code></a>
build(deps): bump crate-ci/typos from 1.48.0 to 1.49.0</li>
<li><a
href="https://github.com/Stranger6667/jsonschema/commit/8285c9619cc2294f2cea3473bb93fc014a20d563"><code>8285c96</code></a>
feat: Canonicalization of a negated array tuple, where each position's
comple...</li>
<li><a
href="https://github.com/Stranger6667/jsonschema/commit/05d579c9ec40f1e2e5c2c84a29d484a48a40a567"><code>05d579c</code></a>
feat: Canonicalization of negated <code>items</code> under Draft 4,
where the violating-...</li>
<li><a
href="https://github.com/Stranger6667/jsonschema/commit/8c53ddb0f4adab9db1ca6a49b7de61546b29989a"><code>8c53ddb</code></a>
feat: Canonicalization of a negated <code>oneOf</code>, where the
complement spells the ...</li>
<li>Additional commits viewable in <a
href="https://github.com/Stranger6667/jsonschema/compare/ruby-v0.49.5...ruby-v0.49.6">compare
view</a></li>
</ul>
</details>
<br />

Updates `vergen-gitcl` from 10.0.1 to 10.0.2
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/rustyhorde/vergen/commit/8355721c8288a4853cb2f2d029ff8f217905c469"><code>8355721</code></a>
chore(deps): dependency updates and Windows lcov fix (<a
href="https://redirect.github.com/rustyhorde/vergen/issues/520">#520</a>)</li>
<li><a
href="https://github.com/rustyhorde/vergen/commit/ff70eebb23a0be1e15a23d07b5f91dc31572200c"><code>ff70eeb</code></a>
chore(msrv): bump MSRV to 1.96.0 (<a
href="https://redirect.github.com/rustyhorde/vergen/issues/519">#519</a>)</li>
<li><a
href="https://github.com/rustyhorde/vergen/commit/13e7cb6ae74143aa130f1dd12e89deb0d31dc353"><code>13e7cb6</code></a>
chore(rake): Rakefile.toml build pipeline and lint updates (<a
href="https://redirect.github.com/rustyhorde/vergen/issues/518">#518</a>)</li>
<li><a
href="https://github.com/rustyhorde/vergen/commit/a39ba617e1232a4da7381575558446dec8de5c69"><code>a39ba61</code></a>
chore: replace deprecated provenance lints with
implicit_provenance_casts (<a
href="https://redirect.github.com/rustyhorde/vergen/issues/517">#517</a>)</li>
<li><a
href="https://github.com/rustyhorde/vergen/commit/8d9813a74034ca9382371e98ed3b0cd5125ac14b"><code>8d9813a</code></a>
chore(rake): tweak macos daily (<a
href="https://redirect.github.com/rustyhorde/vergen/issues/516">#516</a>)</li>
<li><a
href="https://github.com/rustyhorde/vergen/commit/cd4af2aaecb5d76c42e79f2b793f9a319169ec50"><code>cd4af2a</code></a>
chore: add Rakefile.toml build pipeline and fix doc references (<a
href="https://redirect.github.com/rustyhorde/vergen/issues/515">#515</a>)</li>
<li>See full diff in <a
href="https://github.com/rustyhorde/vergen/compare/10.0.1...v10.0.2">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-11 15:52:51 -07:00
dependabot[bot] ec2aa4d154 chore: bump taiki-e/install-action from 2.85.8 to 2.85.10 (#3793)
Bumps
[taiki-e/install-action](https://github.com/taiki-e/install-action) from
2.85.8 to 2.85.10.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/taiki-e/install-action/releases">taiki-e/install-action's
releases</a>.</em></p>
<blockquote>
<h2>2.85.10</h2>
<ul>
<li>
<p>Update <code>uv@latest</code> to 0.12.2.</p>
</li>
<li>
<p>Update <code>tombi@latest</code> to 1.2.7.</p>
</li>
<li>
<p>Update <code>cosign@latest</code> to 3.1.3.</p>
</li>
<li>
<p>Update <code>coreutils@latest</code> to 0.10.0.</p>
</li>
<li>
<p>Update <code>cargo-rdme@latest</code> to 2.2.0.</p>
</li>
<li>
<p>Update <code>cargo-crap@latest</code> to 0.4.3.</p>
</li>
</ul>
<h2>2.85.9</h2>
<ul>
<li>
<p>Update <code>zola@latest</code> to 0.23.1.</p>
</li>
<li>
<p>Update <code>wild@latest</code> to 0.10.0.</p>
</li>
<li>
<p>Update <code>mise@latest</code> to 2026.8.2.</p>
</li>
<li>
<p>Update <code>just@latest</code> to 1.58.0.</p>
</li>
<li>
<p>Update <code>jaq@latest</code> to 3.1.1.</p>
</li>
<li>
<p>Update <code>cargo-nextest@latest</code> to 0.9.143.</p>
</li>
<li>
<p>Update <code>cargo-crap@latest</code> to 0.4.2.</p>
</li>
<li>
<p>Update <code>biome@latest</code> to 2.5.7.</p>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md">taiki-e/install-action's
changelog</a>.</em></p>
<blockquote>
<h2>[2.85.10] - 2026-08-07</h2>
<ul>
<li>
<p>Update <code>uv@latest</code> to 0.12.2.</p>
</li>
<li>
<p>Update <code>tombi@latest</code> to 1.2.7.</p>
</li>
<li>
<p>Update <code>cosign@latest</code> to 3.1.3.</p>
</li>
<li>
<p>Update <code>coreutils@latest</code> to 0.10.0.</p>
</li>
<li>
<p>Update <code>cargo-rdme@latest</code> to 2.2.0.</p>
</li>
<li>
<p>Update <code>cargo-crap@latest</code> to 0.4.3.</p>
</li>
</ul>
<h2>[2.85.9] - 2026-08-06</h2>
<ul>
<li>
<p>Update <code>zola@latest</code> to 0.23.1.</p>
</li>
<li>
<p>Update <code>wild@latest</code> to 0.10.0.</p>
</li>
<li>
<p>Update <code>mise@latest</code> to 2026.8.2.</p>
</li>
<li>
<p>Update <code>just@latest</code> to 1.58.0.</p>
</li>
<li>
<p>Update <code>jaq@latest</code> to 3.1.1.</p>
</li>
<li>
<p>Update <code>cargo-nextest@latest</code> to 0.9.143.</p>
</li>
<li>
<p>Update <code>cargo-crap@latest</code> to 0.4.2.</p>
</li>
<li>
<p>Update <code>biome@latest</code> to 2.5.7.</p>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/taiki-e/install-action/commit/6c6fd71fe4fb72c3697d269963d0e15df8adedad"><code>6c6fd71</code></a>
Release 2.85.10</li>
<li><a
href="https://github.com/taiki-e/install-action/commit/37cec23487191ef9aef8b1315865bd6dc584bef4"><code>37cec23</code></a>
Update zola manifest</li>
<li><a
href="https://github.com/taiki-e/install-action/commit/4914ea4fea5759852f8cda51753420130b883d65"><code>4914ea4</code></a>
Update <code>uv@latest</code> to 0.12.2</li>
<li><a
href="https://github.com/taiki-e/install-action/commit/0ce64163d455e35fedc5f5925221d4a71f05c990"><code>0ce6416</code></a>
Update <code>tombi@latest</code> to 1.2.7</li>
<li><a
href="https://github.com/taiki-e/install-action/commit/1f89e2fb52c482b53fcf083bf64b5abd5d6563ab"><code>1f89e2f</code></a>
Update osv-scanner manifest</li>
<li><a
href="https://github.com/taiki-e/install-action/commit/65ef13f21e6dd200e402682be3b1bc896f6aa862"><code>65ef13f</code></a>
Update kingfisher manifest</li>
<li><a
href="https://github.com/taiki-e/install-action/commit/9f6a5a8ec701c59d62ac1013b92714e7410b2cae"><code>9f6a5a8</code></a>
Update <code>cosign@latest</code> to 3.1.3</li>
<li><a
href="https://github.com/taiki-e/install-action/commit/df08c38f9c0580a749efefc712ba563ff2107db5"><code>df08c38</code></a>
Update <code>coreutils@latest</code> to 0.10.0</li>
<li><a
href="https://github.com/taiki-e/install-action/commit/dc7bb1f807a876bf372de55372b320860efe4019"><code>dc7bb1f</code></a>
Update <code>cargo-rdme@latest</code> to 2.2.0</li>
<li><a
href="https://github.com/taiki-e/install-action/commit/9970698e35256036b84ecfb8a70b90fe068a934b"><code>9970698</code></a>
Update <code>cargo-crap@latest</code> to 0.4.3</li>
<li>Additional commits viewable in <a
href="https://github.com/taiki-e/install-action/compare/v2.85.8...v2.85.10">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=taiki-e/install-action&package-manager=github_actions&previous-version=2.85.8&new-version=2.85.10)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-11 15:52:48 -07:00
Maximilian Roos 8bf8ae44a5 docs(release): scope the changelog length rule to bullets, on a three-point scale (#3798)
Two corrections to the changelog length rule
[#3774](https://github.com/max-sixty/worktrunk/pull/3774) added, both
surfaced by writing 0.72.0's section to it in
[#3778](https://github.com/max-sixty/worktrunk/pull/3778).

**The whole-release ~1,200-word ceiling is gone.** It gated a number
dominated by things that aren't prose: of that section's 1,314 measured
words, 320 are the bold entry titles and 76 are trailing PR links and
`thanks @…` credits, leaving 918 words of description. A release-level
total also scales with entry count, so a release with more changes reads
as more verbose without any single bullet being longer. The per-bullet
numbers are the rule; the release total was a second, worse proxy for
the same thing.

**One number became a scale: 35 typical, 60 for a dense entry, 80 for
the two or three headline entries.** A flat 40 put more than half of a
carefully-trimmed section in violation, which makes the rule noise
rather than a signal. 60 is roughly 43 words of description once the ~17
words of title, links, and credit are subtracted — two full sentences,
which is what an entry combining several PRs actually needs.

The measuring command reported the release total. It now reports the
average and the count over 60:

```
30 entries, 43 avg, 3 over 60
```

Worth flagging against this change: on 0.72.0's section those 3 *are*
the designated headline entries, so the outer bound flags nothing there.
The live signal is the average — 43 against a 35 target. The
verification gate flags entries over 60 to match.

> _This was written by Claude Code on behalf of max-sixty_

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 08:51:34 -07:00
Maximilian Roos 897f7d7193 fix(tests): extend the spawn pin to benches; correct the pin's cost note (#3792)
Follow-up to #3784, prompted by a history audit of the spawn-flake
family. Benches still spawned `env!("CARGO_BIN_EXE_wt")` — the uplifted
path the suite stopped spawning — so a concurrent build could fail a
bench run's spawns; all 11 sites now route through `wt_bin()` and
`test_wt_spawns_are_pinned` scans `benches/` too, making the rule
exceptionless. The pin's docstring also claimed the hardlink shares the
`deps/` artifact's inode: true where cargo uplifts by hardlink (Linux),
but macOS uplifts by copy-on-write clone — the pin keeps the clone,
whose blocks stay shared with `deps/` (measured: cloning the 70 MB
binary consumes 8 KB), so the no-cost conclusion stands with the
mechanism now stated per platform, plus why nothing sweeps the
directory.

> _This was written by Claude Code on behalf of max-sixty_

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 15:36:11 -07:00
Maximilian Roos f13ef96637 docs(worktree): correct three specs the selector refactor left stale (#3786)
Three specs in #3785's selector refactor describe mechanisms that change
removed or altered. Comment-only; no behavior change.

Each is wrong in a way a reader would act on rather than merely notice:

**`worktree_is_unusable`** closed with "`false` for a path git has no
registration for". The `!path.exists()` early return makes that untrue
as a claim about the return value — such a path answers `true` when it
is simply gone. The sentence was only ever about the `prunable` lookup,
so it now says so, and records that no caller reaches the combination
(all three take their path out of the listing).

**`normalize_selector`** named three call sites, two of which no longer
call it, and spent a paragraph explaining the string-comparison design
the refactor deleted — documenting a removed mechanism as current, which
is the worst of the three. It now names its one home and says what made
a single home possible.

**`ResolvedTarget::selector`** credited only a rewrite with taking the
path arm off, missing `--create`, which takes it off without rewriting
anything.

The first was raised on #3785 after I had worked its other threads, so
it never got a reply there. The other two came from re-reading the
neighbouring specs while fixing it.

<details>
<summary>Why these were worth a change rather than a note</summary>

A spec that describes a deleted mechanism is worse than no spec:
`normalize_selector`'s explained why each assembly of the resolution
ladder had to normalize for itself, which was true before `Selector`
carried "may this token name a path?" as a fact rather than a string
comparison. A reader adding a fourth entry point would have followed it
and re-introduced the per-site normalization the refactor removed.

</details>

> _This was written by Claude Code on behalf of max-sixty_
2026-08-09 13:18:42 -07:00
Maximilian Roos 715af4cd72 fix(tests): pin the spawned wt binary against concurrent cargo uplifts (#3784)
Two test-suite flakes fixed at the root, both dependencies on machine
load and concurrent builds.

**Concurrent-cargo spawn `NotFound`.** Cargo uplifts `target/debug/wt`
by removing the path and recreating it, so a second `cargo` against the
same target directory leaves the binary every test spawns absent for a
fraction of a millisecond per rebuild — the one-off `NotFound` spawn
failures that pass on re-run. `wt_bin()` now returns a hardlink pinned
under `target/debug/wt-test-bin/<mtime>-<len>/`: the uplift unlinks only
the uplifted name, so the pin keeps serving the observed binary through
any number of concurrent rebuilds, at no disk cost beyond the `deps/`
artifact whose inode it shares. `test_wt_spawns_are_pinned` keeps every
spawn routed through it. Reproduced by re-creating the uplift every 200
ms alongside a full `cargo nextest run`: 302 of 4583 tests failed before
this change (147 as direct `NotFound` spawn panics, five of them
byte-identical to the original shell-wrapper report), 4585 of 4585 after
— with an unrelated external cargo also rebuilding `wt` mid-validation,
absorbed the same way.

**`--reap` probe races.** `test_remove_reap_kills_process` predicted the
reap guard's verdict with its own `lsof`/`ps` snapshot, and under load
either probe's spawn can stall past the 5 s bound, whose fail-safe empty
result flips the outcome — a prediction `wt` then contradicts, or `wt`
reporting "No processes to reap" for a live child. The prediction now
reads the session's controlling terminal directly (`/dev/tty` opens iff
the session has one — the property the child inherits at spawn), the
probe timeout is env-pinnable (`WORKTRUNK_TEST_PROBE_TIMEOUT_MS`, set to
60 s in the static test baseline; production keeps its 5 s bound), and
the discovery poll uses the suite's 60 s presence-poll convention.
Looped 15/15 green at load average ~60, where the previous shape failed
2/10.

Not covered here, noted as follow-ups: benches still spawn
`env!("CARGO_BIN_EXE_wt")` directly (same hazard, separate runner,
outside the guard's scan), and the reap "spared" branch has no
deterministic end-to-end test (needs a PTY-held child).

> _This was written by Claude Code on behalf of max-sixty_

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 07:06:08 -07:00
Maximilian Roos cf822f75cf fix(worktree): guard a registered path that no longer holds its worktree (#3785)
A registered worktree's path was trusted to still hold that worktree.
Three defects followed, one of them destroying data, and the check that
would have caught them — where it existed at all — was `Path::exists()`.

## `wt remove --force` deleted an unrelated repository

A clone that came to sit at a stale registration's path was removed
whole, including uncommitted work and, for a repo never pushed, the only
copy of its objects. wt's own hint routed the user there: the dirty gate
reads `git status` in that directory, reports the occupant's changes as
this worktree's, and offers `--force` as the cure.

```console
$ wt remove feature
✗ Cannot remove worktree: feature has uncommitted changes
  ?? precious.txt                                          # ← the other repo's file
↳ ... to lose uncommitted changes, run wt remove --force feature
```

git refuses that same removal, `--force` included (`validation failed …
is not a .git file`). Worktrunk's fast path renames the directory into
trash rather than asking git to, so git's validation never ran.
`ensure_belongs_to_repo` makes it, comparing the directory's git dir
against this repository's: a linked worktree's sits under
`<common>/worktrees/`, the main worktree's *is* the common dir, anything
else answers to someone else. One comparison covers both worktree kinds
and also rejects a `.git` file pointing at another repo, which git's
shape test accepts.

It runs at planning, ahead of the dirty gate, and again at the rename
for callers that stage without planning. Now:

```console
$ wt remove --force feature
✗ Directory @ ../repo.feature is not this repository's worktree
↳ Removing it could destroy unrelated data; move the directory aside, then run git worktree prune
```

## A recreated worktree directory leaked git's exit 128

`wt switch`, `wt merge`, and `wt step push` walked into `git rev-parse
--git-dir failed (exit 128)`. Two of them probed `Path::exists()` first,
which a deleted-and-recreated directory passes; the third asked nothing.

`worktree_is_unusable` is the union of both tests, because neither
implies the other. `exists()` catches the absent directory; git's
`prunable` catches the recreated one. `prunable` alone is *not* the
wider test it looks like — git withholds the attribute from a **locked**
worktree even when its directory is gone, since prunability is its
pruning policy and a lock means "don't prune this":

```console
$ git worktree list --porcelain          # wt2 locked, all three directories removed
worktree /tmp/ptest/wt1
prunable gitdir file points to non-existent location
worktree /tmp/ptest/wt2
locked removable media                   # ← no prunable line
worktree /tmp/ptest/wt3
prunable gitdir file points to non-existent location
```

A locked worktree on an unmounted volume is exactly what
`prepare_worktree_removal`'s lock guard exists for, so a `prunable`-only
test would read it as healthy. All three commands now give the message
the merely-deleted case already gave.

`wt remove` keeps `exists()`, deliberately: there it is the precondition
for the cleanup path rather than a health test, since
`prune_worktree_entry` unregisters via `git worktree remove`, which
skips validation only while the directory is absent. No scoped git
command clears the recreated case, so it reports and names the repo-wide
`git worktree prune` that does.

## `wt switch docs/` missed a branch sitting right there

Git's ref format forbids a trailing `/`, so the branch lookup never had
a candidate — and shell completion produces exactly that spelling
whenever a `docs` directory sits beside the branch. Selectors are
normalized before resolution.

## Resolving selectors through one ladder

The three fixes landed in three of the four places that assemble "expand
shortcuts, try the branch, try the path, classify the failure" by hand.
Each gated its path attempt on "did something rewrite this token?",
answered by comparing an expansion's output against its input:

| where | the comparison |
|---|---|
| `resolve_worktree` | `branch == name` |
| `plan_switch` | `target.branch == branch` |
| `target_worktree_at_path` | `target.filter(\|t\| *t == resolved)` |
| `resolve_base_ref` | `resolved == base` |

That is a fact the rewriting step knows, re-derived downstream from its
output, and it is wrong in both directions. A shortcut can expand to the
token it was given — `-` pointing at the branch you are already on — and
string equality reads that as a literal, turning the path arm back on
for a token nobody typed. Normalization breaks it the other way, which
is why the trailing-separator fix needed threading through three call
sites.

`Selector` carries the fact instead: `expand_shortcut` reports whether
it fired, `wt switch` reports its `pr:`/`mr:` dispatch and remote-prefix
strip, and `names_a_path()` replaces all four comparisons.
`resolve_selector` is the ladder, and `plan_switch` expands into it
rather than re-implementing its phases.

`names_a_path()` gates both path steps together — the worktree-by-path
lookup and the directory verdict — which is what `wt switch --create`
needs: the argument names a branch to create, so `branch_only()` takes
the arm off at the producer rather than each consumer re-testing
`create`.

It also reaches the directory verdict, so `ResolvedWorktree` gains
`NoWorktreeAtPath` and the four sites that called `path_selector_error`
themselves stop re-deriving it. The docstring defending that laziness
didn't survive checking — the function returns on `is_valid_branch_name`
before touching the filesystem, so every ordinary branch name already
short-circuited.

|  | before | after |
|---|---|---|
| `normalize_selector` call sites | 3 | 1 |
| `path_selector_*` call sites | 4 | 2 |
| "was it rewritten?" comparisons | 4 | 0 |

## Navigating the diff

- `src/git/repository/mod.rs` — `Selector`, `normalize_selector`, the
new `ResolvedWorktree` variant.
- `src/git/repository/worktrees.rs` — `expand_shortcut`,
`expand_selector`, `resolve_selector`, `usable_worktree_for_branch`.
- `src/git/repository/working_tree.rs` — `ensure_belongs_to_repo`, the
ownership check.
- `src/git/remove.rs`, `src/commands/repository_ext.rs` — where it gates
removal, and why before the dirty gate.
- Call sites: `commands/worktree/switch.rs`,
`commands/worktree/push.rs`, `commands/merge.rs`, `commands/remove.rs`,
`git/repository/config.rs`.

## Size

Comments and docstrings are the largest share: the ownership check and
the four conditions behind the directory verdict all look like things to
simplify away, so the reason each exists is recorded where it's
enforced.

| | + | − |
|---|---:|---:|
| Production code | 277 | 142 |
| Comments & docstrings | 320 | 75 |
| Tests | 325 | 8 |
| Snapshots | 186 | 0 |
| Docs | 6 | 0 |
| **Total** | **1114** | **225** |

## Testing

Seven new tests. The data-safety one drives the real binary and asserts
the filesystem afterwards, not just the exit code — removal stages by
rename and deletes in a detached process, so a passing exit would not
have caught a staged-then-deleted tree. The others cover the recreated
directory (switch and remove), the trailing separator, `--create`
against a worktree registered at that path, and, at the unit boundary,
the four states of `worktree_is_unusable` — healthy, absent,
locked-and-absent, recreated — and the selector's path-ness, including
the degenerate case string equality got wrong. Each new test was
confirmed to fail with its fix reverted.

One more covers an omitted merge target in a repo whose default branch
can't be determined. `^` had a test for that error; the omitted-target
route to the same message had none. The gap predates this branch — the
closure is byte-identical to the one it replaces and codecov records
those lines as missed at the base commit too — but relocating them into
`resolve_target_selector` re-counted them as patch lines, which is what
surfaced it.

Local gate green: 4593 tests, lints, doctests, rustdoc under
`-Dwarnings`.

<details>
<summary>Behavioral matrix, verified against a build</summary>

```console
docs/ (trailing sep)      ▲ Worktree for docs @ ../repo.docs
detached by path          ▲ Worktree for detached worktree @ ../repo.det
leftover dir              ✗ No worktree @ ../repo.leftover
recreated dir             ✗ Worktree directory missing for rec
shortcut ^                ▲ Worktree for main @ ../repo
remove leftover           ✗ No worktree @ ../repo.leftover
--base docs/              ✓ Created branch nf from docs
foreign-repo remove       ✗ Directory @ ../repo.frn is not this repository's worktree
                             precious.txt survives
```

</details>

<details>
<summary>Also swept, and one thing left alone</summary>

Three more instances of the same shape, fixed here:

- `resolve_base_ref` was the fourth copy of the comparison, so `--base
docs/` now resolves too.
- `hint_for_repo` suggested `wt switch ^` after an existence probe a
recreated directory passes, pointing at a worktree the switch then
refuses.
- The pre-switch hook's `target` var used the bare shortcut expander, so
a hook saw `docs/` where the switch resolved `docs`.

The identical unborn/stale default-branch block in
`require_target_branch` and `require_target_ref` is extracted. The rest
of that pair differs in its existence predicate, extra arms, and final
error; sharing it would cost more in parameters than the duplication
does.

Left alone: `live_sibling_checkout` decides whether another worktree
still holds a branch during removal, and also uses `exists()`. Switching
it to `prunable` would make branch deletion *more* likely in a corner
case where the detached path already answers the other way. That is a
data-safety surface and a separate decision.

</details>

> _This was written by Claude Code on behalf of max-sixty_
2026-08-09 06:23:21 -07:00
Worktrunk Bot a41cdb826c docs(config): name every template variable format_path builds (#3782)
Found by the nightly survey while reviewing
`src/config/user/accessors.rs`.

`UserConfig::format_path`'s `# Arguments` block doesn't match what the
function builds. It documents `{{ main_worktree }}`, `{{ branch }}`, and
`{{ owner }}`, and describes the `repo` parameter as being for "template
function access" — but the function also inserts `{{ repo }}` (from
`main_worktree`, not from `repo`) and `{{ repo_path }}` (read off
`repo`). Both omissions are in `default_worktree_path()` in the same
file — `"{{ repo_path }}/../{{ repo }}.{{ branch | sanitize }}"` — so
the docstring omits two of the three variables the default template
uses.

The `{{ owner }}` line was also a bare bullet in the middle of the
parameter list, describing a template variable rather than an argument,
and it didn't note that `owner` is only inserted when
`primary_remote_parsed_url()` returns a URL it can parse.

This corrects the parameter descriptions, moves the template-variable
note out of the argument list, and links the user-facing variable list
at
[worktrunk.dev/config](https://worktrunk.dev/config/#worktree-path-template)
rather than duplicating it here (verified: the page returns 200 and
carries that anchor).

No test: the change is a doc comment only, with no behavior to exercise.
`cargo fmt --check` and `cargo doc --no-deps -p worktrunk` are clean.

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
2026-08-09 04:59:26 -07:00
Worktrunk Bot ec0f3a344f fix(output): strip stderr color on a pipe where std's eprint! kept it (#3771)
## Problem

`worktrunk::styling`'s `eprint!` is anstream's and strips ANSI when
stderr isn't a terminal; std's prelude macro of the same name keeps it.
A file that imports one but not the other — or neither — gets a mix, and
adjacent lines of the same message block disagree about whether a
redirected stderr carries escapes.

`wt list 2>&1 >/dev/null | cat -v` in a repo with a deprecated `[ci]`
block, on `main`:

```
^[[33mM-bM-^VM-2^[[39m ^[[33mProject config: ^[[1m[ci]^[[22m is deprecated in favor of ^[[1m[forge]^[[22m^[[39m
M-bM-^FM-3 To see details, run wt config show; to apply updates, run wt config update
```

The warning is `eprint!("{warnings}")` at `src/config/deprecation.rs`,
which resolved to std's macro; the hint directly beneath it is the
`eprintln!` imported from `styling` four lines later. Anyone redirecting
`wt` narration to a file gets escapes on one line and not the next.

This is the stderr counterpart of what #3746 and #3766 fixed on stdout,
and it went unnoticed for the reason named in `verbatim.rs`'s own
docstring: the suite sets `CLICOLOR_FORCE=1`, which forces color on
*both* printers, so no snapshot could disagree no matter which macro was
in scope. `output_system_guard` doesn't cover it either — it scans for
`print!`/`println!` tokens under `src/commands/`, not for which
`eprint!` a file imported.

## Solution

The rule is now structural rather than per-site.
`check_stderr_macros_come_from_styling` in `output_system_guard.rs`
walks every `.rs` file under `src/` and flags a bare
`eprint!`/`eprintln!` whose file lacks the matching `worktrunk::styling`
import. A call satisfies it either way — importing the macro, or
qualifying the call as `styling::eprintln!(…)`, which several files
(`git/repository/mod.rs`, `config/user/mod.rs`,
`commands/config/alias.rs`) already do. Two files are allowlisted with a
reason: `testing/mock_stub.rs` relays a stub's captured stderr verbatim,
so its bytes are fixture data; `remove_dir.rs`'s one call is a
`#[cfg(test)]` skip diagnostic, not narration a user redirects.

Reverting the source fixes below makes it name exactly those five lines
and nothing else.

The sites it fixes:

- `src/config/deprecation.rs` — the deprecation warning block above.
- `src/commands/config/update.rs` — the `format_update_preview` block
shown before `wt config update`'s prompt, reachable with a tty stdin and
a redirected stderr.
- `src/output/prompt.rs` — the `[y/N/?]` prompt; its blank-line
`eprintln!` was already explicitly qualified as
`worktrunk::styling::eprintln!`, so the two disagreed within four lines.
Both now come from one import.
- `src/output/global.rs` — the file the first scan couldn't see, because
that scan looked for "imported `eprintln` but not `eprint`" and this
file imports neither. Its four styled `eprintln!` calls
(`print_outdated_shell_wrapper_hint_once`, `warn_retired_exec_once`,
`warn_exec_scrubbed_once`) all resolve to std's, so a user mid-upgrade
running `wt … 2>log` gets `ESC[…m` around the shell-wrapper repair hint.
The module's own docstring already claimed the contract the code didn't
have — *"Regular output still uses `eprintln!`/`println!` directly (from
`worktrunk::styling` for color support)"*. One added import makes it
true; under the suite's `CLICOLOR_FORCE=1` no snapshot moves.
- `src/commands/for_each.rs` — the pre-spawn ANSI reset.
`output/handlers.rs` runs the identical three lines
(`stderr().flush()?`, `eprint!("{}", anstyle::Reset)`,
`stderr().flush().ok()`) immediately before building its `Cmd`, but
through anstream's `eprint` *and* anstream's `stderr`; `for_each` used
std's for both, so the same operation wrote a literal `ESC[0m` into a
redirected stderr where `handlers` dropped it. Both halves move together
— the flushes have to name the stream the reset was written to, so
switching `eprint!` alone would flush std's handle while anstream's
buffer held the write. The `std::io::stderr()` handed to `Stdio::from`
four lines down is a different thing and stays.

## Tests

`test_stderr_narration_strips_ansi_when_piped` in
`output_system_guard.rs`, alongside the closed-consumer test #3766
added. It clears `CLICOLOR_FORCE` and sets `NO_COLOR` (which only
anstream honors), triggers the `[ci]` deprecation, and asserts stderr
carries the warning and no `\x1b`. Confirmed to fail on the pre-fix
source with exactly the escapes quoted above, and to pass with it.

That test proves what the property buys at one site;
`check_stderr_macros_come_from_styling` is what holds it at all of them.
No runtime test can: the suite's `CLICOLOR_FORCE=1` forces color on both
printers, so a snapshot agrees whichever macro is in scope, and the
property is about every stderr write in the binary rather than any one
path.

The module docstring is updated for both — the `Allowed:` list no longer
reads flatly as "`eprintln!` / `eprint!` (stderr is safe)", which was
the sentence someone skims before making this exact mistake.

## Verification

`cargo clippy --all-targets`, `cargo fmt --check`, `cargo test --lib
--bins` (2,454 passed), and the integration suite (1,961 passed) all run
locally. One integration test fails in this sandbox and is unrelated:
`test_copy_ignored_preserves_file_executable_permissions` expects `0644`
and sees `0664`, because the sandbox's umask is `002` rather than the
runner's `022` (confirmed by `umask` → `0002`). It touches none of these
files; CI will confirm.

---------

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
2026-08-09 04:59:24 -07:00
Worktrunk Bot ec574b6c35 ci: bump pinned cargo-nextest 0.9.143 and worktrunk 0.72.0 (#3783)
## Summary

Weekly CI pin check found the following drift (these inline `version:`
strings are invisible to Dependabot — it follows `Cargo.toml` deps and
`uses: foo@vN` refs, not pinned versions inside `with:` blocks):

- `cargo-nextest`: 0.9.140 → 0.9.143 (MSRV 1.91, compatible with our
1.96) — pinned in `coverage.yaml`, `actions/test-setup`, and
`actions/claude-setup`; all three moved together.
- `worktrunk`: 0.71.0 → 0.72.0 (MSRV 1.96, compatible with our 1.96) —
the CI-installed `wt` that runs `wt hook pre-merge`, bumped to the
current release; pinned in `ci.yaml` (×2) and `nightly.yaml`.

## Already up to date

- `cargo-affected`: 0.4.0, `cargo-insta`: 1.48.0, `cargo-llvm-cov`:
0.8.7, `cargo-msrv`: 0.19.3, `cargo-udeps`: 0.1.61, `lychee`: 0.24.2
- `hustcer/setup-nu` (nushell): 0.114.1 — matches the current nushell
release across all four call sites
- Runner images: ubuntu-24.04, windows-2022

## Notes

- windows-2022 stays pinned
([actions/runner-images#12677](https://github.com/actions/runner-images/issues/12677)
— windows-2025 lacks the D: drive).
- **cargo-nextest 0.9.143 has nothing config-facing to adjust.** The
0.9.140 → 0.9.143 range is dynamic-library-search-path fixes (build-dir
layout v2, `build.build-dir`, `[[example]]` targets), archive filterset
fixes, an opt-in `junit.report-skipped` setting we don't set, and a
listing progress bar. The one behavior change — ordering the Cargo
artifact directory ahead of `deps` on the dylib search path, matching
Cargo since 1.93 — doesn't affect this repo, which links no `dylib`
dependency.
- **worktrunk 0.72.0 is only exercised through `wt hook pre-merge`** in
these three jobs, so the release's `wt merge` / `wt step push` two-tree
changes and the `branch_outcome` JSON rename don't reach CI. The
relevant one is the opposite direction: 0.72.0 fixes `wt` writing ANSI
to a pipe and exiting 101 on `Broken pipe`, which is exactly the non-tty
shape these jobs run in.
- **`zola` is deliberately left at 0.22.1** — see below.

## Deferred: zola 0.22.1 → 0.23.2

`taiki-e/install-action`'s `tool: zola@0.22.1` in `check-docs` (and the
matching pin in `publish-docs.yaml`) is behind, but 0.23.0 is not a
routine bump. Upstream calls it "probably the most breaking version of
Zola that will happen"
([CHANGELOG](https://github.com/getzola/zola/blob/master/CHANGELOG.md)):
**shortcodes are removed entirely** and Tera is updated to v2 with its
own [migration
guide](https://github.com/Keats/tera/blob/master/MIGRATION.md).
`docs/templates/shortcodes/` and `docs/templates/macros.html` both
exist, so this needs a real docs-site migration rather than a
version-string change, and it would land in the same PR as the live-site
publish pin. Left for a separate change; flagging it here so it isn't
silently skipped each week.

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
2026-08-09 04:56:50 -07:00
Maximilian Roos f0d9c725a4 docs(tend): bound the data-loss hold to what the deletion can reach (#3779)
The data-loss hold in the tend review reference was purely lexical: any
deletion token appearing in a diff matched, with no bound on what the
deletion could reach. On #3776 that meant holding on `rm -rf "$TEMP"`
against a `mktemp -d` the same script created three lines earlier, and
on `rm -f` against the container's apt lists inside `task setup-web`.
Neither can touch a worktree.

The trigger list is unchanged. Two things now bound it:

- The first clause fires on adding a deletion or on widening what an
existing one can delete, so restructuring one that keeps firing on the
same or fewer paths is no longer a match.
- A new paragraph names the surface the hold protects: a worktree, a
repository, a branch, uncommitted work, or a file worktrunk writes on
the user's behalf — the inventory in CLAUDE.md § Data Safety. A deletion
reaches none of it when everything under its target can be regenerated,
or when it is confined to a throwaway CI or development environment.

The carve-out turns on contents, not on how recently the directory was
created. `wt step promote` is the in-repo counterexample to the naive
version: `stage_ignored` moves both worktrees' ignored files into
`.git/wt/staging/promote` and `distribute_staged` removes the directory
at the end of the same operation, so a directory the code just created
holds the user's only copy in between — which is why
`check_leftover_staging` refuses to proceed when it survives.

Checked against cases that must still hold: `remove_dir_all` on a
worktree path, `git clean -fdx` in a shipped hook, and a Taskfile step
deleting `.git/wt/` state all match, since none of those targets is
regenerable or confined to a throwaway environment.

This file lists `rm -rf` and its neighbours as review triggers, so the
diff edits a file that contains one. It executes nothing.

`cargo run -- hook pre-merge --yes` passes: 4583 tests, 1 skipped.

> _This was written by Claude Code on behalf of max-sixty_

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 17:09:49 -07:00
Maximilian Roos 4d7f8c944e fix(env): drop Intel macOS from the flake, and install pwsh and jq for web setup (#3776)
Three follow-ups from #3768, plus a bug the verification turned up.

**The flake stops declaring outputs for `x86_64-darwin`.** nixpkgs drops
Intel macOS in 26.11: evaluating anything for that system against
`nixos-unstable` (`26.11pre-git`) throws. The rev `flake.lock` pins is
26.05-era, so it still evaluates today, carrying nixpkgs' own warning
that "26.05 will be the last release to support x86_64-darwin". Naming
three systems rather than `eachDefaultSystem` drops Nix support for
Intel Macs now, ahead of that bump. Release binaries are untouched:
`dist-workspace.toml` still ships `x86_64-apple-darwin` and nightly
still tests it on `macos-15-intel`.

**`git` leaves the devShell's `packages`.** It arrives with the
`checks`, which crane folds in via `inputsFrom`, the same mechanism that
already supplied `python3`, `procps` and `lsof`.

**`task setup-web` installs `pwsh` and `jq`.** Without them Claude Code
web can't run `--features shell-integration-tests`, which is what the
pre-merge gate runs. PowerShell comes from the release `.deb` rather
than the tarball, because pwsh aborts at startup without libicu and only
the `.deb` declares that dependency for apt to resolve. The verification
loop runs each tool instead of looking for it on PATH, since the tarball
install left a `pwsh` that was on PATH and still aborted.

**A `set -e` abort found while testing that.** The `sources.list.d`
cleanup was an `&&` chain, and under `set -e` a chain ending false takes
the whole task down. This one ends false on an unmatched glob and on a
`.list` file with no `[` line, so setup was dying before it installed
anything on a stock Debian box as well as an empty one. It's an `if`
now.

## Verification

No `nix` on the machine this was written on, so the flake was checked in
a `nixos/nix` container and the Taskfile block in an amd64 Debian one.

<details>
<summary>flake: three systems evaluate, x86_64-darwin is gone, git
survives its deletion</summary>

```
== devShell evaluates per system ==
x86_64-linux     OK   g172vwl0g339zsxx9l6mz5pca6w9jbcx-nix-shell.drv
aarch64-linux    OK   pgvq71zs48bx3naddncms954jyqpl0bl-nix-shell.drv
aarch64-darwin   OK   d17q1772si0x0hj1lgpnin8wiq4zlpr2-nix-shell.drv
x86_64-darwin    FAIL: flake does not provide attribute 'devShells.x86_64-darwin.default'

== systems the flake declares ==
["aarch64-darwin","aarch64-linux","x86_64-linux"]

== tools in the x86_64-linux devShell ==
  git: present        jq: present         nushell: present
  powershell: present python3: present    procps: present
  lsof: present       fish: present       zsh: present
  bash: present       gh: present         pre-commit: present

== nixfmt --check flake.nix ==
  clean (exit 0)
```

The x86_64-darwin claim, checked against nixpkgs directly rather than
inferred:

```
== nixos-unstable lib.version ==
"26.11pre-git"
== x86_64-darwin eval on nixos-unstable ==
  error, pointing at release-notes#x86_64-darwin-26.11
== x86_64-darwin eval on the pinned rev (flake.lock) ==
evaluation warning: Nixpkgs 26.05 will be the last release to support x86_64-darwin
"hello-2.12.3"
```

Not verified: nothing was built, only evaluated. The nightly `nix-flake`
job runs `nix flake check` on PRs touching `flake.nix`, which covers
that on x86_64-linux.

</details>

<details>
<summary>setup-web: the block run under Task's own interpreter, in an
amd64 Debian container</summary>

The edited block was extracted into a minimal Taskfile and run by `task`
itself, so mvdan/sh parses it rather than bash. `curl` and nushell are
container prereqs, not part of what's under test.

```
=== running the extracted block under Task ===
Installing shell-integration test dependencies...
pwsh installed
bash available
zsh available
fish available
nu available
pwsh available
jq available
task exit: 0

=== does the installed pwsh actually run? ===
7.6.4
jq-1.6
/usr/bin/pwsh

=== rerun is idempotent ===
Installing shell-integration test dependencies...
bash available   zsh available   fish available
nu available     pwsh available  jq available
```

Two earlier runs are why the shape changed. The first died at the
`sources.list.d` glob. The second installed PowerShell from the release
tarball: every tool reported "available" and `pwsh` then aborted with
`Couldn't find a valid ICU package installed on the system`, which is
what moved the install to the `.deb` and the check from `command -v` to
`--version`.

</details>

`cargo run -- hook pre-merge --yes` passes: 4574 tests, 1 skipped.

## Notes

`task setup-web` still requires nushell to be present rather than
installing it, unchanged here.

> _This was written by Claude Code on behalf of max-sixty_

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 14:56:07 -07:00
Maximilian Roos d7fc65ba80 docs(changelog): rewrite 0.72.0's notes to the length and ordering standard (#3778)
0.72.0's notes ran 32 entries at 101 words each.
[#3774](https://github.com/max-sixty/worktrunk/pull/3774) replaced "1–3
sentences" with a word ceiling and made reader interest the second
ordering dimension inside each fixed section; this applies both to the
release that motivated them, since the next release calibrates against
this section.

Every user-facing claim, PR link, and contributor credit carries over —
the only dropped `@` is `@attacker.example` inside an example that went
with its paragraph. Two pairs of related bullets are combined: the
piped-stdout panic with the piped-color fix, and the three shell-wrapper
cleanup PRs.

Reordering, by section:

- **Improved** now leads with the forge-host classification restored to
self-hosters, then the `[projects]` glob entry, then the `wt merge`
target-sync change that used to lead. The old first entry was the
release's longest at 279 words and opened on its own mechanism.
- **Fixed** leads with the piped-output panic and the CI status that
read a running check as passed — the two most readers hit — and ends
with the display and tally fixes.
- **Internal** entries are one sentence each.

### Where it lands against the three ceilings

The skill sets three numbers. Measured with its own `awk` command: **30
entries, 1,314 words, 43 average.**

| | Ceiling | Here |
| --- | --- | --- |
| Per bullet | 40 | 17 entries above it, 13 at or under |
| Headline band | 2–3 entries up to 80 | 3 (80, 78, 70) |
| Whole release | ~1,200 | 1,314 |

The whole-release number is 9% over and the per-bullet count misses on
more than half the entries, so the standard is partly met, not met. What
the measure counts is worth knowing before reading those two as prose
bloat: of the 1,314 words, 320 are the bold entry titles and 76 are
trailing PR links and `thanks @…` credits, leaving 918 words of actual
description — **31 per entry**. Hitting 1,200 with 30 entries and three
headliners means a typical bullet of ~34 measured words, or roughly 22
words of prose after a 10-word title. That is tighter than the 40 reads,
and it may be the ceiling rather than this section that wants adjusting;
I've left the skill alone here rather than widen this PR.

The published [v0.72.0 release
body](https://github.com/max-sixty/worktrunk/releases/tag/v0.72.0) is
updated to match; its 17 assets and install sections are untouched. The
CHANGELOG at the `v0.72.0` tag keeps the original text, so this is a
`main`-only edit.

> _This was written by Claude Code on behalf of max-sixty_

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 14:22:23 -07:00
Maximilian Roos c37d07d87a refactor(output): resolve color in one place via anstream's global ColorChoice (#3777)
`wt` had five independent mechanisms deciding whether escape sequences
reach the consumer: the anstream macros, a `println_verbatim!` macro
that bypassed them, `ProgressiveTable`'s raw stdout writes, clap's
per-command `ColorChoice`, and a raw `anstyle::Reset` in
`terminate_output`. This collapses them onto the ecosystem primitive:
`anstream::ColorChoice::write_global()`, which `AutoStream::choice`
consults before tty detection — the mechanism behind every
`--color=always` flag. Color now resolves in exactly one place, and code
emits styles freely while the stream decides what survives.

For navigating the diff:

- **Payload surfaces declare their choice once.** The statusline
(`src/commands/statusline.rs`) declares `Always` — a shell prompt or
Claude Code captures and re-renders its line, so the escapes are the
answer, not presentation; `Always` outranks `NO_COLOR` per the
no-color.org convention, preserving shipped behavior byte-for-byte.
`--help-page` declares `Always`/`Never` per mode and `--help-md`
declares `Never` (`src/help.rs`). `println_verbatim!` is deleted; these
surfaces print through the ordinary macros.
- **`help.rs` keeps one color mapping.** The five per-command
`cmd.color(...)` calls were inert — clap consults `color_when` only in
`err.print()`, and every path here uses `err.render()` — so the embedded
`--help` reference block always renders `.ansi()` and the stream strips
or passes it. All 16 generated doc outputs (`--help-page`, `--plain`,
`--help-md` across commands) are byte-identical before/after.
- **`ProgressiveTable` consults the same choice rather than writing
through a stream** (`src/commands/list/progressive_table.rs`) —
anstream's strip adapter would eat its cursor-control CSI, so it reads
`AutoStream::choice` once and strips content with
`anstream::adapter::strip_str`, the exact transform the buffered path
applies. `NO_COLOR` now works on default `wt list`.
- **One truncator** (`src/styling/line.rs`):
`display::truncate_to_width` — which cut escape-blind — is merged into
`truncate_visible`, which now trims trailing whitespace before the `…`
and appends a reset only when the kept prefix carries an escape. Styled
output is byte-identical (`ansi_cut`'s style closers both block the trim
and trigger the reset); one snapshot line changes, a plain branch name
losing a needless `[0m`.
- **stderr joins the model**: `-v` diagnostics route through
`AutoStream::auto(stderr)` (`src/logging.rs`), so `NO_COLOR` and piping
reach them, and the lone `ceprintln!` (std stderr, hardcoded escapes)
becomes the anstream macro (`src/main.rs`).
- **The guard widens**
(`tests/integration_tests/output_system_guard.rs`): the stdout scan
covers all of `src/` rather than `src/commands/`, and the new
`test_color_follows_the_consumer` pins six unforced-color cases — the
suite's global `CLICOLOR_FORCE=1` previously made anstream's strip/pass
decision invisible to every test.

Behavior changes, all in the strip direction: `NO_COLOR` and piped-strip
now reach progressive `wt list`, `-v` stderr diagnostics, and the clap
error tips; `terminate_output`'s reset is stripped when color is off;
truncation no longer leaves a `[0m` on plain text or a space before the
ellipsis. The statusline's `Always` semantic is now pinned by test
rather than incidental.

Testing: the full pre-merge gate is green (4581 tests) plus the
feature-gated PTY picker suite; `test_color_follows_the_consumer` covers
the unforced matrix, and `test_list_progressive_honors_no_color` pins
the progressive path on a real PTY (no SGR under `NO_COLOR`,
cursor-control CSI still flowing); docs-sync verifies the generated
pages byte-for-byte. The `writing-user-outputs` skill paragraph that
described `println_verbatim!` now describes the `write_global()`
declaration.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

> _This was written by Claude Code on behalf of max-sixty_

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-08 13:43:43 -07:00
Maximilian Roos 00ab0ffa26 Canonicalize benchmark fixtures and variants (#3761)
Benchmark fixtures still encoded the benchmark that first needed each
repository state, which left overlapping recipes and variants after the
earlier harness consolidation. This change reduces the fixture catalog
to two provenance-based bases: `Generated` builds an ordinary Git
repository locally, while `Imported` copies the pinned `rust-lang/rust`
corpus. Worktree, branch, and remote-ref populations remain parameters
on `Generated`; prune candidates and backdrop are overlays that work
with either base.

The generated base deliberately combines heterogeneous worktree states,
history-spread branches, and optional remote refs so ordinary list,
completion, picker, first-output, alias, remove, and prune benchmarks
can share it. Imported history-spread branches and clean base-tip
worktrees carry their own commits, preserving the base populations
without making them incidental prune candidates when overlays advance
the default branch.

The benchmark matrix now keeps single-factor contrasts: list scaling
uses the 1- and 8-worktree endpoints; alias dispatch has a startup
floor, two population endpoints, and one warm/cold variable-resolution
pair; completion keeps one full-surface case; remove and prune vary
cache or hook state only where the command exercises it. Historical
recipes, redundant cache rows, and intermediate scaling points are
removed. Manual setup paths live under `target/`, and the benchmark
guide documents the resulting fixture and cache model.

Tests: `cargo run -- hook pre-merge --yes` after merging current `main`
(4,571 tests); targeted Criterion test-mode runs; `cargo test -p
wt-perf`; benchmark check, clippy, formatting, and diff checks.

> _This was written by Codex on behalf of max-sixty_
2026-08-08 13:38:00 -07:00