Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion test/e2e/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ transports: STATEFUL_TRANSPORTS, // or an explicit list
note: 'stateless hosting has no server→client back-channel'
```

`addedInSpecVersion` / `removedInSpecVersion` bound the spec versions a requirement applies to; a behavior changed by a spec release gets a sibling entry linked via `supersedes`.
`addedInSpecVersion` / `removedInSpecVersion` bound the spec versions a requirement applies to. A behavior changed by a spec release gets a sibling entry: the new entry lists every retired id it replaces in `supersedes` (an array, requires `addedInSpecVersion`), and each retired
entry points back via `supersededBy` (requires `removedInSpecVersion`). A coverage gate enforces that the links resolve and are exactly symmetric.

## Running

Expand Down
21 changes: 18 additions & 3 deletions test/e2e/coverage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,25 @@ test('every transport-restricted requirement explains why in note', () => {
expect(missing).toEqual([]);
});

test('every supersedes reference points at an existing requirement id', () => {
test('supersedes/supersededBy links are symmetric and resolve', () => {
const bad: string[] = [];
for (const [id, req] of Object.entries(REQUIREMENTS)) {
if (req.supersedes !== undefined) {
expect(REQUIREMENTS[req.supersedes], `${id} supersedes unknown id '${req.supersedes}'`).toBeDefined();
for (const oldId of req.supersedes ?? []) {
const old = REQUIREMENTS[oldId];
if (!old) bad.push(`${id}: supersedes unknown id '${oldId}'`);
else if (old.supersededBy !== id)
bad.push(`${id}: supersedes '${oldId}', but that entry's supersededBy is '${old.supersededBy}'`);
}
if (req.supersededBy !== undefined) {
const successor = REQUIREMENTS[req.supersededBy];
if (!successor) bad.push(`${id}: supersededBy unknown id '${req.supersededBy}'`);
else if (!successor.supersedes?.includes(id))
bad.push(`${id}: supersededBy '${req.supersededBy}', but that entry's supersedes array does not include '${id}'`);
if (req.removedInSpecVersion === undefined)
bad.push(`${id}: has supersededBy but no removedInSpecVersion (only a retired entry can be superseded)`);
}
if (req.supersedes !== undefined && req.addedInSpecVersion === undefined)
bad.push(`${id}: has supersedes but no addedInSpecVersion (a superseding entry is by definition new)`);
}
expect(bad).toEqual([]);
});
29 changes: 23 additions & 6 deletions test/e2e/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,16 @@
export const ALL_TRANSPORTS = ['inMemory', 'stdio', 'streamableHttp', 'streamableHttpStateless', 'sse'] as const;
export type Transport = (typeof ALL_TRANSPORTS)[number];

export const ALL_SPEC_VERSIONS = ['2025-11-25'] as const;
export type SpecVersion = (typeof ALL_SPEC_VERSIONS)[number];
/**
* Every spec version the manifest may reference — used for typing
* `addedInSpecVersion` / `removedInSpecVersion` bounds and knownFailure
* scoping. Includes versions that are not yet part of the active matrix.
*/
export const KNOWN_SPEC_VERSIONS = ['2025-11-25', '2026-07-28'] as const;
export type SpecVersion = (typeof KNOWN_SPEC_VERSIONS)[number];

/** The spec versions cells are registered for (the active matrix axis). */
export const ALL_SPEC_VERSIONS = ['2025-11-25'] as const satisfies readonly SpecVersion[];

/**
* Arguments every test body receives. Expand with new matrix axes here so
Expand All @@ -31,12 +39,21 @@ export interface Requirement {
/** Free-form rationale for how the entry is set up (e.g. why certain transports are excluded). */
note?: string;

/** First / last spec versions a requirement applies to; changed behaviors are sibling entries linked via `supersedes`. */
/** First / last spec versions a requirement applies to; changed behaviors are sibling entries linked via `supersedes`/`supersededBy`. */
addedInSpecVersion?: SpecVersion;
removedInSpecVersion?: SpecVersion;
/** Requirement id this entry replaces (for behaviors changed by a spec release). */

supersedes?: string;
/**
* Requirement ids this (new) entry replaces. The structural link from a superseding entry to the
* retired entries it covers: each listed id's `supersededBy` points back at this entry. Semantic
* context about how/why the behavior changed belongs in `note`, not here.
*/
supersedes?: readonly string[];
/**
* Requirement id of the entry that replaces this (retired) one. The structural link from a retired
* entry to its successor: that entry's `supersedes` array includes this id. Semantic context about
* how/why the behavior changed belongs in `note`, not here.
*/
supersededBy?: string;
Comment thread
claude[bot] marked this conversation as resolved.
Comment thread
claude[bot] marked this conversation as resolved.
knownFailures?: readonly KnownFailure[];

deferred?: string;
Expand Down
Loading