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)
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.
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_MAJORconstant exported from@objectstack/spec/kernel(drift-tested against the package major). — feat(protocol): enforce the engines.protocol handshake (ADR-0087 P0) #2650@objectstack/metadata-core:checkProtocolCompat(),assertProtocolCompat(), structuredProtocolIncompatibleError(OS_PROTOCOL_INCOMPATIBLE) carrying both versions + themigrate meta --from Ncommand; 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) #2650installPackageruns the handshake before the registry write. — feat(protocol): enforce the engines.protocol handshake (ADR-0087 P0) #2650AppPlugin.start()for code-defined stacks (the sameassertProtocolCompatcall).objectstack lintflags a package with noengines.protocolrange (nudge toward declaring it).create-objectstackand thedefineStacktemplates emitengines: { protocol: '^<current major>' }by default — the ratchet that closes grandfathering.Acceptance criteria
engines.protocolis rejected at install with the structured diagnostic (error code + both versions + migrate command asserted); it does not reach the registry write. — feat(protocol): enforce the engines.protocol handshake (ADR-0087 P0) #2650@objectstack/downstream-contract+ the api-surface snapshot stay green (2 added, 0 breaking; snapshot regenerated). — feat(protocol): enforce the engines.protocol handshake (ADR-0087 P0) #2650Boundaries
migrate meta).Refs: #2643, #2650, ADR-0087 (D1), ADR-0078, ADR-0025 §3.2/§3.10.