The website is released from its own branch and renders the changelog from
that branch's catalog, so the 1.x publication could dispatch Pages and still
fail on a changelog page it does not own (v1.39.2: "public changelog returned
HTTP 404" after every product surface had shipped).
- Postflight now verifies tags, CLI and Desktop releases, npm, the Desktop
Stable manifest on dl.reasonix.io and the Homebrew cask, attaches
release-event.json, and completes. sync-release-site.sh becomes
reconcile-release-publication.sh without the Pages dispatch/watch stages.
- The stable manifest check stays: it reads dl.reasonix.io/latest/latest.json,
the Desktop updater pointer on R2, not the website. The Homebrew check moves
out of the site verifier and reads the tap through the contents API.
- Drop the homepage/changelog/hydrated-DOM probes, observe-release-site.sh,
release-site-fetch, and the pages.yml dispatches in release-stable.yml and
release-desktop.yml together with the actions: write they needed.
- Ledger: the site fragment becomes a pointer fragment (stableManifest,
homebrew); homepage and changelog surfaces are gone.
Rendering now resolves every refs number through the GitHub GraphQL API.
A pull request gains "by @author". An issue gains "fixed in #M by @author"
for each merged pull request GitHub links as closing it (the ClosedEvent
closer and closedByPullRequestsReferences; mentions are not fixes); an
issue fixed by a direct push stays plain. Bot authors are never credited.
A number that names a discussion or nothing stays plain with a warning.
The closing Contributors / 贡献者 section lists exactly the credited
humans, once each, in first-appearance order. It replaces the old Thanks
section, which linked the catalog's contributors (github-actions[bot]
included) without mentioning them; that field no longer feeds the notes.
Lookups are cached per run, bounded in concurrency, and retried with
backoff on 5xx, rate limits and dropped connections. A failure that
persists, or an authorisation failure, renders the ref as its plain link
and fails the command with a release_credits.* code.
Because rendering now reads GitHub, the seal job renders the notes once
into the candidate payload (evidence/release-notes.md, recorded as
notes.renderedPath and bound to renderedSha256). Promotion publishes
those sealed bytes with no network call, so a rename, a newly linked fix
or an outage after sealing cannot change or block the release. Candidates
sealed before this carry no renderedPath and render offline as before.
Every job that renders, and every caller of a reusable workflow that
does, gains issues: read and pull-requests: read.
Move the website and Cloudflare platform services to their dedicated protected branches. Keep main-v2 focused on the product and retain only the product-side release integration contract.
The candidate spent most of its 48 minutes compressing a 580 MB payload
with NSIS LZMA, a single-threaded pass of 5-6 minutes, three times per
architecture's worth of work on the critical path:
- the Windows build job built an unsigned installer and portable archive
that nothing uses when the release is signed (windows-sign rebuilds both
from the signed payload): ~8.5 minutes of the arm64 build. Signed
release builds now stop at the signing payload
(REASONIX_WINDOWS_PAYLOAD_ONLY); every other caller packages as before.
- windows-sign signed and packaged amd64, then arm64, so the two
compressions ran back to back (5.6 + 5.1 minutes). The finalize script
now runs in three phases: Certum signing of both payloads in the one
session, both packages built at once in separate worktrees (no
credentials involved), then Certum sealing of each installer in turn.
Without FINALIZE_PHASE it still does all three.
Installer bytes and compression settings are unchanged.
Expand every wildcard in the expected shard artifact pattern so CodeQL accepts the rerun contract test. The test still compares each shard report name with the workflow download pattern.
This reverts commit 1aa4a1fb3.
The 1.x line keeps the CLI's install channels (npm `reasonix`, the
Homebrew cask and the CLI update pointers) until 2.x is stable; the
studio line now publishes them only when STUDIO_PUBLISHES_CLI is set.
Without this, 1.39.1 would ship its fixes while npm and Homebrew stayed
at 1.39.0.
Restored workflow steps are pinned to the same full-length SHAs the
rest of main-v2 uses, and the contract test that cuts release-npm.yml at
setup-go matches the pinned form.
A pull request authored with GITHUB_TOKEN never starts other workflows, so
the weekly acknowledgments PR never reported the checks branch protection
requires and could only land by bypassing it. The step now uses the
dedicated ACKNOWLEDGMENTS_TOKEN secret when it is set, and falls back to
GITHUB_TOKEN otherwise.
The contributor table also listed github-actions[bot]; bot accounts are
now filtered before the top 20 are taken (30 are fetched to keep 20).
Checked by running the script against the live API: the previous version
writes one github-actions row, this one writes none.
desktop-windows-go-group (I-P) ran `go test -run ^Test[I-P] ./...` and
reasonix/desktop hit Go's default 10m per-package alarm:
run 36165720818 / job 108174359373 (main-v2) FAIL reasonix/desktop 600.369s
run 36161103402 / job 108158948888 FAIL reasonix/desktop 600.159s
run 36166824914 / job 108179614742 FAIL reasonix/desktop 600.199s
The reruns of the same commits passed with reasonix/desktop at 348.6s
(job 108167833614) and 401.9s (job 108309233796).
Root cause: no test hangs. At the alarm the running test had been going
for 1-15s (TestLegacyTopicArchiveVisibility,
TestOpenCanonicalSessionAfterWorkspaceRemovalFromNativeSurface,
TestProjectTreeMigratesNewCLISessionAfterProjectDirMarker), and the
largest gap in the product log (66-86s after the parallel-NN.jsonl
writes) covers TestParallelDesktopTabs... through
TestPurgeMigratedSourcesBusyWriterResumesFromTombstone, which take 27.9s
locally: the same ~3x Windows slowdown seen everywhere else. I-P holds
651 tests and is the heaviest group by far (other groups: 66-244s on
the same runners, Q-S up to 398s), so on a slow hosted runner its
package run alone crosses 600s.
Fix: split I-P into I-M (367 tests, 69s locally) and N-P (314 tests,
67s locally) in the partition driver and in both matrices that use it
(desktop-windows-go-group and desktop-go-race). The partition test now
also holds the race matrix to the driver's group list so the two cannot
drift. The alarm stays at Go's default: it is the hang detector, and
each group now has roughly twice the headroom under it.
Reproduction (macOS, CGO_ENABLED=0, same alarm scaled to local speed;
I-P takes ~138s here, so 120s stands for 600s on a runner ~5x slower,
where the failing jobs were):
before: go test -run '^Test[I-P]' -timeout 120s .
panic: test timed out after 2m0s, FAIL 120.874s
after: go test -run '^Test[I-M]' -timeout 120s . ok 78.2s / 86.7s / 96.8s
go test -run '^Test[N-P]' -timeout 120s . ok 67.1s / 86.5s / 71.1s
The v1.38.9 desktop release page reads "This vv1.38.9 desktop release":
resolve already emits the version with its v prefix, and the disclosure
template added another one.
Normalize the prefix in the step instead of assuming either convention,
so the public text stays correct if the resolver output ever changes, and
assert the shape in the release workflow contract test.
internal/crashreport gained process_unix.go (built on darwin) with the CLI
fatal-crash capture, but the package was not in darwinRoots, so pull
requests touching it skip the macOS lane. release-control's
"every darwin-specific package is inside the pull-request group" test
caught it on the main-v2 push; the pull request itself skipped
release-control by path.
scripts/macos-go-tests.test.mjs: 2 pass / 1 fail before, 3 pass after.
The Linux reasonix-desktop service was the one Go binary in the package
built with cgo left at its default. On the ubuntu-22.04 release runner that
links it against glibc 2.35, so the desktop-v1.39.0 tarball's service names
/lib64/ld-linux-x86-64.so.2 and needs GLIBC_2.32/2.34: it will not start on
glibc 2.31 systems (Debian 11, Ubuntu 20.04, Kylin V10, UOS 20), while the
launcher, migrator, CLI and update helper beside it are already static.
Nothing on Linux needs cgo: every `import "C"` file is darwin-only, sqlite
is pure Go, and the Linux StatusNotifierItem tray is pure-Go D-Bus. The
tray files were gated on cgo, though, so a plain CGO_ENABLED=0 build would
have swapped in the no-op tray; they now select the D-Bus tray on linux
regardless of cgo.
- scripts/desktop-build.sh builds the Linux service with CGO_ENABLED=0.
- verify.mjs reads the ELF program headers of every Go member of the Linux
tarball and .deb and refuses one that names a program interpreter, so the
release build fails instead of shipping a glibc floor again.
Test: lib.test.mjs adds ELF parsing and archive-member cases; on the base
it fails at import ("does not provide an export named
'checkStaticGoMembers'"), with the fix 23/23 pass. Against real binaries,
checkStaticGoMembers reports the desktop-v1.39.0 release service as
"reasonix-desktop is dynamically linked (interpreter
/lib64/ld-linux-x86-64.so.2)" and returns no errors for the service built
by the fixed script path. `GOOS=linux CGO_ENABLED=0 go list` now selects
tray.go, tray_loop_external.go and tray_health_unix.go instead of
tray_nocgo.go; linux vet/test compile and darwin vet pass.
Fixes#10372
desktop-windows-package has failed on main-v2 since 27d99bbdb with
"windows-upgrade-fixture: startup imported or recreated the legacy
session". ffdd87ef8 (#10759) made a restored legacy tab report its
historical source, so the tab now shows the import banner, and made the
banner action a real PrepareSession import. The first-launch acceptance
ran with -PrepareHistoricalSession, clicked that action, and the fixture
then found the import it forbids. Startup itself still imports nothing.
- The first launch no longer prepares. The UI wait accepts the import
banner beside a visible transcript body when it was not asked to
prepare, and the fixture keeps its no-import verification.
- The restart launch prepares. `verify --phase restart` now requires
exactly one legacy SourceMapping for the fixture's legacy path whose
session is the global workspace's only session, no operation left
open, the active tab carrying that session and topic, the authored
legacy history unchanged, and the topic databases and backups intact.
- ci-paths routes the acceptance scripts and the upgrade fixture to
the packaging surface. They run only inside desktop-windows-package,
and a pull request that changed only them skipped that job.
Test: TestWindowsUpgradeFixtureImportsLegacyOnlyWhenPrepared restores
the saved tab, prepares its source through PrepareSession and opens the
result with OpenSession. The restart verification fails on the untouched
state, and the first-launch verification fails on the imported state.
Against the previous fixture the test fails with:
prepared verification passed before the legacy source was imported
and with that pre-import check removed it reproduces the CI error:
restart: startup imported or recreated the legacy session
TestVerifyPreparedImportRequiresOneCommittedLegacyImport covers the
rejected import shapes.
ci-paths.test.mjs pins the new packaging routes; it fails on the
previous pattern for scripts/test-windows-upgrade-startup.ps1.
desktop-go-race is eleven parallel partitions of the whole desktop module
under -race, on every pull request that touches native code. The same
partitions already run without -race on each pull request (desktop-go and
the Windows groups), so the race sweep mostly repeated that work per PR head.
It now runs on pushes and manual dispatch only. The desktop aggregate gets
RACE_REQUIRED, which mirrors the job's own condition, so a pull request
expects the sweep to be skipped and still fails if it unexpectedly runs.
A data race introduced by a pull request is reported by the push run after
merge.
scripts/ci-workflow.test.mjs evaluates the job condition and the aggregate
expectation over every event/native/notes-only combination and runs the
aggregate script for the new pull-request cases; both fail against the
previous ci.yml.
A tag is a mutable pointer: whoever controls the action's repository can
move v7 to new code and every workflow here runs it on the next event,
with this repository's token and on its self-hosted runners. A commit SHA
cannot be moved.
Each reference keeps its tag as a trailing comment, which is the form
Dependabot's github-actions ecosystem (already configured) reads and
updates. The composite actions pulled in by reference
(attest-build-provenance, upload-pages-artifact, the Windows signing
setup) already pin their own nested actions, so the repository-level
"require full-length SHA" policy can be enabled once this lands.
Six workflow contract tests asserted the literal tag spelling
(upload-artifact@v7, setup-node@v7, attest-build-provenance@v3). They now
require the pinned form "@<sha> # vN": the major version is still checked,
and a reference that drifts back to a floating tag fails them.
The Studio line now owns the CLI install channels: npm `reasonix` with its
`@reasonix/cli-*` platform packages, the esengine/homebrew-reasonix cask, and
the R2 CLI update pointers (`cli/{stable,preview}/latest.json`) that
`reasonix upgrade` and the site read through the release gateway. A 1.x
release must never write any of them again.
- release-npm.yml is removed, and release-promote/release-stable no longer
call it or wait on it; the legacy recovery input publish_npm goes with it.
- release.yml no longer hands GoReleaser a tap token, no longer pushes the
prepared cask, no longer advances the CLI channel pointers, and no longer
requires npm `latest` to have moved. It still creates the vX.Y.Z GitHub
release, its notes, the immutable cli/releases/<tag>/latest.json record,
and the Desktop compatibility asset.
- .goreleaser.yaml drops homebrew_casks.
- verify-stable-release-artifacts.sh and the publication ledger stop
requiring npm package identities, npm dist-tags, and the cask version.
- Publisher helpers with no remaining caller are removed:
publish-homebrew-cask, finalize-npm-official-release, resolve-npm-release,
npm/publish-candidate, decide-cli-pointer-update, compare-cli-release-tags.
- release-workflows.test.sh now fails if any workflow holds NPM_TOKEN or
HOMEBREW_TAP_TOKEN, names the tap, runs npm publish or dist-tag, or
writes a CLI channel pointer.
Candidate preparation, tags (including npm-vX.Y.Z), Desktop publication, the
Stable manifest, Pages, and the release-event record are unchanged.
Problem: v1.38.12 candidate preparation is blocked by Cloudflare challenges against curl, even with the updater User-Agent.
Root cause: Cloudflare scores the transport as well as the header; protected Actions probes repeatedly returned HTTP 200 for the shipped Go updater client and 403 for curl/Node profiles.
Fix: Reuse the Go updater HTTP request identity for every Stable manifest gate and postflight, with explicit Go setup, bounded response size, HTTP/challenge rejection, and JSON version validation.
Verification: three protected Go probes returned HTTP 200 for v1.38.12; focused Go and shell fixtures, release control contracts, release workflow contracts, and local public preflight pass.
Problem: Candidate preflight still receives a Cloudflare challenge after adding the updater User-Agent to curl, while Node fetch with an updater User-Agent succeeds.
Root cause: User-Agent alone does not establish whether the shipped Go HTTP client can reach the public endpoint; client transport behavior may also matter.
Fix: Add read-only protected probes for the current and target Stable updater using Go net/http and Node fetch. Do not expose the Cloudflare token to the Go probe.
Verification: Go probes return HTTP 200 locally; gofmt, node --check, and git diff --check pass.
Problem: GitHub runners receive Cloudflare challenge 403 for the public Stable manifest when the release gate uses curl default headers, blocking candidate preparation although the Desktop updater can read the same endpoint.
Root cause: Public-access checks and postflight used a generic request identity that Cloudflare classifies differently from the shipped updater.
Fix: Centralize manifest requests through a versioned Stable updater User-Agent across candidate, publication, site recovery, and public verification. Keep HTTP and JSON validation fail-closed.
Verification: release control contracts and site ownership fixtures pass; three protected Actions diagnostics showed default 403 and updater 200.
* fix(release): inspect Cloudflare config rules for download access
Problem: GitHub runners receive a Cloudflare challenge for the Stable manifest, and the existing diagnostic does not inspect path-scoped configuration rules.
Root cause: The read-only diagnostic omits the http_config_settings phase, so it cannot report whether Browser Integrity Check is already overridden for the download path.
Fix: Include the configuration settings phase in the protected ruleset inspection.
Verification: git diff --check and node --check scripts/inspect-cloudflare-download-access.mjs.
* fix(release): probe the actual updater request profile
Problem: The public manifest diagnostic uses only Node default headers, while the Desktop updater sends a distinct User-Agent to avoid edge bot challenges.
Root cause: A 403 for the diagnostic client alone cannot distinguish runner-wide blocking from User-Agent scoring.
Fix: Report a second read-only probe using the current Stable updater request profile.
Verification: git diff --check and node --check scripts/inspect-cloudflare-download-access.mjs.
Problem: the protected release candidate is blocked by a Cloudflare challenge, while the existing Actions diagnostic collapses GraphQL failures into a generic error. Root cause: the query includes fields and filters without a verified schema contract and the diagnostic hides whether failure is schema or permission. Fix: query documented fields over a short window, filter only the public manifest in memory, and report allowlisted error classes without visitor or credential data. Verification: node syntax check, repository lint, and staged diff check passed.
* Improve historical session startup and lazy loading
* Fix history preparation retirement across readers
Problem: source replacement and immediate reopening could expose a closed SQLite cache to retained history readers.
Root cause: reader cancellation did not follow preparation retirement, the last release removed cache ownership before close, and compatibility paging opened a separate owner.
Fix: share preparation across both APIs, propagate source cancellation, retain and drain cache-close barriers, and retire unbound borrowers during shutdown.
Verification: deterministic shared-reference, replacement, immediate reopen, compatibility cursor and shutdown tests passed with the related history paging race suite.
* Index history read fences and extend lazy catalog pages
Problem: paging repeatedly cloned the registry and scanned every source mapping, while existing groups, time filters and pinned shells bypassed lazy catalog reads.
Root cause: read invalidation lacked immutable reverse indexes and source fences could mix a captured WAL view with live metadata.
Fix: share exact-publication lifecycle tokens and one verification per validation; reuse lazy pages for all-session views, frozen time filters and automatic pinned shells; capture source fences from their listing transaction. Document remaining full-materialization and recovery limits.
Verification: registry, history lifecycle and catalog paging race suites; deterministic WAL-capture replacement regression; transcript tests; full root Go suite. Desktop full partition sweep remains in progress.
* Validate fresh macOS images and join history batch workers
* Page native history outlines and resolve direct read anchors
* Read schema-one history through cancellable native projections
* Handle unused history roots and verify packaged native readers
* Page grouped history without materializing every session
Problem: group filters expanded all legacy rows and loaded unrelated canonical
headers. Disappeared history roots could also be omitted from completeness.
Root cause: group membership forced the full snapshot adapter, and listing
availability independently skipped absent roots instead of using discovery.
Fix: retain immutable catalog pages with source-key membership predicates,
admit canonical group and pin members before Stat, and use metadata discovery
state for absent roots. Keep source hashes and persisted schemas unchanged.
Verification: catalog and Desktop pagination/organization regressions, related
race tests, sidebar browser interactions, and stronger packaged legacy reader
checks. Root-wide validation and the new packaged group check continue separately.
* Share native history preparation with compatibility content readers
Problem: Unbound history field requests could enter compatibility repair after
native paging had already prepared a read-only view of the same source.
Root cause: Paging and content RPCs borrowed different history preparation
owners and did not share cancellation and source validation boundaries.
Fix: Centralize native pager borrowing and use it for compatibility field
reads, including explicit targets, while retaining unsupported-format readers.
Verification: Focused paging/content regressions and race tests pass, including
Unicode chunk reads, unchanged authoritative bytes, and peer cancellation.
The native package harness now exercises unbound content reads as well.
* Bound queued history iterators and resolve original compatibility sources
Problem: Rotating discovery retained one descriptor per historical root, and
cold compatibility RPCs rejected global files after workspace restoration.
Root cause: Scan admission had no retained-iterator budget; read validation
mistook the destination workspace for the captured historical source root.
Fix: Limit queued discovery to eight iterators with a visible-workspace
reserve, retaining admitted progress. Share known-root source resolution
across cold paging, field reads, and legacy previews outside App.mu.
Verification: Catalog full race suite and Desktop focused race tests pass.
Tests cover retained progress, priority switching, eventual completion, old
global source reads, full Unicode content, and unknown/symlink root rejection.
The packaged smoke caught the original directory failure; retain compact
page diagnostics so an absent field ref identifies its actual read error.
* Filter historical metadata pages without materializing the library
Problem: Sidebar text queries bypassed lazy paging and built every legacy
session row before returning a small matching page.
Root cause: The bounded catalog adapter excluded text requests because SQL
case conversion could not preserve display-layer Unicode/title semantics.
Fix: Filter bounded catalog batches through the existing Go matching rules
inside the retained WAL snapshot. Read captured source identities instead
of resolving candidate files and account for retained query text.
Verification: Catalog and Desktop focused race tests cover sparse matches,
Unicode, cancellation, fixed-cut continuation, source keys and localized
titles. Sidebar browser interaction tests pass. Native smoke now also checks
text filtering within a group; sparse queries can still scan all metadata.
* fix(desktop): admit explicit group targets without scanning history
Problem: ordinary group edits enumerated all legacy source pages before
applying a workspace organization mutation.
Root cause: group commands shared manual reordering's full-source admission.
Fix: admit only resolved group targets inside the revision CAS, normalize
physical source keys, retain explicit ungrouping fences, and recheck source
adoption and lifecycle ownership inside the registry transaction.
Verification: organization and group regressions pass with the race detector,
including deterministic adoption-before-commit rejection and late preference
import. Document the remaining full-source manual and legacy preference paths.
* fix(desktop): decouple catalog admission from metadata refresh
Problem: catalog startup and filesystem event delivery waited for whole
project/topic metadata transactions and restored-path index writes.
Root cause: startup and the watch loop synchronously owned these writes,
while user edits started independent unjoined metadata goroutines.
Fix: register watches before restored identities settle, enqueue restored
paths, and coalesce all metadata refresh triggers into one cancellable worker
joined by the catalog lifecycle. Publish admission without waiting on writes.
Verification: catalog/sidebar regressions and race tests pass. Deterministic
coverage holds a real SQLite writer during admission, blocks refreshes to
verify coalescing, and checks cancellation and lifecycle wakeup ownership.
Whole-projection metadata reconciliation and synchronous database integrity
validation remain explicitly documented acceptance gaps.
* refactor(history): separate projection readers and preparation stages
Split native pager source validation, DAG projection, compatibility windows, catalog paging, and controller usage accounting into focused owners. Remove the unreachable continuation rebind in the empty-key topic branch. Preserve source validation, transaction boundaries, and snapshot ownership without increasing lint baselines.
Verification: repository lint; agent, sessioncatalog and session package suites; focused Desktop history/catalog/snapshot/navigation tests; frontend typecheck and transcript suite.
* fix(history): serialize discovery progress with catalog writers
Problem: the 100k native discovery fixture stalled with SQLITE_BUSY. Root cause: progress publication bypassed the process-local mutation lock while other catalog writers used it. Fix: route progress through the same boundary and preserve its generation predicate and cancellation behavior. Add a production-package scale harness and record the incomplete history.12 result without relaxing its gate.
Verification: focused metadata/reconcile/catalog race tests, repository lint, script syntax check; native scale harness smoke and history.12 measurement exposed the failure. A fresh package run is still required.
* fix(history): move metadata integrity audits behind startup admission
Problem: cached catalog opening still scanned every database page before returning a sidebar page. Fix: open advisory metadata separately from the full background audit while keeping content projection validation synchronous. Corruption revokes the generation, stops its work, closes all read leases, and reopens through the existing validated quarantine path with a monotonic revision floor. Old cursors return stale_cursor; canceled or transient audits cannot quarantine data, and retries remain bounded.
Verification: root Go suite; projectiondb and sessioncatalog race suites; Desktop catalog/history/snapshot race regressions; real CHECK-constraint corruption, source preservation, lease closure, late publication, cancellation and retry tests; repository lint. Native package performance acceptance remains pending.
* fix(history): avoid redundant discovery and source-wide metadata writes
Problem: Large legacy catalogs could finish discovery then immediately restart,
while registry refresh rewrote unrelated discovered topics.
Root cause: Startup recovery used the full-root invalidation notification
alongside watcher-owned discovery; metadata membership reset and orphan cleanup
scanned all topics.
Fix: Publish only registry metadata after reservation recovery, and use the
existing metadata_present flag with an optional partial index to constrain
membership refresh. Source mutation retains topic cleanup ownership. Record
incomplete history.13 scale evidence and warmup progress.
Verification: sessioncatalog suite, focused catalog race tests, Desktop
startup/catalog/history race tests, repolint, JavaScript syntax check, and diff
whitespace checks passed. New package scale acceptance remains required.
* fix(history): bound directory watching and merge undispatched invalidations
Problem: Large catalogs continued to restart discovery after the initial scan.
Root cause: Catalog monitoring bypassed the platform directory backend and used
macOS kqueue registration that enumerates children and opens per-file handles.
Unwatched roots were also invalidated on every metadata tick, and queued jobs
retained pre-dispatch changes as unnecessary follow-up scans.
Fix: Reuse native directory watching, preserve canonical event/access routing,
separate watch retries from the rotating audit, and merge invalidations before
scan dispatch while retaining genuine in-flight source changes.
Verification: Focused catalog queue and Desktop catalog/watcher race tests pass,
including 512-file native registration under a 256-descriptor process limit,
canonical events, recovery gaps, durable journals and late file discovery.
Repolint and diff checks pass. Record the incomplete history.14 scale evidence;
the new production package still requires measurement.
* fix(history): preserve cold reads when execution recovery fails
Problem: lease-blocked native navigation could leave a blank transcript, and cache failures could silently start full compatibility replay.
Root cause: passive readiness refresh switched startup-error tabs to controller-dependent Follow; activation failure settled independent history state. Native preparation did not distinguish ordinary errors from explicit unsupported formats, and root invalidation retained native watcher registrations.
Fix: coalesce matching cold reads, fence rebuild/reconnect paths, and let only read failures settle history errors. Restrict compatibility fallback to explicit unsupported results, positively identify ancient event envelopes, and remove replaced native watches.
Verification: deterministic activation regressions (59 assertions), frontend typecheck/lint, transcript suite, loading browser tests, focused agent/Desktop race tests and Go lint pass. Root full suite previously passed; Desktop default package timeout remains under investigation with exhaustive deterministic shards. Production history.15 failure is documented; new-package acceptance remains pending.
* refactor(history): share runtime rebuild subscription handling
Problem: the cold-read fence duplicated rebuild handling and exceeded the existing initial bundle budget by 380 bytes.
Root cause: single-tab and settings-wide rebuilds maintained equivalent subscription ownership logic in separate branches.
Fix: iterate the affected identities through one stop/invalidate/rebuild path, retaining the cold-read and activation guards without changing the asset budget.
Verification: activation regressions (59 assertions), prompt lifecycle tests and TypeScript checks pass; the complete production build will verify the unchanged bundle gate.
* fix(history): coalesce startup watches before resuming discovery
* fix(history): keep startup locale sync out of discovery
* feat(history): add shared on-demand native full-text search
* feat(history): add bounded discovery source diagnostics
* desktop: ignore noisy root watcher writes
* desktop: drain native watch backlog before initial history discovery
* refactor(history): remove retired startup migration workers
* fix(history): coalesce watcher overflow before journal writes
* fix(history): replace invalid scans at slice boundaries
Problem: large metadata discovery finished obsolete generations before replaying queued root changes, while unchanged rows rewrote topic projections.
Root cause: invalidations were consumed only at EOF and discovery always published full upserts.
Fix: supersede invalid iterators between slices with bounded coalescing; preserve incomplete journals and recheck unchanged rows transactionally before updating scan presence only.
Verification: sessioncatalog/historywork race suites, deterministic restart and projection regressions, Desktop watcher/startup race tests, and sessioncatalog lint pass. Native scale acceptance remains pending.
* perf(history): project registry metadata in bounded slices
Problem: registry refresh held the catalog writer for the entire registry and rewrote unchanged metadata.
Root cause: metadata-only discovery reused the atomic full-projection refresh and a whole-task timeout restarted large observations.
Fix: serialize cancellable observations, share background slice admission, compare persisted rows, defer paged retirement until input completes, and bound each writer transaction. Preserve atomic refresh for other catalog modes.
Verification: root Go suite, catalog/historywork race suites, Desktop catalog race coverage, frontend typecheck/transcript and loading/sidebar/navigation/scroll browser gates pass. Retain the preceding history.24 package's 90-run native scale evidence separately.
* feat(history): resume interrupted checkpoint index preparation
* test(history): verify checkpoint preparation across process exit
* feat(history): resume streamed legacy display index imports
* refactor(history): separate maintenance admission and publication steps
* feat(history): resume native event preparation across process exit
* fix(history): preserve cold reading windows during passive readiness refresh
* test(history): record production scale results and exercise native cache paging
* perf(history): load cold refresh proof on demand
* perf(history): defer session restoration policy with its reader proof
* perf(history): keep restoration policy outside the eager module graph
* feat(history): resume DAG preparation across all durable phases
* fix(history): include protected windows in resident cache capacity
* docs(history): document DAG resume and native cache findings
* fix(history): release evicted read bindings without affecting replacements
* test(history): qualify native cache resources and startup scale
* docs(history): record history.29 cache qualification
* test: align history migration fixtures with current schemas
Keep the junction migration regression focused on the last pre-invalidation schema and derive the projection schema assertion from the migration ledger. Complete the history hydration Meta fixture so frontend test type checking covers the current contract.
* fix: align startup bundle gate with measured producer output
Problem: the history startup PR exceeded the raw frontend bundle gate by 275 bytes on the Linux producer and the migration regression comment exceeded repolint's essay budget.
Root cause: the earlier budget ceiling was based on a pre-rebase producer measurement, while the test fixture note was four lines at a three-line position limit.
Fix: set the raw initial gate to the smallest measured 2056.6 KiB ceiling, keep all compressed and chunk budgets unchanged, and condense the migration note without removing coverage.
Verification: node scripts/check-bundle-budget.mjs; go run ./tools/repolint; focused sessioncatalog migration tests; git diff --check.
* fix: format desktop catalog runtime
Problem: the desktop producer gate rejected one unformatted composite literal in session catalog runtime code.
Root cause: the latest history loading changes left the pinned-only field alignment outside gofmt output.
Fix: apply the repository formatter without changing behavior.
Verification: gofmt -l ., git diff --check, and go run ./tools/repolint.
* fix: use passing producer bundle ceiling
Problem: the Desktop producer still exceeded the raw startup bundle gate by 112 bytes after the previous measured adjustment.
Root cause: the Linux CI build measured 2106070 bytes while the 2056.6 KiB ceiling allowed only 2105958 bytes.
Fix: set the raw initial budget to the smallest 2056.8 KiB ceiling that contains the producer result; compressed, chunk, CSS, locale, and residency budgets remain unchanged.
Verification: node scripts/check-bundle-budget.mjs, go run ./tools/repolint, gofmt -l ., and git diff --check.
* fix: allow Windows history sources to be replaced during paging
Problem: Windows pager fencing tests failed when a source or event log was replaced while a display index was being built.\n\nRoot cause: os.Open uses a Windows sharing mode that denies delete and rename, so the deterministic source-change interleaving failed before the pager could validate the new pathname identity.\n\nFix: add a platform-owned replaceable read helper. Windows opens sources with read, write, and delete sharing; Unix keeps the ordinary descriptor. All legacy pager builders, event-kind probing, and paged event/DAG reads use the shared helper.\n\nVerification: go test ./internal/agent -count=1; GOOS=windows GOARCH=amd64 go test -c ./internal/agent; go run ./tools/repolint; go test ./...; git diff --check.
* fix: match Windows replaceable read handle flags
Problem: the first shared-delete handle still left the DAG source replacement interleaving failing on Windows.\n\nRoot cause: the native source handle did not use the same reparse-aware backup semantics as the repository's proven replaceable regular-file reader.\n\nFix: align the fileops helper flags with the existing Windows repair reader while retaining read, write, and delete sharing.\n\nVerification: GOOS=windows GOARCH=amd64 go test -c ./internal/agent; focused pager tests pass on the host.
* fix: grant Windows pager handles replace access
Problem: atomic replacement of a DAG event log was still denied on Windows despite delete sharing.\n\nRoot cause: the replacement operation also needs delete access on the existing native source handle.\n\nFix: request DELETE together with GENERIC_READ while retaining read, write, and delete sharing.\n\nVerification: GOOS=windows GOARCH=amd64 go test -c ./internal/agent.
* fix: make Windows source replacement regression portable
Problem: the pager source-fencing regression used os.Rename(temp, existing) while the target was open, which Windows rejects; granting DELETE access then broke ordinary concurrent rewrites.\n\nRoot cause: the test encoded Unix replacement semantics that are unavailable for an open Windows target, while the production reader must remain compatible with normal writers.\n\nFix: keep the shared-delete read handle at read access and model replacement by moving the old pathname aside before installing the new file. The test still verifies a changed native identity is rejected before publication.\n\nVerification: focused pager source-change tests, GOOS=windows GOARCH=amd64 go test -c ./internal/agent, go run ./tools/repolint, and git diff --check.
* fix: join startup cold history on runtime ready
Problem
A runtime-ready event could start ordinary session hydration while startup cold history was still in flight, issuing a duplicate history read for the same session.
Root cause
The ready reconciliation only shared the normal session-load owner. Cold startup reads were tracked separately, so the readiness path did not join that owner.
Fix
Join matching cold-history promises before starting ordinary hydration. After the certified window settles, install the live follower and continue ancillary hydration with the cached window. Keep the failure path on the existing reader and record the measured 163-byte startup bundle increase with the smallest producer-safe budget update.
Verification
- pnpm exec tsx src/__tests__/ready-event-meta-sync.test.tsx
- pnpm typecheck
- pnpm test:typecheck
- pnpm lint:hooks
- pnpm test:transcript
- pnpm build
- go run ./tools/repolint
- git diff --check
* Fix protected history windows and catalog context races
Problem
Navigation could install a transcript before the active tab was pinned, allowing the resident cache budget to evict the just-opened window. Catalog callbacks could also reread the mutable App context while the lifecycle test replaced it, producing a data race.
Root cause
Active-tab pinning was deferred to a post-paint effect, and the restored catalog watcher used the App context accessor instead of its owning worker context.
Fix
Publish the active pin at the dispatch boundary before the first history page commits. Let the catalog watcher pass its existing context into historical discovery while retaining the default accessor for synchronous callers.
Verification
- tab-switch-hydration: 101 passed
- desktop TestConcurrentRebuildSessionCatalogCallersShareFailure with -race: 3 runs passed
- desktop TestHistorySliceSourceField/cold_index_hit: 100 runs passed
- pnpm typecheck and pnpm test:typecheck passed
- pnpm lint:hooks and pnpm test:transcript passed
- go run ./tools/repolint passed
- git diff --check passed
* Fix Windows history upgrade acceptance and test cleanup
Problem: Windows CI rejected visible upgraded history and could not remove a test source directory.
Root cause: the UI acceptance helper required an activation button even after lazy history had rendered, while the test closed its historical service after TempDir cleanup.
Fix: accept a healthy visible transcript without activation and close the test app services before removing the source directory. Keep pending and recovery-state rejection covered.
Verification: focused desktop Go test, go vet, repolint, golangci-lint, and diff checks passed. Windows PowerShell and installer checks remain for CI.
* Align history CI checks with lazy legacy loading
Problem: the Desktop CI matrix failed startup worker, cold index, and packaged upgrade checks after history loading changed.
Root cause: those checks still assumed startup discovery took the old coordinator lock, equal-timestamp sidecars were trustworthy, and opening a legacy tab automatically migrated it without rewriting JSONL. The current shutdown path snapshots an active legacy tab.
Fix: deterministically block the actual startup worker, establish sidecar ordering for the index-hit fixture, and verify legacy path, authored content, registry non-import, restart, and backups in the packaged fixture.
Verification: focused desktop tests with repeated and race runs, upgradefixture tests, go vet, repolint, golangci-lint, and diff checks passed. Windows packaged acceptance remains the terminal CI gate.
* fix: resolve Windows junction identities without breaking upgrade locks
Problem: Windows users whose existing data directory is a junction cannot
start sessions after configuration locks begin requiring physical identity.
Recovery also labels runtime-start failures as failed draft saves.
Root cause: EvalSymlinks can leave a junction leaf unchanged and reject its
children, splitting aliases or failing before the OS lock is acquired.
Fix: resolve existing Windows paths through native metadata handles, retain
legacy workspace lock spellings, isolate/rebuild derived catalog identities,
and separate historical draft submission errors from persistence errors.
Add a read-only probe and native regressions to the Windows PR selectors.
Preserve current eager session creation and authoritative user data formats.
Verification: owning Go suites, host/Windows golangci-lint, repolint, frontend
production/test typechecks, draft ownership and recovery regressions, real
Chromium recovery interactions, and Windows selector coverage passed.
Native Windows execution and complete upgrade qualification remain CI/manual
gates; cross-compilation does not establish those results.
* fix: preserve native Windows error kinds and verify historical lock spelling
Problem: native Windows CI rejected dangling-link and loop error kinds, and
the legacy-lock fixture expected the unexpanded runner temporary path.
The recovery UI also exceeded an almost exact startup-byte budget.
Root cause: os.IsNotExist does not traverse arbitrary wrapping, native
reparse exhaustion uses ERROR_CANT_RESOLVE_FILENAME, and EvalSymlinks expands
8.3 ancestors even when it preserves a junction leaf.
Fix: use wrapped-error matching and the native reparse-loop code; derive the
fixture's legacy key with the historical filesystem algorithm. Keep all
rejection and lock assertions. Remove a duplicated browser invocation.
Adjust only the raw startup ceiling to the measured producer size: same
source/dependencies/commit stamping adds 506 B raw (0.024%) and 120 B gzip;
Linux Electron measures 2103802 B, rounded to 2054.5 KiB.
Verification: pathidentity, workspacelease and probe tests; host/Windows
lint; repolint; bundle budget; same-toolchain base/candidate production builds.
All compressed, chunk, CSS, locale and residency budgets remain unchanged.
* test: exercise native junction identity across Windows volumes
Problem: the reported regression crosses system and data volumes, while the
initial regression fixtures only created same-volume junctions.
Root cause: filesystem alias tests did not exercise drive-prefix changes.
Fix: create a temporary junction from the system temp drive to RUNNER_TEMP
and compare entry and missing-tail identities with the physical target.
Require the two-volume fixture on GitHub Windows runners. Single-volume
local machines still run the unconditional same-volume regression suite.
Verification: Windows-target lint and cross-compilation; the native Windows
PR lane is required to execute the cross-volume fixture.
* Preserve credential ACL recovery and bind artifact consumers to producer Node
Problem: native identity resolution blocked credential ACL repair before edit locking, and prepared frontend consumers selected different Node patch versions.
Root cause: FILE_READ_ATTRIBUTES requires permissions that legacy read-deny ACLs remove; floating Node majors resolve independently on hosted runners.
Fix: use zero-access metadata handles, keep read denials intact until credential recovery, cover linked and unrecorded ACL cases, and propagate the producer Node version to every artifact consumer without weakening verification.
Verification: pathidentity/config tests, Windows cross-compilation and golangci-lint, 40 workflow/artifact/selector tests, repolint and diff checks passed. Native Windows execution remains a CI gate.
* Bind memory screening consumers to their build Node version
Problem: a memory shard rejected the prepared build before running because its floating Node major selected a different patch version.
Root cause: the independent app-memory workflow owns a second producer-consumer identity contract.
Fix: propagate its producer runtime to shards and aggregation, preserving exact identity validation. Add a workflow regression for both consumers.
Verification: 51 workflow and memory protocol tests, repolint, and diff checks passed.
* Open Windows identity handles with exact native access rights
Problem: CreateFile with zero desired access still fails against legacy credential DENY RX ACLs.
Root cause: Win32 adds SYNCHRONIZE and FILE_READ_ATTRIBUTES, as already documented by the credential ACL recovery owner.
Fix: open a zero-access NtCreateFile handle that follows reparse points, use the native namespace for long/UNC paths, and translate NTSTATUS failures back to Win32 codes for existing error classification.
Verification: Windows compilation and golangci-lint, repolint, and diff checks passed. Existing save/load/revision tests require identity to resolve while content reads remain denied; native CI verifies this contract.
* Request valid metadata access for native Windows identity queries
Problem: NtCreateFile rejects a zero access mask, breaking ordinary Windows identity queries.
Fix: request FILE_READ_ATTRIBUTES without implicit synchronization; when an ACL denies that right, try READ_CONTROL, which file owners retain for security metadata. Neither handle grants content access or mutates the ACL. Preserve NTSTATUS classification and all denial assertions.
Verification: Windows compilation and golangci-lint, repolint and diff checks passed; Windows execution is required before qualification.
Problem: region-first filtering hid MiMo API versus Token Plan access and new connections retained the V2.5 model catalog.
Root cause: product selection depended on region, MiMo lacked a one-time model migration, and floating CI Node versions made frontend artifact producers and consumers disagree.
Fix: select access type before cluster and protocol, clarify independent credentials and usage, default new connections to V2.6, and atomically append models to eligible official connections without changing existing defaults or custom data. Preserve previous-writer TOML compatibility, share repeated frontend defaults within existing budgets, and pin artifact workflows to one exact Node runtime.
Verification: root and Desktop suites, previous-reader migration round trips, frontend and browser regressions, unchanged production bundle budgets, cross-platform CI, race checks, Windows packaging, memory screening, CodeQL, and release-control contracts passed. All review threads were clear before merge.
* fix(desktop): restore eager session creation with durable input recovery
Problem: deferred draft creation changed explicit New Session behavior and left upgraded users dependent on draft-only input and recovery paths.
Root cause: manual creation, composer persistence, and legacy draft submission shared identities and lifecycle assumptions that no longer match formal sessions.
Fix: create formal sessions through durable idempotent operations, persist unsent input separately with revision and receipt fencing, retain legacy drafts as recovery-only data, and retire automatic empty-session cleanup. Repair model, attachment, Goal, and submission wiring while preserving current canonical storage and history formats.
Verification: tagged 1.38.3 through 1.38.11 compatibility fixtures; all 11 desktop test partitions; focused race, composer and lifecycle regressions; source and test type checks; repository lint; signed macOS arm64 packaging and isolated restart/image/lifecycle smoke tests. Native Windows and Linux application validation remains pending.
* fix: repair rollback CI gates and confine session deletion
Problem: rollback CI failed lint and the frontend producer gate, while CodeQL reported existing deletion paths outside a rooted filesystem boundary.
Root cause: reserved initialization ignored binding release errors, historical tests used outdated lint syntax, and the raw budget sat below the measured main-v2 baseline. Deletion rebuilt absolute trash and cache paths after validating the session identity.
Fix: join release errors, modernize fixture loops without removing assertions, use rooted rename and removal for session deletion, and round only the initial raw bundle gate from 2053.9 to 2054.1 KiB. Same-toolchain main-v2 measures 2103208 bytes versus 2103296 bytes for this branch, an 88-byte increase.
Verification: root and Desktop golangci-lint 2.12.2, Desktop Windows lint, reserved initialization and historical fixture regressions, deletion confinement tests, production bundle guards, and repolint passed. No compatibility coverage was removed.
* test: preserve historical fixture bytes across Windows checkout
Problem: all Windows history partitions failed provenance hashes, and frontend CI still asserted the retired draft-on-new behavior.
Root cause: Git autocrlf rewrote JSONL and metadata bytes emitted by tagged writers. A source-regex chrome assertion duplicated navigation coverage while encoding the superseded product contract.
Fix: disable text conversion only for the two historical fixture trees, retaining original hashes and offsets. Remove the obsolete regex assertion; formal-creation, legacy recovery, remote navigation, and intent-fencing behavior tests remain.
Verification: a core.autocrlf=true checkout preserved all 264 tagged historical file hashes, and app-chrome tests passed. The complete frontend CI runner is running locally; replacement Windows CI will verify the native migration matrix.
* test: align recovery assertions and native observation lifecycle
Problem: frontend tests still expected active empty-session cleanup and hidden content-free drafts; the native browser smoke timed out reading its initial hidden macOS page.
Root cause: those assertions retained the retired draft-first product contract. The native fixture bypassed the observation lease that production host calls acquire before executing DOM scripts.
Fix: assert that cleanup cannot be resumed while manual restore remains available, keep settings-only legacy drafts discoverable with workspace isolation, and acquire/release the existing observation lease for the initial hidden-page snapshot and fixture setup.
Verification: all 505 frontend CI commands passed across the unchanged completed set and remaining set; Electron typecheck and repolint passed. Native macOS browser smoke passed 100 lifecycle cycles with no remaining pages. Timeouts, product code, and coverage counts are unchanged.
* fix(windows): restore Git Bash and shell settings
Problem:
- Windows settings no longer exposed the earlier shell layout or Git Bash runtime.
- WSL aliases and retained PowerShell paths could misroute interpreter selection, while custom runtimes could be absent from detection.
- ConPTY could corrupt Git Bash UTF-8 output and completion markers.
Root cause:
- Shell resolution and capability inventory applied different path compatibility rules.
- Machine-driven Windows Bash sessions reused a terminal-oriented transport.
- The settings presentation had diverged from the runtime state model.
Fix:
- Restore Git Bash discovery, selection, and the compact sandbox settings surface.
- Centralize configured-path compatibility, reject WSL aliases, and align inventory with resolution.
- Use a tracked persistent pipe for machine-driven Git Bash sessions and add Windows contract coverage.
Verification:
- go test ./...
- go test -race ./internal/persistentshell ./internal/sandbox -count=1
- focused desktop shell support tests
- frontend typechecks, lint, CSS and application-layer checks, and 154 focused assertions
- native Windows 11 ARM64 shell and integration regressions
* chore(frontend): calibrate sandbox bundle budget
Problem:
- The restored localized sandbox labels exceeded the raw startup budget by 1606 bytes.
Root cause:
- The raw budget was calibrated before the new runtime, status, and write-scope labels entered the startup locale payload.
Fix:
- Ratchet only the raw initial asset budget to the measured payload with bounded headroom.
- Keep compressed, chunk, stylesheet, locale, and resident-history limits unchanged.
Verification:
- pnpm build
* fix(release): preflight executable contracts and recover site independently
Problem: late public-access failures required full release recovery and could leave incomplete site evidence. Root cause: static control checks did not execute critical contracts, while website reconciliation lived only inside publication YAML. Fix: check offline contracts and runner manifest access early, centralize public-state reconciliation, and add protected site-only recovery with a shared lock, owned Pages correlation, final ownership check, and persistent failure ledgers. Verification: executable preflight, shell-boundary failure regressions, release workflow contracts, CI path tests, and actionlint passed without live publication.
* fix(release): serialize legacy and site recovery
Problem: legacy recovery could overlap site reconciliation. Root cause: its lock was scoped per version instead of sharing the publication lock. Fix: join the common stable-release-publication concurrency group. Verification: workflow lint and site recovery lock regressions passed.
* fix(release): bind atomic tag activation to an authorized publisher
Problem: release approval could precede a GH013 tag-creation failure. Root cause: activation inherited the default Actions token rather than an explicitly authorized maintainer identity. Fix: require a dedicated credential, inspect inherited rulesets before approval, bind the actor ID, and recheck before the existing atomic push without checkout-token fallback. Verification: identity CLI and policy regressions, local atomic tag tests, release workflow contracts, CI path tests, and actionlint passed.
* test(release): assert the dedicated tag credential boundary
Problem: the old workflow regression required a writable default Actions token during activation. Root cause: the assertion encoded the retired credential implementation rather than the approval and identity boundary. Fix: require read-only default permissions, explicit publisher credentials, actor binding, no checkout fallback, and activation after approval. Verification: CI workflow and identity regressions passed together.
Sparse completion events and later history hydration could erase durable tool identity, append duplicate result rows after the final answer, and downgrade completed state. Preserve canonical metadata, repair uniquely matchable legacy checkpoint identities, and reconcile bounded resident records through one conflict-aware projection with stable tool keys and anchored local notices.
Keep distinct conflicting formal results independently readable and prevent old session requests from updating a new binding. Preserve pagination, content-on-demand and existing resident-memory limits without changing provider payloads, prompts, session exports or the persistent message format.
Harden CI at the observed failure boundaries: carry frontend producer attempts into artifact consumers, attribute actual bounded Markdown Worker listeners, synchronize reconnect assertions with retained lifecycle publications, and use a fresh bounded functional wait for packaged upgrade history. Retain native geometry, visible history, restart integrity and independent startup gates.
Verification includes transcript/store/follower/reducer regressions, 240- and 1,000-turn browser scenarios, native Electron layout, backend and race suites, packaged Windows first-import/restart acceptance, and all three full memory-screening shards. Real remote-host validation remains unavailable; binding tests do not replace it. No release is published by this change.
Problem: Superseded runs retain queued always() aggregates and metrics, blocking replacement CI until manually force-cancelled.
Root cause: Unconditional aggregation survives workflow-level cancellation even after all useful worker jobs exit.
Fix: Gate aggregates and metrics on !cancelled(). This explicit status function still runs against failed dependencies, preserving fail-closed result assertions for live workflows.
Verification: 35 CI workflow/path contract tests and actionlint pass, covering cancelled aggregation and failed dependency execution.
Problem: public manifest failures lacked endpoint diagnostics, and base Desktop inventory had drifted.
Root cause: HTTP errors were terse and recent Desktop source changes were not reflected in generated indexes.
Fix: report selected public response headers on failure and regenerate the inventory without changing Desktop behavior.
Verification: executable observer regressions and desktopinventory tests pass.
Problem: recovery could skip Pages and report success after an HTTP 403.
Root cause: a curl pipeline hid the failed read, and any unequal pointer was treated as newer.
Fix: share a checked manifest observation and require a strictly newer stable version before preserving the site.
Verification: executable HTTP-error and version-order regressions, publication ledger tests, and full release workflow contracts pass.
Problem: publication receives an empty source SHA after provenance checks. Root cause: generic camel-case conversion splits SHA into s_h_a, violating consumer output names. Fix: explicitly map all workflow fields and reject malformed source/control identities. Verification: real CLI GITHUB_OUTPUT regressions cover publish and rehearsal, fail with the original conversion, and pass with the repair; candidate and release workflow contracts pass.
Problem: independent Windows signing fails before installer assembly. Root cause: finalization deletes tracked NSIS inputs and packaging assumes a native build created its output directory. Fix: preserve committed packaging sources and initialize generated output at the packager boundary. Verification: fresh-checkout finalization covers amd64 and arm64, both original defects fail the new regression, all release contracts and diff checks pass.
Problem: candidate preparation fails before jobs start. Root cause: GitHub validates permissions of skipped nested publisher jobs against the read-only caller. Fix: grant the required permission union at the Desktop call and validate all candidate and publication callers. Verification: regression rejects original workflow; release workflow contracts, actionlint, and diff checks pass.
Problem:
The isolated Windows Bot package can terminate at process level with only a package FAIL line, leaving no test name or final event for root-cause analysis.
Root cause:
The Bot lane used the default go test text stream, which omits enough subprocess event detail when the test binary exits without a Go panic stack.
Fix:
Emit go test JSON for the existing single-process Bot lane and lock that diagnostic contract in the Windows selector test. This does not retry, split, or skip the package.
Verification:
- node --test scripts/windows-go-tests.test.mjs scripts/ci-workflow.test.mjs
- go run ./tools/repolint
- git diff --check
Problem:
Windows PR checks asserted retired sandbox and POSIX shell assumptions, and ACP/Bot failures were mixed into a parallel residual package lane.
Root cause:
The focused selector did not follow platform shell registration, current schema requirements, unsupported OS sandbox behavior, or the packages that own lifecycle cancellation.
Fix:
Resolve the registered primary shell in tests, assert platform-specific schema, split supported and unsupported sandbox contracts, centralize Windows PR selections with coverage assertions, run the full builtin package for shell/permission ownership changes, and isolate ACP and Bot package execution. Defender setup now fails closed so native crash evidence has a known prerequisite.
Verification:
Selector and workflow contract suites pass, builtin tests pass on macOS, and builtin, ACP, and Bot packages cross-compile for windows/amd64.
Problem: native upgrade evidence showed readable history with a stale Import and open action.
Root cause: canonical bind and resume paths assigned identity fields directly; frontend seed merges also retained omitted historical-source metadata.
Fix: route canonical binding through the identity setter, explicitly clear absent preparation metadata during seeding, and require native acceptance to reject leftover preparation controls.
Verification: reproduced failures in canonical binding, open, and both compatibility resume paths before the fix. Focused race tests, frontend banner and tab metadata tests, test typecheck, production build/budget, lint and inventory checks now pass. Startup payload remains within the existing adjusted budget.
Problem: packaged upgrade startup treated retained historical tabs as corrupt and expected automatic conversion.
Root cause: saved-tab reconciliation had no unprepared-source state, and upgrade acceptance still asserted the retired eager migration flow.
Fix: project discovered sources into neutral pending tabs, route the explicit action through normal navigation, and omit empty probe directories. Update native acceptance to click only on first import while preserving transcript, restart, and data-integrity assertions. Record the measured 243-byte startup metadata increment with a bounded raw-size allowance.
Verification: focused startup/reconciliation tests; historical and saved-tab race tests; TypeScript, navigation, banner, recovery and controller-meta tests; frontend production build and bundle checks; golangci-lint, repository lint and generated inventory. Native Windows acceptance remains for CI.