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

    Fields

    No fields configured for Task.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions