Skip to content

refactor(storage): retire result-bearing coalesced control barriers from first-party composition - #2195

Merged
Jurij89 merged 11 commits into
integration/2052-system-record-syncfrom
refactor/2179-retire-string-barriers
Aug 9, 2026
Merged

refactor(storage): retire result-bearing coalesced control barriers from first-party composition#2195
Jurij89 merged 11 commits into
integration/2052-system-record-syncfrom
refactor/2179-retire-string-barriers

Conversation

@Jurij89

@Jurij89 Jurij89 commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Retire the purpose-string control-barrier contract from first-party composition. The unsoundness (refactor(storage): retire result-bearing coalesced control barriers #2179): coalescing is keyed by a runtime (storeId, purpose-string) while each caller picks a static T, so a later same-purpose caller receives the first transition's promise under its own T. The typed-key path (runTypedControlBarrier + createStoreControlBarrierKeyV1, landed in refactor(storage): extract the control-barrier subsystem from StorePriorityScheduler #2169) binds T to the key once; this PR finishes the migration so no first-party code calls the string API at all.
  • The managed lane is typed-only structurally (design refined across review rounds 1–3, replacing an earlier poisoned-callback approach and then a second exported factory): createSystemRecordLaneControllerV1 remains the only controller constructor, with overloads accepting either SystemRecordLaneControllerTypedDepsV1 (no string-barrier member at all) or the legacy-capable public deps, normalizing internally. The managed coordinator's options forbid the string member and it passes a typed-deps literal, so first-party composition cannot regress onto the purpose-string contract by any edit short of changing an internal interface. A complete-literal @ts-expect-error pin keeps that structural absence honest — re-adding the member fails the typecheck lane with TS2578 (verified live). The duplicate-registration guard runs before any caller-supplied deps property is read, pinned by a throw-on-every-access Proxy test and mutation-checked.
  • Deprecate runControlBarrier and the run() 'control-barrier' admission with the migration path and the removal boundary stated: both are removed together at the next allowed breaking version, not before — both removals are source-incompatible for external callers. Until then externals keep working unchanged; the lifecycle suite (string-only barriers throughout) is the compat pin. Coalescing, timeout, sealing, quiescence and metrics behavior are untouched — both entry points share the coordinator.
  • Make the type contracts provable instead of decorative: nothing typechecked storage test files (package tsconfig includes only src/; vitest does not typecheck), so every expectTypeOf in the suite was inert. A narrow tsconfig.typetests.json lane compiles the barrier contract file in CI. The suite-wide gap is flagged as follow-up, not absorbed here.

Related

Diagrams

Lifecycle transition under the barrier

Before:

sequenceDiagram
    participant Lane as system-record lane
    participant Deps as controller deps
    participant Sched as StorePriorityScheduler
    Lane->>Deps: typedBarrier('enable', transition)
    Deps->>Sched: runTypedControlBarrier(storeId, key, transition)
    Note over Deps,Sched: if typedBarrier were ever removed:
    Lane->>Deps: barrier('system-record.enable', transition)
    Deps->>Sched: runControlBarrier(storeId, purpose, transition)
    Note over Sched: silent fallback onto the unsound<br/>string contract — as Promise#60;T#62;
Loading

After:

sequenceDiagram
    participant Lane as system-record lane
    participant Deps as controller deps
    participant Sched as StorePriorityScheduler
    Lane->>Deps: typedBarrier('enable', transition)
    Deps->>Sched: runTypedControlBarrier(storeId, key, transition)
    Note over Deps,Sched: the coordinator's deps have NO string member —<br/>falling back is a type error, pinned by<br/>an #64;ts-expect-error tripwire in the typecheck lane
Loading

Files changed

File What
packages/storage/src/adapters/sparql-http.ts Supplies only typedBarrier; no string barrier exists in managed composition
packages/storage/src/adapters/system-record-managed-coordinator-v1-internal.ts Options drop the string member; calls the typed-only builder
packages/storage/src/system-record-materializer-v1.ts One factory with typed/legacy overloads; shared deps base (SystemRecordLaneControllerSharedDepsV1); guard-before-deps-read ordering; deprecation docs with removal boundary
packages/storage/src/store-priority-scheduler.ts runControlBarrier + StoreControlBarrierAdmissionV1 deprecations: migration path, removal boundary, behavior-parity statement
packages/storage/test/system-record-materializer-lifecycle-v1.test.ts Compat-path pin: an external string barrier that throws at the boundary fails enable loudly and closed
packages/storage/test/system-record-capability-probe-errors-v1.test.ts Injection follows the composition: mock sits on the typed builder (the route production takes)
packages/storage/test/store-control-barrier-contract-v1.test.ts Type contracts: result type bound to the key; void key = effect barrier by type; @ts-expect-error pins (no smuggled result type, no forged key)
packages/storage/tsconfig.typetests.json, packages/storage/package.json typecheck:type-contracts lane so the type assertions are compiled, not decorative
.github/workflows/system-record-managed-ownership.yml CI step running the type-contract lane

Test plan

  • Storage curated CI lane: 33 files / 564 tests green
  • Affected suites: 176 tests green across contract, lifecycle, epoch-adapter, scheduler, admission, barrier-integration
  • typecheck:type-contracts lane green; both @ts-expect-error pins proven live (fixing the suppressed error away yields TS2578)
  • Structural mutant: re-adding a string barrier member to the coordinator options turns the @ts-expect-error pin into an unused suppression — typecheck lane fails with TS2578 (verified live)
  • Negative type assertions live in a never-invoked function: compiled by the typecheck lane, never executed by vitest (no real barriers enqueued, no floating promises)
  • First-party caller sweep: zero runControlBarrier( call sites outside the scheduler's own deprecated method
  • String-only compat pin: the lifecycle suite supplies string barriers throughout and stays green

… composition

Completes the migration #2169 started. The typed-key path
(runTypedControlBarrier + createStoreControlBarrierKeyV1) already carried
every production lifecycle transition; what remained of the string contract
was one live production call site and a silent re-animation hazard.

sparql-http no longer supplies a real string barrier. The controller deps
member stays required -- required-ness is the compile-time guard that already
caught a barrier shipping with zero callers once in this stack -- but managed
composition now satisfies it with a POISONED callback that throws a named
error. The fallback in createSystemRecordLaneControllerV1 reaches the string
barrier only when typedBarrier is absent, i.e. only under the future edit
that removes it; the poison turns that edit from a silent migration onto the
deprecated unsound-cast contract into an immediate failure. Mutation-checked:
deleting typedBarrier from the production deps fails the epoch-adapter test
with exactly the poison's message. A lifecycle test pins the fail-closed
shape -- the only handoff interaction is the fail-closed order; no physical
step runs.

runControlBarrier and the run() 'control-barrier' admission are deprecated
with the migration path and the removal boundary stated (both go together at
the next allowed breaking version; both are source-incompatible for external
callers before it). External composers keep working: the deps interface shape
is unchanged, the string members remain, and the lifecycle suite -- which
supplies string-only barriers throughout -- is the compat pin.

Type contracts are now PROVEN, not decorative: nothing typechecked storage
test files (package tsconfig includes only src/, vitest does not typecheck),
so every expectTypeOf in the suite was inert. A narrow tsconfig.typetests
lane compiles the barrier contract file in CI; the new @ts-expect-error pins
(a transition cannot smuggle a different result type past its key; a key
cannot be forged from a literal) were verified live by TS2578 when their
errors are fixed away. The suite-wide typecheck gap is flagged for follow-up
rather than absorbed here.

Closes #2179.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread packages/storage/src/adapters/sparql-http.ts Outdated
Comment thread packages/storage/test/store-control-barrier-contract-v1.test.ts Outdated
… by poison

Review round 1 of #2195, both findings taken.

The poison pill is gone, replaced by the stronger design the review pointed
at: createSystemRecordLaneControllerTypedV1 is the typed-only core (deps have
NO string-barrier member at all), the public createSystemRecordLaneControllerV1
becomes the compatibility adapter that normalizes legacy deps down to it, and
the managed coordinator's options drop the string member entirely. First-party
composition now cannot regress onto the purpose-string contract by any edit
short of changing an internal interface -- enforcement moved from a runtime
trap to the type system, and there is no impossible callback left to explain.

The invariant's tripwire moved with it: a complete-literal @ts-expect-error
pin proves the coordinator options reject a barrier member, and the pin
discriminates -- re-adding the member turns it into an unused suppression and
fails the typecheck lane (verified live, TS2578). The literal is complete
apart from `barrier` deliberately: an incomplete one could not discriminate,
because missing-member errors would keep the suppression alive either way.

The poison lifecycle test is reframed as what it also proved and what still
matters: an external string barrier that throws at the boundary fails enable
loudly and CLOSED -- the only handoff interaction is the fail-closed order.

Negative type assertions move out of the runtime path into a never-invoked
function: the typecheck lane still compiles every @ts-expect-error, but vitest
no longer executes intentionally-invalid calls or leaves their promises
floating.

The capability-probe injection follows the composition change: the coordinator
now imports the typed builder, so the mock sits there. Its comment states the
scope honestly -- a module mock cannot intercept the public adapter's
module-internal call; the injection covers the route production takes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread packages/storage/src/system-record-materializer-v1.ts Outdated
Comment thread packages/storage/src/adapters/system-record-managed-coordinator-v1-internal.ts Outdated
Comment thread packages/storage/src/adapters/sparql-http.ts Outdated
Jurij89 and others added 3 commits August 9, 2026 14:14
…sh coordinator evidence

Review round 2 of #2195, all three findings taken.

SystemRecordLaneControllerSharedDepsV1 now carries lease/handoff/executor/
setAdmissionActive exactly once; the public legacy deps and the typed-only
deps are barrier-specific extensions of it, and the session deps build on the
same base. A future shared dependency is added in one place and reaches both
composition routes or neither -- the drift the duplication invited.

The coordinator composition test was STALE AND RED on this branch: it mocked
the legacy factory, so the coordinator's import of the typed builder resolved
to undefined under the mock. My curated-lane runs never saw it because the
file was in no lane at all -- the reviewer caught from static reading what my
green lane hid. The test now mocks the typed factory (mocking ONLY that
export, deliberately: a coordinator that reaches for the legacy factory again
finds undefined and fails at the call), asserts the deps production passes
carry typedBarrier and that 'barrier' is structurally absent from them, and
the file joins the storage conformance lane so the changed boundary stays
covered.

The typed-barrier rationale now lives in ONE place -- on
SystemRecordLaneControllerTypedDepsV1 -- with one-line pointers at the
coordinator option and the sparql-http call site.

Full storage suite run this time, not just the lane: 918 passing; every
failure is in the oxigraph-worker files, timeout-shaped, varies in count
between identical runs, and reproduces identically on the untouched
integration head under the same machine load. Not attributable to this diff,
which does not touch the worker adapter.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ore any deps read

Review round 3 of #2195, both findings taken.

The second exported factory is gone. createSystemRecordLaneControllerV1 is
again the only controller constructor, with overloads accepting either the
typed-only deps or the legacy-capable public deps and normalizing internally
-- the shape split lives in the types, not in parallel factories. The managed
coordinator passes a literal typed as SystemRecordLaneControllerTypedDepsV1,
so its structural no-string-member invariant and the typecheck-lane pin are
unchanged, while nothing leaks a second deep-importable construction surface.

The duplicate-registration guard runs before ANY caller-supplied dependency
property is read. The round-2 shape read deps.typedBarrier (and the shared
members) in the compatibility wrapper before the core's guard, so a second
registration with a throwing accessor surfaced the composer's error instead
of SystemRecordControllerRegistrationError. Collapsing to one factory also
collapses the ordering question: guard first, then normalization. Pinned by a
test whose deps object throws on EVERY property access -- the typed refusal
can only surface if nothing was read first -- and mutation-checked: hoisting
a single deps read above the guard fails the test with exactly the trap's
message.

The mocks follow: one factory means one injection point again, and the
capability-probe injection now covers every construction route rather than
one of two.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review round 4 of #2195, finding taken.

normalizeControllerBarrierV1 discriminates the deps union with 'barrier' in
deps, so the compiler itself proves the legacy branch has a string barrier to
wrap and the typed-only branch has a required typedBarrier -- the previous
`as SystemRecordLaneControllerDepsV1` asserted what is now checked. A future
deps change that breaks either premise fails to typecheck at the normalizer
instead of surviving inside the cast. No casts remain in the factory.

The registration-guard pin is tightened to match: `in` narrowing triggers a
Proxy `has` trap, not `get`, so the booby-trapped deps now trap BOTH -- any
inspection of composer deps before the guard trips the test, including the
normalizer's discriminating check. Mutation-checked: hoisting the normalizer
call above the guard fails the test with exactly the has-trap's message.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread packages/storage/tsconfig.typetests.json Outdated
Comment thread packages/storage/src/store-priority-scheduler.ts Outdated
…etest file

Review round 5 of #2195, both items taken.

The negative type contracts live in store-control-barrier-contract-v1
.typetest.ts: declare-d shapes only, no runtime, compiled by the
typecheck:type-contracts lane and not matched by the vitest glob. The runtime
spec is executable-only again -- no never-invoked function, no coordinator
type assertions in a scheduler test. The stale @deprecated parenthetical on
runControlBarrier (it described the retired poison design) now states the
structural invariant.

The move surfaced a tsc parsing hazard worth recording: a wrapped comment
line that BEGINS with the expect-error token is parsed as a REAL directive.
My explanatory comment wrapped exactly that way, creating a phantom directive
on a prose line that read as unused and failed the lane -- while the same
sentence in the runtime file happened to wrap differently and was clean. The
comment is reworded, the file warns about the trap, and all three pins were
re-proven to discriminate in their new home (interface mutant flips the real
directive to TS2578; forged-key raw error is TS2345).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread .github/workflows/system-record-managed-ownership.yml Outdated
Jurij89 and others added 2 commits August 9, 2026 15:45
Review round 6 of #2195, both items taken.

The #2179 rationale lives once, on SystemRecordLaneControllerTypedDepsV1;
the normalizer is a one-line doc and the factory doc states only what it
owns (one constructor, both shapes, guard-before-deps-read). This also fixes
a real docs bug the consolidation surfaced: the factory's docblock sat
before the normalizer's, so API docs attached to the wrong symbol.

The CI lane comment now names both files the lane compiles -- the runtime
spec's expectTypeOf assertions and the never-executed .typetest.ts negative
contracts -- instead of the pre-split single file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review round 7 of #2195. The deprecation guidance told external composers to
migrate to the typed shape while the barrel exported only the legacy deps
type -- the migration target was reachable by deep import alone. Both the
typed deps and the shared base are now first-class barrel exports, so the
documented path is the public path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread packages/storage/src/index.ts Outdated
Comment thread packages/storage/test/store-control-barrier-contract-v1.typetest.ts
…yped deps shape

Review round 8 of #2195, all three findings taken.

The barrier vocabulary, the three deps shapes, and the normalizer move to
system-record-lane-controller-contract-v1.ts -- the compatibility boundary
reads on its own, and the 2k-line materializer keeps lane-session mechanics.
The materializer re-exports everything, so no import surface changes; the
contract module imports only TYPES back, keeping the value graph acyclic.

SystemRecordLaneControllerSharedDepsV1 is un-published from the barrel: it
is a factoring device, not a valid factory input, and a public name for an
incomplete shape is compatibility burden without use. (My round-7 change
exported it alongside the typed deps; that was an overshoot.)

The typed-only factory input now carries its own structural pin: a complete
literal with excess `barrier` under a suppression, so the typed deps
interface growing a string member -- even optional -- flips the directive to
unused and fails the lane. Mutation-checked: adding `barrier?` to the
interface yields TS2578 at exactly that pin. The coordinator-options pin
alone could not see this regression.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread packages/storage/src/system-record-lane-controller-contract-v1.ts
Comment thread packages/storage/src/system-record-materializer-v1.ts Outdated
Jurij89 and others added 2 commits August 9, 2026 17:09
The materializer re-exports only the contract types with real consumers; the
normalizer (constructor plumbing) and the shared deps base (not a valid
factory input) stay reachable only through the contract module, which the
barrel does not publish.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…structor

Review round 10 of #2195. This finding asked for a typed core constructor
with the legacy entry point adapting into it -- which round 3 (id 3743757711)
had rejected in its exported form as a second deep-importable construction
surface. The shape that satisfies BOTH recorded objections at once: the one
exported constructor stays the only construction surface and becomes a thin
adapter (guard, normalize, delegate), and the session construction moves into
a MODULE-PRIVATE typed-only core that knows nothing about the legacy shape.
No overload, union, or normalizer call remains below the adapter's body.

The round-3 ordering contract is untouched: the duplicate-registration guard
runs in the public adapter before any caller-supplied deps property is read,
and the private core documents that it assumes the guard already ran. Pinned
by the existing accessor-trap registration test, which stays green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Jurij89
Jurij89 merged commit 39f0ff0 into integration/2052-system-record-sync Aug 9, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants