Follow-up from the review of #2243. Filed rather than taken there, on cost/benefit — not because it is delicate. The measurement below is the point of this issue: whoever picks it up should meet it as a safe mechanical rename with a known scale.
What
The replacement derivation in packages/storage/src/system-record-next-state-v1-internal.ts is general over every materialization operation, but the model still wears active-only names. A tombstone plan produced by deriveSystemRecordReplacementV1 is asserted and passed around as SystemRecordActiveReplacementCompleteV1 while the plan can carry status: 'tombstone', 'dirty', or 'quarantined'.
Rename to neutral names — SystemRecordReplacementDerivationV1 / SystemRecordReplacementCompleteV1 / assertAuthenticSystemRecordReplacementCompleteV1 — and update the executor and apply-command signatures to match. Retain active-specific names only where a caller genuinely requires active-only behaviour; note that SystemRecordActiveReplacementIssueV1 (the issue type) is genuinely active-specific and is not part of this rename.
Also drop the identity alias at :163, export type SystemRecordReplacementDerivationV1 = SystemRecordActiveReplacementDerivationV1; — it is the half-migration this issue completes.
The measurement that sizes it
This was initially assessed as correctness-adjacent, on the theory that an asserts value is narrowing a tombstone-carrying value to an active-only type was compile-silently unsound. That is not what is happening here, and the difference matters for how the work is scoped. SystemRecordActiveReplacementCompleteV1 (:131-135) is a single generic interface:
export interface SystemRecordActiveReplacementCompleteV1<
Outcome extends 'ready' | 'already-applied' = 'ready' | 'already-applied',
> {
readonly outcome: Outcome;
readonly plan: SystemRecordMaterializationPlanV1;
}
Two fields, neither active-specific: outcome ranges over 'ready' | 'already-applied' and has nothing to do with the status axis, and plan is the general materialization plan. The module's own comment at :165 says as much — "every complete variant uses the same CAS plan." There is no active-only Complete type for the assert at :836-838 to be narrowing away from, so nothing is mis-narrowed and there is no type-safety hole.
(Contrast #2231, which genuinely had two different types — an active-only alias and a wider union — where asserting the narrower one was unsound. That combination is what made #2231 correctness-adjacent. Only the compile-silence half of that precedent carries over here, and compile-silence is harmless when the asserted type is correct.)
Scale: 93 references across 6 files — ...CompleteV1 26, ...DerivationV1 10, ...ReadyV1 18, the assert 10, deriveSystemRecordActiveReplacementV1 29; three source files (system-record-next-state-v1-internal.ts, system-record-atomic-apply-executor-v1-internal.ts, system-record-apply-command-v1-internal.ts) and three test files.
Why it is still worth doing
The name does not currently lie about the type — but it invites a future lie. A later author reading SystemRecordActiveReplacementCompleteV1 may add an active-only assumption to code handling it, narrowing on the name rather than on the value, and the type will not stop them because the type is general and the name is the only signal. That is a latent hazard with a real trigger.
Why this is safe to take, unlike the sibling refactors
Worth stating explicitly so this is not deprioritised into the same bucket as #2244: this is a mechanical rename. The compiler catches every one of the 93 sites, and the change is line-stable — it rewrites identifiers on existing lines rather than moving code. The stack's line-anchored mutation proofs and site-anchored pin citations therefore survive untouched. #2244 is deferred because doing it destroys that evidence chain; this one is deferred purely on cost/benefit against a converging stack.
Related: #2052 (umbrella), #2243, #2244.
Follow-up from the review of #2243. Filed rather than taken there, on cost/benefit — not because it is delicate. The measurement below is the point of this issue: whoever picks it up should meet it as a safe mechanical rename with a known scale.
What
The replacement derivation in
packages/storage/src/system-record-next-state-v1-internal.tsis general over every materialization operation, but the model still wears active-only names. A tombstone plan produced byderiveSystemRecordReplacementV1is asserted and passed around asSystemRecordActiveReplacementCompleteV1while the plan can carrystatus: 'tombstone','dirty', or'quarantined'.Rename to neutral names —
SystemRecordReplacementDerivationV1/SystemRecordReplacementCompleteV1/assertAuthenticSystemRecordReplacementCompleteV1— and update the executor and apply-command signatures to match. Retain active-specific names only where a caller genuinely requires active-only behaviour; note thatSystemRecordActiveReplacementIssueV1(the issue type) is genuinely active-specific and is not part of this rename.Also drop the identity alias at
:163,export type SystemRecordReplacementDerivationV1 = SystemRecordActiveReplacementDerivationV1;— it is the half-migration this issue completes.The measurement that sizes it
This was initially assessed as correctness-adjacent, on the theory that an
asserts value isnarrowing a tombstone-carrying value to an active-only type was compile-silently unsound. That is not what is happening here, and the difference matters for how the work is scoped.SystemRecordActiveReplacementCompleteV1(:131-135) is a single generic interface:Two fields, neither active-specific:
outcomeranges over'ready' | 'already-applied'and has nothing to do with the status axis, andplanis the general materialization plan. The module's own comment at:165says as much — "every complete variant uses the same CAS plan." There is no active-only Complete type for the assert at:836-838to be narrowing away from, so nothing is mis-narrowed and there is no type-safety hole.(Contrast #2231, which genuinely had two different types — an active-only alias and a wider union — where asserting the narrower one was unsound. That combination is what made #2231 correctness-adjacent. Only the compile-silence half of that precedent carries over here, and compile-silence is harmless when the asserted type is correct.)
Scale: 93 references across 6 files —
...CompleteV126,...DerivationV110,...ReadyV118, the assert 10,deriveSystemRecordActiveReplacementV129; three source files (system-record-next-state-v1-internal.ts,system-record-atomic-apply-executor-v1-internal.ts,system-record-apply-command-v1-internal.ts) and three test files.Why it is still worth doing
The name does not currently lie about the type — but it invites a future lie. A later author reading
SystemRecordActiveReplacementCompleteV1may add an active-only assumption to code handling it, narrowing on the name rather than on the value, and the type will not stop them because the type is general and the name is the only signal. That is a latent hazard with a real trigger.Why this is safe to take, unlike the sibling refactors
Worth stating explicitly so this is not deprioritised into the same bucket as #2244: this is a mechanical rename. The compiler catches every one of the 93 sites, and the change is line-stable — it rewrites identifiers on existing lines rather than moving code. The stack's line-anchored mutation proofs and site-anchored pin citations therefore survive untouched. #2244 is deferred because doing it destroys that evidence chain; this one is deferred purely on cost/benefit against a converging stack.
Related: #2052 (umbrella), #2243, #2244.