feat!: v0.3.0 API break, concrete constructors, error taxonomy and evidence floors - #100
Open
ccuetoh wants to merge 3 commits into
Open
feat!: v0.3.0 API break, concrete constructors, error taxonomy and evidence floors#100ccuetoh wants to merge 3 commits into
ccuetoh wants to merge 3 commits into
Conversation
Lands the pre-stable API batch from the v0.1.1 review. Breaking: - token.New and session.New return concrete *Tokenizer and *Session. Both interfaces are gone, and ./mock with them. - Equal returns (bool, error) so key rotation and schema drift stop looking identical to a non-match. - Match and Calibrate take a MatchPolicy. Its zero value is the old behavior; DefaultMatchPolicy adds a two-field evidence floor so records sharing only a country stop scoring 1.0. - ValidateRecord and RecordFromMap return joined errors with sentinel leaves instead of []error. - session.Option replaces token.Option on session.New. - token.New requires a 32 byte secret. - MustParsePath is now MustParseFieldPath, ROCPoint is PRPoint, Calibration.ROC is Calibration.PR, AnnotatedToken.Version is FieldSetVersion. - token.Score is deleted. Match subsumes it. Fixes: - session.Match, Equal and MatchCLK reject tokens minted under a different FieldSet. - Field-specific normalization no longer applies to custom orgs. - Calibrate returns the plateau midpoint instead of its bottom edge. - Absence is len(field) == 0 everywhere, so JSON round-trips cannot flip a verdict. Token bytes and fingerprints are unchanged. v0.2 tokens stay valid. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BLyvbE4thBJxFxs8xq6xJm
Adds THREAT_MODEL.md, RELEASING.md and GOVERNANCE.md, rewrites the security policy around tags, and documents the whole API break in the changelog. Adds 19 godoc examples, including the wire round trip the library exists for. The v0.2 tag is two components, which Go does not resolve, so the README install line drops the version pin and RELEASING.md records the tag format. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BLyvbE4thBJxFxs8xq6xJm
Adversarial review of the branch confirmed ten findings. Three touch behavior. Tokenize now refuses to run when the locked key buffers are gone, not just when Destroy set the flag. The pooled HMACs read their key lazily, so a buffer destroyed by memguard.Purge or its interrupt handler would key them with nil and emit tokens derived under an empty secret. Calibrate computes F1 from the counts. The harmonic mean of precision and recall lands a unit in the last place away from itself for count pairs that are mathematically equal, which split one plateau into several and returned an edge instead of the midpoint. Calibrate excludes pairs with nothing comparable under every policy. Match reports them as non-matches at every threshold, so counting them as matches at threshold 0 fit the threshold against a decision Match never makes. The rest are documentation. The changelog said Equal previously returned false for two all-absent tokens; it returned true, and that is the one bool that changes in this release. ValidateRecord reports in schema order, not sorted order. A joined error cannot be ranged over. Only a Session stamps and checks the fingerprint. Quality metrics are unchanged on all three corpora. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BLyvbE4thBJxFxs8xq6xJm
|
| Branch | claude/codebase-review-v0.1.1-6goaju |
| Testbed | ubuntu-latest |
Click to view all benchmark results
| Benchmark | Latency | Benchmark Result nanoseconds (ns) (Result Δ%) | Upper Boundary nanoseconds (ns) (Limit %) |
|---|---|---|---|
| BenchmarkNgrams-4 | 📈 view plot 🚷 view threshold | 1,003.00 ns(+6.10%)Baseline: 945.32 ns | 1,228.91 ns (81.62%) |
| BenchmarkNormalizeCountry-4 | 📈 view plot 🚷 view threshold | 444.30 ns(+10.70%)Baseline: 401.35 ns | 521.75 ns (85.16%) |
| BenchmarkNormalizeDate-4 | 📈 view plot 🚷 view threshold | 693.00 ns(+27.65%)Baseline: 542.89 ns | 705.76 ns (98.19%) |
| BenchmarkNormalizeIdentifier-4 | 📈 view plot 🚷 view threshold | 702.30 ns(+15.51%)Baseline: 608.00 ns | 790.40 ns (88.85%) |
| BenchmarkNormalizeName-4 | 📈 view plot 🚷 view threshold | 2,017.00 ns(+17.24%)Baseline: 1,720.36 ns | 2,236.46 ns (90.19%) |
| BenchmarkTokenizeDeterministic-4 | 📈 view plot 🚷 view threshold | 4,465.00 ns(+10.06%)Baseline: 4,056.77 ns | 5,273.80 ns (84.66%) |
| BenchmarkTokenizeProbabilistic-4 | 📈 view plot 🚷 view threshold | 28,899.00 ns(-16.02%)Baseline: 34,411.88 ns | 44,735.45 ns (64.60%) |
|
| Branch | claude/codebase-review-v0.1.1-6goaju |
| Testbed | ubuntu-latest |
⚠️ WARNING: Truncated view!The full continuous benchmarking report exceeds the maximum length allowed on this platform.
🚨 27 Alerts
🐰 View full continuous benchmarking report in Bencher
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #100 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 12 13 +1
Lines 881 957 +76
=========================================
+ Hits 881 957 +76
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Lands the v0.3.0 batch from the pre-stable review: 14 findings, all of the remaining API breaks, plus the threat model and release policy.
Token derivation does not change. HKDF info strings, format discriminators and the fingerprint encoding are untouched, the golden vector files are byte-identical to main, and v0.2 tokens and persisted fingerprints stay valid. The one exception is spelled out in the changelog: normalization no longer applies canonical field rules to custom orgs, so a non-
srirachapath in an identifier, date, contact or name namespace normalizes differently now.Breaking
token.Newreturns*token.Tokenizerandsession.Newreturns*session.Session. Both interfaces are gone, and./mockand mockery with them. Consumers that want an interface can declare a narrow one.Equalreturns(bool, error). Key rotation and schema drift stop looking identical to a different person.MatchandCalibratetake aMatchPolicy. Its zero value is the old behavior;DefaultMatchPolicyadds a two-field evidence floor, so records agreeing only oncountry=USstop reporting a match at 1.000.session.Optionreplacestoken.Optiononsession.New, withWithKeyID,WithStrictFingerprintand aWithTokenOptionsescape hatch.token.Newrequires a 32 byte secret.ValidateRecordandRecordFromMapreturn joined errors with sentinel leaves instead of[]error.MustParsePathtoMustParseFieldPath,ROCPointtoPRPoint,Calibration.ROCto.PR,AnnotatedToken.Versionto.FieldSetVersion.token.Scoreandfieldset.Validateare deleted.Fixed
Sessionrefuses to score tokens minted under a differentFieldSet. The fingerprint was computed atNewto catch drift but was only stamped, never checked.Calibratereturns the plateau midpoint. The old tie-break returned the bottom edge, the operating point sitting closest to the non-match distribution.len(field) == 0everywhere, so a JSON round trip that turnsnullinto""cannot flip a verdict.acme::identifier::mrnand forcedacme::date::hiredinto ISO 8601.Docs
THREAT_MODEL.mdstates what each token form leaks and who has to hold the secret, and says plainly that tokens are pseudonymous rather than anonymous.SECURITY.mdis rewritten around tags, since a fix on main is invisible togo get.RELEASING.mdandGOVERNANCE.mdare new. 19 godoc examples, including the wire round trip the library exists for.The
v0.2tag is two components, which Go does not resolve.RELEASING.mdrecords the format and the README install line drops the version pin.Verification
100% coverage held,
golangci-lintclean,go vetclean, all nine example programs run, golden vectors pass with their files untouched.Quality metrics on the gated bench suite are unchanged from v0.2: OpenSanctions AUROC 0.9152 and best F1 0.8745, FEBRL4 and NCVR AUROC 1.0000 with F1 at or above 0.999.
The diff was then reviewed adversarially across five lenses, with every finding sent to an independent skeptic. 10 of 29 survived and are fixed in the last commit; the other 19 were refuted, most as pre-existing behavior or design preference. Three of the survivors were behavioral:
destroyedflag, but the pooled HMACs read their key lazily. A buffer destroyed bymemguard.Purgeor its interrupt handler left the flag false, so the HMAC keyed itself with nil and emitted tokens derived under an empty secret. Key liveness now gates tokenization.Calibratecomputed F1 as the harmonic mean of precision and recall, which lands a unit in the last place away from itself for count pairs that are mathematically equal. That fragmented plateaus and returned an edge. F1 now divides the counts.Calibratekept pairs with nothing comparable in the sweep, where threshold 0 counted them as matches even thoughMatchnever calls them one.Each of the three fails under mutation of its guard.
🤖 Generated with Claude Code
https://claude.ai/code/session_01BLyvbE4thBJxFxs8xq6xJm
Generated by Claude Code