Recipe: Domain-Agnostic Contract Object Validation¶
1. Scenario and Non-Scenario¶
Applicable: you need to validate whether a document conforms to a Foundation-registered contract object (e.g., project-manifest), choosing a validation strategy by dialect.
Not applicable: needs to validate the consumer's own business Schema (the consumer should hold it itself; Foundation does not replace it); needs to mix domain-semantic validation into the generic contract.
Quickstart Profile v2 is a separate candidate path. The consumer still owns its business schemas; Engineering Kit only compiles an explicit schema collection into offline validators selected by $id, and does not own the method-to-schema choice.
2. Architecture Choice and Capability ID¶
Preferred capability: foundation.contracts.object-validation (validateDocument / compileSchema / detectDialect). A pure Ajv implementation, supporting draft-07 and 2020-12.
3. Preconditions and Trust Boundary¶
- The document to be validated carries the
$idof a registered contract object. - Trust boundary:
validateDocumentnever modifies the caller's input; a normalized copy is returned only in the result'sdatafield.
4. Minimal Code¶
import { validateDocument } from "skill-family-contracts";
const document = {
schemaVersion: 1,
kind: "skill-family.project-manifest",
project: { id: "my-project", name: "My Project", description: "Example" },
contracts: { version: "1.0.0", profile: "generic" },
managedFiles: ["package.json"],
updatedAt: "2026-01-01T00:00:00Z",
};
const result = validateDocument(document, {
schemaId: "https://contracts.skill-family.example/v1/project-manifest.json",
dialect: "2020-12",
});
if (!result.valid) console.error(result.errorCode);
The code above shows using $id to specify the target Schema and retrieving { valid, errorCode, errors, data }.
5. Expected Output and Evidence¶
On successful validation result.valid is true; on failure errorCode is a stable code such as SFC1001. Evidence: packages/skill-family-contracts/test/validator.test.mjs and test/schemas.test.mjs.
6. Safety / Failure Negative Case¶
// Negative case: unknown $id -> returns valid:false, errorCode = SFC1002 (UNKNOWN_SCHEMA_ID)
const bad = validateDocument(document, {
schemaId: "https://contracts.skill-family.example/v1/does-not-exist.json",
dialect: "2020-12",
});
// does not throw, returns a structured result:
// { valid: false, errorCode: "SFC1002",
// errors: [{ message: "unknown schema $id: https://contracts.skill-family.example/v1/does-not-exist.json" }] }
if (!bad.valid) console.error(bad.errorCode); // SFC1002
7. Copy-Paste Verification Command¶
node --test packages/skill-family-contracts/test/validator.test.mjs \
packages/skill-family-contracts/test/schemas.test.mjs
8. Business Logic the Caller Continues to Own¶
- Semantic interpretation of specific business fields.
- Domain-level validation rules (not within the Foundation generic contract).
9. Upgrade and Rollback Notes¶
- The contract authority version
CONTRACTS_VERSION = 1.4.0, and the npm package version0.3.0run in parallel and are not mixed. - Error codes are frozen and do not drift; Schema changes are handled as a new contract-version task, not by modifying frozen content in place.
- Candidate v2 must be pinned to exactly 0.3.0; integrations that still require v1 must remain pinned to exactly 0.2.1.