Commit dca25e1
* fix(metadata-protocol): never invent event_seq/version from a failed history read (#4867)
`SysMetadataRepository.nextEventSeq()` and `nextItemVersion()` both folded
EVERY read failure of `sys_metadata_history` into `return 1` — the shape #4825
just fixed on the legacy `DatabaseLoader` path, sitting unchanged on the
canonical transactional one, and here on TWO numbers rather than one.
With rows already in the table, one flaky read handed the next row
`event_seq = 1` / `version = 1`: a collision with an existing row, written
successfully, logged nowhere. `version` is the worse half — `nextItemVersion()`
reads MAX from history precisely so a delete + recreate keeps incrementing
instead of restarting at 1, so a read failure restored exactly the behaviour
the method exists to prevent, while `MetadataManager.rollback(type, name,
version)` and the rollback REST route resolve a snapshot BY that number.
Being inside a transaction does not help: a transaction serialises concurrent
writers, but a successfully committed transaction commits a wrong number just
as durably. What it does give is the clean remedy — throw, and the whole write
rolls back rather than committing an invented number.
Now discriminated by error type, reusing #4825's discriminator rather than
starting a second vocabulary: only a genuine missing table returns 1; every
other read failure reports the consequence and the remedy once at `error`
(AGENTS.md degradation log levels) and rethrows.
`isMissingTableError()` was internal to `@objectstack/metadata`, so it is now
exported deliberately through a new leaf subpath, `@objectstack/metadata/errors`
— not the package root, whose entry would drag the manager, every loader and
their deps behind a 40-line predicate, which is what would tempt the next
author into copying it instead.
Fixes #4867
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NrmBxj8rK2uGCnh9aipjwX
* test(metadata-protocol): record the #4867 engine double in the delete-dispatch ledger
`check:engine-double-contract` (#4550, landed today) flags the fake engine in
`sys-metadata-repository.history-counters.test.ts`: its `delete` does not route
through `assertEngineDeleteDispatch` from `@objectstack/objectql`.
The gate's preferred remedy — add objectql as a devDependency — is not merely
unreviewed here, it is CYCLIC. `@objectstack/objectql` already depends on
`@objectstack/metadata-protocol` in `dependencies`, so the edge makes turbo
refuse the graph outright; measured by adding it and reverting:
Cyclic dependency detected:
@objectstack/metadata-protocol#build, @objectstack/objectql#build
So this takes the gate's other sanctioned route: a measured baseline entry
naming the cycle as the reason, classified DEBT rather than EXEMPT because the
ledger's own rule reserves EXEMPT for doubles nothing drives, and this one is
driven (the #4867 delete-path test).
The entry's `closes` names the only route that actually exists — sink the
predicate into a package both sides already depend on — because the four
sibling metadata-protocol entries prescribe the devDependency this commit just
measured to be impossible. Filed separately rather than edited here: their text
is not this PR's to rewrite.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NrmBxj8rK2uGCnh9aipjwX
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent c5adfe1 commit dca25e1
9 files changed
Lines changed: 665 additions & 5 deletions
File tree
- .changeset
- packages
- metadata-protocol
- src
- metadata
- src
- scripts
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
35 | 35 | | |
36 | 36 | | |
37 | 37 | | |
| 38 | + | |
38 | 39 | | |
39 | 40 | | |
40 | 41 | | |
| |||
0 commit comments