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.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions