Release readiness

This checklist separates public-beta checks that can be run on a normal development machine from stable-release checks that require signing credentials, clean machines, or live provider accounts.

Local release smoke

Run this before tagging a beta or release candidate:

powershell -NoProfile -ExecutionPolicy Bypass -File scripts\release-smoke.ps1 `
  -Model "phi4-mini:latest"

The script writes a receipt under .agent-runs\release-smoke-* and runs:

Use -SkipLiveModel only for local diagnostics. A release candidate or stable gate must record every skipped check as a skip and must not treat a partial receipt as complete release evidence.

By default, the smoke script uses the committed review fixtures under test-fixtures\source-import-extracted. Those prove the bulk-import review parser on realistic extracted text. For release-candidate or stable evidence, pass the full source-file fixture folder so the Rust extraction gate proves CSV, XLSX, TXT, DOCX extraction and public-beta PDF-disabled guidance.

For stable release evidence, run:

powershell -NoProfile -ExecutionPolicy Bypass -File scripts\release-smoke.ps1 `
  -FixtureDir "tests\fixtures\source-import" `
  -Model "phi4-mini:latest" `
  -Stable

The stable run fails if the working tree is dirty or if desktop smoke, live model, here.now, or import fixture gates are skipped. Use -AllowDirty only for a non-release diagnostic run. The -FixtureDir value must point to a folder available on the machine running the gate. Use the committed tests\fixtures\source-import fixture folder for normal release checks, or pass any equivalent full source-file fixture folder that contains the expected CSV, TXT, XLSX, DOCX files and PDF-disabled guidance fixtures.

For Windows, cleanroom evidence may be produced locally in an isolated app-data profile, Windows Sandbox, or VM. For the v0.3.3 Apple Silicon beta, local preflight must verify the unsigned Gatekeeper path, manual Ollama detection, and packaged source-to-Workbench behavior. Because DMG output is not byte-reproducible, the tag workflow additionally smoke-tests the exact hosted DMG on a fresh Mac runner and publishes a receipt binding that DMG's SHA256, size, ARM64 architecture, resources, and isolated first launch. Intel Mac proof is neither required nor accepted because Intel Macs are unsupported.

Release-candidate packaging receipt

After tests and smoke checks pass, generate a local RC evidence receipt before publishing any release asset:

powershell -NoProfile -ExecutionPolicy Bypass -File scripts\prepare-rc-evidence.ps1 `
  -ReleaseSmokeReceipt ".agent-runs\release-smoke-YYYYMMDD-HHMMSS\release-smoke-receipt.json" `
  -ModelBakeoffReceipt ".agent-runs\model-bakeoff-YYYYMMDD-HHMMSS.json" `
  -DependencyAuditReceipt ".agent-runs\dependency-audit-YYYYMMDD-HHMMSS.json" `
  -InstallerSmokeReceipt ".agent-runs\windows-installer-smoke-YYYYMMDD-HHMMSS\windows-installer-smoke-receipt.json" `
  -PackagedWalkthroughReceipt ".agent-runs\packaged-first-run-walkthrough-YYYYMMDD-HHMMSS\packaged-first-run-walkthrough-receipt.json"

The receipt records the exact branch, commit, app versions, Tauri bundle targets, artifact paths, SHA256 hashes, smoke-check results, model-bakeoff receipt, dependency-audit receipt, Windows installer-smoke receipt, packaged first-run/core-flow receipt, and checksum-manifest output. It is a local evidence artifact; it does not push, merge, tag, or publish. By default it fails if the smoke receipt is missing, from a different repo or commit, dirty/diagnostic, contains failed/skipped checks, no current-version installer artifacts are present, stale historical installer artifacts are present in the bundle folder, the installer-smoke SHA256 does not match the current release artifact SHA256, or the required model-bakeoff/dependency-audit/installer/package artifacts are missing. Use the diagnostic flags only for local investigation, not release evidence.

The model bakeoff receipt must show that the configured default model in src\models.json passed every bakeoff case. The dependency audit receipt must be clean, run npm audit at the documented moderate threshold or stricter, and include Rust advisory checking through cargo-audit; a receipt that only exists but contains failures is not release evidence. If cargo-audit uses ignored RustSec advisories, every ignored ID must have a current machine-readable waiver in docs/security-advisory-waivers.json, and the dependency and RC receipts must copy exactly matching waiver entries into release evidence. Missing, extra, duplicated, expired, or incomplete waivers fail RC evidence. The Windows installer smoke receipt must prove NSIS silent install, installed app start from the packaged installer, first-run screenshot capture, uninstaller presence, and silent uninstall, and its recorded installer SHA256 must match the current RC artifact SHA256. MSI lifecycle proof is backlog/proof-needed until MSI is reintroduced as a public beta artifact.

For v0.3.3, RC packaging evidence covers the signed Windows installer and the unsigned, unnotarized Apple Silicon DMG. Linux installer proof remains backlog/proof-needed and is deferred to v0.3.4.

Apple Silicon packaged preflight

Build and exercise the local candidate on an Apple Silicon Mac:

npm run tauri build -- \
  --config src-tauri/tauri.macos.conf.json \
  --target aarch64-apple-darwin \
  --bundles dmg \
  -- --locked

node scripts/macos-packaged-smoke.mjs \
  "src-tauri/target/aarch64-apple-darwin/release/bundle/dmg/The Civic Desk_0.3.3_aarch64.dmg"

The smoke script mounts the DMG read-only, requires a thin ARM64 executable, checks prompt and browser-extension resources, records the absence of a Developer ID identity and notarization, launches the packaged app with isolated app data and Ollama forced absent, and requires database initialization. Its receipt is local preflight evidence. Because rebuilding a DMG changes its bytes, the tag workflow repeats the smoke against the exact hosted artifact and publishes that separate receipt.

Hosted release workflow

The GitHub release workflow is intentionally conservative during public beta:

This prevents an unverified installer from appearing before Windows Authenticode proof, platform checksums, explicit Mac unsigned-release disclosure, and local release-gate evidence have been reviewed.

Successor release provenance

The published v0.3.2 installer was rebuilt from commit 796b8700831f964beea97630c51d71a40a9b724f, while the original v0.3.2 tag still identifies commit 4e2fa0a9706e10a53fab3a904afb30d7fec43f56. Consequently, GitHub's automatic v0.3.2 source archives do not identify the source used for the current downloadable installer. The release page records the product build commit and is the authoritative provenance note for that historical exception.

Do not move, replace, or silently delete the published v0.3.2 tag. v0.3.3 resolved the successor-provenance problem with a fresh tag at exact product commit e94a2f94885c1e6013129c3d854662cc3c8e5b27; its evidence, checksums, release body, and uploaded artifacts identify that commit and the published bytes.

Historical v0.3.2 evidence and the final-candidate rule

Windows release candidates now pass an Authenticode signing gate that validates the installer, installed application, and uninstaller, including timestamps. The evidence listed below belongs to an earlier candidate and is retained for audit history; it is not current-candidate or current-download proof. Final beta evidence must be regenerated after the candidate commit is merged and signed, and it must match the exact installer hash that completes the cleanroom gate and is later uploaded under owner authorization.

This does not publish, merge, or tag the release by itself. Scott must still approve the product push, tag, hosted GitHub Release, and GitHub Pages update.

Evidence levels

Public beta

Release candidate

Stable

Source import fixtures

The full local fixture suite expects realistic files in a machine-available fixture folder. For repo-local release evidence, restore or copy the fixture artifact to:

tests\fixtures\source-import

If the gate machine uses a different workspace path, pass that machine's equivalent full source-file fixture folder with -FixtureDir. Do not hard-code another user's Windows profile path in release directives.

The committed lightweight review fixtures live under:

test-fixtures\source-import-extracted

The full source-file set includes clean CSV, messy XLSX, human notes TXT, DOCX briefing, PDF examples, and XLSX edge cases. PDF examples should fail with public-beta PDF-disabled guidance until hardened PDF parsing is added.

Model bakeoff

There are two model checks:

Run this to record comparison reliability and timing:

$env:MODEL_BAKEOFF_MODELS="qwen2.5:7b,gpt-oss:20b,gemma4:e4b,phi4-mini:latest,llama3.2:3b"
$env:MODEL_BAKEOFF_TIMEOUT_MS="240000"
node scripts\model-bakeoff.mjs

The result is written under .agent-runs\model-bakeoff-*.json. A release candidate may use a narrower default-model receipt when the purpose is to prove the current configured default; do not describe that receipt as a full comparison bakeoff.

For this public-beta line, phi4-mini:latest is the default scan model because .agent-runs\model-bakeoff-2026-06-30T20-26-21-425Z.json recorded valid JSON output for both civic-signals and empty-noise cases. That comparison receipt included qwen2.5:7b, gpt-oss:20b, gemma4:e4b, phi4-mini:latest, and llama3.2:3b with MODEL_BAKEOFF_TIMEOUT_MS=240000; gemma4:e4b also passed both cases but was slower on the civic-signal case in that run. Keep using the comparison set for model-selection decisions until repeated evidence supports a different default.

Security checks

Run:

npm audit --audit-level=moderate
cd src-tauri
cargo audit

Warnings from transitive desktop framework dependencies should be recorded in the release notes if they cannot be upgraded safely before release.

Current Rust advisory exceptions live in src-tauri\.cargo\audit.toml; the matching rationale, owner, release-note text, and review date live in docs\security-advisory-waivers.json. The release gate should not accept a dependency audit receipt that suppresses advisories without matching current waiver metadata.

Stable-release blockers

These cannot be fully completed from one Windows development machine:

Rollback

If a release is bad:

  1. Mark the GitHub release as pre-release or remove it if it is actively harmful.
  2. Publish a correction note in the release body.
  3. Tag a patch release from the last known-good commit.
  4. Keep the failed smoke receipt and model bakeoff result for diagnosis.