Skip to content

ADR-0087 P1: the conversion layer — most breaks require zero consumer action #2645

Description

@os-zhuang

Part of #2643 (ADR-0087). Highest-leverage phase. A versioned, declarative load-time conversion layer so that lossless breaks (renames, moves, alias removals) require no action from a consumer for one full major — the Kubernetes storage-version / conversion model applied to metadata.

Depends on P0 (needs PROTOCOL_VERSION + the loader handshake seam).

Scope (D2)

  • For each protocol major N, a versioned table conversions/N.ts in @objectstack/spec: per renamed/moved/re-shaped surface, a transform N−1 shape → N shape, applied centrally at load (the same seam objectstack validate uses), emitting a structured deprecation notice per applied conversion.
  • Scope guard: losslessly mappable changes only — renames, alias removals, field moves, enum re-spellings. A change with no lossless mapping is out of scope (→ P2 migration).
  • Window: the loader applies a conversion for one major (N accepts N−1 shapes), then retires it from the load path in N+1 — but never deletes it: a retired conversion graduates into the P2 migration chain as that major's mechanical step.
  • Seed with the shipped 11.0 renames as the first (retroactive) tablehttp_request/http_call/webhookhttp, and the client-react alias set — proving that, had this existed, protocol 11 would have needed zero consumer action.
  • Prove the PD Add comprehensive test suite for Zod schema validation #12 retirement path: promote one existing consumer-side executor fallback (e.g. cfg.filter ?? cfg.filters) into a declared, expiring conversion entry, then delete the ??.

Why this does NOT violate Prime Directive #12

PD #12 bans scattered, unowned consumer-side dialect fallbacks. The conversion table is the opposite on every axis PD #12 cares about: one central table (not N ??s), versioned + declared in the spec (owns its history), loud (a deprecation notice per application), tested (old→new fixture pair per entry), expiring (dropped on schedule). It is the explicit version history of one contract, not a second de-facto dialect. See ADR-0087 §"Why the conversion layer does not violate PD #12".

Acceptance criteria

  • A metadata file authored with a 10.x shape covered by a conversion entry loads clean under 11.x, emits exactly one structured deprecation notice, and the runtime sees only the canonical shape.
  • Each conversion entry ships with an old-shape → new-shape fixture pair; a CI check runs every entry's fixture through the load path.
  • The retroactive 11.0 table converts the flow-node aliases and client-react aliases end-to-end.
  • One executor ?? fallback is retired through a conversion entry and deleted (PD Add comprehensive test suite for Zod schema validation #12 debt paydown demonstrated).
  • Removing a conversion from the load path in N+1 does not delete its history — it must appear as a P2 chain step (guarded once P2 exists; until then, tracked).

Boundaries

  • Converts at load only; never rewrites the consumer's source files (that is P2's explicit migrate meta).
  • Semantic (non-lossless) changes are explicitly excluded and belong to P2.

Refs: #2643, ADR-0087 (D2), AGENTS.md Prime Directive #12, ADR-0059.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    Fields

    No fields configured for Task.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions