Files
OpenMAIC/.github
wyuc 1c5078842a feat(storage): RuntimeStore — sessions + append-only records, browser backend (#869 Part B) (#880)
* feat(storage): RuntimeStore contract — interface + backend-agnostic suite (#869)

* feat(storage): BrowserRuntimeStore — IndexedDB backend for the runtime layer (#869)

* test(storage): runtime version-skew coverage; run the storage suite in CI (#869)

Backend-specific tests seed raw session rows (mirroring the document
backend's reStampStage) to cover the version-skew surface the public API
cannot express: a stale stamp fails loud while the runtime ladder is empty,
a future stamp reads through unchanged but rejects appendRecord and
setSessionStatus, and a stamp-stripped row hits the no-unversioned-epoch
guard. Also covers the injected payloadValidators override (the contract
case deferred to the backend test, as document-contract does for scene
validators).

The @openmaic/storage suite — including the pre-existing DocumentStore /
KV / asset contracts — never ran in CI at all; add the missing step after
the dsl one. README gains the RuntimeStore section.

* fix(storage): harden RuntimeStore writes and listings per cross-review (#869)

- mergeLearner now takes the same gates as every other write: rejects
  empty learner keys, self-merges return 0 without a transaction,
  future-stamped rows throw (mutating a newer client's data), and every
  re-keyed envelope is validated before put — any throw aborts the whole
  merge atomically instead of contaminating the target partition.
- listSessions sorts by the instant a timestamp denotes (Date.parse),
  not by string order, which mis-ranks numeric zone offsets; and it
  tolerates corrupt rows by omission (the listDocuments precedent) so
  one poison row cannot make a partition unenumerable — direct reads
  stay fail-loud.
- appendRecord validates the ACTUAL completed record (with the assigned
  seq) inside the transaction, on top of the pre-flight, and derives seq
  from a key cursor's primaryKey instead of deserializing the previous
  record's payload.
- Docstrings now enumerate the corrupt-stamp throw paths and no longer
  claim every query is partition-scoped (mergeLearner is the one
  deliberate cross-stage sweep).
- The stale-row test comment no longer promises an impossible one-line
  flip; the IDBKeyRange shim moves to test/setup.ts alongside the other
  guarded shims.

* fix(storage): mergeLearner migrates stale sessions before re-keying (#869)

The re-key previously spread the RAW stored row, unlike setSessionStatus
and appendRecord which migrate a stale session in place before mutating
it. Once the runtime ladder gains a real step, that would either write an
old runtimeDslVersion into the target partition or fail validation when a
migration adds required fields. Build the updated row from
migrateSession(row) instead (order: future-guard, migrate, re-key,
validate, put).

Observable today (empty ladder): a below-epoch stale row makes the merge
abort with the ladder's no-migration-path error — pinned by a new backend
test with nothing moved.

* fix(storage): reads gate stored sessions through envelope validation (#869)

Reads previously ran only version resolution, so a row with a valid
runtimeDslVersion but corrupt other fields (a non-ISO createdAt, a bogus
status) came back as-is — contradicting the documented corrupt-row
semantics, which covered only version corruption. Extend the same policy
to the whole envelope: getSession validates the migrated row and fails
loud as a stored-row integrity error; listSessions omits rows whose
envelope validation fails, exactly like version-resolution failures.

setSessionStatus and appendRecord need no change: the status update
validates the full post-update envelope before writing (catching corrupt
non-status fields), and appendRecord's own writes are already validated.
2026-07-09 00:03:51 +08:00
..