| 1 | # Releasing Reasonix |
| 2 | |
| 3 | Reasonix prepares immutable, accepted release candidates before it creates any |
| 4 | public tag. Publication consumes those exact files after one approval; recovery |
| 5 | observes public state and fills only missing stages. |
| 6 | |
| 7 | The public identity remains compatible with existing clients: |
| 8 | |
| 9 | | Surface | Immutable tag | Public result | |
| 10 | | --- | --- | --- | |
| 11 | | CLI | `vX.Y.Z` | GitHub Release and Homebrew | |
| 12 | | npm | `npm-vX.Y.Z` | root and six platform packages; official aliases | |
| 13 | | Desktop | `desktop-vX.Y.Z` | signed GitHub Release, immutable R2 directory, and Stable manifest | |
| 14 | |
| 15 | All three tags must identify the candidate product SHA. They are created in one |
| 16 | atomic push and must never be moved, deleted, or recreated. The Go SDK module |
| 17 | (`sdk/go`) retains its independent `sdk/go/vX.Y.Z` version line. |
| 18 | |
| 19 | ## Normal release |
| 20 | |
| 21 | 1. Run **Prepare release** with `X.Y.Z` and review the generated bilingual |
| 22 | Notes PR. |
| 23 | 2. Merge the Notes PR after its required checks and review complete. The merge |
| 24 | automatically starts **Prepare release candidate**. A maintainer may also |
| 25 | dispatch that workflow on protected `main-v2` with `version`. Both entrypoints |
| 26 | freeze the protected event SHA before the runner starts; arbitrary SHA inputs |
| 27 | and PR-head execution are not accepted. |
| 28 | 3. Wait for the candidate workflow to build the shared CLI/npm binaries, build |
| 29 | and sign all Desktop platforms, run native final-package acceptance, and |
| 30 | seal the payload and evidence. Record the candidate ID printed in its |
| 31 | summary, for example `v1.39.0-0123456789ab-abcdef012345`. |
| 32 | 4. From an authenticated maintainer checkout, run: |
| 33 | |
| 34 | ```sh |
| 35 | ./scripts/release-stable.sh CANDIDATE_ID |
| 36 | ``` |
| 37 | |
| 38 | 5. Review the candidate ID, full product SHA, Notes digest, signing policy, |
| 39 | platform receipts, and payload hashes in **Publish release candidate**. |
| 40 | Approve its `release` environment once. |
| 41 | 6. Wait for CLI, npm, and Desktop publication, Stable pointer convergence, the |
| 42 | owned Pages deployment, hydrated download verification, and the publication |
| 43 | ledger. |
| 44 | |
| 45 | The candidate is fixed when Notes are reviewed. Later `main-v2` merges do not |
| 46 | invalidate it. A product fix or a change to embedded Notes creates a new |
| 47 | candidate. Do not replace source files during packaging or claim an old binary |
| 48 | contains new embedded Notes. |
| 49 | |
| 50 | ## Candidate contract |
| 51 | |
| 52 | The record binds the candidate ID, version, full product SHA, build and |
| 53 | acceptance control SHAs, Notes catalog and rendered-body hashes, workflow run |
| 54 | and attempt, exact payload artifact ID, signing fingerprint, every file size |
| 55 | and SHA-256, and native Windows/macOS acceptance receipts. GitHub artifact |
| 56 | attestations bind both the record and every payload file to the protected |
| 57 | candidate workflow on `main-v2`. |
| 58 | |
| 59 | Candidate payloads are retained for 30 days. Records, native receipts, |
| 60 | publication ledgers, and timing reports are retained for 90 days. An expired |
| 61 | unpublished payload must be prepared again. Published files are verified from |
| 62 | their immutable public channels and are not rebuilt because an Actions artifact |
| 63 | expired. Candidate artifacts contain no credentials or real user data. |
| 64 | |
| 65 | To revoke an unpublished candidate, add its exact ID to the comma- or |
| 66 | whitespace-separated repository variable `RELEASE_REVOKED_CANDIDATES`. |
| 67 | Preparation reuse and publication both fail closed for listed IDs. |
| 68 | |
| 69 | The six CLI binaries are each built once. CLI archives, Homebrew checksums, and |
| 70 | npm platform tarballs reuse those bytes. Windows architectures build in |
| 71 | parallel, then share one Certum session; completed architecture bundles can be |
| 72 | reused by a failed-job rerun. Windows native acceptance runs in parallel after |
| 73 | signing. Desktop platforms do not wait for unrelated platform acceptance before |
| 74 | starting their own downstream work. |
| 75 | |
| 76 | ## Publication and recovery |
| 77 | |
| 78 | Publication verifies the record attestation, exact artifact IDs, payload |
| 79 | attestations and hashes, Notes identity, protected source ancestry, signatures, |
| 80 | and acceptance receipts before requesting approval. It then atomically creates |
| 81 | the three tags and publishes CLI, npm, and Desktop in parallel from the sealed |
| 82 | payload. Tag creation no longer starts a second legacy release pipeline. |
| 83 | |
| 84 | For any interrupted publication, run: |
| 85 | |
| 86 | ```sh |
| 87 | ./scripts/release-stable.sh CANDIDATE_ID recover |
| 88 | ``` |
| 89 | |
| 90 | Recovery uses the same global publication lock and one approval. |
| 91 | |
| 92 | If activation has not started, recovery creates all three absent tags in one |
| 93 | atomic push. If all tags already identify the candidate, it reuses them. |
| 94 | Partial tag sets and conflicting identities always stop recovery. |
| 95 | |
| 96 | Each publisher re-reads its external state: |
| 97 | |
| 98 | - matching immutable content is reused; |
| 99 | - missing content is uploaded; |
| 100 | - ambiguous requests are queried before retrying; |
| 101 | - conflicting immutable content stops the stage; |
| 102 | - signing and native acceptance are not repeated; |
| 103 | - a newer npm, R2, Homebrew, or site pointer is never rolled back by an older |
| 104 | candidate recovery; |
| 105 | - a site-only failure reruns Pages and hydrated-site verification without |
| 106 | rebuilding product files. |
| 107 | |
| 108 | The publication ledger records observed tag SHAs, every CLI and Desktop release |
| 109 | asset, all seven npm package identities and registry integrity values, pointer |
| 110 | outcomes, Stable manifest, Homebrew, changelog, and homepage state. Recovery |
| 111 | always queries the actual service again; the ledger is evidence, not a source of |
| 112 | truth for later mutations. |
| 113 | |
| 114 | ## Verification and timing |
| 115 | |
| 116 | Run **Verify release** with `X.Y.Z` for a read-only public check. It validates |
| 117 | the immutable tags, GitHub release contents, all npm packages and candidate |
| 118 | identity, the current Stable manifest when the version owns it, Homebrew, |
| 119 | changelog, and the browser-hydrated download DOM. For an older version, newer |
| 120 | public pointers are preserved and reported rather than treated as a reason to |
| 121 | roll them back. |
| 122 | |
| 123 | Candidate and publication workflows upload JSON timing evidence and summarize |
| 124 | queue, runner, build, signing, acceptance, upload, site deployment, and total |
| 125 | wall time. Diagnostic timing failures do not invalidate a sealed candidate or a |
| 126 | verified publication. |
| 127 | |
| 128 | ## Legacy recovery |
| 129 | |
| 130 | **Legacy release recovery** remains available only for releases created before |
| 131 | the candidate pipeline. It retains historical surface selection and recovery |
| 132 | guards. New releases must use candidate IDs; do not add hard-coded run IDs or |
| 133 | version exceptions to the new workflows. |
| 134 | |
| 135 | Historical Preview, Canary, and RC artifacts remain readable, but those paths |
| 136 | are not normal publication entrypoints. npm `canary` and `next` remain |
| 137 | compatibility aliases for the official line. |
| 138 | |
| 139 | The release is complete only when the immutable files, current public pointers, |
| 140 | hydrated website, publication ledger, and read-only verification all agree. |
| 141 |