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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions