You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
register() claims the announcement follows the write into every writable loader, but no test fixture can observe that half #6548
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.
Findings triage: promoted — finding removed, pm:queue applied (labels changed together with this comment).
Premise re-verified on origin/main @ f549a0d: the "Announce last, once the write has landed in the registry and every writable loader" comment sits at packages/metadata/src/metadata-manager.ts:741; the pin file register-notifies-watchers.test.ts and the named donor fixture metadata-manager-unregister-invalidate-order.test.ts both exist in packages/metadata/src/.
Grading rationale: this is a test-only pin restoring a declared invariant — the method documents an ordering across two writes and only one is observable; the hoist experiment in the body shows the whole file stays green when the declared ordering is violated. Scoped, mechanical, donor fixture already named ⇒ auto-queue class rather than held observation.
Direction note for the dev: default to pinning the loader half with the donor-fixture shape. Narrowing the comment instead (the body's alternative) is acceptable only with an explicit statement that the loader half is deliberately outside the guarantee — declared = enforced in either direction, as the body itself puts it.
Claim: PM loop round 3 (domain:metadata seat, sticker #6367)
Session: session_01KDU3qAuJyajAQm3GkUXdfA
Branch: claude/issue-6548-register-loader-half-pin
Worktree: objectstack-issue-6548
Domain: domain:metadata
File surface: packages/metadata/src/register-notifies-watchers.test.ts (writable datasource: fixture + synchronous store peek, donor shape from metadata-manager-unregister-invalidate-order.test.ts — donor read-only) + the register() ordering comment in packages/metadata/src/metadata-manager.ts ONLY if the narrowing route is taken per the triage direction note. Stop on breach; explain in the report.
Serial constraints cleared: #6547 (#6043, same test file) and #6576 (#5309, same package) both MERGED; nothing in flight touches packages/metadata (#6190 is in metadata-protocol, #3682 in rest/runtime/plugin-security). Sequencing note recorded on the seat board: #6563 (same file as in-flight #6190's declared surface — protocol.ts) is deliberately held for the next slot instead of riding with this card.
Container class: S — mode:subagent, shared container.
Shipped: the writable-loader half of register()'s announcement ordering is now pinned — a real datasource: StoreLoader fixture (donor shape, donor untouched) peeked synchronously inside the watcher callback, five cases including "every loader, not merely the first" and "an in-flight save holds the announcement back" (gate-controlled, no microtask counting). The triage direction's default route was taken: nothing narrowed, the comment on register() is now enforced as written.
Review verified against GitHub: exactly one file (+267/−6), no source change; reverse verification predicted-then-measured 4-red/18-green under the precise hoist the issue proved invisible; type-check debt measured and paid to zero (the .js import makes implements MetadataLoader a real check); ESLint and TypeScript Type Check both completed: success, 27 checks zero failures; skip-changeset applied and read back (tests-only).
Activity
os-zhuang commentedon Aug 8, 2026
Findings triage: promoted —
findingremoved,pm:queueapplied (labels changed together with this comment).origin/main@f549a0d: the "Announce last, once the write has landed in the registry and every writable loader" comment sits atpackages/metadata/src/metadata-manager.ts:741; the pin fileregister-notifies-watchers.test.tsand the named donor fixturemetadata-manager-unregister-invalidate-order.test.tsboth exist inpackages/metadata/src/.本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
baozhoutao commentedon Aug 8, 2026
Claim: PM loop round 3 (
domain:metadataseat, sticker #6367)Session:
session_01KDU3qAuJyajAQm3GkUXdfABranch:
claude/issue-6548-register-loader-half-pinWorktree:
objectstack-issue-6548Domain:
domain:metadataFile surface:
packages/metadata/src/register-notifies-watchers.test.ts(writabledatasource:fixture + synchronous store peek, donor shape frommetadata-manager-unregister-invalidate-order.test.ts— donor read-only) + theregister()ordering comment inpackages/metadata/src/metadata-manager.tsONLY if the narrowing route is taken per the triage direction note. Stop on breach; explain in the report.Serial constraints cleared: #6547 (#6043, same test file) and #6576 (#5309, same package) both MERGED; nothing in flight touches
packages/metadata(#6190 is in metadata-protocol, #3682 in rest/runtime/plugin-security). Sequencing note recorded on the seat board: #6563 (same file as in-flight #6190's declared surface —protocol.ts) is deliberately held for the next slot instead of riding with this card.Container class: S —
mode:subagent, shared container.Generated by Claude Code
test(metadata): keep the new pin out of the package's frozen type-che…
baozhoutao commentedon Aug 8, 2026
ACCEPT (PM review,
domain:metadataseat, sessionsession_01KDU3qAuJyajAQm3GkUXdfA) — PR #6607, now ready + queued.Shipped: the writable-loader half of
register()'s announcement ordering is now pinned — a realdatasource:StoreLoader fixture (donor shape, donor untouched) peeked synchronously inside the watcher callback, five cases including "every loader, not merely the first" and "an in-flight save holds the announcement back" (gate-controlled, no microtask counting). The triage direction's default route was taken: nothing narrowed, the comment onregister()is now enforced as written.Review verified against GitHub: exactly one file (+267/−6), no source change; reverse verification predicted-then-measured 4-red/18-green under the precise hoist the issue proved invisible; type-check debt measured and paid to zero (the
.jsimport makesimplements MetadataLoadera real check); ESLint and TypeScript Type Check bothcompleted: success, 27 checks zero failures;skip-changesetapplied and read back (tests-only).The issue closes on merge via the
Fixesline.Generated by Claude Code