feat(foundation): offline synthetic scope/permission inspector (FI-FILBERT-8 APPROVED r6)
Rocko-authored, Filbert-reviewed inspector (r6 manifest a4a44930...) with full review/build/verdict evidence under docs/plans/reviews. 43/0 selftests, oracle zero-disagreement, foundation checker PASS. Owner A9 acceptance recorded separately.
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Test-only differential bridge for scripts/foundation/verify-schema.py.
|
||||
*
|
||||
* node scripts/foundation/validate-record.mjs <corpus.json>
|
||||
*
|
||||
* Input: a JSON array of {name, raw} where raw is the exact JSON text of one
|
||||
* candidate record. Output (stdout): a JSON array, same order, of
|
||||
* {name, parse: "ok" | <strict-json error code>, byteOffset,
|
||||
* schemaValid: true|false|null, profileValid: bool, verdict, rule, path}
|
||||
*
|
||||
* schemaValid is the inspector's record-schema verdict (null when the strict
|
||||
* parser rejected the text, or when the kind is unsupported and therefore not
|
||||
* judged beyond the kind gate); it reproduces the pinned checker's verdict,
|
||||
* including its end-of-string newline tolerance on id/runtimeId/digest patterns.
|
||||
* profileValid is the strict bundle/profile verdict: strict lexical parse AND
|
||||
* shape-valid AND a supported kind AND the strict typed-string profile (addendum
|
||||
* FI-C2-1: the whole value satisfies the grammar). A schema-valid, profile-invalid
|
||||
* record reports rule "profile-pattern-mismatch" with its first violating path.
|
||||
* The two columns are deliberately separate (charter §8, §10.5).
|
||||
*
|
||||
* This bridge is not part of the inspector and may read the named file; the
|
||||
* inspector itself never imports it.
|
||||
*/
|
||||
|
||||
import { readFileSync } from "node:fs";
|
||||
import { parseStrict, StrictJsonError, utf8Encode } from "./strict-json.mjs";
|
||||
import { validateRecordShape } from "./resolve.mjs";
|
||||
|
||||
export function judgeRaw(raw) {
|
||||
let value;
|
||||
try {
|
||||
value = parseStrict(utf8Encode(raw));
|
||||
} catch (e) {
|
||||
if (e instanceof StrictJsonError) {
|
||||
return { parse: e.code, byteOffset: e.byteOffset, schemaValid: null, profileValid: false, verdict: "unparsed", rule: null, path: null };
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
const v = validateRecordShape(value);
|
||||
// Unsupported kinds are refused at the kind gate; their envelope/payload is not
|
||||
// judged, so the schema column is "not assessed" (null), never "valid".
|
||||
const schemaValid = v.verdict === "unsupported-kind" ? null : v.verdict === "valid";
|
||||
const violations = v.verdict === "valid" ? v.profileViolations : [];
|
||||
return {
|
||||
parse: "ok",
|
||||
byteOffset: null,
|
||||
schemaValid,
|
||||
profileValid: v.verdict === "valid" && violations.length === 0,
|
||||
verdict: v.verdict,
|
||||
rule: v.rule ?? (violations.length > 0 ? "profile-pattern-mismatch" : null),
|
||||
path: v.path ?? (violations.length > 0 ? violations[0] : null),
|
||||
};
|
||||
}
|
||||
|
||||
function main() {
|
||||
const file = process.argv[2];
|
||||
if (!file || process.argv.length !== 3) {
|
||||
process.stderr.write("usage: validate-record.mjs <corpus.json>\n");
|
||||
process.exit(2);
|
||||
}
|
||||
const corpus = JSON.parse(readFileSync(file, "utf8"));
|
||||
if (!Array.isArray(corpus)) throw new Error("corpus must be an array");
|
||||
const out = corpus.map((c) => {
|
||||
if (typeof c.name !== "string" || typeof c.raw !== "string") throw new Error("corpus entry needs {name, raw}");
|
||||
return { name: c.name, ...judgeRaw(c.raw) };
|
||||
});
|
||||
process.stdout.write(`${JSON.stringify(out)}\n`);
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user