Skip to content

[finding] Unanchored autonumber counter readback diverges cross-side by design — engine reads the LAST digit run, driver-sql concatenates EVERY digit — and #6555's {0000} declared default makes "unanchored" the default shape #7287

Description

@os-zhuang

Observation-class finding, surfaced during #6560's implementation (OS-DEV-REPORT on that card) and filed by the spec-lane PM seat (#6017, session session_01PiRUoQkTSBBmpyXBY3cVn2) so the deliberately-preserved divergence has a tracking record now that its reachability changes. Unassigned, unrouted — for triage.

The fact (measured by the #6560 dev, both sides pinned by their own existing tests)

For the anchored case (non-empty prefix/suffix) the counter readback rule is now shared: readAutonumberCounter in packages/spec/src/data/autonumber-format.ts (PR #7247), both sides call it. For the unanchored case (both affixes empty) the two sides deliberately differ and PR #6553 preserved both byte-for-byte:

  • engine (packages/objectql/src/engine.ts, readStoredAutonumberCounter legacy arm): takes the last digit run'SO-2024-0007'7;
  • driver-sql (scanMaxNumericTail unanchored arm): concatenates every digit'SO-2024-0007'20240007.

Spec's shared export returns undefined for the unanchored slot rather than picking a side — correctly refusing to claim an agreement that does not exist (#7247's recorded rationale).

Why file it now

#6555's Route-3 ruling (PR #7265, in flight) makes {0000} the declared default for format-less autonumber fields — and {0000} has no prefix/suffix, so the default authoring shape resolves to the unanchored readback path on both sides. For pure-digit stored values the two readings agree ('0007' → 7 both ways); they diverge only on unanchored fields whose stored values carry non-digit content (legacy/migrated data, e.g. dash-separated strings in a field later declared autonumber). That is narrow but it is the same "same metadata, different driver, different number" class as #6555 itself — one seam over (readback instead of render).

Suggested treatments (for triage, no recommendation pre-empted)

  1. Rule which unanchored reading is the contract and hoist it into readAutonumberCounter (a behavior change on one side — the reason refactor(spec,objectql,driver-sql): share the autonumber counter readback as spec's inverse of renderAutonumber (#6560) #7247 refused to do it as a rider);
  2. Declare the divergence out-of-contract: unanchored fields with mixed-content stored values are unsupported for seeding, documented in the TSDoc both sides already share;
  3. Record-only (this card).

Dedup

readAutonumberCounter / scanMaxNumericTail / unanchored: only #6560 (closed by PR #7247, which scoped this out explicitly) and #6555 (render-side fork, different function). The #6553 PR documented the preservation but no open card tracks the divergence itself.

Refs: #6560 (OS-DEV-REPORT + PR #7247), #6553, #6555 (PR #7265), #6468.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions