From f31da221f406e3fd19928c1a075660e90971d464 Mon Sep 17 00:00:00 2001 From: Guilherme Souza Date: Fri, 26 Jun 2026 08:24:00 -0300 Subject: [PATCH] feat(compliance): list undeclared features as notes after validation When validate-compliance runs, any feature IDs absent from the sdk-compliance.yaml file are now printed to stdout so SDK owners can see at a glance what they haven't declared yet (those features default to not_implemented silently today). --- .../capability-matrix/src/compliance-cli.ts | 10 +++++- scripts/capability-matrix/src/compliance.ts | 5 +++ .../capability-matrix/test/compliance.test.ts | 36 ++++++++++++++++++- 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/scripts/capability-matrix/src/compliance-cli.ts b/scripts/capability-matrix/src/compliance-cli.ts index 40a3a64..99cb796 100644 --- a/scripts/capability-matrix/src/compliance-cli.ts +++ b/scripts/capability-matrix/src/compliance-cli.ts @@ -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 { @@ -45,6 +45,14 @@ async function main(): Promise { 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); }); diff --git a/scripts/capability-matrix/src/compliance.ts b/scripts/capability-matrix/src/compliance.ts index a339c84..18a0bbf 100644 --- a/scripts/capability-matrix/src/compliance.ts +++ b/scripts/capability-matrix/src/compliance.ts @@ -91,6 +91,11 @@ export function collectFeatureIds(areas: LoadedArea[]): Set { return ids; } +export function findMissingFeatureIds(raw: RawCompliance, knownIds: Set): 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 { diff --git a/scripts/capability-matrix/test/compliance.test.ts b/scripts/capability-matrix/test/compliance.test.ts index 58a2220..f0cbc59 100644 --- a/scripts/capability-matrix/test/compliance.test.ts +++ b/scripts/capability-matrix/test/compliance.test.ts @@ -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[] { @@ -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"]);