Skip to content

register() claims the announcement follows the write into every writable loader, but no test fixture can observe that half #6548

Description

@baozhoutao

Found while doing #6043 (PR #6547). Filed separately per PD #10. Observation-class: no user-visible defect today — the gap is in coverage, not behavior.

Fact

register() in packages/metadata/src/metadata-manager.ts documents its announcement ordering as covering two writes:

// Announce last, once the write has landed in the registry and every
// writable loader — a subscriber that re-reads on the event must not
// race ahead of the data it is meant to observe.

register-notifies-watchers.test.ts is the file that pins this contract. After PR #6547 it asserts the registry half properly (synchronously, inside the watcher callback, at broadcast time). The writable-loader half is not asserted anywhere, and cannot be from that file as it stands:

  • the fixture's only loader is MemoryLoader, whose contract declares protocol: 'memory:';
  • register()'s persistence loop is gated on loader.contract.protocol !== 'datasource:' || !loader.contract.capabilities.write and continues otherwise;
  • so register() never saves to any loader in that file, and moving the announcement across the save loop changes nothing observable there.

Measured

Hoisting the notifyWatchers(...) block above the realtime publish and the writable-loader save loop — leaving the registry write in place — keeps the whole file green, both before and after PR #6547:

 Test Files  1 passed (1)
      Tests  15 passed (15)

That hoist violates the ordering the comment declares. Nothing reports it.

Why this is observation-class rather than a defect

A subscriber that re-reads via get() resolves against the in-memory registry, which outranks every loader, so it never observes the loader write — the half that IS pinned is the half consumers actually depend on. The unpinned half would only matter to a consumer reading through a loader directly, bypassing the registry, which nothing does today.

Shape of the fix (not chosen)

Needs a writable datasource: loader fixture whose save() records the store's state, then a synchronous peek at that store inside the watcher callback — the same shape #6043 used for the registry. metadata-manager-unregister-invalidate-order.test.ts already builds a writable datasource: loader backed by a real store and could be the donor.

Alternatively, if the loader half is deliberately NOT part of the guarantee, the comment on register() should be narrowed to say so — declared = enforced, in either direction.

Related

#6043 (where this surfaced), #3112 (origin of the test file), PR #6547.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions