# Release process

1. Ensure the intended commit is clean and tagged.
2. Install the exact lockfile with `npm ci`.
3. Run `npm run release:artifact`.
4. Record the reported SHA-256, then run
   `npm run package:release -- --force` twice and verify both reported digests
   match it. Inspect an existing exact-version artifact before deliberately
   replacing it.
5. Inspect ZIP paths, manifest, legal documents and absence of maps/secrets.
6. Publish ZIP and `.sha256` on the matching Gitea release.
7. Use the public immutable URL and digest in Toolbox Portal.

The package script sorts paths without locale-dependent collation, rejects
symlinks and credential-like paths, normalizes file timestamps and modes,
verifies manifest/source identity, stages both outputs and rolls back the pair
on publication failure, replaces an existing exact version only with
`--force`, and produces:

```text
release/regex-tools-0.3.0.zip
release/regex-tools-0.3.0.zip.sha256
```

Before packaging, the engine verifier checks the closed PCRE2, Python and Java
file sets in `dist/`, including checksums, metadata, licences/notices, module
and WebAssembly identities and self-hosted relative asset references.

The existing v0.1.0 and v0.2.0 ZIPs and matching sidecars are historical
immutable artifacts. Creating v0.3.0 must not replace, rename or remove them.
