Skip to content

storage: rename active-named replacement derivation types to the neutral replacement model #2247

Description

@Jurij89

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions