Skip to content
Open
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
10 changes: 9 additions & 1 deletion scripts/capability-matrix/src/compliance-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { fileURLToPath } from "node:url";
import { dirname, join, resolve } from "node:path";
import { parse } from "yaml";
import { loadAreas } from "./load.js";
import { validateCompliance, collectFeatureIds } from "./compliance.js";
import { validateCompliance, collectFeatureIds, findMissingFeatureIds } from "./compliance.js";
import type { RawCompliance } from "./compliance.js";

function repoRoot(): string {
Expand Down Expand Up @@ -45,6 +45,14 @@ async function main(): Promise<void> {
process.exit(1);
}
console.log("OK — compliance file is valid.");

const missing = findMissingFeatureIds(raw, knownIds);
if (missing.length > 0) {
console.log(`\n${missing.length} feature(s) not declared (treated as not_implemented):`);
for (const id of missing) {
console.log(` - ${id}`);
}
}
}

main().catch((e) => { console.error(e); process.exit(1); });
5 changes: 5 additions & 0 deletions scripts/capability-matrix/src/compliance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ export function collectFeatureIds(areas: LoadedArea[]): Set<string> {
return ids;
}

export function findMissingFeatureIds(raw: RawCompliance, knownIds: Set<string>): string[] {
const declared = new Set(Object.keys(raw.features ?? {}));
return [...knownIds].filter((id) => !declared.has(id)).sort();
}

// Returns a map from SDK symbol name → capability matrix feature ID.
// Built from the symbols arrays declared in sdk-compliance.yaml.
export function buildSymbolIndex(raw: RawCompliance): Map<string, string> {
Expand Down
36 changes: 35 additions & 1 deletion scripts/capability-matrix/test/compliance.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, it, expect } from "vitest";
import { validateCompliance, normalizeCompliance, collectFeatureIds, buildSymbolIndex } from "../src/compliance";
import { validateCompliance, normalizeCompliance, collectFeatureIds, buildSymbolIndex, findMissingFeatureIds } from "../src/compliance";
import type { LoadedArea } from "../src/types";

function areas(ids: string[]): LoadedArea[] {
Expand Down Expand Up @@ -169,6 +169,40 @@ describe("buildSymbolIndex", () => {
});
});

describe("findMissingFeatureIds", () => {
it("returns ids present in knownIds but absent from raw.features", () => {
const raw = { sdk: "javascript", features: { "auth.sign_up": "implemented" } };
const missing = findMissingFeatureIds(raw, knownIds);
expect(missing).toContain("auth.sign_in_with_password");
expect(missing).toContain("auth.mfa_enroll");
expect(missing).not.toContain("auth.sign_up");
});

it("returns empty array when all known ids are declared", () => {
const raw = {
sdk: "javascript",
features: {
"auth.sign_up": "implemented",
"auth.sign_in_with_password": "implemented",
"auth.mfa_enroll": "not_implemented",
},
};
expect(findMissingFeatureIds(raw, knownIds)).toEqual([]);
});

it("returns all ids when features is empty", () => {
const raw = { sdk: "javascript", features: {} };
const missing = findMissingFeatureIds(raw, knownIds);
expect(missing).toEqual([...knownIds].sort());
});

it("returns ids in sorted order", () => {
const raw = { sdk: "javascript", features: {} };
const missing = findMissingFeatureIds(raw, knownIds);
expect(missing).toEqual([...missing].sort());
});
});

describe("collectFeatureIds", () => {
it("collects all feature ids from loaded areas", () => {
const loaded = areas(["auth.sign_up", "auth.sign_in_with_password"]);
Expand Down