mirror of
https://github.com/esengine/DeepSeek-Reasonix.git
synced 2026-11-07 10:45:59 -10:00
studio
184
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
a14b28d1f7 |
fix(e2e): enforce provider credential gate
Summary: Write the repository provider secret to the ephemeral global Reasonix credential file used by the candidate process. Fail fixed-suite e2e runs when any task fails or is skipped while still posting the report. Why: The e2e workflow injected the key only as a process variable, which the credential boundary intentionally ignores. The harness then reported 0/5 but returned success, creating a false-green release signal. Testing: - bash scripts/release-workflows.test.sh - actionlint .github/workflows/e2e-bot.yml - git diff --check Privacy: - Scanned the public diff for credentials, local user paths, and private temporary paths. |
||
|
|
8675031b19 |
fix(config): stop tests reading the developer's real credentials (#7290)
Closes #2419. RunWithIsolatedUserState redirects HOME, USERPROFILE, XDG_* and AppData, which covers every location addressed by a path. The OS keyring is not one of those: it is per-user and has no environment variable in front of it, so legacy credential migration walked straight past the sandbox into the real store. On a machine with credentials saved, TestMigrateImportsLegacyStateHomeDotEnv- Credentials fails by printing the developer's actual API key where its own fixture was expected. On a clean CI runner the keyring is empty, so it passes — the suite only breaks for people who use the tool they are working on, which is why the failing package in #2419 stayed unexplained for two months. Substituting the lookup once in TestMain makes isolation the default for the package; the three cases that exercise the lookup already install their own. Two things verify it, because they fail in different places: scripts/credential-leak-check.mjs runs the suite and flags any credential printed that appears nowhere in the repo's sources — a value the tests did not write can only have come from the machine. It reported one leak before this change and none after, and it is the only check that can observe the bug at all, since it needs a populated keyring. TestKeyringLookupStaysOutOfTheRealStore asserts the substitution itself, so it holds on an empty CI keyring where the leak is invisible. Removing the substitution turns it red. Neither prints a credential; the tool reports a SHA-256 prefix and the guard reports a byte count. |
||
|
|
fe319846a1 |
test(windows): gate Scoop desktop launch path
Problem: The Windows release checks only exercised launcher path resolution in a unit test, so packaging regressions across Scoop installation, its current junction, and Start Menu shortcuts could still ship. Root cause: CI did not install a Reasonix fixture through the real Scoop CLI or launch the generated shortcut. Fix: Add an isolated Windows integration test that builds a versioned fixture, installs it with Scoop v0.5.3, verifies the current junction and shortcut contract, and launches the desktop marker through that shortcut. Run it on every Windows CI leg. Verification: - go test ./internal/desktoplauncher/... ./internal/installlayout/... - go vet ./internal/desktoplauncher/... ./cmd/reasonix-launcher - GOOS=windows GOARCH=amd64 go build ./cmd/reasonix-launcher - actionlint .github/workflows/ci.yml - git diff --check |
||
|
|
b75f2f0dbb |
fix(release): verify versioned Windows portable archives
Summary: - validate both legacy flat and versioned-v1 portable layouts - use the protected control-plane verifier during immutable tag recovery - keep exact Authenticode and payload hash checks for all six executables Verification: - bash scripts/release-workflows.test.sh - go -C desktop test . -run TestWindowsReleaseSignsPayloadBeforeRepackaging -count=1 - actionlint - ./scripts/cache-guard.sh - git diff --check |
||
|
|
df04b11b06 |
Merge pull request #7227 from SivanCola/feature/stable-only-release-flow
feat(release): keep one official Stable publication path / 保留单一正式版发布路径 |
||
|
|
ab24371d98 |
fix(release): bind stable tags to reviewed candidate
Problem: The stable helper could consume immutable version tags if main-v2 advanced while exact-SHA CI or the protected relay was starting. Manual CLI and Desktop recovery also still accepted RC tags despite the official-only release contract. Root cause: The helper sampled main-v2 once, pushed only tag refs, and the orchestrator required the tags to equal the later branch tip. The candidate was not proven to be the commit that changed the target reviewed Notes record. Fix: Bind normal candidates to their reviewed Notes change, revalidate exact push CI from the protected control plane, include main-v2 as a guarded ref in the atomic tag transaction, and accept the immutable candidate after later protected branch advances. Restrict manual CLI and Desktop recovery to official versions while retaining historical reusable-workflow parsing. Verification: - bash scripts/release-stable.test.sh - bash scripts/release-workflows.test.sh - node --test scripts/release-notes.test.mjs - actionlint on all active release workflows - site contract tests, SignPath contract tests, Desktop control-plane test, cache guard, and git diff --check |
||
|
|
08e8e79bcf |
Merge latest main-v2 release fixes
Problem: main-v2 advanced again while the updater conflict resolution was under verification, leaving the branch behind the current base. Root cause: PR #7222 merged an npm alias propagation fix after the first integration merge was prepared. Fix: Merge the new base commit without altering the resolved Desktop updater semantics. Verification: node --test scripts/finalize-npm-official-release.test.mjs; bash scripts/release-workflows.test.sh; git diff --cached --check. |
||
|
|
8847d5b9b8 |
feat(release): keep one official Stable publication path
Problem: The dual-channel release controls and the proposed GitHub App activation add operator choices, owner-only setup, and failure modes that are unnecessary now that users consume only official releases. Design: Retain the proven three-tag Stable orchestrator, retire public Preview relays, and add one exact-SHA helper that validates reviewed notes and push CI before atomically creating the CLI, npm, and Desktop tags. Keep historical Preview inputs readable only for compatibility. Experience: Prepare release accepts one version. After the notes PR merges, a maintainer runs one command and approves the release environment once. The website and public documentation expose only the official line and reasonix upgrade. Safety: Preserve immutable tags, protected control-plane checks, SignPath validation, idempotent recovery, npm provenance checks, and bounded alias convergence. No persisted runtime format changes are introduced. Verification: Release workflow contracts, atomic tag failure cases, SignPath contract tests, website public-surface tests, actionlint, cache guard, and diff checks pass locally. |
||
|
|
99cf86bdf1 |
Merge main-v2 and preserve atomic official updates
Problem: PR #7199 conflicted with the official single-channel updater changes on main-v2, which prevented GitHub from producing a merge ref and running the full CI matrix. Root cause: Both branches changed the Desktop updater settings, Wails contracts, configuration compatibility fields, and updater tests from the same older baseline. Fix: Merge main-v2 while retaining the single ApplyUpdateRequest flow, force every new UI operation onto the official stable-compatible channel, remove the retired channel switch helper and Safe Mode payload, and preserve request/version fences for stale async completions. Verification: GOWORK=off go test ./... and go vet ./... in both Go modules; focused go test -race for config, install layout, window state, settings, and updater paths; pnpm test:all and pnpm build; Windows amd64 Desktop and update-helper cross-builds; cache guard, cache-impact, and release workflow contract tests. |
||
|
|
75c4308810 | fix(release): wait for npm alias propagation | ||
|
|
dfc9f19563 |
chore(release): align legacy npm aliases
Problem: The bridge release advanced npm latest to 1.19.2 while the compatibility canary and next aliases still selected public prereleases. Root cause: The legacy Stable publisher owns latest only, and the single-channel finalizer is not active yet. Fix: Add a zero-input, release-approved one-time workflow that verifies all seven package records against the exact 1.19.2 candidate SHA before atomically aligning latest, canary, and next. Fail closed on provenance drift and verify every final alias. Verification: node --test scripts/finalize-npm-official-release.test.mjs bash scripts/release-workflows.test.sh go run github.com/rhysd/actionlint/cmd/[email protected] .github/workflows/migrate-npm-release-aliases.yml .github/workflows/ci.yml git diff --check |
||
|
|
9fede8b281 |
feat(desktop): finish v1.20 remaining fault tolerance, API and packaging
Problem: v1.20 still lacked local config recovery, kept StartupTracker/old split update bindings in the product surface, and packaged flat Windows/Linux layouts with Guard assumptions. Root cause: LoadForRoot failed closed on corrupt TOML; desktop still bound download/install as separate Wails methods; portable and Linux packaging still assumed a flat Guard release unit; helper still allowed a flat publish fallback. Fix: - Config load: corrupt user config uses last-known-good in memory (else built-in defaults) without rewriting the original file; project config and MCP JSON failures are isolated with LoadWarnings. - Desktop surfaces configWarnings with Open config / Reload / doctor repair banner actions; records LKG after UI ready and on clean shutdown. - Remove StartupTracker from launch control (observe previous run for crash diagnostics only). - Wails update surface is ApplyUpdateRequest only; download/install are private. - Helper versioned-v1 activation is mandatory for staged Windows payloads. - Windows portable packaging/verification and Linux tarball use versioned/thin launcher contracts; macOS bundle rejects Guard. - Fault-injection and legacy flat migration fixture tests for installlayout. Verification: - go test ./internal/config ./internal/installlayout ./cmd/reasonix-launcher ./cmd/reasonix-legacy-migrator ./internal/repair - cd desktop && go test ./cmd/update-helper/ and focused package tests - pnpm --dir desktop/frontend test:updater (31 passed) Out of scope for local automation: Windows DPI 100x soak, Preview 72h gate, production migration success rate, and live signed-installer UI coverage. |
||
|
|
79449ef801 |
feat(desktop): versioned helper activation, drop Guard, NSIS dual layout
Problem:
The Windows update helper still published a flat six-member release unit with
Guard health recovery. The Guard recovery product remained as a first-class
binary and packaging still installed Guard as the permanent entry point.
Root cause:
Post-stage publish was hard-wired to flat InstallRoot members and pending
receipt recording. Packaging and docs still treated reasonix-guard as the
launcher/recovery surface.
Fix:
- Prefer versioned-v1 activation from staged NSIS payloads: write
versions/<ver>/{desktop,cli,helper}, swap current.json, place thin launcher
and portable alias at InstallRoot, remove flat desktop/guard leftovers.
- On versioned success, clear the pending transaction without health probation
and relaunch via reasonix-launcher.
- Resolve InstallRoot and update-helper source for versioned trees; claim
pending targets from the prepared transaction rather than hard-coded flat
paths.
- NSIS dual mode: /REASONIXSTAGE=1 still extracts the six-member flat payload
for 1.18-1.19.1 helpers; normal install writes versioned layout and does not
persist Guard.
- Delete cmd/reasonix-guard. Compatibility payloads still build
reasonix-guard from reasonix-legacy-migrator only. Point recovery docs at
reasonix doctor.
- Linux .desktop Exec uses reasonix-launcher.
Verification:
- go test ./internal/installlayout ./cmd/reasonix-launcher
./cmd/reasonix-legacy-migrator ./internal/repair
- cd desktop && go test ./cmd/update-helper/
- focused desktop packaging/window/updater tests
- GOOS=windows go test -c ./cmd/update-helper and desktop package
|
||
|
|
0e78ef425e |
feat(desktop): start v1.20 Guard removal and versioned update layout
Problem: Desktop shutdown can panic on Windows when Wails queries window geometry after DPI reports 0. Product Safe Mode and Guard health policy force global capability loss and leave users stuck on pending-update loops. The flat install layout cannot support atomic version activation. Root cause: beforeClose/shutdown called WindowGetSize/Position/IsMaximised on a tearing-down WebView2 surface. StartupTracker and REASONIX_SAFE_MODE drove a process-wide degraded mode. Updates relied on a single pending transaction with health probation and Guard rollback. Fix: - Persist window state only from frontend reports; Go rewrites last-known state without native queries during hide/shutdown. Accept Windows border origin x=-8,y=-8 and add regression coverage. - Introduce installlayout (current.json schema 1, staging activation, version GC) plus a permanent thin reasonix-launcher and a one-shot legacy migrator (compat binary name reasonix-guard). - Add ApplyUpdateRequest single-action update path, install_layout field validation, and frontend update-and-restart UX. - Make SafeModeRequested always false; strip desktop startup recovery that forced Safe Mode; keep doctor/crash diagnostics non-gating. - Point desktop-build Windows launcher/migrator builds at the new cmds. Verification: - go test ./internal/installlayout ./cmd/reasonix-launcher ./cmd/reasonix-legacy-migrator ./internal/config ./internal/boot - focused desktop window-state/updater/safe-mode tests - pnpm --dir desktop/frontend test:updater (31 passed) Remaining for full v1.20.0 gate: NSIS dual-mode packaging, versioned payload activation in update-helper, delete cmd/reasonix-guard source, full config last-known-good resilience, macOS/Linux packaging and release docs. |
||
|
|
58bae90bf5 |
Merge latest main-v2 into macOS icon fix
Integration:
- Update PR #7123 to main-v2 at
|
||
|
|
04984cdfc0 |
fix(release): keep control checkout outside git status
Problem: The first stable CLI publication failed before GoReleaser could build because the protected release-control checkout appeared as an untracked directory in the immutable product checkout. Root cause: Preview recovery reused an existing CLI release and never exercised GoReleaser dirty-state validation. A new stable release does, and the nested control-plane checkout made the otherwise clean product tree dirty. Fix: Add only the exact release-control directory to the product checkout local exclude file, verify the nested checkout is ignored, and fail closed if any other product-tree dirt remains. Resolve the common Git directory absolutely so normal clones and linked worktrees behave consistently. Verification: - bash scripts/release-workflows.test.sh - actionlint v1.7.7 release workflows - bash scripts/cache-guard.sh - bash -n scripts/release-workflows.test.sh - git diff --check |
||
|
|
b377ec5773 |
fix(release): accept immutable legacy desktop pointers
Problem:\nPreview recovery rejected the existing public pointer even though its assets use the official immutable version directory.\n\nRoot cause:\nThe fallback validator replaced every legacy Preview asset base with the historical rolling desktop-preview directory. This made version-bound legacy manifests with a null release notes URL impossible to validate.\n\nFix:\nTry the immutable legacy layout first, then retain the rolling directory as a second Preview-only compatibility fallback. Add workflow contract coverage and a null release-notes manifest regression.\n\nVerification:\n- bash scripts/release-workflows.test.sh\n- actionlint v1.7.7 release workflows\n- bash scripts/cache-guard.sh\n- git diff --check |
||
|
|
d3d94d15c3 |
Fix Desktop recovery manifest ordering
Problem: Desktop Preview recovery compared a rebuilt manifest with the immutable published manifest before adopting the validated published record. Root cause: The directory comparison ran before the existing manifest was validated against its payload sizes and digests and copied into the recovery candidate. Fix: Validate and adopt the immutable manifest before comparing directories, and lock the ordering with static and behavioral regression coverage. Verification: - bash scripts/release-workflows.test.sh - actionlint for release-desktop.yml - bash scripts/cache-guard.sh - bash syntax and git diff --check |
||
|
|
d1385aa47e | Harden authenticated Desktop release recovery | ||
|
|
a47909afb3 |
Preserve valid minisign signatures during recovery
Problem: Rebuilding an immutable Desktop candidate produces byte-distinct minisign files, so recovery rejects an otherwise identical published directory. Root cause: The directory verifier treated nondeterministic detached signatures as reproducible payload bytes. Fix: Verify candidate and published signatures with the embedded public key, allow only verified signature-byte differences, and copy existing signatures into the recovery set so immutable objects are preserved. Verification: Release workflow contracts and Actionlint pass; public Preview Linux and Windows installer assets verify against the embedded key. |
||
|
|
fb705e02b1 |
Run CLI recovery policy from control plane
Problem: Historical Preview recovery checks out the immutable product candidate, so recovery scripts added after that tag are absent. Root cause: The CLI publisher mixed candidate source and publication control policy in one checkout, unlike the Desktop publisher. Fix: Add a protected workflow-SHA control checkout and run the CLI publication decision from it while keeping product builds pinned to the approved candidate. Verification: release-workflows.test.sh and Actionlint pass; regression assertions reject candidate-controlled publication policy. |
||
|
|
47986edb02 | Accept immutable paths in legacy Preview validation | ||
|
|
13e772af35 | Make CLI release recovery idempotent | ||
|
|
fffbce9d53 | fix(desktop): restore macOS icon safe area | ||
|
|
0125c6c4f0 |
Add explicit Preview release recovery
Problem: A failed Preview publisher cannot consume a workflow fix by rerunning the original Actions attempt, while the immutable Preview tag must not be moved to the repaired main-v2 commit. Root cause: The documented all-surface Preview recovery path still required the Preview tag to equal current main-v2, making recovery of an ancestor candidate impossible. Fix: Add an opt-in Preview recovery input, keep the control workflow pinned to protected current main-v2, allow only a historical Preview tag that remains on main-v2 history, and propagate the recovery authorization to the CLI publisher. Verification: - bash scripts/release-workflows.test.sh - bash -n scripts/resolve-preview-release.sh scripts/release-workflows.test.sh - git diff --check |
||
|
|
1ec831e37c |
Fix orchestrated Desktop mirror tag validation
Problem: Orchestrated Desktop releases passed an empty direct tag input to the R2 mirror revalidation step, blocking Preview publication and the next Stable mirror. Root cause: The mirror job used inputs.tag even though Preview and Stable orchestrators authorize releases through inputs.approved_cli_tag. Fix: Revalidate the orchestrator-approved CLI tag in the mirror job and lock the mapping with a workflow contract regression assertion. Verification: - bash scripts/release-workflows.test.sh - git diff --check |
||
|
|
fae6f7e295 |
fix(release): authorize preview desktop orchestration
Problem: - The protected Preview orchestrator passed orchestrated=true to the Desktop release workflow. - The immutable candidate resolver treated every orchestrated call as Stable and rejected every Desktop Preview before signing preflight. Root cause: - release-desktop.yml did not forward the trusted orchestrator kind to the resolver. - The resolver had a hard-coded Stable-only check, and contract tests covered only standalone Preview resolution. Fix: - Forward and validate the stable/preview orchestrator identity in every candidate resolution step. - Permit the Preview orchestrator to authorize its approved SHA, including recovery after main-v2 advances, while retaining ancestry and tag checks. - Add success, mismatch, and recovery regression coverage. Verification: - bash scripts/release-workflows.test.sh - git diff --check - Privacy scan of the patch |
||
|
|
249ffd7936 |
fix(release): make npm publication resumable
Problem: An interrupted Stable or Preview event could publish some or all npm packages before another surface failed. Rerunning the documented orchestrator then failed on immutable versions that already existed, so postflight could not create the release marker. Root cause: npm/build.mjs always published every package directly under the public dist-tag and required that tag to equal the recovering version. Fix: Record the approved candidate SHA in every package, reuse only matching immutable versions, fill missing packages under a temporary tag, and advance public dist-tags monotonically after the full package set exists. Conflicting provenance fails closed and stale recovery cannot roll a newer channel backward. Verification: - node --test npm/publish.test.mjs - bash scripts/release-workflows.test.sh - actionlint release workflows - npm six-platform dry build - ./scripts/cache-guard.sh |
||
|
|
413422feee |
fix(release): enforce unified Preview publication
Problem: - Preview events for different versions could overlap and let an older npm canary publish last. - CLI, Desktop, and npm child workflows still exposed standalone public Preview paths that bypassed the all-surface release marker. Root cause: - The Preview concurrency key included the version tag, while npm had no channel-level serialization. - Child workflow dispatch inputs and resolvers treated Preview publication as independently recoverable. Fix: - Serialize every Preview event and npm dist-tag publisher, and require public CLI, Desktop, and npm Preview releases to come from release-preview.yml. - Keep Stable/RC recovery and non-publishing Desktop signing checks available, document unified Preview recovery, and add regression coverage. Verification: - bash scripts/release-workflows.test.sh - actionlint v1.7.7 on all release workflows - git diff --check |
||
|
|
c54516a516 |
fix(release): preserve legacy manifests and exact notes
Problem: - Existing CLI and Desktop manifests predate exact release-note URLs, RC notes had no supported catalog path, and unpublished exact routes could 404. - The Preview SignPath call-graph test also retained the old allowlist. Root cause: - New manifest validation and byte comparisons did not distinguish legacy persisted objects from newly generated candidates. - Standalone RCs were treated as public changelog identities, and version routes were generated only after publication markers. Fix: - Add strict and legacy validation plus semantic comparators that normalize only the missing release-notes field and preserve immutable Desktop manifests. - Reuse Stable notes for standalone RCs, generate safe unpublished redirects, and update the SignPath contract test. - Add focused workflow, manifest, route, and RC regression coverage. Verification: - bash scripts/release-workflows.test.sh - go test ./... and go vet ./... - desktop go test ./... and go vet ./... - site npm test and npm run build - node --test scripts/release-notes.test.mjs - actionlint with the repository CI allowlist - live v1.18.0 CLI and Desktop manifest compatibility checks |
||
|
|
b6da01dbdc |
feat(release): separate Stable and Preview histories
Context: Stable and Preview need independent reviewed identities while CLI, Desktop, npm, and changelog publication remain one protected release event. Changes: - Add a protected Preview orchestrator with a shared ordinal and SignPath preflight. - Bind reviewed release notes to immutable all-surface publication markers. - Route downloads and changelog archives by channel with accessible theme-aware navigation. - Harden release authorization, manifests, tests, and operator documentation. Compatibility: - Legacy catalog entries without managed status remain readable and public. - Existing Stable pointers remain unchanged, and Preview continues mirroring the legacy canary pointer for older Desktop clients. - No persisted user state or Wails JSON contract changes. Verification: - bash scripts/release-workflows.test.sh - node --test scripts/release-notes.test.mjs - cd desktop && go test ./cmd/sign ./internal/update - cd site && npm test - cd site && npm run build - actionlint for all release workflows |
||
|
|
7411f9b16c |
fix(ci): take the sweep's age from the whole report, not the version field
Validating the sweep against live data before enabling it turned up two ways it would have aged current reports. #6563 filled the form's version field with 1.0.0 and named the real build, v1.17.13, further down in the reproduction steps. 1.0.0 shipped, so the published-tag check could not catch it, and the report would have been swept as seventeen minors old. Age now comes from the newest released version mentioned anywhere in the body: a report cannot be older than the newest build it names. Filtering to published versions keeps Node versions and window sizes out. Reading the whole body also let feature requests in, and "does it still reproduce" is a question only a defect report can answer, so the sweep now requires the bug label. The target count is unchanged at 160, but the membership is not: six typo'd reports and seven feature requests left, and old reports whose only version evidence was in prose came in. |
||
|
|
cd184dabe5 |
feat(ci): ask before closing reports too old to confirm
65% of the open tracker has never had a reply, and 160 open bug reports name a release nine or more minors back — old enough that nobody can say whether the symptom survived. Leaving them open costs the reports that can be acted on; closing them on age alone would be a guess dressed up as triage. So: ask, wait 21 days, close only what nobody answered. A close is "not planned" with reopening invited, and any comment after the ask — from anyone — takes the issue back out of the sweep. Severity labels (data-loss, security, crash) and anything a maintainer already replied to never enter it at all, because a bot must not time out a judgement a human already made. The version check is against the published tag list, not the version format. A dry run first targeted six issues reporting "1.7.18", "1.7.13" and "1.7.10": 1.7.x only ever reached 1.7.0, so those are dropped digits from 1.17.x — current releases that parse cleanly as eleven-minor-old ones. Six current reports would have been swept as ancient. Posting stays behind the STALE_SWEEP_ENABLED repository variable, so merging this cannot start commenting on its own. |
||
|
|
88925b5caa |
feat(ci): ask reporters to verify on the release that carries their fix
A PR writing "Fixes #N" auto-closes its issue on merge. One writing "Refs", "Related" or "Addresses" does not, so the report stays open waiting for a follow-up after the release that nobody sends. Auditing the last 400 merged PRs found 33 open issues referenced by shipped work; only three could be closed on the evidence, and most of the rest were blocked on nothing but an unanswered "please re-test". On every published release this now diffs against the previous tag in the same series, collects the issues those PRs referenced, and asks each still-open reporter to verify. It never closes anything — deciding a report is resolved stays human. Two traps the audit hit are pinned by tests rather than left to be rediscovered: - "owner/repo#123" is an upstream reference. Matching it once pulled a wailsapp/wails issue number in as if it were ours. - GitHub's issues API answers for pull requests too, so a consolidation PR listing "PR #5576 by @x" reads as a perfectly good open issue. A dry run against desktop-v1.18.0 showed 26 of 40 references were pull requests; without the check the bot would have asked their authors to re-test. The PR template now says where to put the keyword, since a "- Fixes #123" list item is exactly the silent no-op that produced this backlog. |
||
|
|
b8c82972f2 |
Harden Guard repair preview confirmation / 加固 Guard 修复预览确认绑定 (#6973)
* fix(guard): bind repair apply to confirmed preview * Harden Guard preview confirmation binding Problem: non-interactive apply-plan could accept a bare --yes, while preview IDs omitted derived-state contents and full pending-update identity. Root cause: confirmation hashes covered only display previews and were checked once before the plan loop, so state-dependent actions could drift after confirmation. Fix: require a prior preview ID for non-interactive apply-plan, bind opaque file and transaction state, re-check each action immediately before execution, and preserve unrelated multi-action behavior. Update English and Chinese recovery docs. Verification: go test ./internal/repair ./cmd/reasonix-guard; go test -race ./internal/repair; go vet ./...; go test ./...; git diff --check. Co-authored-by: SivanCola <[email protected]> * Serialize confirmed Guard repair mutations Problem: preview IDs were rechecked before mutation, but another process could still replace a target during the final rename window or race on last-repair.json, losing or mixing undo records. Root cause: repair actions lacked a shared transaction lock and per-target cross-process locks, while confirmed snapshot/config bytes were read again after approval and replacement writes could overwrite a concurrent recreation. Fix: serialize repair bookkeeping before canonical target locks, revalidate confirmed state inside the lock and around renames, publish restored content with create-only atomic writes, bind complete pending-update identity, preserve concurrent writers, and keep undo under the same lock order. Add focused coverage for wait/recheck, rename windows, snapshot bytes, unreadable LKG nodes, transaction serialization, and rollback identity. Verification: go test ./internal/fileutil ./internal/config ./internal/repair ./cmd/reasonix-guard; go test -race ./internal/fileutil ./internal/repair; go vet ./...; env -u DEEPSEEK_API_KEY go test ./...; Windows test-binary cross-compilation for internal/repair and cmd/reasonix-guard; git diff --check. Co-authored-by: SivanCola <[email protected]> * Normalize Windows repair lock test paths Problem: the Windows CI test waited for a raw backslash path while the lock hook receives the canonical lower-case slash form, so the test timed out before exercising the recheck. Root cause: the regression test compared platform-specific input text instead of the production lock key representation. Fix: normalize test hook paths with the same Windows case and separator rules used by repair mutation locks. Verification: focused repair lock tests pass on macOS; Windows amd64 test-binary compilation passes. Co-authored-by: SivanCola <[email protected]> * test(jobs): remove StartForSession race Keep the worker blocked while assertions inspect its state and protect job fields with the existing mutex. This prevents the race job from observing concurrent completion writes when validating accepted input. Co-authored-by: PaulDing98 <[email protected]> * fix(repair): bind target identity into Guard preview confirmation Problem: - previewId hashed only exported preview fields and content-based state IDs, so identical reasonix.toml content in two project roots produced the same confirmation and apply-plan --yes could mutate the unconfirmed project. - Mutation locks used Abs/Clean only, so symlink path aliases (and macOS case variants) took different locks for the same real node. - rollback_update bound only the pending transaction JSON and skipped target locks, so a stable pending update could still roll back an unconfirmed live release unit. - When a target was recreated after quarantine rename, the move was not recorded in last-repair, so UndoLastRepair could restore an unrelated prior repair. Root cause: - Confirmation identity omitted canonical target identity; lock keys and rollback binding did not converge on real paths or live release-unit state; post-rename failure paths returned before persisting the already-moved node. Fix: - Embed an irreversible canonical target identity in per-file state IDs used by previewId, while verifying quarantined nodes against the original path identity. - Resolve existing symlink parents and case-fold on Windows/macOS when building mutation-lock keys and target identities. - Bind current release-unit file states into rollback preview state, take shared target locks during prepare/rollback, and re-check under those locks. - Persist the quarantine/move transaction when a concurrent recreate is detected so undo restores the confirmed node and retains the rewrite as redo. Verification: - go test ./internal/repair/ -count=1 - go test -race ./internal/repair/ ./internal/fileutil/ -count=1 - go test ./cmd/reasonix-guard/ -count=1 - go vet ./internal/repair/ * fix(repair): close symlink, app-bundle, and macOS handoff confirmation gaps Problem: - canonicalRepairPath EvalSymlinks'd the full path, so two leaf symlinks sharing a referent produced the same previewId, and renaming a confirmed symlink leaf changed its identity and made apply fail. - App-bundle rollback bound only directory kind/mode for the live .app and omitted the backup tree, so interior executable drift still accepted an old previewId and rolled back. - macOS self-update held mutation locks only while writing the pending transaction; the actual mv/ditto ran in a shell helper that ignored the shared flock, racing Guard rollback into mixed bundles. Root cause: - Leaf nodes are what Lstat/Rename mutate, but identity resolved through them; directory release units need tree digests; handoff and repair used different processes without a shared lock holder over the swap window. Fix: - Resolve only parent-directory symlinks and reattach the original leaf name for mutation locks and preview target identity. - Bind full tree digests for directory release-unit nodes and include backup paths in pending-update preview state. - Replace the macOS shell handoff with a detached self-subprocess that waits for the old PID, then holds LockRepairMutations around the directory swap. - Export LockRepairMutations/Timeout for desktop handoff and add regressions. Verification: - go test ./internal/repair/ -count=1 - go test -race ./internal/repair/ -count=1 - go test ./cmd/reasonix-guard/ -count=1 - cd desktop && go test -count=1 -run TestMacUpdate . - go vet ./internal/repair/ and desktop * fix(repair): secure updater claims and path case locks Problem: The detached macOS updater trusted filesystem paths from command-line arguments, and repair identities folded path case by operating system rather than by the target filesystem. Root cause: The handoff only checked that a pending-update file existed before swapping bundles. Path canonicalization assumed every macOS and Windows filesystem was case-insensitive. Fix: Persist optional handoff paths and owner identity in the pending transaction, claim them under strict pending and target locks, revalidate the staged bundle before swapping, and remove filesystem paths from child arguments. Detect case sensitivity from the actual macOS directory or Windows per-directory flag before folding lock and preview identities. Verification: - go test ./internal/repair/ -count=1 - go test -race ./internal/repair/ ./internal/fileutil/ -count=1 - go test ./cmd/reasonix-guard/ -count=1 - cd desktop && go test -count=1 -run TestMacUpdate . - cd desktop && go test -race -count=1 -run TestMacUpdate . - go vet ./internal/repair/ - cd desktop && go vet . - GOOS=windows GOARCH=amd64 go test -c ./internal/repair * fix(repair): scope case probe helper by platform Problem: The Linux golangci-lint job reported existingRepairPathParent as unused. Root cause: The helper lived in a common file while its only callers were excluded from Linux builds by Darwin and Windows build tags. Fix: Move the helper into a Darwin-or-Windows platform file so it is compiled exactly where the filesystem case probes use it. Verification: - golangci-lint run --timeout=5m - go test ./internal/repair/ -count=1 - cd desktop && go test -count=1 -run TestMacUpdate . - GOOS=windows GOARCH=amd64 go test -c ./internal/repair * fix(repair): preserve verified rollback recovery Problem: - File rollback could install backup bytes that changed after preflight validation. - A failed macOS backup restore still cleared pending recovery state and cleanup material. - NFC and NFD aliases on APFS produced different repair lock and preview identities. Root cause: The staged-copy hash was ignored, macOS rollback cleanup was unconditional, and canonical repair paths handled case aliases without filesystem-aware Unicode normalization. Fix: Verify the staged rollback bytes against the transaction hash before mutating live targets. Make macOS rollback compensating and retain pending, backup, and staging state when restoration fails. Normalize repair identities to NFD only on APFS and HFS while preserving distinct names on other filesystems. Verification: - go test ./internal/repair/ -count=1 - go test -race ./internal/repair/ ./internal/fileutil/ -count=1 - go test ./cmd/reasonix-guard/ -count=1 - cd desktop && go test ./... -count=1 - cd desktop && go test -race -count=1 -run TestMacUpdate . - go vet ./internal/repair/ - cd desktop && go vet . - golangci-lint run --timeout=5m - GOOS=windows GOARCH=amd64 go test -c ./internal/repair * Harden transactional updater handoffs Problem: Repair and self-update paths could continue without a pending-state lock, apply a prepared update after its transaction or release unit changed, or restore an unverified macOS bundle tree. Linux also persisted a CLI sibling that transaction validation rejected. Root cause: Pending transitions used a best-effort unbounded lock, file updaters locked paths without claiming the exact prepared transaction, and app-bundle handoff/rollback validation did not bind both the staged tree and original backup tree across the rename window. Fix: Use bounded fail-closed pending locks and a consistent pending-to-target lock order. Claim Linux and Windows replacements by version, creation identity, platform, and canonical release-unit paths. Bind macOS staging, original, installed, and backup trees; recover rejected handoffs only when the original bundle is still verified; protect backup paths and reject symlink escapes. Add focused concurrency, drift, rollback, compatibility, and cross-platform tests. Verification: - go test ./internal/repair/ ./internal/fileutil/ ./cmd/reasonix-guard/ -count=1 - go test -race ./internal/repair/ ./internal/fileutil/ ./cmd/reasonix-guard/ -count=1 - cd desktop && go test ./... -count=1 - cd desktop && go test -race ./... -count=1 - go vet ./... and cd desktop && go vet ./... - golangci-lint run --timeout=5m ./... - Windows amd64/arm64 test-binary compilation for repair, desktop, and update-helper Co-authored-by: SivanCola <[email protected]> * Complete fail-closed repair and updater transactions Problem: Repair, undo, and self-update entry points could act on state that changed while waiting for locks. File-update completion also needed a crash-safe installed-unit binding, and platform handoffs could execute mutable or incompletely claimed payloads. Root cause: Invocation identity was sampled too late or incompletely, mutable pending state doubled as installed state, cleanup paths did not consistently compare ownership, and Windows/macOS handoffs did not bind every source, backup, staging, and release-unit transition. Fix: Bind every mutating invocation before lock waits and recheck it under a pending-to-target lock order. Persist create-only installed-state sidecars, preserve recovery material on ambiguity, use atomic no-replace publication, validate complete app and file trees, isolate and hold the verified Windows installer through execution, and make rollback/undo crash-retryable. Verification: go test ./internal/repair ./internal/fileutil ./cmd/reasonix-guard -count=1 cd desktop && go test ./... -count=1 Windows amd64 and arm64 update-helper test-binary compilation gofmt -l internal/repair desktop git diff --check Co-authored-by: SivanCola <[email protected]> * Document fail-closed legacy update recovery Problem: Recovery documentation described additive update state as generally backward compatible and a Linux comment still described the obsolete mutable-pending design. Root cause: The text was not updated when installed release-unit state moved to a transaction-unique sidecar and legacy destructive actions began requiring explicit ownership proof. Fix: Document immutable pending state, crash-safe sidecar recovery, and the distinction between readable legacy transactions and authorization for handoff, healthy commit, or app-bundle rollback. Verification: git diff --cached --check Focused repair and updater tests, full race suites, vet, lint, and cross-platform compilation passed against the merged main-v2 baseline. Co-authored-by: SivanCola <[email protected]> * Close updater execution and compensation races Problem: - Generic failure-marker writes could let a stale invocation overwrite a newer transaction marker. - Legacy app rollback could move a symlink backup before rejecting it, and file publish could leave a tampered staged node live. - Windows self-update could execute a helper that changed after transaction prepare, overwrite a concurrently allocated cache path, or add a portable alias that was absent before. - macOS updater commands were resolved through PATH. Root cause: Mutation ownership was not rebound under the pending lock at every public entry point. Helper validation proved only PE architecture, staged-file verification followed symlinks, and helper cache publication used replace semantics. Fix: Rebind generic failure markers after lock acquisition, reject non-directory app backups before mutation, require regular-file staged payloads, compensate failed publication by retaining the rejected node and restoring the verified prior file, and preserve pending recovery state. Bind the Windows helper to the exact prepared release-unit SHA-256, create a unique cache node without replacement, freeze it through process creation, preserve the original portable-alias shape, and use absolute macOS system command paths. Verification: - go test ./... -count=1 - go test -race ./internal/repair ./internal/fileutil ./cmd/reasonix-guard -count=1 - cd desktop && go test ./... -count=1 - cd desktop && go test -race ./... -count=1 - go vet ./... and cd desktop && go vet ./... - golangci-lint 2.12.2 for root and desktop - cache guard: 8/8 pass - Windows amd64/arm64 repair, desktop, and update-helper test-binary compilation - Linux amd64 repair test-binary compilation Co-authored-by: SivanCola <[email protected]> * Tidy desktop updater dependencies Problem: The desktop CI go-mod-tidy gate failed because the updater no longer imports github.com/minio/selfupdate, but the module files still retained that dependency and its stale transitive checksums. Root cause: The transactional updater implementation replaced the selfupdate package without running the desktop module tidy gate afterward. Fix: Remove the unused selfupdate requirement and the transitive checksum entries that are no longer reachable from the desktop module graph. Verification: - cd desktop && go mod tidy (idempotent) - cd desktop && go vet ./... - focused workspace-rebuild timing tests: 10/10 pass - git diff --check Co-authored-by: SivanCola <[email protected]> * Harden prepared repair journals and Windows release-unit signing Problem: - Repair mutations could leave ambiguous undo state after a crash between rename and last-repair.json publication. - Concurrent writers could replace a pending journal or cleanup node after ownership checks, turning cleanup into deletion of unowned material. - Windows portable/installer updates did not bind signed payload identity through packaging, and Linux release-unit publication did not return exact installed-state receipts. Root cause: Undo bookkeeping treated last-repair.json as the only durable intent, cleanup used path-based remove after verification, and Windows packaging trusted unsigned payload layout without a create-only signed manifest contract. Fix: - Introduce create-only pending-repair.json write-ahead journals, promote or discard them under the repair lock, and fail closed on ambiguous recovery. - Displace cleanup targets to unique siblings, re-verify identity, and never unlink a pending journal that changed before cleanup. - Bind Windows signed payload manifests through packaging/signing and require exact installed-state receipts for Linux release-unit publication. Verification: - go test ./internal/repair -count=1 (pass) - focused security coverage for prepared intent, pending cleanup ownership, legacy healthy/rollback fail-closed, and macOS staging cleanup Co-authored-by: SivanCola <[email protected]> * fix(ci): use resolved version for payload manifest Problem: The Windows payload manifest step referenced a nonexistent ver step output, so actionlint rejected the release workflow. Root cause: The build job receives the release version from needs.resolve.outputs.version, but the step used steps.ver.outputs.version. Fix: Bind the signed payload manifest to the resolved release version output. Verification: actionlint v1.7.7 and scripts/release-workflows.test.sh pass. * fix(repair): bind prepared create ownership to published Windows modes Problem: - Windows CI rejected undo after a crash following snapshot create: the restored config file remained because CreatedStateID ownership failed. - Desktop packaging contract tests still expected steps.ver.outputs.version after the release workflow switched to needs.resolve.outputs.version. Root cause: - Prepared create intents predicted FileMode 0o600, but Windows Lstat reports regular files as 0444/0666. Undo then treated the published file as an unowned concurrent write and left it in place. - The packaging test was not updated with the resolved-version workflow fix. Fix: - Predict create-state IDs with platform-published file modes and rebind the pending journal to the exact on-disk node after AtomicCreateFile. - Align the Windows signing packaging contract test with the resolve job version output. Verification: - go test ./internal/repair -count=1 - go test ./internal/repair -run TestPreparedCreateStateIDMatchesAtomicCreate,TestApplyRepairPlanPersistsMissingSnapshotTargetBeforeCreate - cd desktop && go test -run TestWindowsReleaseSignsPayloadBeforeRepackaging . Co-authored-by: SivanCola <[email protected]> * fix(repair): reject prepared create ownership drift Problem: A file replaced between AtomicCreateFile and ownership rebinding could be claimed by the repair transaction and removed by undo. Root cause: The pending journal was rewritten to bind whatever state happened to be present after publication instead of proving it matched the precomputed owned state. Fix: Keep the write-ahead ownership binding immutable and fail closed when the published path differs; add a regression test that preserves a concurrent replacement through undo. Verification: focused crash/create tests, go test ./internal/repair, repair/fileutil race tests, Guard tests, go vet, and Windows repair test cross-compile pass. --------- Co-authored-by: SivanCola <[email protected]> Co-authored-by: PaulDing98 <[email protected]> |
||
|
|
ac814a5040 |
fix(release): complete and harden Stable/Preview channels / 补全并加固正式版与预览版渠道 (#6997)
* complete stable and preview channel loop * fix(site): harden release-channel fallbacks and version scopes Problem: Website CLI Stable fallbacks pointed at GitHub releases/latest asset URLs, incomplete GitHub releases were turned into 404 download links, and Desktop/CLI Stable races overwrote every .rxv version chip. Root cause: CLI releases set make_latest:false so Latest belongs to Desktop Stable; releaseAssetMap synthesized missing CLI assets; updateStableVersion was shared across both product surfaces. Fix: - CLI fallback always uses the releases page, never /releases/latest. - Require all 7 CLI assets before accepting a release (match Worker). - Scope package-manager .rxv updates to CLI Stable only; desktop version chips use data-release-version="desktop". Verification: node --test site/src/scripts/release-channels.test.mjs site/src/scripts/download-link.test.mjs * fix(desktop): unify update channel controls Problem: The desktop settings page separated channel selection, update status, and install actions across detached rows. Root cause: The channel picker and updater state machine were rendered as independent controls, so switching channels did not read as one actionable workflow. Fix: Merge current version/status, Stable and Preview selection, manual refresh, auto-check guidance, and state-specific download/install actions into one compact settings flow. Add the localized channel auto-check hint for English, Simplified Chinese, and Traditional Chinese. Verification: pnpm --dir desktop/frontend run typecheck; pnpm --dir desktop/frontend run check:css; pnpm --dir desktop/frontend run test:updater (18 passed); pnpm --dir desktop/frontend run build; git diff --cached --check. * feat(cli): persist release channel selection Problem: CLI users had to repeat the verbose --channel flag, and a plain upgrade always returned to Stable instead of following the user's selected public channel. Root cause: The native updater had no user-global channel preference, while the website and docs exposed only one-off flag syntax. Fix: Add a user-global [cli].update_channel setting, support upgrade stable|preview as persistent switches, retain --channel as a one-off compatibility override, reject conflicting arguments, and synchronize English/Chinese docs plus website commands. Verification: - env -u DEEPSEEK_API_KEY GOCACHE=/private/tmp/reasonix-go-cache go test ./... - env GOCACHE=/private/tmp/reasonix-go-cache go vet ./internal/cli ./internal/config - npm test (site) - npm run build (site) - git diff --check * fix(release): keep CLI channel pointers monotonic Problem: Older Stable recovery runs and out-of-order Preview publishers could overwrite a newer public CLI channel pointer. Root cause: The release workflow wrote cli/<channel>/latest.json unconditionally and had no channel-scoped serialization or strict version comparison. Fix: Serialize CLI releases by channel, publish immutable per-tag metadata first, validate the current pointer, and move it only when a strict Stable or Preview version tuple is newer. Verification: bash scripts/release-workflows.test.sh; bash -n scripts/compare-cli-release-tags.sh scripts/release-workflows.test.sh; git diff --check. * fix(config): preserve symlinked config files Problem: Persisting the selected CLI update channel could replace a symlinked user config with a regular file. Root cause: AtomicWriteFile renamed a temporary file over the symlink path instead of the symlink's final target. Fix: Resolve complete config symlink chains before atomic writes and route both incremental saves and Config.WriteFile through the shared helper. Verification: go test ./internal/config; focused writable-target and multi-level symlink regression tests; go test ./...; go vet ./internal/cli ./internal/config. * fix(release): harden stable and preview end-to-end Problem: The Stable/Preview loop still allowed stale or cross-channel release pointers, unsafe Desktop release origins and retries, updater events detached from their checked target, and concurrent config writers that could lose user changes. Root cause: Release publication lacked one immutable candidate and strict manifest/recovery contracts; Desktop operations were not bound to request, channel, and version; config locks followed logical paths and several writers loaded stale or malformed snapshots. Fix: - authorize Desktop candidates from protected main-v2, separate the current control plane from recovered source commits, validate immutable assets, and make GitHub/R2 publication monotonic and resumable - bind Desktop checks, downloads, verification, and installs to one request/channel/version state machine with strict size and manifest validation - validate CLI, Desktop, website, and Worker release data consistently across Stable and Preview - canonicalize and pin config targets, serialize all affected writers, preserve safe symlinks, and fail closed on malformed configuration Verification: - go test -race ./...; go vet ./...; go build ./... - cd desktop && go test -race ./... && go vet ./... && go build ./... - frontend typecheck, updater tests (31), CSS checks, and production build - site tests (44) and production build - Worker typecheck and tests (74) - release workflow contracts, actionlint, cache guard, shell syntax, diff check, and Linux/Windows cross-builds Co-authored-by: ffff2004 <[email protected]> * fix(config): remove obsolete edit wrappers Problem: The release-channel hardening left two private config edit wrappers unreachable, causing the pinned golangci-lint unused check to fail. Root cause: Callers now resolve and pin final config targets through the transactional edit path, but the superseded convenience wrappers remained in place. Fix: Remove only the two unreferenced wrappers while preserving the resolved-target and timeout helpers used by production code and concurrency tests. Verification: - go test ./internal/config - go vet ./... - golangci-lint v2.12.2 run --timeout=5m - git diff --check * fix(release): satisfy workflow shell lint Problem: The final CI lint job passed golangci-lint but failed actionlint because the CLI metadata publisher assigned expected_prerelease without reading it. Root cause: Prerelease validation moved into validate-cli-release-manifest.sh, leaving duplicate channel-specific shell assignments in the workflow. Fix: Remove the unused assignments and keep prerelease validation centralized in the manifest validator. Verification: - actionlint v1.7.7 on all release workflows - bash scripts/release-workflows.test.sh - bash -n on release helper scripts - git diff --check * fix(release): close desktop channel blockers Problem: Desktop Preview-to-Stable switches failed for older Debian Stable builds, updater Wails methods broke older one-argument callers, and config edit locks could split across TMPDIR or macOS case aliases. Root cause: The privileged helper rejected every downgrade, strict request binding reused legacy exported method names, and lock identity depended on process-local temp directories without Darwin path folding. Fix: Allow only signed public Preview-to-Stable Debian downgrades, preserve legacy updater methods beside strict request-bound APIs, and use a stable hardened per-user lock registry with Darwin alias normalization. Verification: Root and Desktop tests, Desktop race, Go vet/build, updater 31/31, site 44/44, Worker 74/74, release workflow contracts, cache guard, actionlint, golangci-lint, frontend typecheck/CSS/build, and focused cross-process/security regressions. --------- Co-authored-by: ffff2004 <[email protected]> |
||
|
|
26b256b5d5 |
refactor(agent): simplify run/tool state and harden live tests / 重构 Agent 状态并隔离真实测试 (#7010)
* refactor(agent): add TaskTool Options constructor / 增加 TaskTool Options 构造入口 Problem: NewTaskTool takes 18 positional parameters, which is hard to extend and easy to mis-order at call sites. Root cause: Construction was only exposed as a long positional helper with no structured options type. Fix: - Add TaskToolOptions and NewTaskToolWithOptions as the internal standard entry point (empty SysPrompt still maps to DefaultTaskSystemPrompt). - Keep the 18-arg NewTaskTool as a compatibility wrapper for one full iteration cycle. - Migrate boot wiring and the shared test helper to Options. - Add equivalence tests for config, schema, empty prompt, resolver, and execute behavior. Verification: go test ./internal/agent -run 'TestNewTaskToolWithOptions|TestTaskTool' -count=1 go test ./internal/boot -count=1 * refactor(agent): split Run and executeOne state machines / 拆分 Run 与 executeOne 状态机 Problem: Agent.Run and executeOne mixed lifecycle, turn policy, and tool policy in two very large methods, raising cognitive load without changing behavior. Root cause: Turn-loop counters and tool-call gating lived inline instead of package- private staged helpers with a single ownership model for locks/defers. Fix: - Keep Run as the lifecycle owner of workspace lease, evidence commit, delivery checkpoint, steer queue, and active-turn cleanup. - Extract runLoopState plus beginRunTurn, runToolLoop, handleFinalResponse, and handleToolRound without changing stream/persist/batch/compact order. - Split executeOne into parseToolCall, resolveToolPolicy (plan/proxy, delivery, recovery/permission), prepareToolExecution, and finishToolExecution behind toolCallPlan, preserving permission-before- lease and PreToolUse-before-execute semantics. Verification: go test ./internal/agent/... -count=1 go test -race ./internal/agent/... -count=1 REASONIX_RELEASE_CACHE_GUARD=1 go test ./internal/agent -run '^TestReleaseCacheHitGuard$' -count=1 * ci: report-only agent complexity monitor / 增加 agent 复杂度报告模式监控 Problem: The repo already runs gofmt, vet, build, full tests, race, and golangci-lint, but had no trend signal for remaining internal/agent hotspots after structured extraction. Root cause: Main lint intentionally avoids funlen/cyclop hard fails that would generate a flood of pre-existing noise. Fix: - Add a report-only funlen+cyclop config scoped to internal/agent production code with generous thresholds (not a hard 50-line rule). - Run it beside the existing lint job with issues-exit-code=0, write a CI summary, and upload the report as an artifact. - Ignore the generated local report file. Verification: bash scripts/agent-complexity-report.sh * test(provider): isolate live DeepSeek cache probe / 隔离 DeepSeek 真实缓存探针 Problem: The environment-gated DeepSeek cache probe ran during ordinary test suites whenever a stale API key was present, and its diagnostics still claimed tool-call reasoning replay should have zero prompt-token cost. Root cause: The probe had no explicit live build tag and retained commentary from the obsolete policy that stripped reasoning_content from DeepSeek tool-call turns. Fix: Gate the probe behind the live build tag, document the tagged invocation, describe required replay versus the empty-key recovery fallback, and fail when the required replay request is rejected. Verification: - go test ./... - go test -race ./internal/provider/openai -count=1 - env -u DEEPSEEK_API_KEY go test -tags live ./internal/provider/openai -run TestRealDeepSeekCacheProbe -count=1 -v - go vet ./internal/provider/openai - ./scripts/cache-guard.sh |
||
|
|
c846ad5559 |
Harden SignPath stable release gating (#6944)
Problem: Stable release v1.17.21 reached SignPath through release-stable.yml while the provider policy allowed only release-desktop.yml, blocking both Windows architectures after CLI and npm publication. Root cause: The provider-side build-definition allowlist was maintained manually, and the stable orchestrator did not prove the complete signing path before starting public publishers. Fix: Add a machine-readable signing contract, workflow call-graph validation, protected fingerprints, exact CODEOWNERS coverage, a zero-publication dual-architecture preflight, and fail-closed standalone attestation checks. Verification: Full PR CI, CodeQL, Windows Desktop packaging, race, coverage, release workflow contract tests, cache guard, root and Desktop Go tests, vet, actionlint, npm dry build, and diff checks passed. |
||
|
|
908ce880e5 |
Add Stable and Preview release channels / 新增 Stable 与 Preview 双发布渠道 (#6155)
Context:
Reasonix needed one public Stable/Preview contract across native CLI archives and Desktop updates while keeping npm prerelease tags independent.
Changes:
- Add protected native CLI Preview prereleases without moving Homebrew or GitHub Latest.
- Add persistent Desktop Stable/Preview selection, channel-aware update state, R2 pointers, and legacy Canary compatibility.
- Preserve the complete production Windows payload and installer signing chain and fail closed when signing or Preview R2 prerequisites are unavailable.
- Add release gateway routing, workflow contracts, compatibility coverage, and bilingual settings copy.
Compatibility:
Missing and unknown Desktop channel settings remain Stable. Legacy canary, beta, and next settings normalize to Preview. New Preview clients reject stale test-signed Canary manifests while older clients retain the mirrored canary pointer.
Verification:
Root and Desktop Go suites, frontend test:all and production build, crash-report Worker typecheck and 45 tests, release workflow contracts, cache guard, release notes tests, six native CLI target builds, CodeQL, race, and Windows NSIS/portable CI all pass on head
|
||
|
|
87999b9747 |
Sign every Windows payload executable / 签署全部 Windows 载荷程序 (#6904)
* fix(release): sign Windows payload executables Problem: Windows Defender can quarantine reasonix-desktop.exe after install or update even though the downloaded NSIS installer is signed. Root cause: The release workflow applied Authenticode only to the finished NSIS container. The desktop executable, support binaries, portable archive entries, and generated uninstaller remained unsigned after extraction. Fix: Sign the exact Windows payload through a versioned SignPath configuration, rebuild the portable archive and NSIS installer from those signed files, verify the payload before signing the outer installer, and fail official releases closed when signing is unavailable. Preserve and inject a signed NSIS uninstaller in the second packaging pass. Verification: - go test ./... (desktop module) - env -u DEEPSEEK_API_KEY go test ./... - bash scripts/release-workflows.test.sh - ./scripts/cache-guard.sh - actionlint release workflows - bash syntax and git diff checks * fix(release): separate canary installer signing config * fix(release): automate gated SignPath approvals Problem: Release certificates require one SignPath approval per request, causing four extra manual approval actions after the GitHub release gate. Root cause: The desktop workflow submitted each request synchronously and waited for an interactive SignPath approver even though the same release authority had already approved the protected GitHub environment. Fix: Submit requests asynchronously, validate the expected signing policy, approve them through the dedicated CI identity, poll to a final successful state, and securely download the signed artifacts. Exercise the same path in canary with an approval-enabled test policy and document the administrator contract. Verification: - cd desktop && go test ./... - bash scripts/release-workflows.test.sh - actionlint release-stable.yml release-desktop.yml - git diff --check * fix(release): default SignPath API endpoint The GitHub connector does not expose a SignPath API URL when requests are submitted asynchronously. Use SignPath's documented SaaS endpoint by default and keep the script override available for self-hosted installations. Verified with desktop Go tests, release workflow contract tests, actionlint, and git diff --check. * fix(release): restrict production signing origin Problem: Repository-level SignPath credentials were available to workflows on ordinary repository branches, while the production signing policy allowed every branch origin. A writer could therefore construct an unreviewed workflow that requested a trusted production signature. Root cause: Stable and prerelease builds ran directly from tag origins, so the SignPath policy could not use one exact protected branch without breaking tag releases. The policy had consequently been configured with an unrestricted branch wildcard. Fix: Relay stable and desktop prerelease tags to the current workflow on protected main-v2, keep immutable tags as build candidates, add a gated non-publishing production-signing smoke mode, and codify the exact main-v2 SignPath origin in tests and administrator documentation. Verification: - bash scripts/release-workflows.test.sh - actionlint v1.7.12 on all changed workflows - cd desktop && go test ./... - cd desktop && go test . -run signing contract tests - ./scripts/cache-guard.sh - git diff --check * fix(release): wait for manual smoke approval Problem: The first production-certificate smoke run failed immediately after submitting its two payload requests when the CI identity had intentionally been removed from the production approver list. Root cause: The request completion helper always called the SignPath Approve API as soon as a request entered WaitingForApproval, even in the explicitly human-reviewed smoke mode. Fix: Add an external-approval mode that polls without approving, enable it only for production signing smoke runs, and document and test the manual verification boundary. Normal canary and release automation retains CI approval. Verification: - bash scripts/release-workflows.test.sh - actionlint v1.7.12 on release-desktop.yml - cd desktop && go test . -run signing contract tests - git diff --check |
||
|
|
1ae7e48ba6 |
Streamline stable release preparation and prepare v1.17.20 notes / 精简稳定版准备流程并准备 v1.17.20 发布说明 (#6893)
* docs(release): prepare v1.17.20 notes Summary: Add reviewed bilingual stable release notes for v1.17.20, covering the machine CLI interfaces, trusted MCP delegation, Debian updater bootstrap, recovery changes, and desktop reliability fixes. Compatibility: Document the one-time Debian package bootstrap, matching Desktop/Remote host requirement, machine identity initialization, and installed-MCP trust boundary without changing runtime behavior. Verification: - node scripts/release-notes.mjs validate - rendered the v1.17.20 GitHub release notes successfully - node --test scripts/release-notes.test.mjs - bash scripts/release-workflows.test.sh - git diff --check * ci(release): reuse release-bound pull requests Problem: Preparing reviewed bilingual notes opened a dedicated pull request for every stable version even when an existing final product or release-infrastructure pull request already defined the candidate boundary. Root cause: The preparation workflow always selected a version-specific release-notes branch and had no validated way to update an existing review branch. Fix: Add an optional target_pr path that accepts only an open same-repository pull request into main-v2 containing the latest trunk. Reuse that branch, expose the rendered draft in the workflow summary, retain the dedicated pull request as a fail-closed fallback, and document the release-PR frequency rule. Verification: - bash scripts/release-workflows.test.sh - node scripts/release-notes.mjs validate - node --test scripts/release-notes.test.mjs - rendered the v1.17.20 release notes successfully - git diff --check |
||
|
|
035f92897f |
fix(desktop): support authorized Debian package updates / 支持 Debian 安装包应用内授权更新 (#6866)
* fix(desktop): support authorized Debian package updates Problem: Ubuntu users who installed the desktop .deb could not update in app because the updater could not replace root-owned system files. Root cause: Linux installs were treated uniformly as writable portable layouts, and the release manifest exposed only the tarball updater artifact. Fix: Detect dpkg-owned installs, publish signed native .deb metadata, and install upgrades through a packaged Polkit helper that re-verifies ownership, signatures, package identity, architecture, and version before delegating to apt. Preserve the portable tarball path and add user-facing progress, metrics, tests, and recovery documentation. Verification: - cd desktop && go test ./... - cd desktop && go vet ./... - pnpm --dir desktop/frontend test:typecheck - pnpm --dir desktop/frontend test:updater - pnpm --dir workers/crash-report typecheck - pnpm --dir workers/crash-report test - Linux amd64 helper test binary cross-compilation - git diff --check * fix(desktop): recover from updater mode changes Problem: The desktop CI failed on two unused updater helpers, and the UI could remain stuck in downloading or authorizing when install mode or manifest availability changed between updater steps. Root cause: The backend opened the manual download page and returned nil after reclassification, while the frontend treated nil as an in-flight operation with no terminal state. Two obsolete helper functions also remained after the refactor. Fix: Return and emit a typed manual-update error before opening the download page, preserve the manual fallback action in the shared updater state, add authorization/install/cancellation/reclassification coverage, and remove the unused functions. Verification: - cd desktop && go test ./... - cd desktop && go vet ./... - pnpm --dir desktop/frontend test:typecheck - pnpm --dir desktop/frontend test:updater - pnpm --dir desktop/frontend build - pnpm --dir workers/crash-report typecheck - pnpm --dir workers/crash-report test - Linux amd64 update-helper test binary and production build - git diff --check * Fix Linux package smoke checks under pipefail Problem: Linux .deb contract smoke checks could fail on valid packages when the producer emitted more data after grep found its match. Root cause: The build script combined set -o pipefail with grep -q. Early grep exit could close the pipe and make dpkg-deb terminate with SIGPIPE, turning a successful contract match into a failed pipeline. Fix: Consume the complete dpkg-deb output with non-quiet grep redirected to /dev/null, and add a packaging guard that rejects the unsafe early-exit patterns. Verification: - cd desktop && go test ./... - cd desktop && go test -run TestDesktopPackagesPreserveNativePlatformLaunchers -count=1 . - pnpm --dir desktop/frontend test:updater - bash -n scripts/desktop-build.sh - git diff --check |
||
|
|
020bf6ebf7 |
fix(release): carry reviewed notes into recoveries (#6843)
Problem: Stable recovery publishers failed when an immutable candidate predated its reviewed release-note entry. A partially successful run also had no safe way to retry only failed channels. Root cause: CLI and Desktop rendered notes from the candidate checkout, while preflight validated notes from the protected control plane. The orchestrator always invoked every publisher during recovery. Fix: Upload the preflight-rendered notes and consume that exact artifact in orchestrated CLI/Desktop publishers. Add opt-out channel switches for manual recovery while retaining public postflight verification for skipped channels. Verification: bash scripts/release-workflows.test.sh; node scripts/release-notes.mjs render --version v1.17.19 --output /tmp/reasonix-release-notes-v1.17.19.md; git diff --check. |
||
|
|
d61b087dd9 |
Fix Windows portable entrypoint collision / 修复 Windows 便携包入口冲突 (#6806)
* fix(desktop): prevent Windows portable entrypoint collision Problem: Windows portable staging used Reasonix.exe for the GUI launcher and reasonix.exe for the bundled CLI. On case-insensitive Windows filesystems, the CLI silently overwrote the launcher, so desktop shortcuts opened the CLI. Root cause: The packaging contract was checked only as source strings and never validated against the final Windows staging directory. Fix: Give the internal Windows CLI sidecar the distinct reasonix-cli.exe name, update installer, updater, and remote lookup contracts, and verify the exact portable file set plus launcher identity before archiving. Verification: - env -u DEEPSEEK_API_KEY go test ./... - cd desktop && go test ./... - cd desktop && go vet ./... && go build ./... - go vet ./... && go build ./... - Windows PE cross-build and portable verifier simulation * fix(repair): accept renamed Windows CLI sidecar Problem: Windows desktop updates record reasonix-cli.exe in the pending release unit, but the repair validator rejects that renamed sidecar when the transaction is read back. Root cause: The packaging and updater contracts were updated to the collision-free filename without extending the repair transaction allowlist. Fix: Allow reasonix-cli.exe only as a non-primary release sibling and cover the complete Windows release unit through a PrepareFileUpdate-to-ReadPendingUpdate round trip. Verification: - go test ./internal/repair - go test -race ./internal/repair - go test ./... - go vet ./... - cd desktop && go test ./... - git diff --check |
||
|
|
93c82a948a |
Remote Workbench with Local Provider Broker / 使用本机模型的远程工作台 (#6728)
* feat(remote): add rpcwire, RuntimeAPI registry, and protocol codegen Problem: Remote Workbench needs a bidirectional JSON-RPC transport and a single Go registry that generates schema, Schema Hash, and TypeScript DTOs so Desktop and Host negotiate strictly by schema, not by full Build ID hard-fail. Root cause: main-v2 lacked rpcwire and a generative Remote protocol surface. Fix: - Port transport-neutral NDJSON JSON-RPC (rpcwire) with strict frames, concurrent requests, cancel, and size limits (adapted from #6725). - Port RuntimeAPI protocol registry + schema generator; regenerate committed artifacts for main-v2 eventwire. - Mark eventwire large fields externalizable for content-ref offload without changing provider-visible semantics. - Extend directions for Provider Broker (host_to_client_request / client_to_host_notification) and register broker methods in the frozen surface. Verification: - go test ./internal/rpcwire ./internal/remote/protocol ./internal/remote/protocolgen ./internal/eventwire - go run ./cmd/remote-protocol-gen -check -root . Co-authored-by: SivanCola <[email protected]> Co-authored-by: taibai233 <[email protected]> * feat(remote): add Desktop Provider Broker and TargetManager Problem: Workbench Hosts must consume models without holding API keys, and the main window needs Local + one Remote adapters with a single active projection. Root cause: No in-process Broker over rpcwire and no target fencing manager on main-v2. Fix: - Desktop Broker endpoint: catalog, stream open/cancel, chunk/end notifications, concurrency limits, DeepSeek ToolCallReasoning catalog flags, connection-wide stream cancel on close (direction from #6722; fencing ideas from #6725). - TargetManager: permanent Local, optional Remote, last-click-wins fencing, busy remote host-swap rejection, restart reconnect hint only (no auto SSH). Verification: - go test ./internal/remote/broker ./internal/remote/workbench/target Co-authored-by: SivanCola <[email protected]> Co-authored-by: taibai233 <[email protected]> * feat(remote): workbench SSH, attach-workspace, adapters, mirror, trust Problem: Remote Workbench needed end-to-end transport, lifecycle, main-window adapters, workspace-safe files, mirrors, and Provider trust — not Serve HTML or HTTP Gateway tunnels. Root cause: Prior foundation only had protocol/Broker/TargetManager; no SSH stdio path, attach proxy, runtime grace, or UI wiring. Fix: - Windows: system OpenSSH + AskPass + Job Object (adapted from #6725); remote command is `reasonix remote attach-workspace --stdio`. - macOS/Linux: Go SSH session stdio to the same attach entry. - attach-workspace + per-workspace runtime on Unix socket; 5-minute grace after detach; Schema Hash gate on initialize. - Desktop Workbench adapters (connect/disconnect/switch Local, remote RPC, Provider trust prompt); OpenRemoteWorkspace uses workbench path. - File containment (relative refs, no symlink leaves, atomic writes). - Local mirror store with digest validation; restore requires session.jsonl. - Durable Provider trust store (host+fingerprint → allowed refs only). - Frontend workbenchTarget helpers + tests. Verification: - go test ./internal/remote/workbench/... ./internal/remote/broker ./internal/rpcwire ./internal/remote/protocol ./internal/cli - cd desktop && go test -run 'TestRemote|TestWorkbench|TestAskPass|TestOpenRemote' - pnpm exec tsx --test desktop/frontend/src/__tests__/workbench-target.test.ts Co-authored-by: SivanCola <[email protected]> Co-authored-by: taibai233 <[email protected]> * docs(remote): record source PR authors as repository co-contributors Problem: Repository co-contribution attribution requires explicit documentation and commit-level Co-authored-by trailers for the source PR authors (#6722, #6725). Root cause: PR body credits alone do not enter the GitHub contribution graph. Fix: Document both source PR authors, what was adopted, and that GitHub co-author attribution uses Co-authored-by trailers with public noreply emails in REMOTE_WORKBENCH docs (EN + zh-CN). Verification: - docs/REMOTE_WORKBENCH.md contains Repository co-contributors table - every commit on this branch includes both Co-authored-by trailers Co-authored-by: SivanCola <[email protected]> Co-authored-by: taibai233 <[email protected]> * fix(remote): close the workbench broker runtime loop Problem: Remote Workbench still mixed Remote state with the Local controller, left parts of the runtime API empty, and could lose reconnect, operation, history, Git, and provider semantics in real main-window use. Root cause: The first integration established the transport and broker direction but did not fully fence target projections, rebuild controller profiles, persist remote execution state, or cover strict protocol and process lifecycle parity. Fix: - route the existing main window through the Host-owned controller, files, Git, history, catalogs, operations, goals, checkpoints, and slash completion - keep provider credentials on Desktop while forwarding strict provider requests, effort, DeepSeek tool-call reasoning, and reconnect-safe stream generations - harden Build ID/schema negotiation, SSH/AskPass process cleanup, runtime socket ownership, workspace containment, mirror pulls, disconnect fencing, and background target isolation - add generated protocol contracts and regression coverage for no-Host-key broker use, paging, reconnect, overflow, lifecycle, and projection behavior Verification: - env -u DEEPSEEK_API_KEY REASONIX_RELEASE_CACHE_GUARD=1 go test -race ./... - cd desktop && go test ./... && go vet ./... && go build ./... - pnpm --dir desktop/frontend test:all && pnpm --dir desktop/frontend build - go run ./cmd/remote-protocol-gen -check -root . - Windows amd64 cross-compilation for root Remote packages and desktop module - scripts/check-cache-impact.sh Co-authored-by: SivanCola <[email protected]> Co-authored-by: taibai233 <[email protected]> * fix(remote): bind attach to its workspace target Problem:\nCI lint found an overwritten normalized workspace, and initialize could replace the SSH attach target before runtime startup.\n\nRoot cause:\nThe attach bootstrap normalized Options.Workspace but then assigned the client-provided initialize workspace to the same variable without comparing the two.\n\nFix:\nKeep the normalized attach target, normalize the requested workspace separately, and reject mismatches before schema negotiation or runtime startup.\n\nVerification:\n- go test ./internal/remote/workbench/attach ./internal/remote/workbench/runtime\n- go vet ./internal/remote/workbench/attach\n- golangci-lint v2.12.2: 0 issues * fix(desktop): remove the legacy remote child window Problem:\nThe same-window Remote Workbench still carried an unreachable child-window launcher and special shell lifecycle, causing Desktop CI unused-code failures and contradicting the selected product model.\n\nRoot cause:\nThe App bindings were migrated to TargetManager, but the old ticket-based Serve HTML launch path and its window-specific startup branches were left behind.\n\nFix:\nRemove remote-window tickets, spawning, middleware, lifecycle branches, and obsolete helpers so every SSH workbench runs in the existing main window.\n\nVerification:\n- cd desktop && go test ./...\n- desktop and root golangci-lint v2.12.2: 0 issues\n- Windows amd64 Desktop cross-compilation\n- wails generate module * fix(remote): preserve workspace diff parity Problem: After rebasing onto #6733, WorkspaceChangeDetail still executed against the Desktop filesystem while a Remote target was active. Root cause: The frozen RuntimeAPI had no method for the newly added per-file workspace diff query. Fix: Add a strict workspace/changeDetail contract, route Desktop through the Host runtime, and generate bounded Git/checkpoint patches inside the selected remote workspace. Verification: go test ./...; go test -race ./...; cd desktop && go test ./...; pnpm test:all; protocol codegen check; vet/build/lint; Windows cross-compile. Co-authored-by: SivanCola <[email protected]> * Harden Remote provider broker lifecycle Problem: the local Provider Broker became active before the live SSH peer was rebound to its trusted fingerprint, and slow provider consumers could block detach while the Host mutex was held. Root cause: peer validation happened after client.Connect returned, while Host stream delivery performed blocking channel sends inside the global lock. Fix: gate Broker activation on the authenticated live peer identity and move ordered Host delivery into bounded per-stream queues with a sole output pump. Verification: go test -race ./internal/remote/broker; go test ./internal/remote/workbench/client; cd desktop && go test -race . -run 'Test(AuthorizeWorkbenchPeerFailsClosed|Workbench|Remote)' -count=1. * Restore Remote sessions across runtime restarts Problem: Remote sessions existed only in the runtime process map, so a normal detach followed by runtime exit made persisted transcripts invisible on reconnect. Root cause: the Host had no durable mapping between protocol session identities, controller profiles, topics, and transcript paths. Fix: add a per-workspace atomic session registry, lazily rebuild controllers after Broker activation, retain temporarily unavailable records, and persist lifecycle/profile changes without holding the runtime mutex across file I/O. Verification: go test -race ./internal/remote/workbench/runtime -count=1; go test -race ./internal/remote/workbench/runtime -run 'TestRuntime(RestoresSessionRegistryAfterProcessRestart|EarlyShutdownDoesNotOverwriteUnreadRegistry)' -count=20. * Keep Remote projections and provider state current Problem: Remote callbacks stayed bound to a stale local tab, provider metadata and authorization were captured at connect time, and the bounded-handler regression test released its blockers before overload dispatch was observable. Root cause: Local navigation was not serialized with Remote activation, Broker descriptors omitted compaction/pricing fields, provider closures captured immutable config, and the rpcwire test synchronized only handler startup. Fix: Fence visible local navigation and rebind callbacks on Remote activation, propagate complete non-secret provider metadata, refresh config and trust-scoped access for live Broker connections, fail closed on empty authorization, and wait for all overload responses before releasing handlers. Verification: go test -race ./internal/boot ./internal/remote/protocol ./internal/remote/broker ./internal/remote/workbench/client ./internal/remote/workbench/runtime ./internal/rpcwire (cd desktop && go test -race ./...) go test -race ./internal/rpcwire -run TestInboundHandlerConcurrencyIsBoundedWithoutBlockingResponses -count=100 go run ./cmd/remote-protocol-gen -check -root . ./scripts/cache-guard.sh * Constrain restored Remote transcript paths Problem: An edited session registry could name an opaque topic that the wire contract permits but restoration rejected, while a nested transcript path could traverse a directory symlink outside SessionDir. Root cause: Registry validation derived a topic_ prefix from an opaque protocol ID and only performed lexical containment plus a final-component symlink check. Fix: Accept any non-empty opaque topic ID, require runtime transcripts to be direct children of SessionDir, close partially built controllers on restore errors, and isolate fallback socket paths per test run. Verification: go test -race ./internal/remote/workbench/runtime -count=3 * Fence late Remote projection callbacks Problem: A callback or snapshot refresh captured for one local tab could complete after Remote was rebound to another tab and still emit into the stale surface. Root cause: Reactivation marked Remote visible before rebinding callbacks, and callback/refresh completion trusted its captured tab ID instead of the current projection binding. Fix: Bind callbacks before activation, route late events through the current Remote tab under the transition fence, redirect snapshot completion to the live binding, and drop completion while Local is active. Verification: cd desktop && go test -race -run 'TestWorkbenchLateCallbackUsesCurrentProjectionTab|TestSetActiveTabSwitchesRemoteProjectionOnlyForDifferentTab|TestConcurrentActivateTopicSerializesSingleSurfacePruning' -count=20 * Synchronize Remote host-key prompt regression Problem: The full desktop race suite detected an unsynchronized map write in the host-key prompt serialization test. Root cause: The test started the first prompt goroutine before registering the second managed host, allowing the goroutine to read the manager map while the test wrote it. Fix: Register every host before launching concurrent prompts and wait for both prompt goroutines during test cleanup. Verification: cd desktop && go test -race -run '^TestHostKeyPromptsAreSerializedForGlobalDialog$' -count=100 .; cd desktop && go test -race ./... * Wait for queued background task teardown Problem: Windows CI could finish TestBackgroundTaskReturnsBeforeSlotFrees while its background job was still persisting transcript artifacts, causing TempDir cleanup to fail with a non-empty directory. Root cause: The test waited only for provider startup and then slept for 50 milliseconds instead of synchronizing with the job's terminal lifecycle. Fix: Scope the job to the test session, capture its returned job ID, wait for jobs.Done, and close the manager on every exit path before temporary-directory cleanup. Verification: go test -race ./internal/agent -run '^TestBackgroundTaskReturnsBeforeSlotFrees$' -count=100; REASONIX_RELEASE_CACHE_GUARD=1 go test -race ./internal/agent; REASONIX_RELEASE_CACHE_GUARD=1 go test ./...; GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go test -c ./internal/agent; go vet ./internal/agent; golangci-lint run ./internal/agent/... * fix(remote): fall back to server root workspace * fix(remote): close review blockers for workbench v1 Problem: Remote Workbench could silently expose the Host root, strand a session after registry persistence failure, leave local provider streams running after cancellation, and fail the Ubuntu/Windows attach tests. Root cause: One-click workspace resolution used an implicit root fallback; profile changes closed or mutated live controllers before durable state succeeded; Broker delivery lacked an abort path and open cancellation did not reach Desktop; attach readiness and tests depended on connection and timing assumptions that were not portable. Fix: Require explicit workspace selection, roll profile changes back while preserving the old controller, propagate best-effort Broker cancellation with abortable delivery, remove the readiness probe generation race, and use deterministic platform-aware attach tests. Verification: Remote, Broker, attach, runtime, rpcwire, race, Desktop, frontend Remote, TypeScript, production build, protocol generation, cache guard, go vet, and repeated cancellation/attach regressions passed. * fix(remote): make attach startup portable Problem: The Windows CI attach integration test could not observe the in-process AF_UNIX listener and reported that the runtime never became ready. Root cause: Startup readiness was inferred with os.Stat on the socket path, but Windows AF_UNIX does not expose a portable socket file. A separate readiness connection is also unsafe because runtime connections own generations. Fix: Let the real attach connection retry bounded socket dials until the runtime listener accepts it, and return immediately after starting the in-process test runtime. Verification: - attach integration test repeated 50 times - attach race test repeated 30 times - Windows amd64 test binary cross-compiled - go test ./internal/remote/... - remote broker, attach, and runtime race tests * fix(remote): keep runtime sockets portable Problem: Windows CI still could not connect to the in-process workbench runtime even with bounded dial retries. Root cause: The generated AF_UNIX address exceeded the platform path limit when the remote home itself was long, as on Windows hosted runners. Fix: Compact the runtime socket hierarchy while keeping it scoped under the Reasonix home, and add a regression test against the portable AF_UNIX length bound. Verification: - attach integration test repeated 50 times - runtime socket path tests repeated 20 times - attach and runtime race tests - Windows amd64 runtime test binary cross-compiled - go test ./internal/remote/... - protocol generation check and cache guard * fix(desktop): preserve native macOS app ownership Problem: Packaged macOS builds used reasonix-guard as CFBundleExecutable, so the Dock and LaunchServices owned a non-UI parent while the Wails child owned the window. The app could stay running without reopening its window from the Dock. Root cause: The packaging step replaced Wails' native bundle entry point to route startup through Guard. macOS application identity cannot be transferred reliably across that parent-child process boundary. Fix: Keep reasonix-desktop as the macOS bundle executable, retain Guard and the CLI as recovery sidecars, and run the equivalent failed-install, crash-loop rollback, repair, and Safe Mode preflight in Desktop before Wails creates WebView. Update packaging contracts and recovery documentation for the platform-specific launch model. Verification: - env -u DEEPSEEK_API_KEY go test ./... - go test ./... (desktop module) - go test -race -run 'TestDesktopStartupRecovery|TestDesktopPackagesPreserveNativePlatformLaunchers' -count=1 . (desktop module) - go vet ./... (desktop module) - DESKTOP_BUILD_SKIP_DMG=1 scripts/desktop-build.sh darwin/arm64 v0.0.0 - Signed packaged App launched with reasonix-desktop as PID 1 child, one Desktop process, no Guard process, and an onscreen CoreGraphics window * fix(desktop): satisfy startup recovery lint Problem: The Desktop CI job rejected the macOS startup recovery helper because continueLaunch was initialized before every return path overwrote it. Root cause: The named return value carried an unnecessary default assignment that go vet did not flag but golangci-lint's ineffassign check did. Fix: Initialize only the Safe Mode result and let each branch set the continue decision explicitly. Verification: - go test -run 'TestDesktopStartupRecovery|TestDesktopPackagesPreserveNativePlatformLaunchers' -count=1 . - go vet ./... * fix(remote): close workbench runtime safety gaps Problem: Remote Workbench could reject typed client requests before session hydration, execute duplicate mutations more than once, lose the committed target during a failed replacement, race controller lifecycle changes, and expose Local saved-session bindings while Remote was active. Root cause: Client authority hydration preserved serialized zero values, the advertised requestId contract had no runtime ledger or request sequencer, TargetManager stored candidates as committed state, and unsupported Remote history actions silently fell through to Local bindings. Fix: Authoritatively hydrate client-owned fields while preserving retry IDs, serialize runtime requests with a bounded 24-hour mutation replay ledger, fence controller profile transitions, promote replacement targets only after successful connection, and fail closed for unsupported Remote saved-session operations. Verification: - env -u DEEPSEEK_API_KEY go test ./... - env -u DEEPSEEK_API_KEY REASONIX_RELEASE_CACHE_GUARD=1 go test -race ./... - cd desktop && go test ./... && go vet ./... && go build ./... - pnpm --dir desktop/frontend test:remote - go run ./cmd/remote-protocol-gen -check -root . - scripts/check-cache-impact.sh with the live PR body Co-authored-by: taibai233 <[email protected]> * fix(remote): keep session rotations authoritative Problem: Completed turns could retain stale approval or ask prompts, while committed new/clear session rotations could return an error after a registry write failure and leave clients on the old runtime epoch. Root cause: Turn completion only cleared replay events when no prompt remained, session rotation treated the fallible registry write as part of the already-committed controller mutation, and the client did not apply new/clear result epochs. Fix: Clear terminal prompt state, return and adopt the committed rotation epoch while logging registry degradation, reset stale runtime state, and invalidate the previous snapshot. Verification: go test ./...; go test -race ./internal/remote/workbench/runtime ./internal/remote/workbench/client; cd desktop && go test ./...; go vet ./...; go run ./cmd/remote-protocol-gen -check -root . * fix(remote): fence snapshot refreshes and fork cleanup Problem: Remote rewind could report failure after destructive state changes committed, late subscribe responses could restore stale runtime authority, and failed forks left transcript artifacts on disk. Root cause: Post-commit registry errors were treated as rollbackable, subscribe responses had no request-authority fence, and fork rollback removed only the in-memory child session. Fix: Keep committed rewind results authoritative, serialize and revision-fence subscription refreshes with automatic stale retry, validate projection freshness, and remove all artifacts for uncommitted forks. Verification: - go test ./... - go test -race ./internal/remote/workbench/client ./internal/remote/workbench/runtime ./internal/control - go vet ./... - cd desktop && go test ./... - go run ./cmd/remote-protocol-gen -check * fix(remote): serialize projections and capability boundaries Problem: Remote subscribe snapshots could overwrite newer events, unsupported Remote Memory surfaces could reach Local state, and the broker test assumed concurrent notification arrival order. Root cause: Snapshot publication, event callbacks, and authority-changing results had no shared projection boundary; Desktop bindings did not fail closed for frozen capabilities; the test ignored stream sequence numbers. Fix: Linearize subscribe commits with session delivery and mutation results, install the initial Remote projection within that boundary, reject unsupported Remote Memory and Auto Research access, and assert broker chunks by sequence. Verification: go test ./...; go test -race ./internal/remote/...; desktop go test ./...; desktop go vet ./...; broker round-trip with coverage at count 500. * fix(remote): preserve active workbench transport Problem: Uninitialized socket probes could evict the live Desktop connection, target transition events exposed candidates as active, and Remote notification overload silently dropped ordered events. Root cause: Connection ownership was transferred before initialize response commit, candidate identity and active identity shared one event shape, and rpcwire used best-effort concurrent notification dispatch. Fix: Add provisional connection gates with post-write activation and exact daemon build checks; keep transition events on the committed target and fence submits; add ordered bounded notification queues with explicit overflow failure and generation-aware Broker delivery. Verification: go test ./... go test -race ./internal/rpcwire ./internal/remote/broker ./internal/remote/protocol ./internal/remote/workbench/... (cd desktop && go test ./...) (cd desktop/frontend && pnpm test:all) * fix(remote): serialize connection teardown Problem: A client closing immediately after initialize could race the response-commit callback, leaving a rejected gate paired with newly activated connection ownership. Root cause: Provisional connection teardown and post-write activation checked and mutated the wire registry under separate critical sections. Fix: Serialize teardown with connection activation, keep the production notification queue memory bound at 256 entries, and lock the initialize post-write contract with a router regression test. Verification: go test ./internal/remote/protocol ./internal/remote/workbench/runtime go test -race ./internal/remote/workbench/runtime ./internal/remote/protocol ./internal/remote/broker ./internal/rpcwire * test(remote): cover broker-backed tool loop Problem: The no-Host-key integration test only proved a single text response. It did not exercise the critical model-to-Host-tool-to-model loop or DeepSeek reasoning replay across the Desktop Broker. Root cause: The stub Provider always returned final text on its first request, so Host tool execution and the second Broker request were outside the end-to-end coverage. Fix: Make the stub request read_file first, assert the Host result and reasoning/tool-call replay in the second request, then complete the turn and verify projected tool events. Verification: - go test ./internal/remote/workbench/runtime -run '^TestRuntimeControllerUsesDesktopBrokerWithoutHostKey$' -count=1 -v - go test -race ./internal/remote/workbench/runtime -run '^TestRuntimeControllerUsesDesktopBrokerWithoutHostKey$' -count=20 - go test ./internal/remote/workbench/... ./internal/remote/broker ./internal/rpcwire - env -u DEEPSEEK_API_KEY go test ./... * test(remote): add physical Windows Linux acceptance Context: The Remote Workbench release gate needs reproducible evidence from a real Windows OpenSSH client to a Linux Host, including Broker-backed tool execution and reconnect cleanup. Change: Add an opt-in Windows ARM64-compatible acceptance test that exercises the production SSH transport, Linux workspace queries, Desktop Broker tool replay, session authority after reconnect, and OpenSSH process reaping. The fixture is environment-gated and keeps Provider execution on the Desktop side. Verification: - GOOS=windows GOARCH=arm64 CGO_ENABLED=0 go test -c -tags reasonix_remote_integration ./desktop - go test ./... - go test -race ./internal/remote/... * fix(remote): align Desktop and Host build identity Context: The Desktop is built from a nested Go module, so Go does not embed vcs.revision there while the bundled Host CLI built from the root module does. Strict Remote Workbench negotiation consequently rejects matching release artifacts as different builds. Fix: Allow release builds to link a validated shared source revision into the protocol package. Capture the clean repository revision before packaging mutations and inject it into both the Wails Desktop and bundled CLI sidecar, preserving the dirty suffix for local builds. Verification: - go test ./... - go test ./... (desktop module) - go test -race ./internal/remote/... - Windows ARM64 acceptance cross-compile with linked source revision - bash -n scripts/desktop-build.sh * fix(remote): preserve direct SSH credentials Context: The Windows Workbench direct-host branch reduced a saved Host to username, address, and port before starting OpenSSH. Explicit identity_file and proxy_jump settings were silently dropped, so valid direct connections could fail authentication or bypass the required jump path. Fix: Add an injection-safe direct configured transport that preserves identity and ProxyJump as distinct argv values. Route the Windows Workbench direct branch through it while retaining normalized IPv4 and IPv6 target parsing. Verification: - go test ./... (desktop module) - go test -race focused Windows Workbench transport tests - Windows ARM64 acceptance cross-compile * fix(remote): share runtime build identity Context: The detached workspace runtime duplicated Build ID discovery instead of using the shared protocol implementation. Linked source revisions used by nested Desktop release builds were therefore ignored, causing one installed CLI to reject its own runtime as a different build. Fix: Use protocol.CurrentBuildID for production runtime identity while retaining the explicit SourceRevision test seam. This keeps Desktop, attach, and detached runtime negotiation on one validated source of truth. Verification: - go test ./internal/remote/workbench/runtime - go test -race ./internal/remote/workbench/runtime - linked-revision focused regression test * fix(remote): allow initial history page Context: The Desktop history client intentionally requests the latest page without a cursor, and the Host runtime supports that state. The wire DTO nevertheless marked cursor as required, so strict validation rejected every initial session/history request after a successful Remote turn. Fix: Make the history cursor optional, regenerate the frozen protocol artifacts, and cover omission plus strict decoding. Strengthen the physical acceptance fixture to verify both product version and exact source revision before connecting. Verification: - go test ./... - go test ./... (desktop module) - go run ./cmd/remote-protocol-gen -check -root . - Windows ARM64 acceptance cross-compile * fix(remote): validate session mirror snapshots Context: After a completed turn the reconnect snapshot includes the persisted session mirror. That complete object incorrectly populated originalBytes while marked non-truncated, violating the frozen externalized-field contract and turning session/subscribe into an internal error. Fix: Emit complete session mirrors without truncation-only metadata and cover the artifact contract directly. Verification: - go test ./... - go test -race ./internal/remote/workbench/runtime - focused session mirror and runtime integration tests * fix(remote): model session mirror payload Context: Remote reconnect snapshots advertised /mirror/session.jsonl as an externalized content pointer, but SessionSnapshot had no schema field at that path. Strict response validation therefore rejected every post-turn reconnect snapshot before Desktop could fetch the mirror. Fix: Add the mirror placeholder to the frozen snapshot DTO, replace its concrete body with null during marshaling, and keep the body behind the existing bounded contentRef transfer. Regenerate Go/TypeScript protocol artifacts and prove that no session body is inlined on the wire. Verification: - go test ./... - go test ./... (desktop module) - go run ./cmd/remote-protocol-gen -check -root . - Windows ARM64 acceptance cross-compile * test(remote): add live Desktop Broker acceptance * test(remote): require live Broker tool loop --------- Co-authored-by: taibai233 <[email protected]> |
||
|
|
3a815ed35d |
fix(docs): self-host the Star History chart (#6755)
Problem: The README's third-party Star History image stopped rendering after GitHub restricted access to timestamped stargazer history. Root cause: The unauthenticated chart service can no longer read this repository's detailed stargazer data, so GitHub Camo receives an upstream failure. Fix: Generate accessible light and dark SVG charts in a trusted secret-backed workflow, publish them to a dedicated asset branch, and reference those repository-hosted images from both READMEs. Verification: - chart generator syntax and 5 unit tests passed - actionlint and git diff checks passed - full repository CI passed on Linux, macOS, and Windows - race, CodeQL, desktop, site, and security checks passed |
||
|
|
65fcd46525 |
Add a Remote SSH module (remote serve + tunnel) / 新增远程 SSH 模块(远端 serve + 隧道) (#6673)
* feat(serve): support --port-file/--token-file/--pid-file for supervised headless serve Extract RunGracefulListener so callers can bind first (--addr 127.0.0.1:0), record the real port, and keep the auth token out of argv. Groundwork for the remote-SSH module's detached remote serve bootstrap. * feat(config): add user-global-only [remote] section for SSH hosts [[remote.hosts]] entries carry connection/forward/workspace settings and name credential env vars (passphrase_env/password_env) instead of secrets, mirroring the provider api_key_env idiom. Like [secrets], loadForRoot pins the section back to the user-global value after the project merge — with a deep copy, because TOML decoding mutates slice backing arrays in place and a shallow pin would alias the project-injected hosts. Remote credential env names flow into CredentialEnvNames so they are filtered from tool subprocess environments. Adds RemoteStateDir/RemoteKnownHostsPath and the internal/store name helpers for the remote-side serve state layout. * feat(remote): SSH transport kernel — dial, auth, host keys, forwards, SFTP New internal/remote package group provides the connection layer for the remote module: - client.go: supervised connection with a single-goroutine state machine, keepalive ([email protected]), full-jitter exponential-backoff reconnect, shared SFTP (generation-checked), and forward re-attach. - auth.go: OpenSSH-like method order (agent, identity file, default identities, password, keyboard-interactive); interactively-obtained secrets are cached in-process so reconnects never re-prompt. - knownhosts.go: system known_hosts read-only + a Reasonix-managed TOFU file; key mismatch is a non-promptable hard error. - dial.go: ProxyJump chains (netclient proxy applies to the first hop only). - forward/: -L (persistent local listener across reconnects) and -R (re-registered on each attach) with typed ErrBindBusy/ErrDuplicateForward. - sftpfs/: list/stat/capped-read-with-text-detection/atomic-write/mkdir/ rename/remove, quarantining the new github.com/pkg/sftp dependency. - netclient/dialer.go: StreamDialer honoring the user's proxy policy (SOCKS5 via x/net/proxy, http/https via CONNECT). - sshconfig.go: ~/.ssh/config import via github.com/kevinburke/ssh_config. - sshtest/: in-process SSH+SFTP server for tests. New deps: github.com/pkg/sftp, github.com/kevinburke/ssh_config (both pure Go). x/* versions kept at main-v2's pins. Cache-impact: none — no prompt/tool/provider path touched. Cache-guard: n/a (new leaf subsystem, not in the system-prompt prefix). * feat(remote): remote serve bootstrap over SSH internal/remote/bootstrap ensures a detached 'reasonix serve' runs for a (host, workspace) pair: reuse a live process (the reconnect path), else detect the remote OS/arch (uname), locate/install/upload reasonix, write a 32-byte token 0600 via SFTP, launch serve bound to 127.0.0.1:0 with --token-file/ --port-file (token never in argv), poll the port file, and persist ServeState JSON. Status/Stop/Logs manage the recorded process. Every remote command operand is POSIX single-quote-escaped (golden test covers '; rm -rf ~'). V1 targets Linux/macOS remotes; cross-platform upload without a matching local binary is a documented limitation directing to serve_install = npm. Cache-impact: none — new leaf subsystem, no prompt/tool/provider path. Cache-guard: n/a. * feat(cli): reasonix remote command group + /remote slash Adds the remote subcommand tree (add/list/remove/import/test/connect/open/ status/forward/serve/fs), mirroring the mcp command shape. Host CRUD writes to the user-global config only (remote hosts are pinned there); connect runs a foreground supervisor that dials, bootstraps remote serve, forwards the serve port with the token in a loopback URL, applies configured -L/-R forwards, and holds the tunnel until Ctrl-C while the remote serve keeps running. Terminal prompts handle key passphrases, passwords, and TOFU host-key confirmation; dialing honors the user's proxy settings via netclient. The /remote slash command shows a read-only host summary. New i18n fields added to all three catalogs. Cache-impact: none — CLI surface + i18n only, no prompt/tool/provider path. Cache-guard: n/a. * feat(desktop): remote SSH Go bindings (remote_app.go) 25 App bindings behind a narrow remoteKernel interface (fake-injected in tests): host CRUD (user-global config), Connect/Disconnect/status, status-driven TOFU host-key confirmation (ConfirmRemoteHostKey resumes a parked dial), SFTP list/read/write-with-mtime-conflict/mkdir/rename/delete, port-forward CRUD, and remote serve EnsureServer/Open/Stop/Status/Logs. desktopRemoteManager wraps internal/remote per host; status/forward/server transitions bridge to remote:status / remote:forwards / remote:server through the asyncRuntimeEmitter (never blocking the kernel). The manager is created lazily on first use and closed in App.shutdown, rejecting any pending host-key prompt. OpenRemoteWorkspace forwards the serve port and opens the tunneled URL via Wails BrowserOpenURL; last workspace/host persist to desktop-remote.json (all-optional fields, backward-compatible). desktop/go.mod gains pkg/sftp + kevinburke/ssh_config via go mod tidy. Cache-impact: none — desktop bindings only, no prompt/tool/provider path. Cache-guard: n/a. * feat(desktop): remote SSH frontend (host manager, explorer, ports, server) - lib/types.ts: Remote* TS mirrors + SettingsTab "remote". - lib/bridge.ts: 25 Remote* AppBindings + browser-dev mock + onRemoteStatus/ onRemoteForwards/onRemoteServer subscribers with a mock fan-out for tests. - store/remote.ts: zustand store; pending_hostkey status drives the global fingerprint dialog and resolves on the next non-pending status. - RemoteHostsPage (Settings > Remote SSH tab): host list with status chip, connect/disconnect/open, add/edit form, ssh_config import picker. - RemotePanel: overlay drawer with Files (lazy tree + textarea editor with mtime-conflict Reload/Overwrite), Ports (live forward rows + add form), and Server (bootstrap phases + open-in-browser + logs) tabs. - RemoteHostKeyDialog: global TOFU modal off the store's pendingFingerprint. - StatusBar remote chip (worst live state, click opens explorer); App.tsx subscribes remote:* app-wide and renders the panel + dialog. - i18n keys added to en / zh / zh-TW (key sets stay aligned). - Editor is a monospace textarea (no CodeMirror/Monaco) per lean-deps. - Tests: src/__tests__/remote-store.test.ts (added to the test script). Cache-impact: none — desktop frontend only. Cache-guard: n/a. * docs: document the Remote SSH module (SPEC, GUIDE, CONFIG_PATHS, CHANGELOG) Adds the internal/remote layout + acyclic dependency note to SPEC 2, a bilingual Remote SSH section to GUIDE.md / GUIDE.zh-CN.md (config, CLI, reconnect/persistence semantics, host-key TOFU, remote-side state layout, desktop UI), the managed known_hosts path to CONFIG_PATHS(.zh-CN), and a CHANGELOG entry. Cache-impact: none — docs only. Cache-guard: n/a. * chore: ignore stray .reasonix state under package dirs A bootstrap test whose RealPath resolved to CWD once wrote a .reasonix/remote/ token under the package dir and it was swept into a commit. The offending file has been scrubbed from history; this guards against recurrence. * fix(remote): CI green — errcheck + cross-platform bind-busy and Windows test skips - client.go: check cl.Wait() return (errcheck lint). - forward: classify WSAEADDRINUSE as bind-busy on Windows (build-tagged addrinuse_windows.go), so ErrBindBusy is reported cross-platform. - sftpfs / bootstrap integration tests skip on Windows: their in-process SFTP server serves the LOCAL filesystem, so on Windows it serves drive paths and the POSIX-remote path translation breaks — a harness limitation, not a product one (V1 remotes are Linux/macOS). Linux/macOS CI covers the flows. Cache-impact: none — test/lint-only + a Windows errno check. Cache-guard: n/a. * test(remote): skip EnsureServe reuse/relaunch/stop tests on Windows at setup TestEnsureServeReusesLiveProcess and siblings pre-write state via pathsFor() (POSIX path.Join) before newFakeConn runs, so on Windows the temp-dir remote home yields a malformed drive path and the setup os.WriteFile fails. Move the Windows skip to the top of each such test (shared skipOnWindows helper) so it fires before any pathsFor/os setup, not just inside the harness ctor. * fix(remote): address review — P1 security / data-integrity / lifecycle Resolves the 14 findings from code review (all verified against the code): Security - Serve token no longer leaks to the remote log: runServe suppresses the token share line under --port-file (supervised), and the launch script creates the log 0600 (umask 077 + chmod). (F1) - ProxyJump no longer sends the target's password to jump hosts: each hop gets its own credential-free AuthOptions with a per-hop cache (persisted on the Client so reconnects don't re-prompt). (F2) - CLI surfaces a misconfigured proxy instead of silently dialing direct; the https-proxy path now TLS-wraps the connection before CONNECT; desktop passes the proxy Dialer it previously omitted. (F14) Data integrity - `remote fs get` streams the full file via sftpfs.Download instead of the 4 MiB-capped preview reader that silently truncated. (F3) - Workspace slug carries an FNV hash of the exact path, so /srv/a-b and /srv/a/b no longer share serve state files. (F10) - Desktop host edit merges: passphrase_env/password_env/forwards (absent from the desktop input) are preserved instead of wiped. (F7) Lifecycle / correctness - Bootstrap gates on `serve --help` advertising --port-file, not a version number (the flags ship in this change, so no released version qualifies). (F4) - Launch uses setsid only when present, so stock macOS (no setsid) works. (F5) - Desktop no longer uploads the GUI binary as the remote CLI (LocalBinary="" makes the upload path fail clearly toward npm). (F6) - Serve tunnel re-binds to the current serve address on every EnsureServer and is torn down on Stop; per-host serveMu serializes concurrent ensures. (F8) - Reuse/Stop verify the pid is actually a reasonix serve (ps args), not just kill -0, so a recycled PID is never mistaken or signalled. (F9) - Desktop connect retries after failure (failed Start drops the managed host) and builds a fully-populated host under the lock so a concurrent Disconnect can always close it. (F11) - SSH handshake is bounded by a conn deadline (ClientConfig.Timeout does not cover NewClientConn), so a banner-less host can't hang the dial or Close. (F12) - ScanSSHConfig returns a non-nil slice so an empty result encodes as [] not null. (F13) Tests: per-hop auth isolation, full-file download vs preview cap, slug collision (/srv/a-b vs /srv/a/b), serve-alive/launch/locate command shape, desktop edit-merge and non-nil scan. go test ./... green in both modules; gofmt/vet clean; Windows cross-build green; cache-guard pass. Cache-impact: none — no prompt/tool/provider/memory path touched. Cache-guard: ./scripts/cache-guard.sh — all cases pass. System-prompt-review: not required — no system-prompt-feeding config field changed. * Harden Remote SSH lifecycle and packaging Problem: Remote SSH could misapply ProxyJump credentials, race connection and service lifecycles, interrupt live forwarding during replacement, and ship Desktop builds without the CLI sidecar required by upload installation. Several UI failures were also silent or left stale state behind. Root cause: Jump aliases shared endpoint-keyed authentication, bootstrap ownership relied on weak state checks without cross-process serialization, and Desktop operations lacked generation-scoped cancellation. Forward replacement stopped the working listener before the replacement was ready, while packaging and updater contracts did not include the CLI binary. Fix: Resolve and authenticate every SSH hop independently, make dialing and Desktop operations context-aware, serialize bootstrap with an atomic remote lock, verify exact process ownership, and make forward replacement failure-atomic. Harden token, SFTP, agent, config, and ssh_config handling; package and update the CLI sidecar on macOS, Windows, and Linux; and surface actionable validation and lifecycle errors in the Desktop UI. Verification: - go test ./... - cd desktop && go test ./... - go test -race for Remote, CLI, store, and Desktop modules - go vet ./... for root and Desktop modules - pnpm test:all and pnpm build - Windows amd64 cross-compilation for Remote, bootstrap, forwarding, SFTP, and CLI packages - clean merge snapshot against latest origin/main-v2 with root, Desktop, and frontend checks passing * Add native Remote SSH workspace windows Problem: Remote SSH was buried in settings, and opening a remote workspace handed the authenticated loopback URL to the system browser instead of preserving a desktop workspace experience. Root cause: The desktop had no window-level Remote SSH control and no isolated Wails launch mode for a second native workspace window. Fix: Add a VS Code-style status entry, host switcher, command actions, and Remote dock. Launch remote workspaces in a lightweight Reasonix process that skips local session ownership, single-instance focus, tray services, and Wails bindings. Pass the loopback URL through a one-shot permission-restricted ticket so the token never appears in process arguments. Verification: - go test ./... - pnpm test:all - pnpm build - wails build -clean - native macOS multi-window smoke test - git diff --check * Tidy Remote SSH module metadata Record the SSH config and SFTP packages as direct dependencies after rebasing onto the latest main-v2 baseline. Remove stale module checksums so the root module remains tidy in CI. * Fix Remote SSH Windows test assumptions Load the fixture SSH config by its explicit path while setting both platform home variables, and use the Windows .exe suffix for the desktop CLI fixture. This keeps the Remote SSH coverage portable across the CI matrix. |
||
|
|
e7a0f1163b |
fix(release): prevent skipped stable publishers (#6629)
Problem: The stable orchestrator reported success after approval even though the CLI, npm, and Desktop publication jobs were skipped through a transitive skipped dependency. The existing recovery dispatch also could not use a workflow fix without moving the already-published tags. Root cause: Each reusable publisher joined an orchestrated guard with a deliberately skipped standalone gate. The cache guard used an explicit status condition, but its descendants did not, so GitHub propagated the skipped ancestor through the dependency chain. Recovery also checked out and executed control scripts from the old release candidate. Fix: Add explicit success conditions to every affected publication stage, keep recovery authorization and verification on protected main-v2 while building the immutable historical tag SHA, and reject non-ancestor or mismatched tag sets. Add a stable postflight that verifies required CLI/Desktop release assets and the exact npm latest version so a no-op release cannot stay green. Verification: - bash scripts/release-workflows.test.sh - go run github.com/rhysd/actionlint/cmd/[email protected] -ignore 'label "windows-11-arm" is unknown' on all release workflows - bash -n on all changed release scripts - verify-stable-release-artifacts.sh against public v1.17.14 artifacts - ./scripts/cache-guard.sh - go test ./... - git diff --check |
||
|
|
3637d0f028 |
ci(release): consolidate GitHub approval and retain SignPath review / 合并 GitHub 审批并保留 SignPath 手工确认 (#6626)
* ci(release): orchestrate stable publishing behind one approval Problem: Stable CLI, npm, and Desktop tags started independent workflows, so one stable release required repeated GitHub environment approvals and separate coordination. Root cause: Each publisher owned its stable tag trigger and protected environment instead of sharing one validated release decision. Fix: Add a stable release orchestrator that validates the atomic three-tag set, requests one environment approval, and invokes guarded reusable publishers in parallel. Preserve standalone prerelease and recovery gates, harden version resolution, and add workflow contract coverage and release documentation. Verification: - actionlint v1.7.7 on release workflows and CI - bash scripts/release-workflows.test.sh - ./scripts/cache-guard.sh - node --test scripts/release-notes.test.mjs - go test ./... * fix(release): bind publishers to approved commit Problem: The reusable publishers trusted a caller path on any ref, rebuilt mutable tags after approval, and the new actionlint gate failed on an unquoted keychain list. Root cause: The orchestrator passed tag names but not its approved commit, while child guards did not require a protected caller ref or matching workflow SHA. The macOS keychain command also relied on shell word splitting. Fix: Require protected caller context, propagate the approved CLI tag and SHA, check out the immutable commit, and revalidate remote tags before publication. Preserve keychain arguments with a Bash array and document the repository-write trust boundary. Verification: - bash scripts/release-workflows.test.sh - go run github.com/rhysd/actionlint/cmd/[email protected] on all release workflows - ./scripts/cache-guard.sh - node --test scripts/release-notes.test.mjs - go test ./... - git diff --check * fix(ci): remove ripgrep dependency from release tests Problem: The release workflow contract test passed locally but failed in GitHub Actions because the lint runner does not provide ripgrep. Root cause: The test script used rg only for simple regular-expression assertions even though CI does not install that optional tool. Fix: Use portable grep -E assertions throughout the release workflow contract test. Verification: - env PATH=/usr/bin:/bin:/usr/sbin:/sbin bash scripts/release-workflows.test.sh - go run github.com/rhysd/actionlint/cmd/[email protected] on all release workflows - git diff --check * docs(release): retain manual SignPath confirmations Context: The release workflow now consolidates GitHub environment approval, while SignPath manual confirmations remain an intentional second-party control. Change: Clarify workflow comments and the release runbook so the one-approval claim applies only to GitHub. Document that the AMD64 and ARM64 signing requests can each require manual SignPath confirmation. Verification: - bash scripts/release-workflows.test.sh - go run github.com/rhysd/actionlint/cmd/[email protected] on all release workflows - git diff --check |
||
|
|
7f00d2c260 |
feat(desktop): Redesign theme experience with official packs / 主题体验改造与官方主题 (#6614)
* feat(desktop): Add Theme Pack V1 engine with theme library / 新增 Theme Pack V1 引擎与主题库 Controlled, non-executable desktop skins: semantic color tokens, density and corner recipes, and an optional local background image with focus and safe area. Includes manifest validation, atomic import with replace confirmation, content-addressed asset serving, live preview with cancel restore, density consumers, transparent layout under backgrounds, contrast warnings, and safe-mode fallback to Graphite. / 受控不可执行的桌面皮肤:语义色令牌、密度 与圆角配方、可选本地背景图(焦点与安全区)。包含清单校验、原子导入与覆盖 确认、内容寻址资源服务、可取消的实时预览、密度消费者、背景下布局透明化、 对比度警告与安全模式回退 Graphite。 * feat(desktop): Redesign theme experience with official packs / 主题体验改造与官方主题 Problem: Appearance settings exposed two competing base-style pickers and applied theme packs immediately on card click, while activeThemeId could store base style ids. Official skins and a gallery/overview IA were still missing. Root cause: Theme Pack V1 layered packs on top of desktop.theme_style without a unified state model or select-then-apply UX, so base styles, official themes, and user packs competed for the same controls. Fix: - Bump desktop-theme-state to schema v2: activeThemeId is official/user only; migrate legacy base-style active ids into desktop.theme_style. - Add GetThemeExperience, ActivateBaseStyle, DisableThemePack, and RestoreGraphiteAppearance; reject base ids in ActivateThemePack. - Embed eight official themes with fixed order, asset routing, and budgets. - Ship AppearanceOverview + ThemeGallery (select ≠ apply, temp preview, immersive detail) driven by themeExperience.ts. - Document the IA and provenance; keep procedural art as placeholders. Verification: - cd desktop && go test ./... - cd desktop/frontend && pnpm typecheck && pnpm test:all && pnpm build - wails generate module (bindings match, no drift) * feat(desktop): complete the theme gallery and editor experience Problem: The theme gallery still had fragmented preview actions, placeholder flagship art, generic base-style previews, and a minimal custom-theme dialog that could not tune semantic colors or independent scenes. Root cause: Theme Pack V1 modeled one background and the gallery treated selection, preview, and persistence as separate interactions without a shared localized editor state. Fix: Upgrade the pack contract compatibly to V2 with independent home and workspace scenes, rebuild the gallery around immediate reversible preview and explicit apply, add localized semantic color and scene controls, replace the eight official artworks, and align base-style previews and actions with the settings UI. Verification: - cd desktop && go test ./... - cd desktop/frontend && pnpm test:all - cd desktop/frontend && pnpm typecheck - cd desktop/frontend && pnpm check:css - cd desktop/frontend && pnpm build - Browser-verified Chinese and English labels, live preview, scene switching, and cancel restoration * fix(desktop): satisfy the full-module formatting gate Problem: The desktop CI job stopped before tests because its repository-wide gofmt check reported main.go and theme_assets.go. Root cause: The earlier theme integration left one struct field with manual alignment and one extra trailing blank line; package tests compile these files but do not enforce gofmt byte formatting. Fix: Run gofmt on both reported files without changing runtime behavior. Verification: - cd desktop && gofmt -l . - cd desktop && go test ./... * fix(desktop): clarify theme scene content placement Problem: The scene editor exposed a vague content-safe-area label without explaining that it controls foreground text and card placement. The desktop CI lint gate also found four empty or unused Theme Pack leftovers. Root cause: The persisted safeArea field was surfaced directly as user-facing copy, while placeholder branches and helpers remained after the Theme Pack integration. Fix: Rename the control to Interface content area, add localized guidance and radio semantics, and remove or complete the four lint findings without changing theme behavior. Verification: - cd desktop && go test ./... - cd desktop && go vet ./... - cd desktop/frontend && pnpm typecheck - cd desktop/frontend && pnpm check:css - cd desktop/frontend && pnpm test:all - cd desktop/frontend && pnpm build - Theme Pack contract: 195 passed - Verified the localized control in the running browser UI * fix(desktop): unify theme confirmation dialogs Problem: Theme deletion, replacement imports, and export-rights checks used window.confirm, which surfaced platform-native dialogs that did not match the Reasonix settings UI. Root cause: The Theme Pack flows lacked a reusable in-app confirmation primitive, so both the gallery and legacy library called the browser API directly. Fix: Add an accessible portal-based Reasonix confirmation dialog with danger styling, safe focus behavior, Escape and backdrop cancellation, and focus restoration. Migrate all six theme confirmation call sites and localize their titles and actions. Verification: - pnpm typecheck - pnpm check:css - pnpm test:all - pnpm build - Browser: delete confirmation stayed in-app, Cancel received focus, Escape restored focus, and the temporary theme was deleted without a native dialog * fix(desktop): restore theme preview and custom fonts Problem: - Custom UI and monospace fonts could no longer be configured from the redesigned appearance page. - Selecting a user theme updated details without starting the advertised global preview. Root cause: - AppearanceOverview discarded both custom font name callbacks and omitted their editors. - ThemeGallery limited non-immersive previews to the catalog tab. Fix: - Restore localized custom font editors and their persistence callbacks. - Preview every non-immersive theme selection, including user packs. - Add focused source-contract coverage for both behaviors. Verification: - pnpm test:all - pnpm build - pnpm check:css - Browser verification of custom font variables, user-pack preview, and Back restoration |