Skip to content

Release 1.0.0: lock the API - #9

Merged
neosergio merged 1 commit into
mainfrom
feat/v1.0.0
Jul 14, 2026
Merged

Release 1.0.0: lock the API#9
neosergio merged 1 commit into
mainfrom
feat/v1.0.0

Conversation

@neosergio

@neosergio neosergio commented Jul 14, 2026

Copy link
Copy Markdown
Owner

The promise: names, parameters, the default output format, the exception contract, and determinism. None of them break without a 2.0.0. A reference generated by 0.1.0 still resolves, byte for byte, and a test pins the strings to prove it -- every argument added across six releases defaults to doing nothing.

Three ways a scheme could fail to verify its own references, all found in review, all now impossible to build.

verify() pulled a reference apart on the separator and deleted every occurrence, so ORD--20260714-..., ORD-20260714-...- and ORD-20260714-047-1283 all came back True. That is exactly the confusion the check character exists to prevent: a caller could not tell a typo from a reference that does not exist. It reads by position now -- prefix matched literally, each field taken at its known width, each separator required where it belongs, nothing allowed to trail, and a character outside the alphabet refused rather than skipped.

A separator drawn from the alphabet was eaten out of the suffix by that same strip. A date_format whose width moves -- %B is "January" one month and "December" another -- broke the length check, so references verified for part of the year and were rejected for the rest. Both fail at construction now. The width guard probes 54 datetimes, not the two suggested: 1 January and 31 December are both midnight, so an unpadded hour renders "0" in each and looks fixed when it is not.

And a check character cannot cover what its alphabet cannot spell. Luhn skips any character it has no index for, so the hyphen in %Y%m%d-%H could be changed to anything undetected, and under an alphabet of letters the date was not protected at all. A checked scheme must now render its date in its own alphabet; an hourly bucket spells itself %Y%m%d%H and is covered like everything else.

Reading by position is what makes the width guard load-bearing rather than decorative, and Hypothesis found the alphabet hole from the other side: under "01" the 2 of a 2026 date is skipped, and editing it to a 0 left the check unmoved. My property strategy could not have found the separator overlap -- it only ever drew "-" and "/", which appear in none of the alphabets it draws. A property test explores the space you hand it and no further. The strategy is widened; a single-character edit anywhere in a reference, and a stray separator anywhere in it, are invariants now.

expected_collisions() is gone, deprecated since 0.3.0. It counted colliding pairs while its name promised a count of collisions to handle.

verify_reference() is gone too, and this is the version that must remove it. Verifying a reference means knowing the alphabet, prefix, separator, length and date format it was generated with, and a free function has to be told all of that with no way to tell when it has been told wrong. It returned False for a valid reference whose configuration the caller mistyped -- which reads as "that is a typo", so you tell a customer their good reference is invalid. It could not see that a reference carried no check character either, and "verified" one about once in len(alphabet). Neither defect is visible from inside the function; only binding the configuration to the object fixes it. Deprecating it into 1.x was my first instinct and it was wrong: 1.0.0 is the last version allowed to remove anything, and a DeprecationWarning most projects never display would have left it quietly working for the whole of 1.x.

The README's "Possible output" blocks have been fabricated since 0.1.0. They showed a suffix of 482731 for a ULID the library renders as 385177. Every literal in the file is now checked against what the library actually returns.

Summary by CodeRabbit

  • New Features

    • Released version 1.0.0 with a stable public API.
    • Added structural parsing through CompactRef.parse(), including parsed reference components and clear invalid-reference errors.
    • Added reference sizing properties and improved collision/retry planning helpers.
    • Added stricter configuration validation and portable date-format checks.
  • Changes

    • Verification now validates reference structure and exact case.
    • Removed deprecated verify_reference() and expected_collisions() APIs; migration guidance is included.
  • Documentation

    • Expanded usage, sizing, parsing, timezone, database, and upgrade guidance.

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@neosergio, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 29 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 135269a6-6fa1-4302-82a3-1a0278e293a0

📥 Commits

Reviewing files that changed from the base of the PR and between 4dd5424 and 6cb0bee.

📒 Files selected for processing (13)
  • .github/workflows/ci.yml
  • CHANGELOG.md
  • README.md
  • ROADMAP.md
  • pyproject.toml
  • src/compactref/__init__.py
  • src/compactref/core.py
  • src/compactref/reference.py
  • tests/test_core.py
  • tests/test_docs.py
  • tests/test_golden.py
  • tests/test_integration.py
  • tests/test_properties.py
📝 Walkthrough

Walkthrough

Version 1.0.0 centralizes configuration validation, adds structured parsing, changes verification to use parsing, removes legacy APIs, updates public exports, expands contract tests and documentation, and broadens CI across operating systems and Python versions.

Changes

API stabilization

Layer / File(s) Summary
Centralized validation and public exports
src/compactref/core.py, src/compactref/__init__.py
Runtime configuration and date/check validation are centralized, while removed helpers and new public symbols are reflected in the package exports.
Positional parsing and verification
src/compactref/reference.py
CompactRef.parse() returns structured reference components, and verify() validates parsed structure before checking the date-and-suffix checksum.
Behavioral and public API contracts
tests/test_core.py, tests/test_docs.py, tests/test_golden.py, tests/test_integration.py, tests/test_properties.py
Tests cover the 1.0.0 API surface, deterministic outputs, parsing and verification rules, configuration constraints, documentation references, and fixed-length integration storage.
1.0.0 release documentation
CHANGELOG.md, README.md, ROADMAP.md, pyproject.toml
Release metadata and documentation describe the locked API, parsing and verification behavior, sizing helpers, migration guidance, timezone extras, and 1.0.0 status.

Cross-platform CI

Layer / File(s) Summary
Cross-platform test execution
.github/workflows/ci.yml
The test job runs across supported operating-system and Python-version combinations and adds dependency consistency checking with pip check.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant CompactRef
  participant ParsedReference
  Caller->>CompactRef: parse(reference)
  CompactRef-->>ParsedReference: structured reference parts
  Caller->>CompactRef: verify(reference)
  CompactRef->>CompactRef: parse(reference)
  CompactRef-->>Caller: verification result
Loading

Possibly related PRs

  • neosergio/compactref#5: Earlier type handling and collision-pair behavior overlap with this release’s validation and sizing API changes.
  • neosergio/compactref#8: Introduced the CompactRef and verify() behavior substantially rewritten here with parsing and new verification semantics.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 56.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the release and API-locking focus of the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/v1.0.0

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@neosergio
neosergio force-pushed the feat/v1.0.0 branch 2 times, most recently from c2c9461 to 21e9121 Compare July 14, 2026 12:07
@neosergio
neosergio requested a review from Copilot July 14, 2026 12:12

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR finalizes the 1.0.0 release by locking CompactRef’s public API and output determinism guarantees, tightening reference verification semantics, and pinning behavior with stronger cross-platform tests and documentation checks.

Changes:

  • Replace separator-splitting verification with positional parsing (CompactRef.parse()) and strict verify() behavior; remove the unsafe verify_reference() free function.
  • Add runtime configuration validation (including “int but not bool” enforcement) and stricter “checked schemes must be checkable” rules (fixed-width/portable/spellable dates; separator not in alphabet).
  • Add golden tests + README/doc consistency checks; expand CI to run tests across OSes and Python versions.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/test_properties.py Expands property testing to cover single-char edits, stray separators, and checkability constraints.
tests/test_integration.py Updates SQLAlchemy integration example to use reference_length and adjusts exhaustion test for checked schemes.
tests/test_golden.py Adds golden-string contract tests pinning outputs, API surface (__all__), defaults, and time-bucketing behavior.
tests/test_docs.py Adds tests that README only references real, supported API names and doesn’t “use” removed functions.
tests/test_core.py Updates tests for removed APIs, new strict verification/parse behavior, and runtime type/value enforcement.
src/compactref/reference.py Introduces parse(), ParsedReference, InvalidReferenceError, and rewrites verify() to be positional/structural + checksum.
src/compactref/core.py Adds portable strftime wrapper, checkability validation, and centralized runtime configuration validation; removes deprecated/unsafe functions.
src/compactref/init.py Updates exports to include new public types/exceptions and bumps version to 1.0.0.
ROADMAP.md Updates roadmap to reflect the 1.0 lock and moves future work into additive-only sections.
README.md Refreshes examples, documents new parsing/verification model, and pins outputs to real library behavior.
pyproject.toml Bumps version/classifier and adds Windows tzdata handling via extras/dev deps.
CHANGELOG.md Adds detailed 1.0.0 release notes documenting the locked contract and behavioral fixes.
.github/workflows/ci.yml Expands CI matrix across OSes/Python versions and adds pip check to enforce no accidental deps.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/test_docs.py Outdated
Comment thread src/compactref/reference.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
src/compactref/core.py (2)

100-128: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Combine the two _PROBE_DATES passes.

widths and rendered each iterate all 54 probe dates through _render_date (up to 108 strftime calls total). A single pass can compute both the rendered string and its characters, halving the work — and this function is called on every checked generate_reference()/CompactRef build (see comment below on the call-site cost).

♻️ Proposed refactor
-    widths = {
-        len(_render_date(moment, date_format)) for moment in _PROBE_DATES
-    }
-    if len(widths) > 1:
+    rendered_by_moment = [
+        _render_date(moment, date_format) for moment in _PROBE_DATES
+    ]
+    widths = {len(rendered) for rendered in rendered_by_moment}
+    if len(widths) > 1:
         raise ValueError(
             f"a checked reference needs a date of a fixed width, but "
             f"{date_format!r} renders to widths {sorted(widths)}. Verifying "
             f"reads a reference by position, so a width that moves would "
             f"make references verify for part of the year and be rejected "
             f"for the rest. Pad the field ('%d', not '%-d') or use a fixed "
             f"one ('%m', not '%B')."
         )

-    rendered = {
-        character
-        for moment in _PROBE_DATES
-        for character in _render_date(moment, date_format)
-    }
-    unrepresentable = sorted(rendered - set(alphabet))
+    rendered_characters = {c for r in rendered_by_moment for c in r}
+    unrepresentable = sorted(rendered_characters - set(alphabet))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/compactref/core.py` around lines 100 - 128, Combine the width and
character validation passes over _PROBE_DATES into one loop, rendering each
moment once with _render_date and accumulating both rendered-string lengths and
characters. Preserve the existing width and unrepresentable-character validation
behavior and error messages in the surrounding validation logic.

315-346: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Skip revalidating CompactRef’s frozen config on every generate() call
generate_reference() still needs its own checks, but CompactRef.generate() forwards a config that was already validated at construction. Re-running _validate_configuration() and, for check=True, _validate_checkable() on every call adds avoidable hot-path overhead; the checked path repeats 108 strftime() calls each time. Add an internal fast path or cache the validator results.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/compactref/core.py` around lines 315 - 346, The CompactRef.generate path
currently revalidates its frozen configuration on every call, including the
expensive _validate_checkable strftime work. Update CompactRef.generate and its
configuration handling to reuse construction-time validation results or take an
internal validated fast path, while preserving generate_reference’s independent
validation and all existing input checks for per-call values such as
generated_at, source, and namespace.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 70-71: Replace the pip check step in the dependency validation
workflow with a direct inspection of the built package metadata, asserting that
its runtime Requires-Dist list is empty. Keep the existing intent and failure
behavior so any dependency declared in pyproject.toml causes CI to fail.

In `@src/compactref/reference.py`:
- Around line 377-386: Update the stray-character validation in the reference
parsing/checking flow to include the checked date field alongside suffix and
check_character when constructing body, while preserving existing behavior for
unchecked schemes. Add a regression test near the existing core tests covering a
checked CompactRef with date_format="0" rejecting a leading date character
outside the alphabet.
- Around line 136-150: Add a shared date-format validator that rejects
locale-dependent directives (%a, %A, %b, %B, %p, %c, %x, and %X) before
deterministic output is established. Invoke it from both CompactRef
initialization near _render_date and generate_reference, while preserving the
existing rendering and checkability validation behavior.

---

Nitpick comments:
In `@src/compactref/core.py`:
- Around line 100-128: Combine the width and character validation passes over
_PROBE_DATES into one loop, rendering each moment once with _render_date and
accumulating both rendered-string lengths and characters. Preserve the existing
width and unrepresentable-character validation behavior and error messages in
the surrounding validation logic.
- Around line 315-346: The CompactRef.generate path currently revalidates its
frozen configuration on every call, including the expensive _validate_checkable
strftime work. Update CompactRef.generate and its configuration handling to
reuse construction-time validation results or take an internal validated fast
path, while preserving generate_reference’s independent validation and all
existing input checks for per-call values such as generated_at, source, and
namespace.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0a272869-accf-403d-a23e-a00d69de84c5

📥 Commits

Reviewing files that changed from the base of the PR and between c8a9c15 and 4dd5424.

📒 Files selected for processing (13)
  • .github/workflows/ci.yml
  • CHANGELOG.md
  • README.md
  • ROADMAP.md
  • pyproject.toml
  • src/compactref/__init__.py
  • src/compactref/core.py
  • src/compactref/reference.py
  • tests/test_core.py
  • tests/test_docs.py
  • tests/test_golden.py
  • tests/test_integration.py
  • tests/test_properties.py

Comment thread .github/workflows/ci.yml Outdated
Comment thread src/compactref/reference.py
Comment thread src/compactref/reference.py
The promise: names, parameters, the default output format, the exception
contract, and determinism. None of them break without a 2.0.0. A reference
generated by 0.1.0 still resolves, byte for byte, and a test pins the
strings to prove it -- every argument added across six releases defaults to
doing nothing.

Three ways a scheme could fail to verify its own references, all found in
review, all now impossible to build.

verify() pulled a reference apart on the separator and deleted every
occurrence, so ORD--20260714-..., ORD-20260714-...- and
ORD-20260714-047-1283 all came back True. That is exactly the confusion the
check character exists to prevent: a caller could not tell a typo from a
reference that does not exist. It reads by position now -- prefix matched
literally, each field taken at its known width, each separator required
where it belongs, nothing allowed to trail, and a character outside the
alphabet refused rather than skipped.

A separator drawn from the alphabet was eaten out of the suffix by that
same strip. A date_format whose width moves -- %B is "January" one month
and "December" another -- broke the length check, so references verified
for part of the year and were rejected for the rest. Both fail at
construction now. The width guard probes 54 datetimes, not the two
suggested: 1 January and 31 December are both midnight, so an unpadded hour
renders "0" in each and looks fixed when it is not.

And a check character cannot cover what its alphabet cannot spell. Luhn
skips any character it has no index for, so the hyphen in %Y%m%d-%H could
be changed to anything undetected, and under an alphabet of letters the
date was not protected at all. A checked scheme must now render its date in
its own alphabet; an hourly bucket spells itself %Y%m%d%H and is covered
like everything else.

Reading by position is what makes the width guard load-bearing rather than
decorative, and Hypothesis found the alphabet hole from the other side: under
"01" the 2 of a 2026 date is skipped, and editing it to a 0 left the check
unmoved. My property strategy could not have found the separator overlap --
it only ever drew "-" and "/", which appear in none of the alphabets it
draws. A property test explores the space you hand it and no further. The
strategy is widened; a single-character edit anywhere in a reference, and a
stray separator anywhere in it, are invariants now.

expected_collisions() is gone, deprecated since 0.3.0. It counted colliding
pairs while its name promised a count of collisions to handle.

verify_reference() is gone too, and this is the version that must remove
it. Verifying a reference means knowing the alphabet, prefix, separator,
length and date format it was generated with, and a free function has to be
told all of that with no way to tell when it has been told wrong. It
returned False for a valid reference whose configuration the caller
mistyped -- which reads as "that is a typo", so you tell a customer their
good reference is invalid. It could not see that a reference carried no
check character either, and "verified" one about once in len(alphabet).
Neither defect is visible from inside the function; only binding the
configuration to the object fixes it. Deprecating it into 1.x was my first
instinct and it was wrong: 1.0.0 is the last version allowed to remove
anything, and a DeprecationWarning most projects never display would have
left it quietly working for the whole of 1.x.

The README's "Possible output" blocks have been fabricated since 0.1.0.
They showed a suffix of 482731 for a ULID the library renders as 385177.
Every literal in the file is now checked against what the library actually
returns.
@neosergio
neosergio merged commit e44c02b into main Jul 14, 2026
12 checks passed
@neosergio
neosergio deleted the feat/v1.0.0 branch July 14, 2026 13:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants