Skip to content

Commit eb23db8

Browse files
committed
fix(spec): the specVersion refusal prescribes the current protocol range, spelled from PROTOCOL_MAJOR
ManifestSchema's guidance for a manifest that writes specVersion told the author to declare engines: { protocol: '^17' } (the range the scaffold stamps). At protocol 18 os init stamps '^18' and the load-time handshake refuses '^17', so following the prescription produced a manifest the runtime refuses to load. The card's sweep missed it because the quotes are backslash-escaped. The range is now spelled from PROTOCOL_MAJOR, so it moves with the handshake it names. manifest-unknown-keys.test.ts pins that the prescribed range is the current major and not the previous one. Claude-Session: https://claude.ai/code/session_01VZqqwTj2wsihZEbfT6yyYN Co-authored-by: Claude <noreply@anthropic.com>
1 parent c80423b commit eb23db8

2 files changed

Lines changed: 10 additions & 1 deletion

File tree

‎packages/spec/src/kernel/manifest-unknown-keys.test.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import { describe, it, expect } from 'vitest';
3939

4040
import { ManifestSchema, PluginEnginesSchema } from './manifest.zod';
41+
import { PROTOCOL_MAJOR } from './protocol-version';
4142
import { ArtifactPackageSchema, ObjectStackDefinitionSchema } from '../stack.zod';
4243
import { formatZodError } from '../shared/error-map.zod';
4344

@@ -104,6 +105,10 @@ describe("unknown keys inside `manifest:` are refused at parse (the silent-drop
104105
const issue = unrecognized(result)!;
105106
expect(issue.keys).toEqual(['specVersion']);
106107
expect(issue.message).toContain('engines.protocol');
108+
// The range it prescribes is the one the handshake admits: the current
109+
// protocol major, never the previous one the handshake refuses.
110+
expect(issue.message).toContain(`protocol: '^${PROTOCOL_MAJOR}'`);
111+
expect(issue.message).not.toContain(`protocol: '^${PROTOCOL_MAJOR - 1}'`);
107112
expect(issue.message).not.toContain('Did you mean');
108113
});
109114
});

‎packages/spec/src/kernel/manifest.zod.ts‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { z } from 'zod';
44
import { CORE_PLUGIN_TYPES } from './plugin.zod';
5+
import { PROTOCOL_MAJOR } from './protocol-version';
56
import { SEMVER_2_0_0_VERSION_PATTERN } from './version-grammar';
67
import { retiredKey } from '../shared/retired-key';
78
import { closedObject, strictObject, strictObjectError } from '../shared/strict-object';
@@ -407,9 +408,12 @@ export const ManifestSchema = strictObject({
407408
+ 'exiting 0. The declared keys are enumerated by `ManifestSchema` (@objectstack/spec, '
408409
+ 'kernel/manifest.zod.ts) and in the package-manifest reference docs.',
409410
guidance: {
411+
// The prescribed range is spelled from PROTOCOL_MAJOR, the value the load-time
412+
// handshake compares and `os init` stamps, so it moves with the protocol instead
413+
// of naming a major the handshake already refuses.
410414
specVersion:
411415
'`specVersion` is not a package-manifest key. The protocol axis the runtime checks at '
412-
+ 'load is `engines.protocol` — declare `engines: { protocol: \'^17\' }` (the range the '
416+
+ `load is \`engines.protocol\` — declare \`engines: { protocol: '^${PROTOCOL_MAJOR}' }\` (the range the `
413417
+ 'scaffold stamps); `specVersion` keeps its meaning only on the marketplace TEMPLATE '
414418
+ 'manifest (`cloud/template-manifest.zod.ts`), which is a different surface.',
415419
},

0 commit comments

Comments
 (0)