@dogfood-lab/schemas 1.2.2 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -33,18 +33,20 @@ npm install @dogfood-lab/schemas
33
33
 
34
34
  ## Usage — TypeScript surface
35
35
 
36
- The package exports `validatePayload` and the compiled validators:
36
+ The package exports `validatePayload` and the compiled validators. `validatePayload(name, payload)` takes the **schema name first** (one of `record`, `recordSubmission`, `finding`, `pattern`, `recommendation`, `doctrine`, `policy`, `scenario` — all camelCase, NOT the file basename) and the payload second. The return shape is `{ valid: boolean, errors: ValidationError[] }` — each error carries `path`, `message`, `keyword` (the Ajv rule that fired, e.g. `required` / `pattern` / `enum`), and optional `params`.
37
37
 
38
38
  ```ts
39
39
  import { validatePayload } from '@dogfood-lab/schemas';
40
40
 
41
- const result = validatePayload(submission, 'dogfood-record-submission');
42
- if (!result.ok) {
41
+ const result = validatePayload('recordSubmission', submission);
42
+ if (!result.valid) {
43
43
  console.error('schema mismatch:', result.errors);
44
44
  process.exit(1);
45
45
  }
46
46
  ```
47
47
 
48
+ `compileSchema(name)` returns the raw Ajv `ValidateFunction` for advanced cases (custom error formatting, direct `validate.errors` inspection). Sibling packages (`@dogfood-lab/verify`, `@dogfood-lab/findings`, `@dogfood-lab/ingest`, `@dogfood-lab/report`) all delegate to `validatePayload` — collapsing N Ajv instances to one per schema per process via the module-scope `validatorCache`.
49
+
48
50
  ## Usage — Raw JSON schemas
49
51
 
50
52
  The raw schema files are available via the `./json/*` subpath export. Use `createRequire` to load them at runtime:
package/dist/index.d.ts CHANGED
@@ -28,5 +28,5 @@ export declare const allSchemas: {
28
28
  readonly scenario: JsonSchema;
29
29
  };
30
30
  export type SchemaName = keyof typeof allSchemas;
31
- export { compileSchema, validatePayload, createAjv, _resetValidatorCacheForTests, type ValidationError, type ValidationResult, } from './validate.js';
31
+ export { compileSchema, validatePayload, createAjv, _resetValidatorCacheForTests, _schemasModuleInstanceCount, type ValidationError, type ValidationResult, } from './validate.js';
32
32
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA2BA,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,oBAAoB,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;IAC5C,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,eAAO,MAAM,YAAY,EAAE,UAA+C,CAAC;AAC3E,eAAO,MAAM,sBAAsB,EAAE,UAA0D,CAAC;AAChG,eAAO,MAAM,aAAa,EAAE,UAAgD,CAAC;AAC7E,eAAO,MAAM,aAAa,EAAE,UAAgD,CAAC;AAC7E,eAAO,MAAM,oBAAoB,EAAE,UAAuD,CAAC;AAC3F,eAAO,MAAM,cAAc,EAAE,UAAiD,CAAC;AAC/E,eAAO,MAAM,YAAY,EAAE,UAAuC,CAAC;AACnE,eAAO,MAAM,cAAc,EAAE,UAAyC,CAAC;AAEvE,eAAO,MAAM,UAAU;;;;;;;;;CASb,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,UAAU,CAAC;AAEjD,OAAO,EACL,aAAa,EACb,eAAe,EACf,SAAS,EACT,4BAA4B,EAC5B,KAAK,eAAe,EACpB,KAAK,gBAAgB,GACtB,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA2BA,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,oBAAoB,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;IAC5C,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,eAAO,MAAM,YAAY,EAAE,UAA+C,CAAC;AAC3E,eAAO,MAAM,sBAAsB,EAAE,UAA0D,CAAC;AAChG,eAAO,MAAM,aAAa,EAAE,UAAgD,CAAC;AAC7E,eAAO,MAAM,aAAa,EAAE,UAAgD,CAAC;AAC7E,eAAO,MAAM,oBAAoB,EAAE,UAAuD,CAAC;AAC3F,eAAO,MAAM,cAAc,EAAE,UAAiD,CAAC;AAC/E,eAAO,MAAM,YAAY,EAAE,UAAuC,CAAC;AACnE,eAAO,MAAM,cAAc,EAAE,UAAyC,CAAC;AAEvE,eAAO,MAAM,UAAU;;;;;;;;;CASb,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,UAAU,CAAC;AAEjD,OAAO,EACL,aAAa,EACb,eAAe,EACf,SAAS,EACT,4BAA4B,EAC5B,2BAA2B,EAC3B,KAAK,eAAe,EACpB,KAAK,gBAAgB,GACtB,MAAM,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -40,5 +40,5 @@ export const allSchemas = {
40
40
  policy: policySchema,
41
41
  scenario: scenarioSchema,
42
42
  };
43
- export { compileSchema, validatePayload, createAjv, _resetValidatorCacheForTests, } from './validate.js';
43
+ export { compileSchema, validatePayload, createAjv, _resetValidatorCacheForTests, _schemasModuleInstanceCount, } from './validate.js';
44
44
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AACrD,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;AAE7C,SAAS,IAAI,CAAC,QAAgB;IAC5B,MAAM,GAAG,GAAG,YAAY,CAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC;IAC7D,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAe,CAAC;AACvC,CAAC;AAcD,MAAM,CAAC,MAAM,YAAY,GAAe,IAAI,CAAC,4BAA4B,CAAC,CAAC;AAC3E,MAAM,CAAC,MAAM,sBAAsB,GAAe,IAAI,CAAC,uCAAuC,CAAC,CAAC;AAChG,MAAM,CAAC,MAAM,aAAa,GAAe,IAAI,CAAC,6BAA6B,CAAC,CAAC;AAC7E,MAAM,CAAC,MAAM,aAAa,GAAe,IAAI,CAAC,6BAA6B,CAAC,CAAC;AAC7E,MAAM,CAAC,MAAM,oBAAoB,GAAe,IAAI,CAAC,oCAAoC,CAAC,CAAC;AAC3F,MAAM,CAAC,MAAM,cAAc,GAAe,IAAI,CAAC,8BAA8B,CAAC,CAAC;AAC/E,MAAM,CAAC,MAAM,YAAY,GAAe,IAAI,CAAC,oBAAoB,CAAC,CAAC;AACnE,MAAM,CAAC,MAAM,cAAc,GAAe,IAAI,CAAC,sBAAsB,CAAC,CAAC;AAEvE,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,MAAM,EAAE,YAAY;IACpB,gBAAgB,EAAE,sBAAsB;IACxC,OAAO,EAAE,aAAa;IACtB,OAAO,EAAE,aAAa;IACtB,cAAc,EAAE,oBAAoB;IACpC,QAAQ,EAAE,cAAc;IACxB,MAAM,EAAE,YAAY;IACpB,QAAQ,EAAE,cAAc;CAChB,CAAC;AAIX,OAAO,EACL,aAAa,EACb,eAAe,EACf,SAAS,EACT,4BAA4B,GAG7B,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AACrD,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;AAE7C,SAAS,IAAI,CAAC,QAAgB;IAC5B,MAAM,GAAG,GAAG,YAAY,CAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC;IAC7D,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAe,CAAC;AACvC,CAAC;AAcD,MAAM,CAAC,MAAM,YAAY,GAAe,IAAI,CAAC,4BAA4B,CAAC,CAAC;AAC3E,MAAM,CAAC,MAAM,sBAAsB,GAAe,IAAI,CAAC,uCAAuC,CAAC,CAAC;AAChG,MAAM,CAAC,MAAM,aAAa,GAAe,IAAI,CAAC,6BAA6B,CAAC,CAAC;AAC7E,MAAM,CAAC,MAAM,aAAa,GAAe,IAAI,CAAC,6BAA6B,CAAC,CAAC;AAC7E,MAAM,CAAC,MAAM,oBAAoB,GAAe,IAAI,CAAC,oCAAoC,CAAC,CAAC;AAC3F,MAAM,CAAC,MAAM,cAAc,GAAe,IAAI,CAAC,8BAA8B,CAAC,CAAC;AAC/E,MAAM,CAAC,MAAM,YAAY,GAAe,IAAI,CAAC,oBAAoB,CAAC,CAAC;AACnE,MAAM,CAAC,MAAM,cAAc,GAAe,IAAI,CAAC,sBAAsB,CAAC,CAAC;AAEvE,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,MAAM,EAAE,YAAY;IACpB,gBAAgB,EAAE,sBAAsB;IACxC,OAAO,EAAE,aAAa;IACtB,OAAO,EAAE,aAAa;IACtB,cAAc,EAAE,oBAAoB;IACpC,QAAQ,EAAE,cAAc;IACxB,MAAM,EAAE,YAAY;IACpB,QAAQ,EAAE,cAAc;CAChB,CAAC;AAIX,OAAO,EACL,aAAa,EACb,eAAe,EACf,SAAS,EACT,4BAA4B,EAC5B,2BAA2B,GAG5B,MAAM,eAAe,CAAC"}
@@ -18,6 +18,14 @@
18
18
  */
19
19
  import type { ValidateFunction } from 'ajv';
20
20
  import { type SchemaName } from './index.js';
21
+ /**
22
+ * Returns how many times this module has been evaluated in the current
23
+ * Node process. Used by the D2B-015 single-cache invariant test to
24
+ * detect a workspace-hoist split. A green seal requires `=== 1`.
25
+ *
26
+ * Test-only export: production code has no reason to read this.
27
+ */
28
+ export declare function _schemasModuleInstanceCount(): number;
21
29
  declare const Ajv2020Ctor: new (opts?: {
22
30
  allErrors?: boolean;
23
31
  strict?: boolean;
@@ -28,6 +36,24 @@ declare const Ajv2020Ctor: new (opts?: {
28
36
  export interface ValidationError {
29
37
  path: string;
30
38
  message: string;
39
+ /**
40
+ * Ajv `errors[].keyword` — the rule that fired (`required`, `pattern`,
41
+ * `enum`, `additionalProperties`, `minLength`, etc.).
42
+ *
43
+ * H3 keyword extension: surfacing this at the canonical seam means
44
+ * downstream wrappers (e.g. ingest's RecordValidationError) can preserve
45
+ * the keyword pin (packages/ingest/ingest.test.js:208-220 asserts
46
+ * `typeof e.keyword === 'string'` for every error). Without this field
47
+ * here, every consumer would have to ride compileSchema() and read
48
+ * `validate.errors` directly to recover the keyword — which would
49
+ * defeat the migration's goal of collapsing N Ajv instances to one
50
+ * per schema.
51
+ *
52
+ * Optional in the type so future synthesised errors (e.g. higher-level
53
+ * "validator failed to compile" envelopes) aren't forced to invent a
54
+ * keyword. Live validation always populates it.
55
+ */
56
+ keyword?: string;
31
57
  params?: Record<string, unknown>;
32
58
  }
33
59
  export interface ValidationResult {
@@ -1 +1 @@
1
- {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,KAAK,CAAC;AAC5C,OAAO,EAAc,KAAK,UAAU,EAAmB,MAAM,YAAY,CAAC;AAK1E,QAAA,MAAM,WAAW,EAED,KAAK,IAAI,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,KAAK;IACxE,OAAO,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,gBAAgB,CAAC;IAC/C,SAAS,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC;CAC5C,CAAC;AAMF,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AAID;;;;GAIG;AACH,wBAAgB,SAAS,IAAI,YAAY,CAAC,OAAO,WAAW,CAAC,CAI5D;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,gBAAgB,CAahE;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,GAAG,gBAAgB,CAepF;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,IAAI,IAAI,CAEnD"}
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,KAAK,CAAC;AAC5C,OAAO,EAAc,KAAK,UAAU,EAAmB,MAAM,YAAY,CAAC;AAuC1E;;;;;;GAMG;AACH,wBAAgB,2BAA2B,IAAI,MAAM,CAEpD;AAKD,QAAA,MAAM,WAAW,EAED,KAAK,IAAI,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,KAAK;IACxE,OAAO,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,gBAAgB,CAAC;IAC/C,SAAS,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC;CAC5C,CAAC;AAMF,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AAID;;;;GAIG;AACH,wBAAgB,SAAS,IAAI,YAAY,CAAC,OAAO,WAAW,CAAC,CAI5D;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,gBAAgB,CAahE;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,GAAG,gBAAgB,CAgBpF;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,IAAI,IAAI,CAEnD"}
package/dist/validate.js CHANGED
@@ -19,6 +19,51 @@
19
19
  import Ajv2020Module from 'ajv/dist/2020.js';
20
20
  import addFormatsModule from 'ajv-formats';
21
21
  import { allSchemas } from './index.js';
22
+ /**
23
+ * Process-global module-instance counter — H3 fix-up.
24
+ *
25
+ * The H3 migration collapsed N Ajv instances to one per schema by routing
26
+ * every consumer through {@link validatePayload}. That single-instance
27
+ * guarantee depends on every consumer resolving `@dogfood-lab/schemas` to
28
+ * the SAME loaded module — same `validate.js` evaluation → same
29
+ * `validatorCache` Map → same compiled `ValidateFunction`s.
30
+ *
31
+ * Under npm workspace hoist resolution this can split: if any consumer's
32
+ * `node_modules/@dogfood-lab/schemas/` symlinks differently than others,
33
+ * Node loads the module twice. Each load gets its own `validatorCache` —
34
+ * the C1 two-Ajv gap RELOCATED to the cache layer instead of sealed.
35
+ *
36
+ * The naive "compileSchema returns the same reference twice" check is
37
+ * vacuous against this failure: a consumer in a split second instance
38
+ * populates ITS OWN cache; the original instance's cache is untouched;
39
+ * `compileSchema()` called twice from the original still returns the
40
+ * same reference. Same assertion, both green and split states.
41
+ *
42
+ * The fix: a counter rooted in `globalThis` (via `Symbol.for`, which is
43
+ * shared across module instances because the Symbol registry is
44
+ * process-global). Every evaluation of this module increments it. The
45
+ * D2B-015 seal test asserts the count is exactly 1 after every consumer
46
+ * has been imported — if hoist split the package, two evaluations
47
+ * happened, count >= 2, gate trips.
48
+ *
49
+ * The Symbol key is namespaced to `@dogfood-lab/schemas` so a second
50
+ * unrelated package can't shadow it. The counter is the only
51
+ * load-time side effect of this module — keeping it minimal so the
52
+ * cost is unobservable.
53
+ */
54
+ const _schemasInstanceSymbol = Symbol.for('@dogfood-lab/schemas.instances');
55
+ const _globalSlot = globalThis;
56
+ _globalSlot[_schemasInstanceSymbol] = (_globalSlot[_schemasInstanceSymbol] ?? 0) + 1;
57
+ /**
58
+ * Returns how many times this module has been evaluated in the current
59
+ * Node process. Used by the D2B-015 single-cache invariant test to
60
+ * detect a workspace-hoist split. A green seal requires `=== 1`.
61
+ *
62
+ * Test-only export: production code has no reason to read this.
63
+ */
64
+ export function _schemasModuleInstanceCount() {
65
+ return _globalSlot[_schemasInstanceSymbol] ?? 0;
66
+ }
22
67
  // CommonJS default-export interop: under NodeNext+esModuleInterop the named
23
68
  // `default` is what we actually want at runtime. Sibling JS packages get this
24
69
  // for free via Node's CJS interop; the TS source has to be explicit.
@@ -72,6 +117,7 @@ export function validatePayload(name, payload) {
72
117
  const errors = (validate.errors ?? []).map(err => ({
73
118
  path: err.instancePath || '/',
74
119
  message: err.message ?? 'unknown error',
120
+ keyword: err.keyword,
75
121
  params: err.params,
76
122
  }));
77
123
  return { valid: false, errors };
@@ -1 +1 @@
1
- {"version":3,"file":"validate.js","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,aAAa,MAAM,kBAAkB,CAAC;AAC7C,OAAO,gBAAgB,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,UAAU,EAAoC,MAAM,YAAY,CAAC;AAE1E,4EAA4E;AAC5E,8EAA8E;AAC9E,qEAAqE;AACrE,MAAM,WAAW,GAAG,CACjB,aAA+D,CAAC,OAAO,IAAI,aAAa,CAI1F,CAAC;AAEF,MAAM,UAAU,GAAG,CAChB,gBAAqE,CAAC,OAAO,IAAI,gBAAgB,CACrC,CAAC;AAahE,MAAM,cAAc,GAAG,IAAI,GAAG,EAAgC,CAAC;AAE/D;;;;GAIG;AACH,MAAM,UAAU,SAAS;IACvB,MAAM,GAAG,GAAG,IAAI,WAAW,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IAChE,UAAU,CAAC,GAAG,CAAC,CAAC;IAChB,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,IAAgB;IAC5C,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACxC,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAe,CAAC;IAC9C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,wBAAwB,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,GAAG,GAAG,SAAS,EAAE,CAAC;IACxB,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC,cAAc,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACnC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,IAAgB,EAAE,OAAgB;IAChE,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAEhC,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACrC,CAAC;IAED,MAAM,MAAM,GAAsB,CAAC,QAAQ,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACpE,IAAI,EAAE,GAAG,CAAC,YAAY,IAAI,GAAG;QAC7B,OAAO,EAAE,GAAG,CAAC,OAAO,IAAI,eAAe;QACvC,MAAM,EAAE,GAAG,CAAC,MAA6C;KAC1D,CAAC,CAAC,CAAC;IAEJ,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AAClC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,4BAA4B;IAC1C,cAAc,CAAC,KAAK,EAAE,CAAC;AACzB,CAAC"}
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,aAAa,MAAM,kBAAkB,CAAC;AAC7C,OAAO,gBAAgB,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,UAAU,EAAoC,MAAM,YAAY,CAAC;AAE1E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,sBAAsB,GAAG,MAAM,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;AAE5E,MAAM,WAAW,GAAG,UAA0C,CAAC;AAC/D,WAAW,CAAC,sBAAsB,CAAC,GAAG,CAAC,WAAW,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;AAErF;;;;;;GAMG;AACH,MAAM,UAAU,2BAA2B;IACzC,OAAO,WAAW,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC;AAClD,CAAC;AAED,4EAA4E;AAC5E,8EAA8E;AAC9E,qEAAqE;AACrE,MAAM,WAAW,GAAG,CACjB,aAA+D,CAAC,OAAO,IAAI,aAAa,CAI1F,CAAC;AAEF,MAAM,UAAU,GAAG,CAChB,gBAAqE,CAAC,OAAO,IAAI,gBAAgB,CACrC,CAAC;AA+BhE,MAAM,cAAc,GAAG,IAAI,GAAG,EAAgC,CAAC;AAE/D;;;;GAIG;AACH,MAAM,UAAU,SAAS;IACvB,MAAM,GAAG,GAAG,IAAI,WAAW,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IAChE,UAAU,CAAC,GAAG,CAAC,CAAC;IAChB,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,IAAgB;IAC5C,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACxC,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAe,CAAC;IAC9C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,wBAAwB,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,GAAG,GAAG,SAAS,EAAE,CAAC;IACxB,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC,cAAc,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACnC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,IAAgB,EAAE,OAAgB;IAChE,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAEhC,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACrC,CAAC;IAED,MAAM,MAAM,GAAsB,CAAC,QAAQ,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACpE,IAAI,EAAE,GAAG,CAAC,YAAY,IAAI,GAAG;QAC7B,OAAO,EAAE,GAAG,CAAC,OAAO,IAAI,eAAe;QACvC,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,MAAM,EAAE,GAAG,CAAC,MAA6C;KAC1D,CAAC,CAAC,CAAC;IAEJ,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AAClC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,4BAA4B;IAC1C,cAAc,CAAC,KAAK,EAAE,CAAC;AACzB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dogfood-lab/schemas",
3
- "version": "1.2.2",
3
+ "version": "1.3.0",
4
4
  "description": "JSON schemas for the testing-os contract spine — record, finding, pattern, recommendation, doctrine, policy, scenario, submission.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -26,13 +26,14 @@
26
26
  "access": "public"
27
27
  },
28
28
  "engines": {
29
- "node": ">=20"
29
+ "node": ">=22"
30
30
  },
31
31
  "dependencies": {
32
32
  "ajv": "^8.18.0",
33
33
  "ajv-formats": "^3.0.1"
34
34
  },
35
35
  "devDependencies": {
36
+ "@types/node": "^25.3.5",
36
37
  "vitest": "^4.1.6"
37
38
  },
38
39
  "author": "mcp-tool-shop",
@@ -1,86 +1,86 @@
1
- {
2
- "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://github.com/dogfood-lab/testing-os/packages/schemas/src/json/dogfood-doctrine.schema.json",
4
- "title": "Dogfood Doctrine",
5
- "description": "A hardened portfolio rule earned from repeated patterns. Doctrine represents durable operating law — the strongest tier of dogfood learning. Must be backed by multiple accepted patterns.",
6
- "type": "object",
7
- "required": [
8
- "schema_version",
9
- "doctrine_id",
10
- "title",
11
- "status",
12
- "doctrine_kind",
13
- "statement",
14
- "rationale",
15
- "based_on_pattern_ids",
16
- "transfer_scope"
17
- ],
18
- "additionalProperties": false,
19
- "properties": {
20
- "schema_version": {
21
- "type": "string",
22
- "pattern": "^\\d+\\.\\d+\\.\\d+$"
23
- },
24
- "doctrine_id": {
25
- "type": "string",
26
- "pattern": "^ddoc-[a-z0-9][a-z0-9-]*$",
27
- "minLength": 6
28
- },
29
- "title": {
30
- "type": "string",
31
- "minLength": 10,
32
- "maxLength": 200
33
- },
34
- "status": {
35
- "type": "string",
36
- "enum": ["candidate", "accepted", "rejected"]
37
- },
38
- "doctrine_kind": {
39
- "type": "string",
40
- "enum": [
41
- "rollout_law",
42
- "evidence_law",
43
- "surface_law",
44
- "verification_law",
45
- "calibration_law"
46
- ]
47
- },
48
- "statement": {
49
- "type": "string",
50
- "minLength": 20,
51
- "maxLength": 500,
52
- "description": "The doctrine rule itself. Must read as a directive, not a suggestion."
53
- },
54
- "rationale": {
55
- "type": "string",
56
- "minLength": 20,
57
- "maxLength": 1000,
58
- "description": "Why this doctrine exists. Must reference recurrence evidence."
59
- },
60
- "based_on_pattern_ids": {
61
- "type": "array",
62
- "minItems": 1,
63
- "items": { "type": "string", "minLength": 1 }
64
- },
65
- "transfer_scope": {
66
- "type": "string",
67
- "enum": ["surface_archetype", "execution_mode", "org_wide"]
68
- },
69
- "strength": {
70
- "type": "string",
71
- "enum": ["emerging", "proven", "foundational"]
72
- },
73
- "review": {
74
- "type": "object",
75
- "additionalProperties": false,
76
- "properties": {
77
- "reviewed_by": { "type": "string" },
78
- "reviewed_at": { "type": "string", "format": "date-time" },
79
- "decision_reason": { "type": "string", "maxLength": 1000 },
80
- "last_action": { "type": "string", "enum": ["accept", "reject", "edit"] }
81
- }
82
- },
83
- "created_at": { "type": "string", "format": "date-time" },
84
- "updated_at": { "type": "string", "format": "date-time" }
85
- }
86
- }
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/dogfood-lab/testing-os/packages/schemas/src/json/dogfood-doctrine.schema.json",
4
+ "title": "Dogfood Doctrine",
5
+ "description": "A hardened portfolio rule earned from repeated patterns. Doctrine represents durable operating law — the strongest tier of dogfood learning. Must be backed by multiple accepted patterns.",
6
+ "type": "object",
7
+ "required": [
8
+ "schema_version",
9
+ "doctrine_id",
10
+ "title",
11
+ "status",
12
+ "doctrine_kind",
13
+ "statement",
14
+ "rationale",
15
+ "based_on_pattern_ids",
16
+ "transfer_scope"
17
+ ],
18
+ "additionalProperties": false,
19
+ "properties": {
20
+ "schema_version": {
21
+ "type": "string",
22
+ "pattern": "^\\d+\\.\\d+\\.\\d+$"
23
+ },
24
+ "doctrine_id": {
25
+ "type": "string",
26
+ "pattern": "^ddoc-[a-z0-9][a-z0-9-]*$",
27
+ "minLength": 6
28
+ },
29
+ "title": {
30
+ "type": "string",
31
+ "minLength": 10,
32
+ "maxLength": 200
33
+ },
34
+ "status": {
35
+ "type": "string",
36
+ "enum": ["candidate", "accepted", "rejected"]
37
+ },
38
+ "doctrine_kind": {
39
+ "type": "string",
40
+ "enum": [
41
+ "rollout_law",
42
+ "evidence_law",
43
+ "surface_law",
44
+ "verification_law",
45
+ "calibration_law"
46
+ ]
47
+ },
48
+ "statement": {
49
+ "type": "string",
50
+ "minLength": 20,
51
+ "maxLength": 500,
52
+ "description": "The doctrine rule itself. Must read as a directive, not a suggestion."
53
+ },
54
+ "rationale": {
55
+ "type": "string",
56
+ "minLength": 20,
57
+ "maxLength": 1000,
58
+ "description": "Why this doctrine exists. Must reference recurrence evidence."
59
+ },
60
+ "based_on_pattern_ids": {
61
+ "type": "array",
62
+ "minItems": 1,
63
+ "items": { "type": "string", "minLength": 1 }
64
+ },
65
+ "transfer_scope": {
66
+ "type": "string",
67
+ "enum": ["surface_archetype", "execution_mode", "org_wide"]
68
+ },
69
+ "strength": {
70
+ "type": "string",
71
+ "enum": ["emerging", "proven", "foundational"]
72
+ },
73
+ "review": {
74
+ "type": "object",
75
+ "additionalProperties": false,
76
+ "properties": {
77
+ "reviewed_by": { "type": "string" },
78
+ "reviewed_at": { "type": "string", "format": "date-time" },
79
+ "decision_reason": { "type": "string", "maxLength": 1000 },
80
+ "last_action": { "type": "string", "enum": ["accept", "reject", "edit"] }
81
+ }
82
+ },
83
+ "created_at": { "type": "string", "format": "date-time" },
84
+ "updated_at": { "type": "string", "format": "date-time" }
85
+ }
86
+ }