feat(scripts): extend trace schema with socketRisk and lastRevalidated

Add socketRisk (9th filter result) and lastRevalidated (nullable ISO date)
to the library-decision trace schema. Downstream enforcement layers
(evaluate-library skill, check.mjs major-bump mode, revalidate.mjs cron)
all depend on these fields being validated at the schema layer first.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-14 17:04:05 +00:00
parent c17d3f147d
commit 3bf6a55481
3 changed files with 81 additions and 0 deletions

View File

@@ -6,6 +6,7 @@ decision: approved | rejected
date: <YYYY-MM-DD> date: <YYYY-MM-DD>
deciders: [<author>, ...] deciders: [<author>, ...]
adr: adr-NNN | null adr: adr-NNN | null
lastRevalidated: null
filter-results: filter-results:
license: <SPDX id> license: <SPDX id>
types: native | "@types/<x>" | none types: native | "@types/<x>" | none
@@ -15,6 +16,7 @@ filter-results:
eu-residency: ok | n/a | self-hostable | fail eu-residency: ok | n/a | self-hostable | fail
cve-scan: clean | "<advisory-id>" | fail cve-scan: clean | "<advisory-id>" | fail
named-consumer: pass | fail named-consumer: pass | fail
socketRisk: clean | flagged | <arbitrary-string>
verification-commands: verification-commands:
- <literal command that produced each filter result> - <literal command that produced each filter result>
accepted-cves: [] accepted-cves: []
@@ -84,6 +86,26 @@ Answer: "Who calls this code path today, or who is blocked waiting for it?"
Hypothetical future callers are not consumers. This filter is the direct Hypothetical future callers are not consumers. This filter is the direct
response to the 2026-05-14 OpenAPI near-miss (ADR-022 §Context). response to the 2026-05-14 OpenAPI near-miss (ADR-022 §Context).
## Filter: socketRisk
<!-- Result: clean | flagged | <arbitrary-string> -->
Run `socket npm:report <package>` (or check socket.dev) for supply-chain
risk signals. `clean` = no issues detected. `flagged` = one or more high- or
critical-severity signals (requires explicit risk-acceptance note here before
approval). An arbitrary string records the specific risk label returned by the
Socket CLI (e.g. `"obfuscated-code"`, `"install-script"`). The `lastRevalidated`
frontmatter field is set to the ISO date of the most recent re-run.
## Field: lastRevalidated
<!-- Value: YYYY-MM-DD | null -->
ISO 8601 date of the last time the Socket supply-chain scan (and any other
time-sensitive filter) was re-run against the current installed version.
`null` = never revalidated since initial adoption (acceptable for fresh traces).
Updated automatically by the weekly revalidation cron (Story 05).
## Prompt: replaces ## Prompt: replaces
<!-- Required: answer in either direction with justification --> <!-- Required: answer in either direction with justification -->

View File

@@ -17,6 +17,7 @@ const filterResultsSchema = z
"eu-residency": z.enum(["ok", "n/a", "self-hostable", "fail"]), "eu-residency": z.enum(["ok", "n/a", "self-hostable", "fail"]),
"cve-scan": z.string().min(1), "cve-scan": z.string().min(1),
"named-consumer": z.enum(["pass", "fail"]), "named-consumer": z.enum(["pass", "fail"]),
socketRisk: z.union([z.literal("clean"), z.literal("flagged"), z.string()]),
}) })
.strict(); .strict();
@@ -29,6 +30,7 @@ export const traceSchema = z
date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "date must be YYYY-MM-DD"), date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "date must be YYYY-MM-DD"),
deciders: z.array(z.string()), deciders: z.array(z.string()),
adr: z.string().nullable(), adr: z.string().nullable(),
lastRevalidated: z.string().nullable(),
"filter-results": filterResultsSchema, "filter-results": filterResultsSchema,
"verification-commands": z.array(z.string()), "verification-commands": z.array(z.string()),
"accepted-cves": z.array(z.string()).optional(), "accepted-cves": z.array(z.string()).optional(),

View File

@@ -14,6 +14,7 @@ function validRaw(overrides = {}) {
date: "2026-05-14", date: "2026-05-14",
deciders: ["alice"], deciders: ["alice"],
adr: null, adr: null,
lastRevalidated: null,
"filter-results": { "filter-results": {
license: "MIT", license: "MIT",
types: "native", types: "native",
@@ -23,6 +24,7 @@ function validRaw(overrides = {}) {
"eu-residency": "ok", "eu-residency": "ok",
"cve-scan": "clean", "cve-scan": "clean",
"named-consumer": "pass", "named-consumer": "pass",
socketRisk: "clean",
}, },
"verification-commands": ["pnpm audit --audit-level=moderate"], "verification-commands": ["pnpm audit --audit-level=moderate"],
...overrides, ...overrides,
@@ -43,6 +45,7 @@ decision: approved
date: 2026-05-14 date: 2026-05-14
deciders: [alice, bob] deciders: [alice, bob]
adr: null adr: null
lastRevalidated: null
filter-results: filter-results:
license: MIT license: MIT
types: native types: native
@@ -52,6 +55,7 @@ filter-results:
eu-residency: ok eu-residency: ok
cve-scan: clean cve-scan: clean
named-consumer: pass named-consumer: pass
socketRisk: clean
verification-commands: verification-commands:
- pnpm audit --audit-level=moderate`; - pnpm audit --audit-level=moderate`;
@@ -125,6 +129,59 @@ describe("validateTrace > rejection cases", () => {
/unrecognized_keys|Unrecognized key/i, /unrecognized_keys|Unrecognized key/i,
); );
}); });
test("missing socketRisk in filter-results fails validation", () => {
const raw = validRaw();
delete raw["filter-results"].socketRisk;
assert.throws(() => validateTrace(raw), /invalid_type|Required/i);
});
});
describe("validateTrace > socketRisk", () => {
test('socketRisk "clean" round-trips', () => {
const result = validateTrace(validRaw());
assert.equal(result["filter-results"].socketRisk, "clean");
});
test('socketRisk "flagged" round-trips', () => {
const raw = validRaw({
"filter-results": {
...validRaw()["filter-results"],
socketRisk: "flagged",
},
});
assert.equal(validateTrace(raw)["filter-results"].socketRisk, "flagged");
});
test("socketRisk arbitrary string round-trips", () => {
const raw = validRaw({
"filter-results": {
...validRaw()["filter-results"],
socketRisk: "obfuscated-code",
},
});
assert.equal(
validateTrace(raw)["filter-results"].socketRisk,
"obfuscated-code",
);
});
});
describe("validateTrace > lastRevalidated", () => {
test("lastRevalidated null is valid", () => {
assert.equal(
validateTrace(validRaw({ lastRevalidated: null })).lastRevalidated,
null,
);
});
test("lastRevalidated ISO date string is valid", () => {
assert.equal(
validateTrace(validRaw({ lastRevalidated: "2026-05-14" }))
.lastRevalidated,
"2026-05-14",
);
});
}); });
describe("parseFrontmatter", () => { describe("parseFrontmatter", () => {