Viewing is open to everyone. Downloading and starring need a GitHub sign-in. Sign in with GitHub

CHANGELOG.md

v0.1.0-build.85 Latest Commit ede38f5

Size
77 KB
Lines
798
Language
markdown
SHA-256
18ad2b2e5000…
View on GitHub

Sign in with GitHub to download. Each download is recorded with your GitHub username.

1# Changelog
2
3This is the development changelog for the `scratch` branch. It has one entry per build (`0.1.0-dev.N`), newest first. Every commit that changes the project adds an entry here, and `tools/check_version.py` checks that the top entry matches `VERSION`.
4
5Related logs:
6
7* [CHANGELOG-STABLE.md](CHANGELOG-STABLE.md): releases tagged on `Stable-Release`.
8* [CHANGELOG-COMMUNITY.md](CHANGELOG-COMMUNITY.md): changes merged into `community`.
9* [Release and versioning map](docs/RELEASE-AND-VERSIONING-MAP.md): which build introduced each format and ABI.
10
11Builds dev.1 to dev.10 predate the repository history, which starts at dev.11 (`fe01f26`). Their entries are kept as written. Some later builds (for example dev.33 to dev.36 and dev.44) were recorded here but committed together with a neighbouring build.
12
13## 0.1.0-build.85 — 2026-11-07
14
15* **Safe Actions** ([spec/CKey-Safe-Actions.md](spec/CKey-Safe-Actions.md)). A Safe Action is a code a reader may act on without asking first: `ckey-action:1:<base64url envelope>`, signed with Ed25519 by a site (the issuer, named by its domain) for one app and one action that app registered, with a single-use request ID and a short lifetime. Anything that does not verify runs nothing. Same-device actions run at once; cross-device actions (sign in a browser elsewhere, approve a payment) run only after a confirmation the SDK owns, which shows the issuer's account of who is asking (device, place, time) fetched from the issuer's own domain. The issuer's scope and the app's registration both count, and the stricter wins. This is the one exception to rule 1 of the [payload conventions](spec/CKey-Payload-Conventions.md) (now draft 0.2).
16* **Core:** SHA-512 and Ed25519 (RFC 8032, canonical-S check) in `src/ed25519.inc`; `ckey_is_safe_action`, `ckey_safe_action_parse`, `ckey_safe_action_verify`, `ckey_safe_action_sign`, `ckey_ed25519_public_key`, struct `ckey_safe_action` and status `CKEY_EXPIRED` (7). Additive: the C ABI stays 4.
17* **Conformance vectors:** `tests/vectors/safe-actions-v1.json`, 22 envelopes from an implementation independent of the core (`tools/safe_actions_vectors.mjs`, Node crypto), each with its required verdict. The core's `ckey_safe_actions` test, the web, Android and .NET tests check every one; CI checks the file is current.
18* **CLI:** `ckey action keygen | public-key | sign | verify`.
19* **SDKs:** a policy class in each SDK (`SafeActions` for the web, `CKeySafeActions` for Swift, Android and .NET) that pins issuer keys, registers actions, and returns not-a-safe-action / rejected / performed / needs-confirmation. Approval is internal to the SDK: only its own prompt (a `<dialog>`, SwiftUI `CKeySafeActionPrompt`, Android `CKeySafeActionPrompt` over AlertDialog, WPF `CKeySafeActionPrompt` or `CKeySafeActionConsolePrompt`) can run a cross-device action. Web bridge `ckw_safe_action`; Android JNI `nativeSafeAction`; .NET P/Invoke over `ckey_safe_action`.
20* **Verified here:** core tests in release and sanitizer builds; web tests; the Android host check (JNI signatures, exports, Safe Action vectors through JNI, Kotlin API checks, 28 JUnit tests); 76 of 76 .NET tests against `libckeynative.so`. Not compiled here: the Swift sources (CI macOS) and `CKey.Wpf` (needs the Windows desktop SDK; CI Windows).
21
22## 0.1.0-build.84 — 2026-11-07
23
24* **Field tests.** `field-tests/suite.json` defines eleven real-world tests: smallest printed size, reading distance, viewing angle, lighting, screens, damage, styles, density, the print layout on small labels, time to read, and payload safety. Each states the claim it checks and why, the set-up and steps, the test codes (payload, version, level, layout, style and print size), what to record, and a pass / partial / fail rubric. `tools/field_tests.py` validates the suite and generates `field-tests/README.md`, one page per test in `field-tests/tests/`, and the GitHub issue form `.github/ISSUE_TEMPLATE/field-test-report.yml`; CI runs it with `--check` and encodes every test code with the CLI. openckey.org reads the same `suite.json` from the release it pins, prints the test sheets with the web SDK, and publishes every result, from the site or from GitHub issues, at openckey.org/tests.
25* No change to the core, formats or packages.
26
27## 0.1.0-build.83 — 2026-11-06
28
29The release that goes to every registry, `v0.1.0-build.83`.
30
31* **npm package renamed `openckey`.** The name `ckey` on npm belongs to an unrelated package published in 2018, so `v0.1.0-build.82` could not be published there. Install with `npm install openckey` and import from `'openckey'`; the API is unchanged. The release tarball is `openckey-<version>.tgz`.
32* No change to the core, formats or other packages.
33
34## 0.1.0-build.82 — 2026-11-06
35
36Tagged `v0.1.0-build.82`; superseded by build.83 (the npm name `ckey` was taken).
37
38* **Style validation without undefined behaviour.** The structural check read `policy`, `modules`, `rings` and `rays` as their enum types before range-checking them, so a caller passing an out-of-range value (the certification test passes 9) was undefined behaviour, and the sanitizer CI job stopped on it. The check now reads each field's raw integer and refuses unknown values as before.
39* **Android host check:** `jni-host-test/run.sh` looked up optional jars in the Gradle distribution with `ls`, and under `set -euo pipefail` a missing jar ended the script with exit code 2 before the Kotlin step. Missing optional jars are now ignored.
40* **Verified here:** the full suite under AddressSanitizer and UBSan with `-fno-sanitize-recover=all` (13 of 13), and the Android host check (JNI signatures, exports, 60 harness checks, Kotlin API checks, 17 JUnit tests).
41
42## 0.1.0-build.81 — 2026-11-06
43
44Tagged `v0.1.0-build.81`; superseded by build.82 (two CI checks stopped its release workflow).
45
46* **.NET native library renamed to `ckeynative`** (`ckeynative.dll`, `libckeynative.so`, `libckeynative.dylib`). Windows file names ignore case, so the native `ckey.dll` and the managed `CKey.dll` were the same file in an application's output folder; one overwrote the other and P/Invoke found no `ckey_get_abi_version` (33 of 65 tests failed on Windows). Every Windows consumer of the package would have hit this. The CMake output name, `NativeLibraryName.Name`, the package's `runtimes/<rid>/native/` assets, the .NET Framework copy target and the CI checks all use the new name.
47* **Verified here:** 65 of 65 .NET tests pass against `libckeynative.so`; a local pack places every native asset and both target frameworks where the CI layout check expects them.
48
49## 0.1.0-build.80 — 2026-11-06
50
51
52* **.NET SDK compiles again.** `CKeyPayload.cs` held a literal LINE SEPARATOR and PARAGRAPH SEPARATOR (U+2028, U+2029) inside a string. C# treats both as line breaks, so the string ended mid-line and the SDK had not compiled since build 67. They are written as `\u2028` and `\u2029`; no other source file holds either character.
53* **.NET certification test:** it still expected every modelled trial to pass. Since build 72 the photo sweep includes sizes too small to read, so a certified style passes some trials, not necessarily all; the test now checks that.
54* **Verified here:** `dotnet build` of `CKey` and `CKey.Wpf` (net8.0 and netstandard2.0) with no errors or warnings, and `dotnet test`: 65 of 65 pass against the Linux native library.
55
56## 0.1.0-build.79 — 2026-11-06
57
58
59* **macOS CI:** GitHub retired the `macos-14` runners ("The configuration 'macos-14-arm64-us-default' is not supported"), so every macOS job (the .NET natives for `osx-arm64` and `osx-x64`, the .NET tests, the Swift package and the release CLI build) was cancelled before it started. They run on `macos-15`.
60
61## 0.1.0-build.78 — 2026-11-06
62
63
64* **Windows .NET build:** the struct-layout check compared the probe's output (carriage returns stripped) with an expected file that the Windows runner had checked out with CRLF endings, so every line differed although every offset and size matched. The check now uses `diff --strip-trailing-cr`, and `.gitattributes` keeps LF endings for the expected layout and the test vectors on every platform.
65
66## 0.1.0-build.77 — 2026-11-06
67
68Fixes for the release build, which failed at the `v0.1.0-build.76` tag.
69
70* **Windows (.NET native library):** MSVC has no `__int128`, which the telemetry core used for exact 128-bit multiply-divides. A `mul_div` helper keeps `__int128` where the compiler has it and otherwise uses a portable 64×64→128 multiply and long division (checked against `__int128` on 3.5 million random and edge-case inputs). The bound check on a histogram's sum no longer needs 128 bits.
71* **Linux (.NET native library):** `libckey.so` exported C++ standard-library template instantiations under GCC 11. A linker version script (`sdk/dotnet/native/exports.map`) now exports `ckey_*` only (57 symbols); there is still no libstdc++ dependency.
72* **Debug and sanitizer builds:** `field_score`, which calls the scan library's calibration scores, moved from the shared internals to `api_scan.inc`. Unoptimized codec and render builds no longer reference scan symbols, so `ckey_interchange_tests` links.
73* **Docs:** the release table and changelogs record build.76 as tagged but not released.
74
75## 0.1.0-build.76 — 2026-11-06
76
77The first release, promoted to `Stable-Release` as `v0.1.0-build.76`.
78
79* **README:** rewritten for people using CKey: a gallery of rendered codes, install lines and a short example for every package (Swift, npm, Maven, NuGet, Homebrew, CMake), what CKey can do, and an index of the documentation. The former README's technical walk-through moved, almost unchanged, to `docs/OVERVIEW.md`.
80* **Gallery:** `docs/gallery/` holds eight codes rendered by the core (plain, print layout, custom colours, Concept, dotted rings, accents, light on dark and a dense v17). `tools/gallery/ckey_gallery.cpp` (target `ckey-gallery`) renders them, reads each one back with the real decoder, certifies it and writes the gallery page. All eight read back and certify.
81* **Publishing:** `tools/release/publish.sh <tag>` publishes a release from the Release workflow's draft (npm, NuGet, Maven Central, Homebrew tap, then the GitHub Release), after checking `SHA256SUMS` and provenance. It rebuilds nothing. `docs/PUBLISHING.md` covers registry accounts and the release procedure.
82* **Lock-down:** every GitHub Action is pinned to a commit SHA with its tag as a comment; `.github/CODEOWNERS` added; `docs/REPOSITORY-SETTINGS.md` lists the branch, tag and security settings to apply in GitHub.
83* **Repository:** `CONTRIBUTING.md` and `SECURITY.md` added; `.gitignore` covers SDK build outputs. `tools/check_version.py` checks `CONTEXT.md` only where it exists, since release branches ship without it. `tools/bump_version.py` also updates the README and SDK READMEs, whose install lines name the version.
84
85## 0.1.0-build.75 — 2026-11-05
86
87* **Swift package resolves in Xcode:** the `CKeyScan`, `CKeyRender` and `CKeyCertify` targets now ship an `include/` directory. Git stores no empty directories, so a fresh clone lacked them and Xcode refused the package graph with "public headers ("include") directory path for 'CKeyScan' is invalid or not contained in the target".
88
89## 0.1.0-build.74 — 2026-11-05
90
91Swift SDK fixes, found while building the Open CKey Scanner iPhone app:
92
93* **`CKeyPayload.classify`, `tel:` and `sms:`:** the number now ends at the first `;` or `?`, so URI parameters are never dialled as digits. Previously `tel:+15550100;ext=7` became `+155501007`.
94* **`CKeyPayload.text(from:)` and `CKeyResult.text`:** only C0 controls other than tab, LF and CR, plus DEL and C1, make a payload binary, as in the web reader. Emoji ZWJ sequences, soft hyphens, bidi marks and BOMs are text.
95* **vCards** must begin with a `BEGIN:VCARD` line and end with an `END:VCARD` line, as in the web reader.
96* **`CKeyCameraScanner(observer:resolution:)`:** new `.uhd4K` (default) and `.hd1080` resolutions. `.hd1080` suits memory-limited camera extensions.
97* **Camera errors:** reported as `.cameraAccessDenied` or `.cameraUnavailable` instead of `.invalidConfiguration`.
98* XCTests added for each fix. They have not been compiled here; `sdk-swift.yml` runs them on macOS.
99
100## 0.1.0-build.73 — 2026-11-05
101
102* **The photo sweep, not fixed rules, decides certification.** Custom backgrounds were refused by the old 3:1 rule even when they scanned well; concept on slate `#555`, for example, has a 2.9:1 accent and is graded "good" by the sweep. Now:
103 * **Hard refusals** remain only for near-invisible marks (below **1.5:1**), mixed polarity, and logo limits (Ring Key, plate, 50% budget).
104 * **Low contrast below 3:1** and strong depth on dark-on-light styles are warnings. They appear in the style check ("style OK, but …; certify it to check that it scans") and in the certification message ("note: …").
105 * **Strict renderers** draw these styles. Adaptive repairs only hard failures up front, and the advisory repairs only if the sweep fails.
106 * **Measured:** concept on navy, forest and burgundy is excellent; on slate, teal and `#777` it is good; on tan and cream it is refused or marginal.
107* Tests updated: `#e8e8e8` on white is the near-invisible refusal case, and `#bbbbbb` renders but fails the sweep.
108
109## 0.1.0-build.72 — 2026-11-05
110
111* **Certification now models real photographs.** The old check decoded the styled symbol from a perfectly known position at a comfortable 8 px per module, so almost every style passed. `ckey_certify_scalable_style` now runs a photo sweep:
112 1. place the symbol in a cluttered frame, with rotation, keystone, glare, defocus and noise;
113 2. run the full reader pipeline: find borders, refine, decode;
114 3. walk a ladder of camera resolutions (9 down to 3 px per module) to find the smallest that still decodes;
115 4. grade the style against plain black on white for the same symbol: **excellent** as small as plain, **good** one step larger, **marginal** two steps, otherwise **fails**.
116 * Certification needs the safety rules plus a grade of good or better.
117 * The message states both sizes, so the cost of a decoration is visible.
118 * The plain reference is cached per symbol.
119 * Typical cost is 0.1–0.7 s for v1–v11 and about 5 s for v31, natively.
120 * The API and ABI are unchanged; `model.px_per_module` now sets the resolution the style must decode at.
121 * The design-system spec and the header document the new semantics.
122* **Profile 0.1 regression coverage:** the simulated-reader test now also scans print-layout symbols (v2, v11, v20) through the exact pipeline the iPhone reader uses. All 48 new scenes decode, and under heavy blur Profile 0.1 decodes far better than the standard layout. The reported CKey Reader failure is consistent with an app build from before build 58, when Profile 0.1 did not exist.
123* **CKey Reader:** the subtitle now lists Profiles 0, 0.1 and 1–3.
124
125## 0.1.0-build.71 — 2026-11-05
126
127* **Removed `services/openckey`,** the Node prototype of the openckey.org service. Its telemetry ingestion, renderer page and abuse controls now live in the proprietary openckey.org site. That site follows the same normative `docs/adoption-and-telemetry.md`, and it pins this repository's commits to build the web SDK.
128
129## 0.1.0-build.70 — 2026-11-05
130
131* **openckey.org is now a separate, proprietary site.** It provides:
132 * the browser renderer;
133 * package downloads;
134 * `/text/{id}` and `/sq/{id}` destinations;
135 * telemetry ingestion.
136 * README, implementation status and context now say so. The CI job for the prototype `services/openckey` is removed. The prototype itself is retired in the next build.
137* **Release workflow:** reuses the SDK workflows, which now accept `workflow_call`, to build and test each package, and attaches it to the draft release:
138 * the npm tarball;
139 * NuGet packages;
140 * a Maven repository zip for Android;
141 * a Swift source zip.
142 * These join the CLI archives, the source archive and the Homebrew formula, so openckey.org can host every package for download.
143* The `sdk-web` workflow also uploads the `npm pack` tarball.
144
145## 0.1.0-build.69 — 2026-11-05
146
147* **Android SDK, Maven `org.openckey:ckey` (`sdk/android/`, spec §3 and §7):** AGP 8 and Kotlin. NDK CMake compiles the repository's own `src/` files with a JNI bridge that exports only the `Java_*` functions.
148 * **Modules:**
149 * `ckey`, the core AAR, with no dependencies beyond the Kotlin standard library;
150 * `ckey-compose`, which provides `CKeyImage`;
151 * `ckey-camerax`, which provides `CKeyAnalyzer`.
152 * **Scan-only and render-only builds:** set the Gradle properties `ckey.native.scan`, `ckey.native.render` and `ckey.native.telemetry`.
153 * **API:**
154 * `CKeySymbol.encode`;
155 * `toBitmap`, which converts straight alpha to Android's premultiplied form;
156 * `toPng`, `toSvg`, `toPdf` and `certify`;
157 * `CKeyStyle`, with presets and a policy;
158 * `CKeyDecoder`, for gray buffers, a `ByteBuffer`, `android.media.Image` or a `Bitmap`;
159 * `CKeyScanner`, which records telemetry sessions;
160 * `CKeyPayload.classify`.
161 * **Telemetry:** `CKey.telemetry` takes 0, 1 or 2; any other value throws `IllegalArgumentException`. One background thread sends batches over HTTPS only, and the SDK tries a send when the app moves to the background.
162 * **Publishing:** maven-publish to a local directory, with no credentials in the build.
163 * **Verified here** with `jni-host-test/run.sh`:
164 * the Kotlin native declarations and the C++ bridge are checked against each other through a generated JNI header;
165 * a JVM harness runs 60 checks under `-Xcheck:jni`, including round trips, premultiplication, exports, refusals, certification and the full telemetry cycle;
166 * the real Kotlin sources compile with Kotlin 2.0.21 against stubs;
167 * 22 Java-side API checks pass, and 17 JUnit tests pass.
168 * **Not verified here:** the Gradle/AGP/NDK build, the Compose module and the instrumented tests run only in `sdk-android.yml`.
169* `tools/bump_version.py` now also covers the SDK READMEs.
170
171## 0.1.0-build.68 — 2026-11-05
172
173* **`docs/IMPLEMENTATION-STATUS.md`:** rewritten against builds 61–67, item by item against the specification. It ends with a list of what only the maintainer can do: deploy openckey.org, publish to the registries, set up branch protection, make real prints and camera captures, and pin action SHAs.
174* **`docs/platform-integration.md`** (new) is for camera-platform teams. It covers:
175 * what to link (only `ckey_codec` and `ckey_scan`);
176 * the decoding pipeline for each frame;
177 * threading and memory;
178 * the security review surface;
179 * conformance;
180 * a proposed device benchmark plan, with a p90 first-decode target to be confirmed by measurement.
181* **README:** new sections on libraries, SDKs and services, and on rendering, certification and verification. The documentation index is extended.
182* **CONTEXT:** records the decisions from builds 61–68.
183* No code changes.
184
185## 0.1.0-build.67 — 2026-11-05
186
187* **Swift package `CKey` (`Package.swift` at the repo root, `sdk/swift/`, spec §3 and §7):** apps add the repository URL in Xcode.
188 * **C++ targets:** `CKeyCodec`, `CKeyScan`, `CKeyRender`, `CKeyCertify` and `CKeyTelemetry`. Each is a one-line shim that compiles the repository's own `src/` file, so nothing is copied.
189 * **Products:**
190 * `CKey`, the full SDK;
191 * `CKeyScanner`, scan only: module `CKeyScanKit`, with no renderer and no telemetry.
192 * **Rendering API:**
193 * `CKeySymbol.encode`;
194 * `cgImage`, `pngData`, `svg` and `pdfData`;
195 * `certify`;
196 * `CKeyStyle`, with presets and a policy.
197 * **Scanning API:**
198 * `CKeyView`, a SwiftUI view that draws native pixels and is transparent by default, plus UIKit and AppKit views;
199 * `CKeyDecoder` for `CGImage` and `CVPixelBuffer`;
200 * `CKeyScanner`, an AVFoundation camera helper that drives telemetry sessions.
201 * **Payloads:** `CKeyPayload.classify`.
202 * **Telemetry:** `CKey.telemetry` takes 0, 1 or 2, and `try CKey.setTelemetry(rawValue:)` rejects anything else. Batches are sent off the scan queue through an ephemeral `URLSession`, and the SDK tries a send when the app moves to the background.
203 * **Privacy:** `PrivacyInfo.xcprivacy` declares analytics that are not linked to the user and not used for tracking.
204 * **Verified here:** the C++ shims and module maps compile with gcc and clang, and their results were checked.
205 * **Not verified here:** the Swift sources have not been compiled. `sdk-swift.yml` runs `swift build`, `swift test` and an iOS Simulator build on macOS.
206* **.NET SDK, NuGet `CKey` (`sdk/dotnet/`, namespace `OpenCKey`):**
207 * **Native library:** one shared `ckey` library built by CMake, exporting exactly the 55 C ABI functions. On Linux it statically links the C++ runtime. The package carries it for `win-x64`, `linux-x64`, `osx-arm64` and `osx-x64`.
208 * **Interop:** `LibraryImport` on net8.0 and `DllImport` on netstandard2.0. Struct layouts are checked against a C `offsetof` probe in tests and in CI.
209 * **API:**
210 * `CKeySymbol.Encode`;
211 * `RenderRgba`, `ToPng`, `ToSvg` and `ToPdf`;
212 * `Certify`;
213 * `CKeyDecoder.Decode`, using the same pipeline as the iOS reader;
214 * `CKeyPayload.Classify`.
215 * **Telemetry:** `CKey.Telemetry` takes 0, 1 or 2, and anything else throws `ArgumentOutOfRangeException`. Batches are sent asynchronously with `HttpClient`. `CKeyScanSession` records scan sessions.
216 * **Optional `CKey.Wpf`** adapter, which returns `WriteableBitmap`.
217 * **Verified here:** the native build, its exported symbols and the struct layouts.
218 * **Not verified here:** the C# has not been compiled. `sdk-dotnet.yml` builds the native libraries on Linux, Windows and macOS, then runs `dotnet test` and `dotnet pack`.
219* `.gitignore`: SwiftPM's `.build/` and `.swiftpm/`.
220
221## 0.1.0-build.66 — 2026-11-05
222
223* **CMake package (spec §7):** `cmake --install` installs the libraries, headers and the `ckey` CLI, plus `CKeyConfig.cmake`.
224 * Consumers call `find_package(CKey)` and link `CKey::core`, or individual targets: `CKey::scan`, `CKey::render`, `CKey::codec`, `CKey::certify`, `CKey::telemetry`.
225 * `examples/c/` has an encode → raster → PNG → scan example and a scan-only example. CI installs the package and builds and runs the example.
226* **Homebrew (proposed in spec §7):** `packaging/homebrew/ckey.rb.in` and `tools/release/make_homebrew_formula.py`, which fills in the tag and the source archive's SHA-256.
227* **Release workflow (`release.yml`, spec §7–8):**
228 * **Trigger:** runs only for `v*` tags. The tag must equal `v` + `VERSION` and sit on `Stable-Release`. The full test suite runs first.
229 * **Artifacts:**
230 * CLI and library archives for Linux x64, macOS arm64 and Windows x64;
231 * the source archive;
232 * the Homebrew formula;
233 * `support-matrix.json`;
234 * `SHA256SUMS`.
235 * **Provenance:** Sigstore build provenance through `actions/attest-build-provenance`.
236 * **Result:** a draft GitHub Release titled `CKey <version>`, for the maintainer to publish.
237* **Deprecate workflow (`deprecate-release.yml`, spec §8.2):** a manual run retitles a release `Deprecated - {release name}` and adds a notice naming the replacement and the reason. The tag and its source stay.
238* **Community workflow (`community.yml`, spec §8):**
239 * runs ASan/UBSan tests on pull requests and pushes to `community`;
240 * uses `pull_request` with a read-only token and no persisted credentials, so fork code never sees secrets;
241 * rejects changes to `VERSION` or to the release workflow, and warns when `CHANGELOG-COMMUNITY.md` has no entry.
242* **CI:** new jobs for the openckey.org service tests, and for synthetic benchmarks with QR Code and Data Matrix through zxing-cpp (uploaded as an artifact).
243* **Web SDK, npm `ckey` (`sdk/web/`):** an Emscripten build of codec, scan, render, telemetry and certify, plus a small C bridge and an ES module API. It matches the openckey.org renderer page:
244 * `encode`, `renderToCanvas`, `renderPixels`, `renderPNG`, `renderSVG` and `renderPDF`;
245 * `decodeImageData`, `createScanner`, `classifyPayload` and `certify`;
246 * `telemetry`, valued 0, 1 or 2, raising `RangeError` otherwise. The telemetry sender uses `fetch` with keepalive, sends no credentials or referrer, and never throws;
247 * TypeScript types.
248 * **Verified here:** 26 JS tests pass, and native bridge tests pass under ASan/UBSan.
249 * **Not verified here:** the wasm build and its tests run only in `sdk-web.yml`, because emsdk is not available in this environment.
250* **`tools/bump_version.py`** bumps every versioned file, including SDK manifests.
251
252## 0.1.0-build.65 — 2026-11-05
253
254* **`.ckey` interchange v1 (spec §2.5; `spec/CKey-Interchange-v1.md`):** `ckey_write_interchange` and `ckey_read_interchange` store a Profile 0 or 0.1 codeword, an optional style and an optional logo reference.
255 * **Layout:** a 12-byte header, type-length-value records, and a closing CRC-32C. Bit 15 marks a record as optional.
256 * **Strict, bounded reader:** at most 64 KiB, all reserved bytes must be zero, and an unknown required record is rejected. The codeword must decode and must be the canonical encoding of its payload.
257 * The file is optional: rendering never needs it. The payload is stored in clear.
258 * **Library:** the reader is in `ckey_codec` and needs no renderer.
259 * **Tests:** new test `ckey_interchange` covers round trips, the rejection cases, and 20,000 mutated files, clean under AddressSanitizer and UBSan.
260* **Conformance suite (spec §6; acceptance criterion 1):** `tools/conformance/ckey_conformance.cpp` and `tests/vectors/conformance-v1.json`.
261 * **19 vectors:** P0 and P0.1 across versions and levels, an empty payload, a vCard, and P1, P2 and P3 legacy encodings.
262 * **Expected outputs:** the codeword, an FNV-1a 64 hash of the canonical geometry with coordinates rounded to 10⁻⁶, a `.ckey` file hash, and a check that a raster decodes. Pixels are checked by behaviour, not by hash.
263 * **Verified identical** under gcc -O0 and -O2, and under clang -O2 with `-ffast-math`.
264 * **New CTest `ckey_conformance_vectors`.** SDKs load the same JSON; see `tools/conformance/README.md`.
265* **Support matrix (spec §8.2):** machine-readable `docs/support-matrix.json` with the current build, ABI, tag form, formats and their status, interchange and telemetry schema versions, packages and deprecations. `check_version.py` checks it.
266* **Benchmark harness (spec §5, §6):** `ckey-bench` (`tools/optical-benchmarks/`).
267 * **Pipeline:** generate, degrade (clutter, rotation, keystone, glare, defocus, noise), then decode with the full reader pipeline.
268 * **Measures:**
269 * recovery;
270 * corrected bytes;
271 * latency p50, p90 and p99;
272 * the smallest side for 90% success, and bytes per 1,000 px² at equal area;
273 * false positives.
274 * **QR Code and Data Matrix** comparison through zxing-cpp with `-DCKEY_BENCH_ZXING=ON`.
275 * A quick, CKey-only synthetic run is committed in `docs/benchmarks/`. It found 0 false positives in 60 clutter frames. Results are explicitly synthetic.
276
277## 0.1.0-build.64 — 2026-11-05
278
279* **ABI 4:** `ckey_style` gains `policy`. Code that fills styles with `ckey_style_default` or `ckey_style_preset_fill` needs no other change.
280* **Style policies (spec §4):** `CKEY_POLICY_STRICT` (the default), `CKEY_POLICY_ADAPTIVE` (renderers repair the style by fixed rules, then check it) and `CKEY_POLICY_OVERRIDE` (renderers draw the style as given; checks report "NOT scan-safe"). Structurally invalid values are refused under every policy.
281* **Optical certification (spec §4; acceptance criteria 5 and 6):** `ckey_certify_scalable_style` certifies through a modelled scan, not a contrast ratio:
282 1. render with the native raster;
283 2. composite over each backdrop;
284 3. apply glare, defocus and noise;
285 4. decode with the real decoder, on 3 seeded trials per backdrop.
286 * It also reports the minimum contrast, the mark/gap luma separation and the logo load.
287 * Adaptive certification returns the repaired style and a `CKEY_ADJUST_*` bit for each change.
288 * **Transparent output** needs a `ckey_backdrop` (colours and/or the host's pixels). Without one the result is "background unknown" and is never certified.
289* **New library `ckey_certify`** (needs scan and render). `ckey_core` now includes it.
290* **New test `ckey_style_certification`:**
291 * The default style certifies at v1, v11 and v31 (v31 in about 1.3 s).
292 * A coarse, blurred model fails v31.
293 * Strict refuses low contrast in the check, the renderer and certification.
294 * Override renders but never certifies.
295 * Adaptive repairs ink, accents and depth.
296 * Transparent output: refused without a backdrop, certified on white, refused for black on #333333, which adaptive fixes by flipping the ink.
297 * A half-black host image is refused.
298 * The concept preset certifies.
299* `spec/CKey-Design-System.md`: new section on policies and certification.
300
301## 0.1.0-build.63 — 2026-11-05
302
303* **Separate libraries (spec §2.1, §6, §9.2; acceptance criterion 8):** the core is now `ckey_codec`, `ckey_scan` (needs only the codec), `ckey_render` and the optional `ckey_telemetry`. `ckey_core` remains as the union of the first three.
304 * A scanner app links no renderer and no telemetry. `ckey_optical_image_decode` now links `ckey_scan` alone, so CI enforces this.
305 * The source is split into shared internals plus one file of public functions per library. `src/ckey.cpp` stays as the amalgamation for the Xcode projects. See `src/README.md`.
306 * `-DCKEY_BUILD_TELEMETRY=OFF` leaves telemetry out entirely.
307* **Telemetry core (spec §9; `docs/adoption-and-telemetry.md`):** `include/ckey/telemetry.h`.
308 * **Modes:** 0, 1 (the default) and 2. Any other value returns `CKEY_INVALID_ARGUMENT` and leaves the mode unchanged. Mode switches follow the documented table: switching to 0 purges everything; 1 → 2 drops the unsent delta and ignores any batch in flight.
309 * **Sessions:** a session starts when scanning starts, succeeds at the first decode, and fails when scanning ends or after a 30 s timeout. Frames are counted separately.
310 * **Data:** counts by profile, and histograms of time to decode and per-frame processing time.
311 * **Batches:**
312 * the exact schema-1 JSON, with per-batch clamping to the schema's limits;
313 * an hourly send cadence;
314 * result handling that drops a batch on 2xx, 400 or 413, and keeps it on 429, 5xx or a network error.
315 * **State:** optional export and import, strictly parsed.
316 * **No networking:** the core never sends; SDK wrappers post to `CKEY_TELEMETRY_ENDPOINT` (`https://openckey.org/api/telemetry/v1/batch`). It uses no clock, heap or exceptions, and is thread-safe.
317 * **Tests:** new test `ckey_telemetry`, clean under AddressSanitizer, UBSan and ThreadSanitizer.
318* **openckey.org service (`services/openckey/`):** a Node 22 service with no dependencies. Run `npm test`; 75 tests pass.
319 * **Telemetry ingestion:**
320 * strict parsing, rejecting unknown fields, non-integer literals, duplicate keys and over-size bodies;
321 * rate limits keyed by a daily-salted HMAC of the client IP, kept in memory only;
322 * daily aggregates only, kept for 400 days;
323 * a summary labelled "not total usage".
324 * **Short links (`/r/:id`):** created by an admin. The interstitial shows the destination host first; `?go=1` redirects directly. Counts are opt-in, per link and per day.
325 * **Hosted text pages (`/t/:id`):** escaped HTML under a strict CSP, plus `.txt`.
326 * **Web renderer page (`/`):** uses the web SDK when it is built.
327 * No IPs, bodies or URLs are logged.
328
329## 0.1.0-build.62 — 2026-11-05
330
331* **Canonical geometry (spec §2.1):** `ckey_scalable_geometry` returns a Profile 0 / 0.1 symbol as a list of primitives in canonical units, in painter's order. The primitives are rect, rect outline, circle, diamond, line, arc and logo, each tagged with a layer and a paint. Every native backend draws this list.
332* **Direct raster backend (spec §2.2, §2.5, acceptance criteria 2 and 4):** `ckey_render_scalable_raster` turns the geometry into pixels with no SVG, file system, network or graphics library.
333 * **Output:** RGBA8 (straight alpha) or GRAY8.
334 * **Three raster policies:**
335 * `COVERAGE`: supersampled antialiasing.
336 * `BINARY`: a pixel is ink when at least 50% of its samples are covered.
337 * `HYBRID`: coverage, with marks thinner than a minimum feature, 1 px by default, widened to it.
338 * **Transparency:** in transparent mode the background is not painted and unpainted pixels have alpha 0.
339 * **Materials:** natively rendered approximations of sheen, vignette, grain, drop shadow and bevel. The logo is supplied by the caller as RGBA and drawn into its clip circle.
340* **PNG export:** `ckey_encode_png`, with DEFLATE implemented in the core (fixed Huffman codes, LZ77), RGBA8, GRAY8 or 1-bit, and pHYs. There is no zlib dependency.
341* **PDF vector export (spec §2.3–2.4):** `ckey_render_scalable_pdf` writes a one-page PDF 1.4 with exact paths, arcs as cubic Béziers, flat ink, accents and background, and an optional transparent mode.
342* **Print path:** `ckey_render_scalable_print` now rasterizes the geometry directly, with the binary policy and a 3×3 sample grid, instead of generating SVG and parsing it back. The core no longer includes the SVG parser, which stays a test and reference tool.
343* **CLI:**
344 * `encode-scalable` gains `--raster out.png --size-px N --policy coverage|binary|hybrid --transparent yes|no` and `--pdf out.pdf` (`--size-in` sets the width).
345 * `--png` and `print-sheet` use the core print raster and PNG encoder.
346* **New test `ckey_native_render`:**
347 * The direct raster matches the rasterized SVG export to within 0.01% of pixels, from SVG's 4-decimal rounding, at v2, v11, v20 and v31 across shapes, ring styles and caps.
348 * All three policies decode at 7 px/module for v1, v9 and v31 in both layouts, and the output is deterministic.
349 * HYBRID adds ink to thin features.
350 * Transparent margins have alpha 0.
351 * The concept preset with depth, grain and a logo decodes.
352 * Geometry API layers are checked.
353 * PNG round-trips through an independent inflater.
354 * PDF cross-reference offsets are valid.
355 * Print output is pure ink or paper.
356* No format changes, and conformance hashes are unchanged.
357* The partial `services/openckey/` files committed by accident in build 61 are finished in the next build.
358
359## 0.1.0-build.61 — 2026-11-05
360
361* **Version scheme:** from this build, versions are `0.1.0-build.N` and release tags are `v0.1.0-build.N`, as the maintainer chose. Builds 1–60 keep their `0.1.0-dev.N` names in this log. See the [map](docs/RELEASE-AND-VERSIONING-MAP.md#tag-and-version-form).
362* **Default look (specification §4):** with no style, every profile now renders black ink on white. This covers the P0 default style and the P1–P3 SVG renderers.
363* **Presets:** the previous cream-on-black concept look is a named preset, `ckey_style_preset_fill(style, CKEY_PRESET_CONCEPT)`. The CLI's `--preset concept` uses it.
364* **Mac renderer:** defaults to black on white. Its colour swap for P1–P3 now goes through placeholders, so a black background cannot be repainted as ink.
365* No format changes. Site hashes are unchanged; only SVG colours changed.
366* Added `docs/adoption-and-telemetry.md`, the normative telemetry definition:
367 * modes 0/1/2 and what switching between them does;
368 * what counts as a scan session;
369 * the batch schema, version 1;
370 * the openckey.org service's obligations.
371
372## 0.1.0-dev.60 — 2026-11-05
373
374* Cleaned up `CHANGELOG.md`:
375 * Removed the duplicate dev.11 and dev.42 entries.
376 * Restored newest-first order.
377 * Recorded what happened to dev.27–30: dev.28's entry had been folded into dev.31, and the other three were never committed builds.
378 * Added a header that explains the log.
379* Added `CHANGELOG-STABLE.md` for tagged `Stable-Release` releases (none yet) and `CHANGELOG-COMMUNITY.md` for `community` merges. Each has an entry template and rules.
380* Added `docs/RELEASE-AND-VERSIONING-MAP.md`. It covers:
381 * how the software, format, profile and ABI numbers relate;
382 * the current support matrix, including legacy formats that still decode;
383 * the history of all 28 formats, with byte, profile, introducing build and status;
384 * the ABI history;
385 * a build-to-commit table;
386 * the procedures for cutting and deprecating a release.
387* README: added sections on the repository, its branches, releases, versions, changelogs and documentation. The current-format list now includes Profile 0 and 0.1.
388* `tools/check_version.py` now also requires the map and both branch changelogs to exist and be linked from the README.
389* No code or format changes.
390
391## 0.1.0-dev.59 — 2026-11-05
392
393* Work moves to the maintainer-only `scratch` branch. It was created from `main` at `47f80a9`; `community` and `Stable-Release` are being set up by the maintainer.
394* Added the consolidated **Rendering, SDK, Distribution, Adoption & Telemetry Specification** (`docs/SPECIFICATION-rendering-sdk-distribution-telemetry.md`), as written by the maintainer.
395* Added `docs/IMPLEMENTATION-STATUS.md`, which maps every section of that specification to the current code. It also flags five conflicts:
396 * the print path rasterizes through SVG;
397 * there is no transparent output;
398 * the default palette is cream on black, not black on white;
399 * the style check certifies from WCAG contrast alone;
400 * the scanner and renderer ship as one library.
401* Added `docs/BRANCHES-AND-RELEASES.md` with the branch model and release rules. Releases are immutable tags on `Stable-Release`, and deprecated releases are retitled `Deprecated - {release name}`.
402* No code changes.
403
404## 0.1.0-dev.58 — 2026-11-04
405
406* **Profile 0.1, a print-robust layout** (Format 0.28.0, byte `1C`).
407 * **Every mark and gap is at least about one module:** equal-arc ring slots (dots at least one module apart on every ring), 0.3-module ring dots, 0.35-module rays at least one module apart, and a compact 8-slot Ring Key.
408 * **Shared with P0:** the same skeleton, 31-version table, framing and correction levels.
409 * **Identification:** the decoder tells the layouts apart by their keys.
410 * **Simulated result:** the same payload decodes at about half Profile 0's printed width. For example, on an old inkjet with plain paper: 30 B at 0.24 in (P0: 0.42), 120 B at 0.32 in (0.68), 1,200 B at 0.91 in (1.47).
411* **ABI 3:** `ckey_scalable_symbol` gains `layout`, and the P0 encode, decode, capacity and logo-radius calls take a layout. `ckey_decode_scalable_gray8` also reports it.
412* **Print output.**
413 * `ckey_render_scalable_print` renders black ink on paper on the printer's dot grid, with no grey for halftoning.
414 * The CLI gains `encode-scalable --png --size-in --dpi --layout` and `print-sheet`, a self-identifying calibration ladder written as a 1-bit PNG that records its dpi.
415 * The CKey Renderer gains a Layout picker and **Export Print PNG…**.
416* **Print simulator** (`tests/print_sim.h`): binary device dots, ink spread, paper blur, then a camera at any resolution.
417 * **Findings for P0:** dark-on-paper prints about 35% smaller than the cream-on-black palette on plain inkjet. Ink-spread compensation does nothing at 600 dpi, because the spread is about one dot. Ring dots fail first.
418 * **Regression test:** a 120-byte payload at 0.42 in on the inkjet model must fail as P0 and decode as P0.1.
419* The reader shows `Profile 0.1 · print · v<N>`. Guides added: `spec/CKey-Printing.md` and `spec/CKey-Format-v0.28.0.md`. The reference rasterizer moved to `src/svg_raster.h`.
420
421## 0.1.0-dev.57 — 2026-11-04
422
423* **Profile 0 design system** (`spec/CKey-Design-System.md`). Styling changes how a symbol is painted, never where a reader samples.
424 * **Fixed:** the frame, finders and keys keep their geometry.
425 * **Mark shapes:** payload modules can be rounded, square, circle or diamond; ring marks joined or dotted; rays round or flat.
426 * **Paint:** colour is free within checks: metallic sheen, radial vignette, up to four accent colours, bevel, depth and grain.
427 * **Logo:** sits in the empty centre, or larger on a plate within an error-correction budget.
428 * **API:** `ckey_style`, `ckey_check_scalable_style` (contrast ≥ 3:1, one polarity, effect limits, logo budget ≤ 50% of the worst block), `ckey_render_scalable_styled_svg`, `ckey_scalable_logo_max_radius`, `ckey_scalable_logo_load`. The plain P0 renderer is now the default style.
429* **P0 decoding no longer depends on the finders' split or the empty centre.** Its skeleton is the frame stroke plus all eight finder arm ends, against the moat and quiet margin, so it is symmetric by design. Orientation comes from the Grid, Ring and Ray Keys. A logo in the centre therefore costs no calibration evidence. No wire change.
430* **Tests.** All 16 mark-shape combinations decode at v2 and v20. The concept palette passes the checker. Low-contrast, mixed-polarity and heavy-shadow-on-light styles are refused. At every level, the largest logo the checker allows still decodes. The test rasterizer gained polygons, `<defs>`, background detection and painter's-order logo plates.
431* **Real-browser check.** The concept look rendered in Chromium (champagne sheen, navy vignette, sage/grey accents, bevel, depth, grain, PNG logo) decodes exactly at v6, v11 and v20, sharp and blurred.
432* **CKey Renderer:** a Profile 0 **Design** panel (shapes, sheen, vignette, accents, bevel, depth, grain, logo picker, size, plate, Concept preset), with the style check shown live.
433* **CLI:** `encode-scalable` gains `--preset plain|concept`, `--modules`, `--rings`, `--rays`, `--logo <file>` and `--logo-radius`.
434* **Known limit:** Profile 0 scatters each byte's bits across the symbol. That keeps stripes and smudges cheap but makes covered area expensive, so logos currently fill the empty centre (about 22% of the width) with little room beyond. Byte-grouped placement would be a future wire-format change.
435
436## 0.1.0-dev.56 — 2026-11-04
437
438* **Profile 0, the scalable family (Format 0.27.0, byte `1B`, profile `00`).** Added alongside Profiles 1–3, which are unchanged.
439 * **One skeleton at 31 sizes.** Every version shares Profile 2's frame, finders, rings and dial in the 29-unit field. Only the module pitch changes, from 29 to 232 modules across (normative integer tables), about 7% finer per step.
440 * **No appearance jumps.** Capacity at level M rises smoothly from 27 to 4,619 bytes, with no step above 1.6×. A 33-character URL is v2, which looks like a locator, not Profile 2.
441 * **No filler waste.** Every site carries a codeword bit. Four correction levels are available: L/M/Q/H.
442 * **The keys identify the version.** The Grid, Ring and Ray Keys are drawn at each version's own module and slot pitch, so the decoder recognizes the version from them. The frame's version byte, RS and CRC confirm.
443* **New core API:** `ckey_encode_scalable`, `ckey_decode_scalable`, `ckey_decode_scalable_gray8`, `ckey_calibration_score_scalable_gray8`, `ckey_render_scalable_svg`, `ckey_scalable_capacity`, `ckey_scalable_modules_across`, and `CKEY_FIELD_SCALABLE` for `ckey_refine_quad_gray8`.
444* **Localization improvements (all profiles):**
445 * Border candidates are ranked by how well the shared skeleton fits, so a dense symbol's interior components can't crowd out its border.
446 * Per-corner refinement iterates down to sub-pixel steps.
447 * P0 aligns on the frame stroke's two edges with coarse-to-fine sample spacing. At v31 the corners resolve to 0.06–0.22 modules, from 2–5.5 px before.
448* **Tests and tools.**
449 * New `ckey_scalable_profile0` suite.
450 * The simulated reader covers P0 v1/v2/v11/v20/v31 and now decodes 144/144 scenes.
451 * New CLI command: `ckey encode-scalable [--version 1-31] [--level L|M|Q|H]`.
452* **Apps.**
453 * The iOS reader tries P0 first and shows `Profile 0 · v<N>`.
454 * The CKey Renderer defaults to P0, with version and correction pickers, and its read check reports `px/module`.
455 * Both use 64 KB output buffers, because P0 payloads exceed P3's 2,048 bytes.
456
457## 0.1.0-dev.55 — 2026-11-04
458
459* **Payload conventions.** New informative document, `spec/CKey-Payload-Conventions.md`. It defines how readers interpret text payloads:
460 * Standard URI forms: `https:`/`http:`, `mailto:`, `tel:` (E.164), `sms:`, `geo:` (RFC 5870), and vCard.
461 * One CKey convention: `address:` followed by a free-text street address, handed to the platform map app.
462 * Safety rules: never act automatically, allowlist only, keep the payload visible, and validate before offering an action.
463 * No wire format changes.
464* **iPhone reader actions.**
465 * The reader offers one tappable action per recognized payload: Open, Email, Call, Message, Show in Maps (`geo:` and `address:`, through `maps.apple.com`, which iOS routes to Maps), or Add to Contacts (vCard, through the share sheet).
466 * `javascript:`, `data:`, `file:` and unknown schemes are shown as text only.
467 * Text detection now accepts CR, which vCard line endings use.
468
469## 0.1.0-dev.54 — 2026-11-04
470
471* **The iPhone reader shows decoded payloads as text.** A payload that is valid UTF-8 without control characters (other than tab and newline) is displayed as text. An `http`/`https` URL gets an explicit "Open host" button and never opens automatically. The hex stays underneath for checking, and binary payloads still show hex.
472* **First successful live scan:** the dev.53 reader decoded `https://citeory.com/tour` on the maintainer's iPhone.
473
474## 0.1.0-dev.53 — 2026-11-04
475
476* **The iPhone reader started almost every decode from the wrong orientation, so P1 never scanned and P2 rarely did.** Its border detector oriented each candidate from the border's second moments (principal axes). A square outline's second moments are the same in every direction, so that fit cannot orient a square, and the angle came from pixel noise.
477 * On rendered P1/P2 symbols, an upright symbol was fitted at −45°, a diamond around the square. Rotations from 0° to 30° gave orientation errors of 11–45°.
478 * The refinement that followed could only scale, shift and nudge corners. It could not recover.
479* **Full-frame localization moved into the shared C++ core**, so it is tested here and identical on every platform.
480 * `ckey_find_borders_gray8` orients each border from its convex hull's minimum-area rectangle, which is exact for a square. It then line-fits each side's straight middle for a perspective-correct quadrilateral.
481 * It requires the whole border in view, instead of capping the border at 90% of the frame. That cap rejected large rotated P3 symbols.
482 * `ckey_refine_quad_gray8` starts from the known border-to-field scale (29/28.65). Its descent covers scale, **rotation** and offset, then per-corner moves.
483 * The iPhone reader now calls both functions. Its Swift border finder, threshold, and principal-axis fit are deleted.
484* **New simulated-reader test** (`ckey_simulated_reader`). Rendered symbols are composited into full 1080×1920 (P1/P2) and 2160×3840 (P3) camera frames with background clutter, rotation 0–33°, 8% keystone, 3×3 blur, noise, and dark-on-light polarity. The test then runs the reader's exact pipeline.
485 * Result: 80/80 scenes decode, with 16 skipped because the rotated symbol cannot fit in the frame.
486 * Restoring the principal-axis orientation drops this to 51/80.
487* Measured cost per frame on the CI-class CPU: about 28 ms (1080p) or 42 ms (4K) to find borders, plus about 11 ms per candidate to refine all three profiles.
488
489## 0.1.0-dev.52 — 2026-11-04
490
491* Ignore `macos/build/` and `macos/DerivedData/`. A command-line `xcodebuild` of the renderer writes there; only `/build/` and `/ios/build/` were ignored.
492* Recorded the first verified CKey Renderer build: `xcodebuild` (Xcode 27A9269, macOS 27.0 SDK, arm64 + x86_64) succeeded on the maintainer's Mac with the dev.51 Swift changes.
493
494## 0.1.0-dev.51 — 2026-11-04
495
496* **The iPhone reader could not decode Profile 3 at all.** It captured 1920×1080, but P3 needs about 11 px per symbol unit, about 1,280 px across its 116-unit field, which is more than the frame's height. Its border detector also capped candidates at 78% of the short frame side.
497 * The reader now prefers 4K capture, falling back to 1080p.
498 * The border search scales its sampling step with frame size, so it keeps about 960 × 540 points and the same angular resolution at constant cost.
499 * The size limits scale with that step, and the upper bound is now 90% of the short side.
500 * Not yet compiled or run on a device in this environment.
501* **The CKey Renderer's preview read check now reports the raster density it decoded at.** It flags results below each profile's decode floor, so a P3 `FAIL` caused by a small window or the Resolution slider is distinguishable from a symbol fault.
502* The app READMEs now describe how to confirm a rebuild picked up the current core, and what correct P2/P3 output looks like.
503
504## 0.1.0-dev.50 — 2026-11-04
505
506An independent second review of dev.49 was run under rule 7 and every verified finding is resolved. Formats 0.25 and 0.26 are unchanged on the wire; their specifications gain clarifications only.
507
508* **P3 isolated ring dots under blur.** Each 3×3 blur turned every isolated P3 ring dot into a 0. The reviewer measured 608/608 failing at 12 px/unit, and P3 failed with two blurs at every density. Ring sites are now re-thresholded by neighbour context, with fill sites' known values as the anchor. P3 now decodes 3/3 at 10–16 px/unit through two blurs. P2 now survives one blur at 10 px/unit and two blurs at 12.
509* **Threshold bound.** The two-means cut bound widened from [0.2, 0.8] to [0.05, 0.95]. P3 width coverage separates near 0.85.
510* **P2 calibration reference.** One "empty" reference, at (5.626, 5.626), was a payload site under the new safe area. It now samples the finder's split gap at 225°.
511* **Faster dense decoders.** They gate each orientation on fixed-key agreement (≥ 80%) before sampling every site. On a 1080p noise frame, P3 decode fell from 18 ms to 0.42 ms per call and P2 from 0.34 ms to 0.05 ms. Calibration scores are value-identical.
512* **Specification clarifications.** v0.25/v0.26 now give full render geometry, the explicit SplitMix32 definition and whitening bit order, the P3 Gray component order, the gap measurement basis, the reason for the ring-33 Ring Key clear field, and an integer-only conformance vector.
513* **Tests.**
514 * The layout tests now check known-answer vectors.
515 * They measure the Cartesian-to-radial-alphabet ink gap; it is 0.58 in both profiles.
516 * They enforce the documented boundary slack.
517 * The round-trip tests add blur across densities and noise rejection.
518 * The SVG payload layer is split into `ckey-data-cartesian` and `ckey-data-polar`.
519 * A dead branch was removed.
520
521## 0.1.0-dev.49 — 2026-11-04
522
523* **Profile 2/3 image decoding fixed.** At HEAD, neither profile could decode its own clean renders at any resolution. The sampler's fixed ±0.12-unit support, sized for P1's one-unit modules, averaged P2/P3's 0.10–0.20-unit dots and rays below threshold. Each feature class now uses a support matched to its width and a threshold calibrated from its own samples (two-means). P3 ray width is read as integrated coverage.
524* **Dead space removed.** These changes ship as Profile 2 Format 0.25 (byte `19`) and Profile 3 Format 0.26 (byte `1A`). Every site now carries a value, and sites are ordered by a keyed bijective hash so the codeword is spread uniformly. A specified SplitMix32 whitening stream (modulation, not ECC) prevents payload-dependent empty or solid regions. The previous P2 layout left everything below y = 22.75 empty. In the previous P3 layout, 400 of 573 rays were blank stubs.
525* **Safe area enforced.** Payload modules now keep a declared ink clearance from the frame, from the finder arcs (including round caps and the concave side), from the Grid Key, and from the radial alphabet. The old moat let modules overlap finder ink (P2 −0.32, P3 −1.5 units).
526* **P3 polar join.** The ring field and Ring Key move outward (24.62 / 25.11), so the Ring Key-to-ray gap matches P2 (1.19 units) instead of a 1.85-unit dark annulus.
527* **Ring arcs over half a turn** now set the SVG large-arc flag. Previously they were drawn as the complementary short arc on the wrong side of the ring.
528* **Profile 1 decoder (format unchanged).** P1's empty reference at (1.5, 11.5) was a payload module, which skewed the threshold whenever that bit was set. It now uses a point in the polar aperture. Thresholds are also per class. On 40 random renders, failures at 6–8 px/unit fell from 19/40 to 0/40, and the committed iPhone fixtures still decode. The P3 calibration score's polar empty reference was a data ray and has moved too.
529* **Tests now run in Release.** `assert()` was compiled out under `NDEBUG`, so CI executed none of the checks. Tests use an always-on `CKEY_CHECK`. New suites cover render → rasterize → decode round trips for all profiles (including all-zero/all-`FF` payloads, rotation, winding, inversion, keystone and blur) and white-box layout checks for site counts, clearance-boundary stability, uniform spread, measured ink gaps and dead space. CI adds a Debug ASan/UBSan job.
530* Site layouts are built once and cached instead of on every call. On a 1080p noise frame, P3 decode fell from about 27 ms to 18 ms per call. The remaining cost is sampling all 8 orientations when the cheap contrast gate passes.
531* The image decoders and calibration scores share one argument check (finite coordinates, `height × stride` overflow).
532* A corrupt Profile 2 length field now returns `CKEY_INTEGRITY_FAILURE` instead of `CKEY_INSUFFICIENT_BUFFER`.
533* The CLI rejects malformed hex such as `1g`, `-1`, ` 1` and `0x`, and reports real profile and format numbers.
534* Stale format references corrected in the README, CONTEXT, header and code comments. The three `patches/` files no longer applied and were removed (they remain in history).
535
536## 0.1.0-dev.48 — 2026-11-04
537
538* Enlarged the radial safety moat surrounding the actual P2/P3 finder arcs and closed the unnecessary annular gap between each radial alphabet and Cartesian field. P2 emits Format 0.23; P3 emits Format 0.24.
539* Corrected the dev.47 real-time diagnostic string so the iOS reader builds.
540
541## 0.1.0-dev.47 — 2026-11-04
542
543* Added a live reader diagnostic panel: detected border side, candidate count, independent P1/P2/P3 key-fit scores, decode-attempt time, and per-profile integrity outcome refresh while scanning.
544
545## 0.1.0-dev.46 — 2026-11-04
546
547* Added an independent native Profile 2 calibration-score ABI and reader fit. Profile 2 no longer uses Profile 1 key coordinates before projective sampling.
548
549## 0.1.0-dev.45 — 2026-11-04
550
551* Added Profile 2 Format 0.22, correcting its Cartesian finder clearance from four square reservations to the actual curved finder quadrants.
552
553## 0.1.0-dev.44 — 2026-11-04
554
555* Corrected Profile 3 finder clearance to reserve the finders' true outer corner quadrants. This removes the four false inward wedges and prevents Cartesian payload marks from printing behind the visible finder arcs.
556* Added Profile 3 Format 0.21: all 34 ring rows now occupy the same outer radius while preserving a 7-unit central quiet zone.
557* The iOS reader now fits compact (P1/P2) and extended (P3) candidates against their own calibration fields. A calibration score from one profile can no longer choose the sampling transform for another profile.
558
559## 0.1.0-dev.43 — 2026-11-03
560
561* Replaced the live reader's generic Vision rectangle path with a measured border-component locator. It fits an oriented quadrilateral from the continuous frame, then independently refines all four corners against the permanent Grid, Ring, Ray, and finder keys before decoding.
562* Explicitly selects the physical back wide camera, holds 1× zoom, limits focus to the far range, and disables video stabilization so the capture geometry does not switch focal systems mid-scan.
563* Added a reproducible synthetic training script and generated on-device logistic candidate ranker. It only orders measured border components; structural keys plus RS and CRC remain the decode acceptance gate.
564* Added Profile 2 Format 0.19's border safety moat and Profile 3 Format 0.20's 34-ring filled polar field and 5-unit frame moat. These mappings prevent payload marks from spilling into the frame and remove the large unused P3 centre.
565
566## 0.1.0-dev.42 — 2026-11-03
567
568* Added Profile 3 capacity/ECC tiers at 256, 384, 512, 768, 1,024, 1,536, and 2,048 payload bytes. The fixed optical codeword stays fully populated; each tier changes the RS data/parity split, and automatic encoding selects the smallest tier that fits.
569* Added deterministic SplitMix32 visual fill to new Profile 2 and Profile 3 frames, protected by RS and checked by frame/CRC validation. This prevents short payloads from rendering as mostly empty fields; it provides no secrecy.
570* Kept decoders compatible with legacy Profile 2 and 3 frames, and added the 0.17/0.18 format specifications.
571* Fixed the Mac renderer's stale-preview behavior on invalid input, stopped padding Profile 1 text into a different payload, debounced regeneration, updated preview effects without reloading the SVG, and rendered Profile 3 at the same module scale as lower profiles.
572* Expanded live scanner candidates from the visible border bounds to account for the border's inset from the canonical sampling field. This is a geometry correction; renderer-origin camera scans still need physical-device validation.
573* C++ core tests, optical locator fixtures, and version-node validation pass. Xcode builds and physical iPhone scans are not available in this environment.
574
575## 0.1.0-dev.41 — 2026-11-03
576
577* Reworked the iOS scanner hot path to try measured quadrilaterals directly against Profiles 1, 2, and 3 before any search. Vision now runs on alternating frames as a rotation/perspective fallback; continuous key-based refinement is restricted to one leading candidate instead of every candidate. Camera capture resolution remains adaptive at 1080p/high.
578* Split detection and CKey decode timing so the displayed measurements no longer include the same work in both values. Updated scanner documentation to distinguish experimental multi-profile camera paths from camera-validated performance.
579* No on-device speed, distance, or reliability target is claimed yet. Xcode and a physical iPhone are required to build and characterize the live scanner.
580
581## 0.1.0-dev.40 — 2026-11-03
582
583* Fixed Swift/C interop in the macOS renderer: C diagnostic message storage is converted from its imported fixed-size tuple safely, and SVG output buffers are passed through scoped mutable buffer pointers. This resolves the renderer's Xcode compile error without changing C ABI or wire-format behavior.
584* Bumped all current software-version nodes. The CKeyReader and renderer still require Xcode validation on macOS; this Linux workspace cannot run Xcode.
585
586## 0.1.0-dev.39 — 2026-11-03
587
588* Corrected the macOS renderer project's current-SDK import and runtime status interpolation, keeping its UI build path explicit and versioned.
589
590## 0.1.0-dev.38 — 2026-11-03
591
592* Added the shared C ABI contracts required for projective grayscale sampling of the Profile 2 and Profile 3 encodings. The Mac rendering laboratory and camera reader use the same versioned native core.
593
594## 0.1.0-dev.37 — 2026-11-03
595
596* Removed the stale Grok-era nested-finder rewrite script so no repository tool can reapply rejected protruding or nested finder geometry.
597
598## 0.1.0-dev.36 — 2026-11-03
599
600* Applied the approved one-arc, inward-facing rounded-corner finder to all profiles. Profile 2 now emits Format 0.15 (`0F`); Profile 3 now emits Format 0.16 (`10`). Their legacy Format 0.9/0.10 codewords remain decoder-readable.
601* Added profile-specific format documents and tests asserting each encoder emits its versioned frame byte and the common canonical finder SVG construction.
602
603## 0.1.0-dev.35 — 2026-11-03
604
605* Established the approved finder construction as CKey Format 0.14.0 / byte `0E`: a continuous rounded-square border and exactly one broad inboard arc per corner. Every arc opens toward the central polar field; only the top-left arc is split for orientation.
606* Applied this normalized construction to the Profile 1 locator, Profile 2 text/binary renderer, and Profile 3 extended renderer. Added profile-specific SVG regression assertions for the one-arc geometry and retained reader calibration sampling of the same signature.
607
608## 0.1.0-dev.34 — 2026-11-03
609
610* Replaced the still-incorrect inside-corner interpretation with CKey Format 0.13.0 / byte `0D`. Finder arcs now form the symbol's convex external rounded corners; frame side segments terminate into the outermost finder arc. Only the top-left finder set is split for orientation.
611* Updated reader calibration geometry and Profile 3 finder reservations to the external-corner definition. Historic formats 8, 11, and 12 remain decoder-readable; new locators emit format 13.
612
613## 0.1.0-dev.33 — 2026-11-03
614
615* Superseded the rejected dev.32 nested-finder interpretation with CKey Format 0.12.0 / byte `0C`: one broad rounded-corner arc per corner, aligned to the structural rounded border. The top-left arc alone has the measured diagonal split; it does not project a three-finger pattern into payload space.
616* Updated Profile 3 clearance and shared reader calibration samples to the actual single-arc geometry. Formats 8 and 11 remain decoder-readable for historic fixtures; new locator encodes emit format 12.
617
618## 0.1.0-dev.32 — 2026-11-03
619
620* Introduced CKey Format 0.11.0 / byte `0B` for the locator's reference-oriented finder correction. Each corner now has three inset, edge-oriented, round-capped quarter arcs; top-right, bottom-right, and bottom-left sets are continuous, while every top-left arc has a measured 0.85-module diagonal clear gap.
621* Applied the same proportional finder renderer to all capacity profiles and corrected Profile 3's reservation geometry to follow the real inset arc envelope rather than the previous large leaf-shaped clearance.
622* Added finder-signature samples to the native continuous calibration score used by CKeyReader's geometry refinement. These samples are localization evidence only; Grid/Ray/Ring keys plus RS and CRC remain the decode acceptance gates. Format 8 remains decoder-readable for historical camera fixtures but is no longer emitted.
623
624## 0.1.0-dev.31 — 2026-11-03
625
626* Added experimental Format 0.10.0 Profile 3 for 1–2,048 byte payloads: ten byte-interleaved systematic RS(255,223) blocks, 320 total parity bytes, CRC-32C, deterministic codeword decode, and SVG rendering.
627* Expanded polar capacity by lateral geometry rather than dot shrinkage: 24 rings at 288 angular slots, exact run-length arcs, and 573 data rays with a calibrated 3-bit Gray-length/stroke-weight alphabet. Cartesian candidate order is a deterministic two-dimensional permutation so all usable regions fill evenly instead of forming top-to-bottom or radial dead zones.
628* Restored the Grid Key to a 2×2 block made of the exact same 0.34-unit marks and 0.5-unit pitch as Cartesian payload; it now genuinely calibrates payload scale and clear-space ratio.
629* Restricted Profile 3 finder reservation to its curved finder strokes and guard bands rather than an entire corner square.
630* Excluded the structural-border guard from Profile 3 payload placement after visual inspection found modules outside the rounded frame.
631* Reserved the interior of each curved finder as well as its stroke guard, while leaving the surrounding exterior Cartesian field available for data.
632* Preserved the camera-validated Format 0.8 locator unchanged. Profile 3 image/mobile decoding remains unimplemented and is explicitly not camera validated.
633
634## 0.1.0-dev.30, dev.29, dev.28, dev.27 — 2026-11-03
635
636* **dev.28** (`98cdcb9`) first added Profile 3, Format 0.10.0. That entry was folded into the dev.31 entry above when dev.31 corrected it, so its text appears there.
637* **dev.27, dev.29 and dev.30** have no commit that changed `VERSION` and no recorded entry. They were not published builds.
638
639## 0.1.0-dev.26 — 2026-11-03
640
641* Added experimental Format 0.9.0 Profile 2 for 1–200 byte text/binary payloads: a framed full RS(255,210) codeword with 45 parity bytes, CRC-32C, and deterministic SVG rendering in a denser 58×58 Cartesian / 144-slot eight-ring polar field.
642* Added the public `ckey_text_symbol`, text encode/decode APIs, a `ckey_render_text_svg` renderer, and `ckey encode-text --utf8` / `--hex` CLI commands. Core tests verify a 200-byte round trip and correction of 22 distinct byte errors; renderer output is XML-valid and includes connected multi-unit payload arcs.
643* Preserved the Format 0.8.0 locator and its camera reader. Profile 2 is not yet accepted by the grayscale/mobile image decoder and must not be represented as camera validated until its own sampling, real-capture, blur, damage, and noise suite passes.
644
645## 0.1.0-dev.25 — 2026-11-03
646
647* Replaced discrete scale/offset ladders in the iPhone reader with continuous, key-driven quadrilateral refinement. The new C ABI calibration-score operation evaluates the permanent Grid, Ring, and Ray keys for contrast and expected evidence under a proposed projective field; it never accepts a payload.
648* The reader starts from each observed border/rectangle, then performs a shrinking-step coordinate search over physical scale and position to maximize that score before one shared-core decode. This removes fixed distance intervals from localization while retaining the decoder's RS and CRC acceptance checks.
649* Kept C ABI version 2 and Format 0.8.0 unchanged: the new function is an additive ABI entry point. Physical-device benchmark evidence remains required.
650
651## 0.1.0-dev.24 — 2026-11-03
652
653* Reverted the dev.23 localization throttle and candidate caps after physical testing reported slower, less reliable reads. Restored the dev.22 farther-reading search behavior rather than retaining a regression.
654* Configured the rear capture stream for 1920×1080 where supported (falling back to `.high`) and enables continuous autofocus plus continuous autoexposure. This addresses the acquisition limit directly: a distant CKey needs sufficient pixels per module before any decoder can recover blur, noise, or damage.
655* Kept format 0.8.0 unchanged. This is a recovery and acquisition-quality change; benchmark results remain unclaimed pending device measurements.
656
657## 0.1.0-dev.23 — 2026-11-03
658
659* Made the border-first CKey locator the bounded hot path: up to six square-border candidates and three framing hypotheses are tested per sampled frame. The earlier broad Vision scale/offset sweep could attempt up to 1,800 core decodes per frame, producing the slow, intermittent far-distance reads reported from dev.22.
660* Throttled generic Vision localization to once per 300 ms, capped it at eight candidates and three framing hypotheses, and retained it only as a fallback for rotated or projective symbols. The direct border locator is sampled every 80 ms and accepts dimmer blurred display edges before the calibration/ECC/CRC checks reject false candidates.
661* Kept format 0.8.0 unchanged. Physical-device latency and damage/blur/noise recovery still require benchmark evidence.
662
663## 0.1.0-dev.22 — 2026-11-03
664
665* Added a CKey-specific, luminance connected-component locator before Vision rectangle analysis. It searches for the continuous bright rounded-square border at half resolution and tests the recovered square bounds directly with the shared C++ decoder.
666* This directly addresses the dev.21 field result: Vision still failed to propose the distant symbol despite the core successfully decoding a 135 px camera-derived fixture when supplied its field corners. Vision remains as the fallback for rotated and projective candidates.
667* Kept format 0.8.0 unchanged. This Swift implementation requires Xcode/iPhone validation and has no distance or latency claim until measured.
668
669## 0.1.0-dev.21 — 2026-11-03
670
671* Fixed distant-symbol candidate selection: the reader previously ranked generic Vision rectangles by area and only tested the largest eight, which the field recording shows were laptop/display rectangles rather than the contained CKey.
672* Added a second Vision request for small, near-square candidates (up to 64 observations) and ranks the merged candidate set by squareness before testing up to 24 candidates. The expensive refinement is limited to the leading four candidates.
673* Kept format 0.8.0 unchanged. This fix requires iPhone/Xcode validation.
674
675## 0.1.0-dev.20 — 2026-11-03
676
677* Expanded the far-distance fallback from 12 to 18 scale hypotheses (0.25×–1.80×) and from nine to 25 center shifts per scale for each of the six leading candidates.
678* Added on-screen Vision candidate-size diagnostics to reveal whether the square detector is selecting the symbol, its surrounding canvas, or a larger object.
679* Kept CKey Format 0.8.0 unchanged. The wider fallback is not yet compiled or benchmarked on-device; the UI reports the candidate bounds for the next field test.
680
681## 0.1.0-dev.19 — 2026-11-03
682
683* Broadened the distant-symbol localization fallback to test 12 candidate scales from 0.40× to 1.40× and a 3×3 center-shift grid for each of the six leading Vision squares. The existing small set of fast hypotheses still runs first.
684* Kept format 0.8.0 unchanged. The wide fallback has not yet been compiled or benchmarked on-device; far-camera fixtures demonstrate decoder performance only when corners are supplied.
685
686## 0.1.0-dev.18 — 2026-11-03
687
688* Fixed the Swift compiler type-check timeout in quadrilateral generation by using explicit Double conversions and an imperative coordinate loop.
689* Replaced deprecated iOS 17 camera orientation properties with supported video rotation angle APIs.
690* Kept wire format 0.8.0 unchanged. Xcode build verification requires the user's Apple toolchain.
691
692## 0.1.0-dev.17 — 2026-11-03
693
694* Added a coarse-to-fine camera localization fallback: the six leading square candidates are tested at smaller and larger scales with small center shifts when the fast hypotheses fail. This addresses distant targets whose display boundary is detected instead of the symbol boundary.
695* Added a far-camera regression image with a 135 px symbol (~4.7 px/module); the C++ image decoder recovers its payload when supplied the observed field corners.
696* Kept format 0.8.0 unchanged. The far-distance fixture validates image decoding with known geometry; live Vision localization and latency still require physical-device measurement.
697
698## 0.1.0-dev.16 — 2026-11-03
699
700* Fixed the iPhone reader's corner-expansion helper: it previously ignored each requested scale, causing every candidate to use the same quadrilateral.
701* Increased camera sampling from 8 to 20 frames per second and exposed CKey check latency while scanning, alongside Vision detection latency.
702* Added a grayscale camera-preview regression crop from the user's laptop-screen capture; the shared C++ decoder recovers its locator payload with the captured perspective and rasterization.
703* Kept wire format 0.8.0 unchanged. Camera live performance is not yet independently measured on the user's iPhone; QR-parity performance is not claimed.
704
705## 0.1.0-dev.15 — 2026-11-02
706
707* Fixed a throttled UI status update that left the reader stuck on “Testing square candidates…” after each frame.
708* Candidate frames now publish their final CKey validation result immediately; empty-frame progress remains throttled.
709
710## 0.1.0-dev.14 — 2026-11-02
711
712* Loosened Vision square-candidate thresholds and added nearby quadrilateral scale hypotheses for the rounded CKey frame.
713* Added live reader status showing whether Vision found candidates or the CKey structural/ECC checks rejected them, plus ongoing localization timing.
714* Added a regression image from the user's exact CKey target at 20 px/module; the C++ image decoder reads its payload successfully. iPhone camera localization still requires on-device validation.
715
716## 0.1.0-dev.13 — 2026-11-02
717
718* Fixed two SwiftUI color inference errors in the iPhone reader by making foreground and status colors explicitly use `Color`.
719* Versioned the build fix. Apple-platform compilation still needs verification in Xcode.
720
721## 0.1.0-dev.12 — 2026-11-02
722
723* Added an experimental iPhone SwiftUI reader that uses Vision for square-candidate localization and calls the existing C++ C ABI for pixel decoding.
724* Added an Xcode project, camera permission copy, live result display, and separate Vision/CKey timing readouts; the app compiles `src/ckey.cpp` directly without duplicating encoding or decoding logic.
725* Recorded iPhone build/run instructions and current camera-validation limits. The app has not been compiled or tested on an Apple device in this environment.
726
727## 0.1.0-dev.11 — 2026-11-03
728
729* Added C ABI grayscale image decoding for a caller-supplied symbol quadrilateral, with projective rectification, calibration-key checks, polarity normalization, data sampling, RS correction, and CRC-32C validation.
730* Added an optical PGM fixture cropped from a browser-rendered v0.8 symbol; tests cover actual pixel-to-payload decode, rotated quadrilateral order, inverted polarity, and invalid geometry.
731* Kept wire geometry at CKey Format 0.8.0. Full-frame symbol localization, lens-distortion correction, camera video scanning, and camera robustness benchmarks remain open.
732
733## 0.1.0-dev.10 — 2026-11-03
734
735* Corrected the finder interpretation to match the supplied geometry blueprint: one thick quarter-arc at bottom-left, top-right, and bottom-right, with the top-left arc split into two pieces for orientation.
736* Kept the corner centerline radius at 4.5 modules and made each arc a 1.0-module band with round caps; the split has a one-module visible gap.
737* Versioned the geometry as CKey Format 0.8.0 / format byte 08 and regenerated the CRC-32C/Reed–Solomon fixture. The payload, RS code, and C ABI remain unchanged.
738
739## 0.1.0-dev.9 — 2026-11-03
740
741* Restored three nested quarter-arc finders at all corners, with the three top-left arcs split by a one-module orientation gap.
742* Made all inner-ring and Ring Key dots the same 0.30-module diameter; Ring Key arc caps are round while its visible length remains exactly four angular pitches.
743* Encoded each of the 69 data dial bits by a short 0.65-module ray or long 1.35-module ray; calibration rays remain reserved and fixed.
744* Versioned the wire geometry as CKey Format 0.7.0 / format byte 07 and regenerated the CRC-32C/Reed–Solomon example vector. The RS code and C ABI are unchanged.
745
746## 0.1.0-dev.8 — 2026-11-03
747
748* Increased the central clear radius and moved the four rings outward into a tighter radial group.
749* Sized each ring dot to 72% of its adjacent-slot chord, so marks are visibly sized and closely spaced at every ring radius.
750* Set the Ring Key arc width to one dot diameter and its centerline length to exactly four slot intervals.
751* Versioned the geometry as CKey Format 0.6.0 / format byte 06; the RS(77,26) code and ABI remain unchanged.
752
753## 0.1.0-dev.7 — 2026-11-03
754
755* Fixed oversized ring dots: SVG circles inherited the parent group’s default 1-module stroke. Circle marks now explicitly use no stroke, rendering at their specified 0.12-module diameter.
756* Added a renderer regression assertion for stroke-free 0.06-radius dots. The four-slot Ring Key arc remains 0.12 modules wide and spans the same four angular slots.
757
758## 0.1.0-dev.6 — 2026-11-03
759
760* Reduced ring data dots and Ring Key dots to 0.12-unit diameter; made the four-slot Ring Key arc exactly match four angular slots and dot diameter.
761* Increased locator protection to shortened RS(77,26), adding 51 mathematically computed parity bytes and using 616 of 621 eligible sites for codeword bits.
762* Bumped the C ABI because the public codeword grew from 42 to 77 bytes.
763* Versioned the wire geometry as CKey Format 0.5.0 / format byte 05 and refreshed the independent vector.
764
765## 0.1.0-dev.5 — 2026-11-03
766
767* Updated locator geometry to one thick arc at three corners and a split top-left orientation arc with a one-module visible gap; enlarged the reserved corner envelopes to eliminate finder/data collisions.
768* Replaced the previous vertical calibration sequence with the specified 2×2 Grid Key.
769* Spread payload placement across angular sectors of the Cartesian matrix and round-robin across the dial plus all four inner rings.
770* Versioned the wire format as 0.4.0 / format byte 04 and regenerated the CRC-32C/Reed–Solomon interoperability vector.
771* Added maintained geometry and version context for the supplied blueprint.
772
773## 0.1.0-dev.4 — 2026-11-02
774
775* Reworked the polar field into four inner concentric rings plus a separate 1.9-unit annular dial band; all 12:00/9:00/9:05 calibration rays now stay within that band rather than extending to the center.
776* Added a round-robin angular traversal, retained the outer Cartesian matrix, and replaced corner dots with three nested arcs; split the top-left finder into two arc segments for orientation.
777* Advanced the wire format to 0.3.0 and preserved earlier version specs and fixtures.
778
779## 0.1.0-dev.3 — 2026-11-02
780
781* Replaced solid QR-like block finders with nested open corner arcs and switched the reference SVG to dark-field/light-mark styling with rounded data modules to follow the supplied target geometry.
782* Advanced the wire geometry version to CKey Format 0.2.0; preserved the 0.1.0 draft and fixture as historical versions.
783* Kept Reed–Solomon error correction and CRC-32C framing; geometry evolved independently of framing.
784
785## 0.1.0-dev.2 — 2026-11-02
786
787* Corrected the format note to reflect the implemented byte-codeword RS decoder and its tested eight-error correction bound.
788* Fixed shared-library C ABI exports and bounded SVG render scale to prevent dimension overflow and unreasonable outputs.
789* Added independent-review findings and synchronized the public software version and fixture metadata.
790
791## 0.1.0-dev.1 — 2026-11-02
792
793* Added draft locator-v1 framing with CRC-32C and shortened RS(42,26) over GF(256).
794* Added C++20 encoder and byte-codeword decoder with correction of up to eight unknown byte errors.
795* Added fixed Cartesian/polar calibration geometry and monochrome SVG renderer.
796* Added C ABI, CLI encoder, independent RS parity reference/vector, unit tests, and CI workflow.
797* Recorded versioning, context, research, license, and milestone rules.
798* Not included: erasure correction, image/camera decoder, perspective recovery, SDKs, browser lab, or measured device benchmarks.