Uh oh!
There was an error while loading. Please reload this page.
feat(storage): enforce operational schema compatibility epochs - #2363
feat(storage): enforce operational schema compatibility epochs#2363Astro-Han wants to merge 20 commits into
Conversation
zhiiw
left a comment
There was a problem hiding this comment.
SQLite compatibility issues are noted inline.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
zhiiw
left a comment
There was a problem hiding this comment.
Remaining SQLite compatibility issues are noted inline.
Uh oh!
There was an error while loading. Please reload this page.
| const current = acquireOperationalStateDatabase(root); | ||
| try { | ||
| for (const [table, key, value] of [ |
There was a problem hiding this comment.
The new bidirectional production-API coverage exercises only session_metadata. The remaining scopes in this loop are still verified as epoch-1 raw INSERT -> current raw COUNT; no current-writer output from runtime, core execution, workflow, usage, artifact, or automation is read or updated by the frozen epoch-1 consumer. A supposedly compatible change in any of those scopes could still break an older reader while this test passes. Please add bidirectional production-shaped coverage for every registered scope, or explicitly narrow the epoch contract.
| return Number(match[1]); | ||
| } | ||
| function countBreakingChanges(source) { |
There was a problem hiding this comment.
This count is not an append-only history check. A later PR can rewrite or remove an existing compatibility declaration while keeping the global epoch valid; for example, with two scopes requiring epoch 2, one historical breaking declaration can be reclassified while the other still keeps the manifest reader epoch at 2. Please structurally compare base and current manifests and require every historical scope/change list to be an exact prefix of the current one, rather than comparing regex counts.
likun666661
commented
Aug 7, 2026
Design-level blocker: before freezing epoch 1, we need to define the compatibility contract that this mechanism is intended to enforce. This PR currently makes one global
These implications do not follow from one another. In particular, reader compatibility does not establish writer compatibility: an old read-modify-write path can discard fields it does not understand, overwrite newer state, or violate a newer cross-table invariant. The lifetime compatibility lock prevents a cross-epoch migration while an old process is alive, but deliberately permits same-epoch mixed-version writers, so it does not address that semantic risk. The frozen-probe/append-only design also appears to turn every historical same-epoch behavior into a permanent support obligation. Mature upgrade systems normally define a finite, asymmetric support matrix and a controlled upgrade sequence; they do not treat all historical readers and writers as universally compatible. I think this PR should first state an explicit policy for each operational scope:
Then the mechanism can encode that policy. A likely shape is to separate minimum reader and minimum writer epochs, distinguish physical schema version from feature/behavior activation, and use expand -> activate -> contract. Old binaries may participate during the bounded expand window; after contract, rollback is snapshot restore rather than silently reopening the database with old code. The probe runner should execute every epoch that the declared support window includes, with production-shaped coverage for every supported matrix cell. #2361 already addresses the urgent data-safety property by removing destructive reset behavior and failing closed on a newer schema. That gives us room to define the narrower product contract before this PR freezes epoch 1. As written, the implementation is rigorous, but it is rigorously enforcing a compatibility promise that has not yet been specified. |
Summary
Refs #2357.
Verification
actionlint .github/workflows/ci.ymlnode scripts/check-operational-schema-compatibility.mjs --base mainnode --test scripts/check-operational-schema-compatibility.test.mjsnpm run format:checknpm run lintnpm run typechecknpm run buildnpm run test:dist --workspace @maka/storage: full suite passednpm run test:dist --workspace @maka/headless: 1,508 passed, 0 failedMigration and review focus
opencode-go/deepseek-v4-flash:max, covering migration/data safety, cross-process lifecycle, governance/tests, and first-principles/Occam design.sessionStore.close()closes the complete persistence cluster.