Skip to content

ADR-0087 P0: enforce the protocol handshake (make engines.protocol real) #2644

Description

@os-zhuang

Part of #2643 (ADR-0087). First phase — smallest change, highest value/cost. Turns a protocol/consumer version mismatch from an arbitrary downstream crash into a structured, machine-actionable load-time refusal, and pays down a standing ADR-0078 violation (a declarable field the runtime ignores).

Status: core enforcement + install seam shipped in #2650 (draft). Remaining items (load-path handshake, lint nudge, scaffold stamping) tracked below — this issue stays open until they land.

The bug this fixes

PluginEnginesSchema.protocol (packages/spec/src/kernel/manifest.zod.ts, ADR-0025 §3.2, protocol-first per §3.10 #3) is declared, documented, and checked by no loader, installer, or CLI command (repo-wide grep: the only non-test hit is the schema's own .describe()). A package built against protocol N and loaded by an incompatible runtime therefore fails deep inside a .parse() or a renderer contract instead of at the boundary.

Scope (D1)

  • PROTOCOL_VERSION / PROTOCOL_MAJOR constant exported from @objectstack/spec/kernel (drift-tested against the package major). — feat(protocol): enforce the engines.protocol handshake (ADR-0087 P0) #2650
  • Pure handshake core in @objectstack/metadata-core: checkProtocolCompat(), assertProtocolCompat(), structured ProtocolIncompatibleError (OS_PROTOCOL_INCOMPATIBLE) carrying both versions + the migrate meta --from N command; refuses only on a positive mismatch, grandfathers absent ranges, never false-rejects an unparsed range. — feat(protocol): enforce the engines.protocol handshake (ADR-0087 P0) #2650
  • Install seam: installPackage runs the handshake before the registry write. — feat(protocol): enforce the engines.protocol handshake (ADR-0087 P0) #2650
  • Load seam: run the handshake on the boot-time durable-package rehydration path and AppPlugin.start() for code-defined stacks (the same assertProtocolCompat call).
  • objectstack lint flags a package with no engines.protocol range (nudge toward declaring it).
  • Scaffold stamping: create-objectstack and the defineStack templates emit engines: { protocol: '^<current major>' } by default — the ratchet that closes grandfathering.

Acceptance criteria

Boundaries

  • No source rewriting here — this phase only refuses; the migration that resolves the refusal is P1 (conversion) / P2 (migrate meta).
  • The error must name the chain/command, not a doc the consumer "should have read" — see ADR-0087 D1.

Refs: #2643, #2650, ADR-0087 (D1), ADR-0078, ADR-0025 §3.2/§3.10.

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