From 67e593ba14d363e548b81194b45793b20803656e Mon Sep 17 00:00:00 2001 From: Perchenko Anton <101743951+PerchAnton@users.noreply.github.com> Date: Sun, 1 Nov 2026 14:35:09 +0200 Subject: [PATCH 1/2] Prepare beta compatibility contract and consumer checks --- CHANGELOG.md | 7 ++++- CONTRIBUTING.md | 6 ++--- README.md | 4 +++ docs/beta-contract.md | 57 +++++++++++++++++++++++++++++++++++++++ docs/evidence.json | 2 +- docs/evidence.md | 2 +- docs/stability.md | 4 +++ package.json | 7 +++-- scripts/check-package.mjs | 23 +++++++++++++++- 9 files changed, 103 insertions(+), 9 deletions(-) create mode 100644 docs/beta-contract.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 8623a67..e40df29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # Changelog -## Unreleased +## 0.1.0-beta.1 (candidate, not published) + +- Define the beta API and unchanged wire-format contract, migration checks and dependency approval boundaries. +- Read all 23 frozen existing-ID/legacy vectors through the installed archive, including exact re-encoding, canonical results and namespace isolation. +- Set the candidate publication tag to `beta`; do not move the existing `alpha` or `latest` tags. Runtime JavaScript and measured hashes remain unchanged; historical evidence is carried forward without new timings or human-performance claims. + - Document adoption decisions for existing ObjectId/search workflows, additional alias and mapping costs, alpha versus wire compatibility, stable-release gates diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 843938d..25711be 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,13 +14,13 @@ The build keeps JSDoc in declarations and removes comments only from emitted JS. ## Publishing an alpha -Use a clean, reviewed commit and verify that its CI passed. Keep the version, changelog and evidence version in sync. A published npm name/version cannot be reused. For a prerelease, keep both `publishConfig.tag` and the explicit publication tag set to `alpha`. +Use a clean, reviewed commit and verify that its CI passed. Keep the version, changelog and evidence version in sync. A published npm name/version cannot be reused. For the beta candidate, keep both `publishConfig.tag` and the explicit publication tag set to `beta`. Do not move the existing `alpha` or `latest` tags. 1. Run `pnpm check`, `pnpm test:demo`, `pnpm test:comparison`, `pnpm test:workflows` and `pnpm test:package`; the matching CI must also cover browsers, PDF extraction/lookup and the supported Node/OS matrix. 2. Pack with `pnpm pack --pack-destination artifacts`. Record the commit, package SHA-256 and npm SHA-512 integrity. Inspect the file inventory and install this exact archive into an isolated consumer before publication. 3. Authenticate to `https://registry.npmjs.org` with `npm login --auth-type=web`. Enter passwords and second-factor challenges only in npm's login flow. Never commit credentials or put them in an issue/PR. Direct publication requires account 2FA or another authentication method supported by npm; prefer the interactive account flow for the first release. -4. Run `npm publish ./artifacts/refmint-VERSION.tgz --dry-run --ignore-scripts --tag alpha --access public --registry=https://registry.npmjs.org`, substituting the actual version. Inspect the name, version, files and integrity. Then publish the same archive by removing only `--dry-run`. -5. Read the public registry metadata back. Check the version, `alpha` tag and tarball integrity, then install `refmint@alpha` in a fresh consumer and exercise both `refmint` and `refmint/codec`. Download the registry tarball and compare it with the published archive. Keep the proof with the release record. +4. Run `npm publish ./artifacts/refmint-VERSION.tgz --dry-run --ignore-scripts --tag beta --access public --registry=https://registry.npmjs.org`, substituting the actual version. Inspect the name, version, files and integrity. Then publish the same archive by removing only `--dry-run`. +5. Read the public registry metadata back. Check the version, `beta` tag and tarball integrity, then install `refmint@beta` in a fresh consumer and exercise both `refmint` and `refmint/codec`. Download the registry tarball and compare it with the published archive. Keep the proof with the release record. The first npm alpha.2 archive has updated README, changelog and contributor documentation. All other files match the original GitHub alpha.2 archive; the original tag, download and checksums remain intact. Future releases should prepare registry instructions before creating either archive. diff --git a/README.md b/README.md index ae48648..d14d3b7 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # RefMint +**Beta candidate:** this checkout prepares `0.1.0-beta.1`; it is not published. +Installation links below still point to the published alpha.3. Read the +[beta API/format contract](docs/beta-contract.md) before upgrading. + **Reference codes you can generate, print and check.** [![CI](https://github.com/PerchAnton/refmint/actions/workflows/ci.yml/badge.svg)](https://github.com/PerchAnton/refmint/actions/workflows/ci.yml) diff --git a/docs/beta-contract.md b/docs/beta-contract.md new file mode 100644 index 0000000..932df64 --- /dev/null +++ b/docs/beta-contract.md @@ -0,0 +1,57 @@ +# Beta API and format contract + +This checkout prepares **0.1.0-beta.1**, not yet published. The registry's current +alpha is **0.1.0-alpha.3**. Beta adds an explicit compatibility boundary and checks +the frozen identifiers through the installed archive. Runtime JavaScript is +unchanged: there is no new speed, size, usability or adoption claim. + +## What stays compatible + +| Surface | Commitment for beta | +| --- | --- | +| Imports | ESM `refmint` and `refmint/codec`; the same exported factories/functions and TypeScript types; no runtime dependencies | +| Platforms | Node 22+; browsers with the standard APIs used by the package; secure Web Crypto is required for random generation | +| Random references | Existing prefix/length configuration and canonical identifiers remain readable; keep configuration with the stored schema | +| Existing-ID codec | Frozen format v1 for uint64/UUID, same canonical bytes and normalization; incompatible encodings require a different wire version | +| Values | uint64 uses `bigint`, including above the safe-number limit; UUID input uses canonical lowercase hyphenated text; no ObjectId encoding or truncation | +| Results | Keep the `ok` discriminant, canonical `id`, `normalized`, codec `value`, and current error codes; human input methods return failures rather than correcting an identifier | +| Diagnostics | Preserve raw-input position semantics and bounded details. Treat human prose and the ordering of optional diagnostic details as non-contractual | +| Rejection | Preserve finite input bounds, Unicode-lookalike rejection and strict canonical methods. Invalid options/encoding values still throw; parse/decode do not coerce arbitrary input objects | + +The random API and existing-ID codec remain different namespaces; neither accepts +the other's format. Checksums detect many entry errors, not a valid-but-wrong record, +and do not provide authorization, secrecy, uniqueness or authentication. A random +reference still needs a unique database index and collision retry. No automatic +database migration, search endpoint or regulated invoice-number replacement occurs. + +## Upgrade checks + +Once published, pin `refmint@0.1.0-beta.1`; publish under **beta**, keeping `alpha` +and `latest` unchanged. Existing alpha.2/alpha.3 callers and identifiers need no +API or data migration. Keep the prior reader during application rollout and rerun +the application's native lookup/permission tests on the exact installed archive. + +`pnpm test:package` reads all 20 existing-ID and 3 legacy frozen vectors through +the installed package, verifies exact re-encoding/canonical results, and rejects +cross-namespace use. It also checks public type branding, private import blocking, +zero dependencies, shipped documentation and unchanged measured runtime hashes. +The fixtures are not regenerated for beta. Existing independent-oracle, input, +workflow, PDF and three-engine browser checks remain required. + +Preserve this contract during beta. A necessary incompatible correction requires a +new prerelease, explicit migration notes and regression evidence; never replace +an existing version. Internal modules, example UI and benchmark timings are outside +this contract. A stable 1.0 support/deprecation policy is a separate decision. + +## What beta does not prove + +The [existing workflow comparison](workflows.md) is synthetic. The +[human study](manual-entry-study.md) still has no results establishing shorter lookup +times or fewer mistakes. An existing number, UUID search or short alias may remain +better; [adoption costs](adoption.md) do not disappear with a version label. + +We can supply exact archive/source hashes, licenses, dependency/type inventory, +compatibility tests and a scoped integration/local-alias comparison. Maintainers +decide whether the workflow needs another code, which source to accept, and who +owns storage, permission checks, deployment and updates. Do not weaken their gates +or label the package approved because a pin or beta package exists. diff --git a/docs/evidence.json b/docs/evidence.json index 2d8abe3..e3acb61 100644 --- a/docs/evidence.json +++ b/docs/evidence.json @@ -1,6 +1,6 @@ { "generated_at": "2026-10-27T13:21:13.955Z", - "version": "0.1.0-alpha.3", + "version": "0.1.0-beta.1", "environment": { "node": "v24.19.0", "platform": "darwin", diff --git a/docs/evidence.md b/docs/evidence.md index 50379d6..be24658 100644 --- a/docs/evidence.md +++ b/docs/evidence.md @@ -4,7 +4,7 @@ Run `pnpm check && pnpm evidence` from a clean checkout after installing the loc ## Correctness -For alpha.3, the timing/size snapshot is carried forward from alpha.2 because all +For alpha.3 and the beta.1 candidate, the timing/size snapshot is carried forward from alpha.2 because all runtime JavaScript hashes are identical. `measurement_version` identifies the measured version; `version` identifies the package carrying the evidence. Original timestamps, environment and samples are retained. This is not a new performance measurement. diff --git a/docs/stability.md b/docs/stability.md index cda9a81..9d09097 100644 --- a/docs/stability.md +++ b/docs/stability.md @@ -1,5 +1,9 @@ # Alpha scope and stability criteria +The checkout now prepares a [beta.1 compatibility contract](beta-contract.md). +It is not yet published. The alpha history and stable-release gates below still +apply; beta does not supply human-benefit evidence or promise stable 1.0 support. + RefMint 0.1.0-alpha.3 is a prerelease. npm publication and successful CI do not change that status. Pin the exact reviewed version for an evaluation and keep the existing identifier/search path available. There is no stable-release date promised. diff --git a/package.json b/package.json index 414acce..ccbaedc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "refmint", - "version": "0.1.0-alpha.3", + "version": "0.1.0-beta.1", "description": "Generate public references or encode existing IDs with strict input diagnostics and a Bech32m checksum.", "type": "module", "license": "MIT OR Apache-2.0", @@ -22,7 +22,10 @@ "node": ">=22" }, "sideEffects": false, - "publishConfig": { "access": "public", "tag": "alpha" }, + "publishConfig": { + "access": "public", + "tag": "beta" + }, "exports": { ".": { "types": "./dist/index.d.ts", diff --git a/scripts/check-package.mjs b/scripts/check-package.mjs index 0afeb82..e90f9b4 100644 --- a/scripts/check-package.mjs +++ b/scripts/check-package.mjs @@ -39,7 +39,7 @@ try { const installed = join(consumer, 'node_modules', 'refmint'); const packaged = JSON.parse(await readFile(join(installed, 'package.json'), 'utf8')); assert.equal(packaged.version, source.version); - assert.equal(packaged.publishConfig.tag, 'alpha'); + assert.equal(packaged.publishConfig.tag, 'beta'); assert.deepEqual(packaged.dependencies ?? {}, {}); const files = await readdir(installed, { recursive: true }); assert.deepEqual((await readdir(join(installed, 'dist'))).sort(), @@ -66,8 +66,10 @@ try { assert.ok((await readFile(path)).length > 0, `${file} -> ${target}`); } } + await writeFile(join(consumer, 'formats-v1.json'), await readFile(join(root, 'tests/fixtures/formats-v1.json'))); await writeFile(join(consumer, 'smoke.mjs'), ` import assert from 'node:assert/strict'; +import { readFile } from 'node:fs/promises'; import { createReferences, createReferenceCodec, planReferences, collisionProbability } from 'refmint'; import { createReferenceCodec as standalone } from 'refmint/codec'; assert.equal(standalone, createReferenceCodec); @@ -83,6 +85,25 @@ const encoded = codec.encode(9007199254740993n); assert.equal(codec.decode(codec.format(encoded).toUpperCase()).value, 9007199254740993n); assert.equal(codec.diagnose(encoded.replace('r', '\\uff52')).details.positions[0], 0); assert.equal(refs.parse(encoded).ok, false); +// Read the old vectors with the installed archive, not the checkout's build. +const frozen = JSON.parse(await readFile(new URL('./formats-v1.json', import.meta.url), 'utf8')); +for (const fixture of frozen.codec) { + const reader = createReferenceCodec({prefix:fixture.prefix,kind:fixture.kind,version:1}); + const value = fixture.kind === 'uint64' ? BigInt(fixture.value) : fixture.value; + assert.equal(reader.encode(value), fixture.reference); + assert.deepEqual(reader.decodeCanonical(fixture.reference), + {ok:true,id:fixture.reference,value,normalized:false}); + assert.deepEqual(reader.decode(reader.format(fixture.reference).toUpperCase()), + {ok:true,id:fixture.reference,value,normalized:true}); + assert.equal(createReferences({prefix:fixture.prefix}).parse(fixture.reference).ok, false); +} +for (const fixture of frozen.legacy) { + const reader = createReferences({prefix:fixture.prefix,randomLength:fixture.randomLength}); + assert.deepEqual(reader.parseCanonical(fixture.reference), + {ok:true,id:fixture.reference,normalized:false}); + assert.equal(reader.parse(reader.format(fixture.reference).toUpperCase()).id, fixture.reference); + assert.equal(createReferenceCodec({prefix:fixture.prefix,kind:'uint64'}).decode(fixture.reference).ok, false); +} await assert.rejects(import('refmint/dist/bech32m.js'), {code:'ERR_PACKAGE_PATH_NOT_EXPORTED'}); `); run(join(consumer, 'smoke.mjs'), [], consumer); From ea808cb254e2e0f25569764529c7e920cea66505 Mon Sep 17 00:00:00 2001 From: Perchenko Anton <101743951+PerchAnton@users.noreply.github.com> Date: Sun, 1 Nov 2026 14:38:35 +0200 Subject: [PATCH 2/2] Clarify prerelease publication instructions --- CONTRIBUTING.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 25711be..d12ba21 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,7 +12,7 @@ For the existing-ID format or comparison, run `pnpm compare` after the build and The build keeps JSDoc in declarations and removes comments only from emitted JS. Keep the full-root and codec-only size figures separate; include compression costs. After runtime changes, refresh the historical comparison with `pnpm compare:previous PATH_TO_PREVIOUS_DIST FULL_COMMIT_SHA` as described in [comparison methods](docs/comparison.md#before-and-after-optimization). The recorded previous baseline is `daaffe813fd0351752bd158314917267acddc1b6`. Do not compare historical timings collected on different machines as a before/after speedup. -## Publishing an alpha +## Publishing a prerelease Use a clean, reviewed commit and verify that its CI passed. Keep the version, changelog and evidence version in sync. A published npm name/version cannot be reused. For the beta candidate, keep both `publishConfig.tag` and the explicit publication tag set to `beta`. Do not move the existing `alpha` or `latest` tags. diff --git a/README.md b/README.md index d14d3b7..491dd54 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ RefMint is a small TypeScript library for public ticket, document and order refe **Retained from alpha.2:** preserve an existing uint64 or UUID with `createReferenceCodec`, inspect invalid input with `diagnose`, and load only the codec with `refmint/codec`. The release includes the optimized implementation and comparison evidence; the tagged source includes the complete comparison form. [Format v1](docs/format-v1.md) ยท [Comparison and measured trade-offs](docs/comparison.md). -**Alpha:** APIs may evolve. The existing-ID v1 wire contract is frozen; incompatible formats require a new version. Install the [npm alpha](https://www.npmjs.com/package/refmint), [download the GitHub prerelease](https://github.com/PerchAnton/refmint/releases/tag/v0.1.0-alpha.3), or build from source. The package uses ES modules and includes TypeScript declarations. Node.js 22+ and browsers with Web Crypto are the targets. +**Published alpha:** APIs may evolve. The existing-ID v1 wire contract is frozen; incompatible formats require a new version. Install the [npm alpha](https://www.npmjs.com/package/refmint), [download the GitHub prerelease](https://github.com/PerchAnton/refmint/releases/tag/v0.1.0-alpha.3), or build from source. The package uses ES modules and includes TypeScript declarations. Node.js 22+ and browsers with Web Crypto are the targets. ![Measured checks and the reference lifecycle](docs/assets/evidence.svg)