Skip to content

Release v0.69.2 - #3590

Merged
max-sixty merged 2 commits into
mainfrom
signpath-code-signing
Jul 25, 2026
Merged

Release v0.69.2#3590
max-sixty merged 2 commits into
mainfrom
signpath-code-signing

Conversation

@max-sixty

Copy link
Copy Markdown
Owner

Cuts v0.69.2, and fixes the Windows code-signing path it depends on.

The signing fix

v0.69.1 shipped an unsigned wt.exe under a green run and a Completed SignPath signing request. archive: false (#3566) had already made the GitHub artifact name worktrunk-x86_64-pc-windows-msvc.zip, and SignPath names its download after the artifact — so with output-artifact-directory: target/distrib the signed zip landed at worktrunk-x86_64-pc-windows-msvc.zip.zip, beside the untouched unsigned build. The checksum step and the release upload both kept reading the original. Confirmed by parsing the published asset's PE certificate table: size=0.

The download now goes to a scratch directory and whatever single file lands there replaces the built zip, so SignPath's naming isn't load-bearing.

Verification, because this failure is invisible

Signing is continue-on-error by design (self-signed test certificate pending SignPath's OSS review), so nothing in the logs distinguishes "signed" from "silently unsigned" — which is how it slipped through twice, two different ways. .github/verify-windows-signature.py reads the zip's PE certificate tables directly, and runs at two points with distinct jobs:

  • before the overwrite — an unsigned release is tolerable while the certificate is a test one; a corrupt one never is, so the replacement has to verify before it can clobber a good build.
  • after — reports what the release actually ships, which is the question the logs never answered.

Rehearsed before landing

The signing path only runs on a tag, so each question about it used to cost a release. This chain was instead run on a Windows runner against the already-published v0.69.1 zip (identical bytes, no build):

saved to …\target\signpath\worktrunk-x86_64-pc-windows-msvc.zip.zip
git-wt.exe: certificate table 8480 bytes
wt.exe:     certificate table 8472 bytes
→ mv → target/distrib/worktrunk-x86_64-pc-windows-msvc.zip
target/distrib/worktrunk-x86_64-pc-windows-msvc.zip: all 2 executables signed
worktrunk-x86_64-pc-windows-msvc.zip: OK   (checksum matches)

That also settled the open question behind #3556: SignPath returns the signed zip itself, not a wrapper, so skip-decompress: true is correct — the extract mode does explode it into loose files.

Release contents

wt remove's fsmonitor sweep (#3581), the troubleshooting doc trim, and this fix. The two refactors in range (#3582, #3584) are behavior-preserving and omitted per convention.

Local gate green (4547 passed). Changelog entries verified against the diffs by subagent; two claims corrected (the doc entry's rationale, and an overclaim attributing v0.69.0's unsigned binary to this bug rather than the separate upload failure #3566 fixed). Data-loss surface reviewed across the cumulative diff.

This was written by Claude Code on behalf of max

…ds it

SignPath names its download after the GitHub artifact, which `archive:
false` already makes `worktrunk-x86_64-pc-windows-msvc.zip` — so writing
it into `target/distrib` landed it at `…zip.zip` beside the untouched
unsigned build. The checksum step and the release upload both kept
reading the original, and v0.69.1 shipped an unsigned binary under a
green run and a Completed signing request.

The download now goes to a scratch directory and whatever single file
lands there replaces the built zip, so SignPath's naming doesn't matter.
The replacement is verified before it overwrites: an unsigned release is
tolerable while the certificate is a self-signed test one, a corrupt one
never is. A final step then reads the shipped zip and reports any
executable without a certificate table — the failure this fixes was
invisible from the logs, and signing stays continue-on-error, so nothing
else would catch it.

Rehearsed against the published v0.69.1 zip on a Windows runner before
landing: sign, verify, replace, checksum, report — `wt.exe` and
`git-wt.exe` both signed, checksum matching the shipped bytes.
@max-sixty
max-sixty force-pushed the signpath-code-signing branch from c439e18 to f741496 Compare July 25, 2026 01:43
@max-sixty
max-sixty force-pushed the signpath-code-signing branch from f741496 to 7369a75 Compare July 25, 2026 01:44
@max-sixty
max-sixty merged commit 6b29c28 into main Jul 25, 2026
43 checks passed
@max-sixty
max-sixty deleted the signpath-code-signing branch July 25, 2026 03:02
max-sixty added a commit that referenced this pull request Jul 25, 2026
Eleven commits landed on `main` while #3590 sat in CI, so they ship in
v0.69.2 with no changelog entry. Most are user-facing, and three are
data-loss fixes — exactly the drift the release skill's step-12 check
exists to catch. The tag is held until this lands.

Entries added, most-impactful first:

- **#3589** — two ways shell integration deleted user data on a
substring guess: an rc line that only *quotes* the init command, and a
user's own `conf.d/wt.fish` deleted outright by `install`'s legacy
cleanup. Plus forge detection moving from `host.contains("github")` to
label-wise matching.
- **#3585 + #3591** — truncate-in-place rc writes replaced by
write-temp-then-rename, then generalized to every user-file write.
- **#3578** — despite its `docs(step):` subject this carries three
behavior fixes, including `wt step push` succeeding mid-rebase and
moving the target branch onto a half-replayed history.
- **#3588 + #3587** — conflict markers can no longer reach a commit via
`wt step relocate --commit`, and `wt merge` refuses in its own name.
- **#3554** — OpenCode marker writes could land in another session's
worktree (thanks @4i3n6, who both reported and fixed it).

Not documented, per convention: #3574 (doc comments only, no runtime or
docs-site surface) and the three dependency bumps.

Also corrects `.github/CLAUDE.md`, which lists three required status
checks; there are four — `fast-checks` is required too, confirmed
against the branch-protection API.

Entries verified against the diffs by subagent. Two corrections came
back and are folded in: the #3589 entry originally promised "two of them
deleted" and described only one, and the #3578 entry mis-quoted the
success line as `✓ Pushed to main` when it carries a commit count.

> _This was written by Claude Code on behalf of max_
max-sixty added a commit that referenced this pull request Jul 25, 2026
…3593)

Follow-up to #3590, now that v0.69.2 has shipped a signed Windows binary
— verified against the published asset rather than the logs:

```
git-wt.exe: certificate table 8480 bytes
wt.exe:     certificate table 8472 bytes
c4343fb5…1a43 *worktrunk-x86_64-pc-windows-msvc.zip   (published .sha256 matches)
```

Comments only; no behavior change.

The signing comments were written while the mechanism was still being
guessed at, so they carried the failed attempts — a ten-line account of
the zip-of-a-zip failure, a six-line account of the `…zip.zip`
collision. The path is proven and the workflow now verifies itself, so
what a reader needs is the current mechanism. Trimmed to that.

One thing added rather than removed. `Recompute checksum` sits between
two `continue-on-error` steps and looks like it should fold into the
replace step above it. It must not: the signing steps are tolerant
because publishing can't depend on a self-signed test certificate, while
a checksum that doesn't match the shipped zip has to fail the release.
That asymmetry is invisible in the YAML, so it's now stated — the next
person to simplify this shouldn't have to rediscover it.

> _This was written by Claude Code on behalf of max_
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants