@dogfood-lab/schemas 1.3.2 → 1.4.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
@@ -70,7 +70,7 @@ import recordSchema from '@dogfood-lab/schemas/json/dogfood-record.schema.json'
70
70
  | | Constraint |
71
71
  |---|---|
72
72
  | JSON Schema dialect | 2020-12 |
73
- | Node | ≥ 20 |
73
+ | Node | ≥ 22 |
74
74
  | `$id` form | `https://github.com/dogfood-lab/testing-os/packages/schemas/src/json/<name>.schema.json` |
75
75
 
76
76
  `$id` is a contract field. Changes that consumers should treat as contract changes bump the monorepo's lockstep version.
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Contract enums — the single source of truth for the three FULL-SET enums
3
+ * that the contract JSON schemas declare identically across multiple files.
4
+ *
5
+ * F1-CONTRACTS-002 (Wave 4, MED). Three full-set enums are hand-maintained as
6
+ * identical literal arrays across the contract schema JSON documents:
7
+ *
8
+ * - product_surface ["cli","desktop","web","api","mcp-server",
9
+ * "npm-package","plugin","library"] — 7 files
10
+ * - execution_mode ["bot","human","mixed"] — 7 files
11
+ * - evidence-kind ["log","screenshot","recording",
12
+ * "transcript","artifact","other"] — 4 files
13
+ *
14
+ * `stageC-cross-contract-enum-seal.test.ts` DETECTS drift between those JSON
15
+ * copies, but nothing EXPORTS the sets as importable values. A consumer that
16
+ * wants to render the surface picker, validate a CLI `--surface` flag, or
17
+ * iterate the evidence kinds had to hand-copy the array — a fresh drift source
18
+ * the seal cannot see.
19
+ *
20
+ * These constants are the value consumers import (NOT a hand-copied literal),
21
+ * mirroring `SUPPORTED_SCHEMA_VERSIONS` in `src/schema-versions.ts`:
22
+ * - downstream packages import the set instead of re-typing it;
23
+ * - the seal test asserts each JSON-schema enum EQUALS the matching constant,
24
+ * converting drift-detect into single-source ENFORCEMENT — a JSON enum that
25
+ * diverges from the constant here goes RED.
26
+ *
27
+ * SCOPE — FULL-SET enums only. `doctrine.transfer_scope` is a DELIBERATE
28
+ * 3-value subset (["surface_archetype","execution_mode","org_wide"]) of the
29
+ * 5-value `transfer_scope` carried by finding/recommendation/pattern, and is
30
+ * intentionally NOT exported here as a sealed full-set enum — the seal must
31
+ * leave it alone.
32
+ *
33
+ * `as const` keeps the literal element types and freezes member order, so the
34
+ * arrays double as `readonly` tuples for `typeof PRODUCT_SURFACES[number]`
35
+ * union derivation downstream.
36
+ */
37
+ /**
38
+ * The full set of product surfaces a tested artifact can target. Order matches
39
+ * the contract JSON enums byte-for-byte (the seal asserts sorted-set identity,
40
+ * but consumers that render a picker get a stable, intentional order).
41
+ */
42
+ export declare const PRODUCT_SURFACES: readonly ["cli", "desktop", "web", "api", "mcp-server", "npm-package", "plugin", "library"];
43
+ /** How the work under test was executed. */
44
+ export declare const EXECUTION_MODES: readonly ["bot", "human", "mixed"];
45
+ /** The kinds of evidence a record may carry. */
46
+ export declare const EVIDENCE_KINDS: readonly ["log", "screenshot", "recording", "transcript", "artifact", "other"];
47
+ /** Union of the eight product surfaces. */
48
+ export type ProductSurface = (typeof PRODUCT_SURFACES)[number];
49
+ /** Union of the three execution modes. */
50
+ export type ExecutionMode = (typeof EXECUTION_MODES)[number];
51
+ /** Union of the six evidence kinds. */
52
+ export type EvidenceKind = (typeof EVIDENCE_KINDS)[number];
53
+ //# sourceMappingURL=enums.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enums.d.ts","sourceRoot":"","sources":["../src/enums.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,6FASnB,CAAC;AAEX,4CAA4C;AAC5C,eAAO,MAAM,eAAe,oCAAqC,CAAC;AAElE,gDAAgD;AAChD,eAAO,MAAM,cAAc,gFAOjB,CAAC;AAEX,2CAA2C;AAC3C,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAC/D,0CAA0C;AAC1C,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAC7D,uCAAuC;AACvC,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC"}
package/dist/enums.js ADDED
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Contract enums — the single source of truth for the three FULL-SET enums
3
+ * that the contract JSON schemas declare identically across multiple files.
4
+ *
5
+ * F1-CONTRACTS-002 (Wave 4, MED). Three full-set enums are hand-maintained as
6
+ * identical literal arrays across the contract schema JSON documents:
7
+ *
8
+ * - product_surface ["cli","desktop","web","api","mcp-server",
9
+ * "npm-package","plugin","library"] — 7 files
10
+ * - execution_mode ["bot","human","mixed"] — 7 files
11
+ * - evidence-kind ["log","screenshot","recording",
12
+ * "transcript","artifact","other"] — 4 files
13
+ *
14
+ * `stageC-cross-contract-enum-seal.test.ts` DETECTS drift between those JSON
15
+ * copies, but nothing EXPORTS the sets as importable values. A consumer that
16
+ * wants to render the surface picker, validate a CLI `--surface` flag, or
17
+ * iterate the evidence kinds had to hand-copy the array — a fresh drift source
18
+ * the seal cannot see.
19
+ *
20
+ * These constants are the value consumers import (NOT a hand-copied literal),
21
+ * mirroring `SUPPORTED_SCHEMA_VERSIONS` in `src/schema-versions.ts`:
22
+ * - downstream packages import the set instead of re-typing it;
23
+ * - the seal test asserts each JSON-schema enum EQUALS the matching constant,
24
+ * converting drift-detect into single-source ENFORCEMENT — a JSON enum that
25
+ * diverges from the constant here goes RED.
26
+ *
27
+ * SCOPE — FULL-SET enums only. `doctrine.transfer_scope` is a DELIBERATE
28
+ * 3-value subset (["surface_archetype","execution_mode","org_wide"]) of the
29
+ * 5-value `transfer_scope` carried by finding/recommendation/pattern, and is
30
+ * intentionally NOT exported here as a sealed full-set enum — the seal must
31
+ * leave it alone.
32
+ *
33
+ * `as const` keeps the literal element types and freezes member order, so the
34
+ * arrays double as `readonly` tuples for `typeof PRODUCT_SURFACES[number]`
35
+ * union derivation downstream.
36
+ */
37
+ /**
38
+ * The full set of product surfaces a tested artifact can target. Order matches
39
+ * the contract JSON enums byte-for-byte (the seal asserts sorted-set identity,
40
+ * but consumers that render a picker get a stable, intentional order).
41
+ */
42
+ export const PRODUCT_SURFACES = [
43
+ 'cli',
44
+ 'desktop',
45
+ 'web',
46
+ 'api',
47
+ 'mcp-server',
48
+ 'npm-package',
49
+ 'plugin',
50
+ 'library',
51
+ ];
52
+ /** How the work under test was executed. */
53
+ export const EXECUTION_MODES = ['bot', 'human', 'mixed'];
54
+ /** The kinds of evidence a record may carry. */
55
+ export const EVIDENCE_KINDS = [
56
+ 'log',
57
+ 'screenshot',
58
+ 'recording',
59
+ 'transcript',
60
+ 'artifact',
61
+ 'other',
62
+ ];
63
+ //# sourceMappingURL=enums.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enums.js","sourceRoot":"","sources":["../src/enums.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,KAAK;IACL,SAAS;IACT,KAAK;IACL,KAAK;IACL,YAAY;IACZ,aAAa;IACb,QAAQ;IACR,SAAS;CACD,CAAC;AAEX,4CAA4C;AAC5C,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAU,CAAC;AAElE,gDAAgD;AAChD,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,KAAK;IACL,YAAY;IACZ,WAAW;IACX,YAAY;IACZ,UAAU;IACV,OAAO;CACC,CAAC"}
package/dist/index.d.ts CHANGED
@@ -28,5 +28,19 @@ export declare const allSchemas: {
28
28
  readonly scenario: JsonSchema;
29
29
  };
30
30
  export type SchemaName = keyof typeof allSchemas;
31
+ /**
32
+ * Test-only seam for d1-schemas-B002. Exposes the module-private {@link load}
33
+ * so the parse-error-context guard can be exercised against a corrupt fixture
34
+ * without touching the real bundled `src/json` tree. Mirrors the existing
35
+ * `_resetValidatorCacheForTests` / `_schemasModuleInstanceCount` test exports —
36
+ * underscore-prefixed, not part of the public API.
37
+ *
38
+ * @param filename schema basename, e.g. `dogfood-record.schema.json`
39
+ * @param dirOverride optional directory to read from instead of the bundled
40
+ * `jsonDir` (tests point this at a TEST_ROOT temp dir)
41
+ */
42
+ export declare function _loadSchemaForTests(filename: string, dirOverride?: string): JsonSchema;
31
43
  export { compileSchema, validatePayload, createAjv, _resetValidatorCacheForTests, _schemasModuleInstanceCount, type ValidationError, type ValidationResult, } from './validate.js';
44
+ export { SUPPORTED_SCHEMA_VERSIONS, type SupportedSchemaVersion, type SupportedSchemaContract, } from './schema-versions.js';
45
+ export { PRODUCT_SURFACES, EXECUTION_MODES, EVIDENCE_KINDS, type ProductSurface, type ExecutionMode, type EvidenceKind, } from './enums.js';
32
46
  //# 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,2BAA2B,EAC3B,KAAK,eAAe,EACpB,KAAK,gBAAgB,GACtB,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA6DA,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;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,UAAU,CAEtF;AAED,OAAO,EACL,aAAa,EACb,eAAe,EACf,SAAS,EACT,4BAA4B,EAC5B,2BAA2B,EAC3B,KAAK,eAAe,EACpB,KAAK,gBAAgB,GACtB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,yBAAyB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,GAC7B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,YAAY,GAClB,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -18,9 +18,37 @@ import { dirname, resolve } from 'node:path';
18
18
  import { fileURLToPath } from 'node:url';
19
19
  const here = dirname(fileURLToPath(import.meta.url));
20
20
  const jsonDir = resolve(here, '../src/json');
21
- function load(filename) {
22
- const raw = readFileSync(resolve(jsonDir, filename), 'utf8');
23
- return JSON.parse(raw);
21
+ /**
22
+ * Load and parse a bundled JSON schema.
23
+ *
24
+ * d1-schemas-B002 — readFileSync + JSON.parse are wrapped so a corrupt or
25
+ * truncated package-bundled `src/json/*.schema.json` (a packaging defect that
26
+ * can ship in a tarball) throws a SELF-IDENTIFYING error at module-init instead
27
+ * of a bare `SyntaxError: Unexpected end of JSON input` that names no file. The
28
+ * rethrown message names the package and the offending filename so the consumer
29
+ * knows exactly which of the eight schemas to inspect/re-install — they do not
30
+ * have to bisect by hand. This is the only structured channel available at
31
+ * module-init (no CLI renderer / no logStage pipeline exists this early), so the
32
+ * helpful contract is "a thrown Error whose message carries the recovery handle."
33
+ *
34
+ * `dirOverride` exists only for the test seam (`_loadSchemaForTests`); production
35
+ * callers always read from the real bundled `jsonDir`.
36
+ */
37
+ function load(filename, dirOverride) {
38
+ const path = resolve(dirOverride ?? jsonDir, filename);
39
+ let raw;
40
+ try {
41
+ raw = readFileSync(path, 'utf8');
42
+ }
43
+ catch (e) {
44
+ throw new Error(`@dogfood-lab/schemas: failed to read bundled schema ${filename} (at ${path}): ${e.message}`);
45
+ }
46
+ try {
47
+ return JSON.parse(raw);
48
+ }
49
+ catch (e) {
50
+ throw new Error(`@dogfood-lab/schemas: failed to parse bundled schema ${filename} (at ${path}) as JSON — the package artifact may be corrupt or truncated: ${e.message}`);
51
+ }
24
52
  }
25
53
  export const recordSchema = load('dogfood-record.schema.json');
26
54
  export const recordSubmissionSchema = load('dogfood-record-submission.schema.json');
@@ -40,5 +68,21 @@ export const allSchemas = {
40
68
  policy: policySchema,
41
69
  scenario: scenarioSchema,
42
70
  };
71
+ /**
72
+ * Test-only seam for d1-schemas-B002. Exposes the module-private {@link load}
73
+ * so the parse-error-context guard can be exercised against a corrupt fixture
74
+ * without touching the real bundled `src/json` tree. Mirrors the existing
75
+ * `_resetValidatorCacheForTests` / `_schemasModuleInstanceCount` test exports —
76
+ * underscore-prefixed, not part of the public API.
77
+ *
78
+ * @param filename schema basename, e.g. `dogfood-record.schema.json`
79
+ * @param dirOverride optional directory to read from instead of the bundled
80
+ * `jsonDir` (tests point this at a TEST_ROOT temp dir)
81
+ */
82
+ export function _loadSchemaForTests(filename, dirOverride) {
83
+ return load(filename, dirOverride);
84
+ }
43
85
  export { compileSchema, validatePayload, createAjv, _resetValidatorCacheForTests, _schemasModuleInstanceCount, } from './validate.js';
86
+ export { SUPPORTED_SCHEMA_VERSIONS, } from './schema-versions.js';
87
+ export { PRODUCT_SURFACES, EXECUTION_MODES, EVIDENCE_KINDS, } from './enums.js';
44
88
  //# 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,EAC5B,2BAA2B,GAG5B,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;;;;;;;;;;;;;;;GAeG;AACH,SAAS,IAAI,CAAC,QAAgB,EAAE,WAAoB;IAClD,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,IAAI,OAAO,EAAE,QAAQ,CAAC,CAAC;IACvD,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACb,uDAAuD,QAAQ,QAAQ,IAAI,MACxE,CAAW,CAAC,OACf,EAAE,CACH,CAAC;IACJ,CAAC;IACD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAe,CAAC;IACvC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACb,wDAAwD,QAAQ,QAAQ,IAAI,iEACzE,CAAW,CAAC,OACf,EAAE,CACH,CAAC;IACJ,CAAC;AACH,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;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAgB,EAAE,WAAoB;IACxE,OAAO,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;AACrC,CAAC;AAED,OAAO,EACL,aAAa,EACb,eAAe,EACf,SAAS,EACT,4BAA4B,EAC5B,2BAA2B,GAG5B,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,yBAAyB,GAG1B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,cAAc,GAIf,MAAM,YAAY,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * SUPPORTED_SCHEMA_VERSIONS — the single source of truth for which
3
+ * `schema_version` values this build of testing-os accepts.
4
+ *
5
+ * F1-CONTRACTS-001 (Wave 2, HIGH). Every contract schema declares
6
+ * `schema_version` as a pattern-only string (`^\d+\.\d+\.\d+$`) — it gates
7
+ * SHAPE, not VALUE. Nothing compared a declared `schema_version` against a
8
+ * supported set, so a submission declaring `schema_version: '2.0.0'` (or
9
+ * `99.0.0`) validated clean against the live 1.x schema whenever its shape
10
+ * happened to fit: a genuinely-incompatible future major was silently
11
+ * mis-validated instead of cleanly refused.
12
+ *
13
+ * This map is the value consumers import (NOT a hand-copied literal):
14
+ * - `packages/verify` imports it to drive the `schema_version` VALUE gate
15
+ * (`validators/schema-version.js`) and to stamp the persisted record's
16
+ * `schema_version` (`record.current`) instead of a hardcoded `'1.0.0'`.
17
+ *
18
+ * Per registered contract: the `current` version plus the accepted MAJOR
19
+ * range `[minMajor, maxMajor]`. The gate compares a payload's MAJOR against
20
+ * that range — patch/minor deltas inside the range PASS; a major above
21
+ * `maxMajor` means "this build is too old, upgrade testing-os"; a major below
22
+ * `minMajor` means "re-emit against the current contract".
23
+ *
24
+ * Keys mirror `allSchemas` for the contracts that carry a `schema_version`
25
+ * field, plus the `agentOutput` swarm envelope. `policy` and `scenario` do
26
+ * NOT carry a `schema_version` contract field and are intentionally absent —
27
+ * the gate only fires on payloads that declare a `schema_version`.
28
+ */
29
+ export interface SupportedSchemaVersion {
30
+ /** The current/canonical version this build emits for the contract. */
31
+ current: string;
32
+ /** Lowest MAJOR this build will accept (inclusive). Below → TOO_OLD. */
33
+ minMajor: number;
34
+ /** Highest MAJOR this build will accept (inclusive). Above → TOO_NEW. */
35
+ maxMajor: number;
36
+ }
37
+ /**
38
+ * The registered contract keys that carry a `schema_version`. `record` and
39
+ * `recordSubmission` share the v1 record family; `agentOutput` is the swarm
40
+ * agent-output envelope (validated separately at collect time, registered
41
+ * here so a future versioned envelope has one home).
42
+ */
43
+ export type SupportedSchemaContract = 'record' | 'recordSubmission' | 'finding' | 'pattern' | 'recommendation' | 'doctrine' | 'agentOutput';
44
+ export declare const SUPPORTED_SCHEMA_VERSIONS: Record<SupportedSchemaContract, SupportedSchemaVersion>;
45
+ //# sourceMappingURL=schema-versions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-versions.d.ts","sourceRoot":"","sources":["../src/schema-versions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,MAAM,WAAW,sBAAsB;IACrC,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,MAAM,uBAAuB,GAC/B,QAAQ,GACR,kBAAkB,GAClB,SAAS,GACT,SAAS,GACT,gBAAgB,GAChB,UAAU,GACV,aAAa,CAAC;AAElB,eAAO,MAAM,yBAAyB,EAAE,MAAM,CAC5C,uBAAuB,EACvB,sBAAsB,CASd,CAAC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * SUPPORTED_SCHEMA_VERSIONS — the single source of truth for which
3
+ * `schema_version` values this build of testing-os accepts.
4
+ *
5
+ * F1-CONTRACTS-001 (Wave 2, HIGH). Every contract schema declares
6
+ * `schema_version` as a pattern-only string (`^\d+\.\d+\.\d+$`) — it gates
7
+ * SHAPE, not VALUE. Nothing compared a declared `schema_version` against a
8
+ * supported set, so a submission declaring `schema_version: '2.0.0'` (or
9
+ * `99.0.0`) validated clean against the live 1.x schema whenever its shape
10
+ * happened to fit: a genuinely-incompatible future major was silently
11
+ * mis-validated instead of cleanly refused.
12
+ *
13
+ * This map is the value consumers import (NOT a hand-copied literal):
14
+ * - `packages/verify` imports it to drive the `schema_version` VALUE gate
15
+ * (`validators/schema-version.js`) and to stamp the persisted record's
16
+ * `schema_version` (`record.current`) instead of a hardcoded `'1.0.0'`.
17
+ *
18
+ * Per registered contract: the `current` version plus the accepted MAJOR
19
+ * range `[minMajor, maxMajor]`. The gate compares a payload's MAJOR against
20
+ * that range — patch/minor deltas inside the range PASS; a major above
21
+ * `maxMajor` means "this build is too old, upgrade testing-os"; a major below
22
+ * `minMajor` means "re-emit against the current contract".
23
+ *
24
+ * Keys mirror `allSchemas` for the contracts that carry a `schema_version`
25
+ * field, plus the `agentOutput` swarm envelope. `policy` and `scenario` do
26
+ * NOT carry a `schema_version` contract field and are intentionally absent —
27
+ * the gate only fires on payloads that declare a `schema_version`.
28
+ */
29
+ export const SUPPORTED_SCHEMA_VERSIONS = {
30
+ record: { current: '1.0.0', minMajor: 1, maxMajor: 1 },
31
+ recordSubmission: { current: '1.0.0', minMajor: 1, maxMajor: 1 },
32
+ finding: { current: '1.0.0', minMajor: 1, maxMajor: 1 },
33
+ pattern: { current: '1.0.0', minMajor: 1, maxMajor: 1 },
34
+ recommendation: { current: '1.0.0', minMajor: 1, maxMajor: 1 },
35
+ doctrine: { current: '1.0.0', minMajor: 1, maxMajor: 1 },
36
+ agentOutput: { current: '1.0.0', minMajor: 1, maxMajor: 1 },
37
+ };
38
+ //# sourceMappingURL=schema-versions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-versions.js","sourceRoot":"","sources":["../src/schema-versions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AA0BH,MAAM,CAAC,MAAM,yBAAyB,GAGlC;IACF,MAAM,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE;IACtD,gBAAgB,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE;IAChE,OAAO,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE;IACvD,OAAO,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE;IACvD,cAAc,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE;IAC9D,QAAQ,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE;IACxD,WAAW,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE;CACnD,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dogfood-lab/schemas",
3
- "version": "1.3.2",
3
+ "version": "1.4.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",
@@ -33,6 +33,7 @@
33
33
  "ajv-formats": "^3.0.1"
34
34
  },
35
35
  "devDependencies": {
36
+ "@dogfood-lab/dogfood-swarm": "*",
36
37
  "@types/node": "^25.3.5",
37
38
  "vitest": "^4.1.6"
38
39
  },
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://github.com/dogfood-lab/testing-os/packages/schemas/src/json/agent-output.schema.json",
4
4
  "title": "Swarm Agent Output (canonical envelope)",
5
- "description": "JSON contract for agent JSON outputs written to swarms/<run-id>/wave-N/<domain>.json. F-252713-017 (Phase 7 wave 1): collect-time schema-conformance gate. Replaces silent normalization in collect.js with a structured error pointing the agent at the canonical shape. Validates the OUTER envelope — every agent output regardless of phase has { domain, summary } at minimum. Phase-specific oneOf branches govern the inner shape: audit outputs carry findings[], feature outputs carry features[], amend outputs carry fixes[] + files_changed[]. Mirrors AUDIT_CATEGORIES + FEATURE_CATEGORIES + SEVERITY_ENUM in packages/dogfood-swarm/lib/output-schema.js — kept in lockstep.",
5
+ "description": "JSON contract for agent JSON outputs written to swarms/<run-id>/wave-N/<domain>.json. F-252713-017 (Phase 7 wave 1): collect-time schema-conformance gate. Replaces silent normalization in collect.js with a structured error pointing the agent at the canonical shape. Validates the OUTER envelope ONLY — every agent output regardless of phase has { domain, summary } at minimum; findings[]/features[]/fixes[]/files_changed[] are all independently optional here (additionalProperties:true). This schema does NOT enforce phase-specific inner shape — there is no oneOf/if-then/dependentRequired branch keyed on phase. Phase-specific inner-shape enforcement (audit outputs require findings[], feature outputs require features[], amend outputs require fixes[] + files_changed[]) is performed separately by packages/dogfood-swarm/lib/output-schema.js (validateAuditOutput/validateFeatureOutput/validateAmendOutput), run by commands/collect.js AFTER this envelope gate. The category/severity/feature-category enums in the $defs below mirror AUDIT_CATEGORIES + FEATURE_CATEGORIES + SEVERITY_ENUM in output-schema.js — kept in lockstep (sealed by packages/schemas/test/stageA-agent-output-lockstep.test.ts).",
6
6
  "type": "object",
7
7
  "required": ["domain", "summary"],
8
8
  "additionalProperties": true,
@@ -103,6 +103,59 @@
103
103
  }
104
104
  },
105
105
 
106
+ "allOf": [
107
+ {
108
+ "$comment": "d1-schemas-003 — additive conditional constraint mirroring the H4 precedent in dogfood-finding.schema.json. enforcement.reason and enforcement.review_after have always carried 'Required if ...' descriptions, but until stage A this was honor-system prose the schema dropped silently — a repo could mark itself `exempt` (opting out of Gate F per policy.schema.json:30) with reason:null/review_after:null and validate clean. The schema now enforces it: a warn-only/exempt mode requires a non-null `reason`; an exempt mode additionally requires a `review_after` date. Note the `not const null` guard — the property type is [string,null], and a literal null would satisfy bare `required`, so the value must be constrained to a real justification.",
109
+ "if": {
110
+ "properties": {
111
+ "enforcement": {
112
+ "type": "object",
113
+ "properties": {
114
+ "mode": { "enum": ["warn-only", "exempt"] }
115
+ },
116
+ "required": ["mode"]
117
+ }
118
+ },
119
+ "required": ["enforcement"]
120
+ },
121
+ "then": {
122
+ "properties": {
123
+ "enforcement": {
124
+ "required": ["reason"],
125
+ "properties": {
126
+ "reason": { "type": "string", "minLength": 1 }
127
+ }
128
+ }
129
+ }
130
+ }
131
+ },
132
+ {
133
+ "$comment": "d1-schemas-003 (exempt half) — an exempt repo is fully opted out of Gate F, so it additionally requires a scheduled review date. Same null-guard reasoning as the reason half.",
134
+ "if": {
135
+ "properties": {
136
+ "enforcement": {
137
+ "type": "object",
138
+ "properties": {
139
+ "mode": { "const": "exempt" }
140
+ },
141
+ "required": ["mode"]
142
+ }
143
+ },
144
+ "required": ["enforcement"]
145
+ },
146
+ "then": {
147
+ "properties": {
148
+ "enforcement": {
149
+ "required": ["review_after"],
150
+ "properties": {
151
+ "review_after": { "type": "string", "minLength": 1 }
152
+ }
153
+ }
154
+ }
155
+ }
156
+ }
157
+ ],
158
+
106
159
  "$defs": {
107
160
  "surface_policy": {
108
161
  "type": "object",
@@ -84,6 +84,17 @@
84
84
  "type": "string",
85
85
  "description": "What a passing result looks like. Required if verifiable is true."
86
86
  }
87
+ },
88
+ "$comment": "d1-schemas-NEW — third instance of the d1-schemas-003 family. The `expected` description has always said 'Required if verifiable is true', but until stage A nothing enforced it, so a step could claim verifiable:true with no pass condition and validate clean. A keyed if/then (NOT a dependentRequired, which fires on key presence and would wrongly trip on verifiable:false) requires `expected` only when verifiable === true. Mirrors the H4 precedent in dogfood-finding.schema.json.",
89
+ "if": {
90
+ "properties": { "verifiable": { "const": true } },
91
+ "required": ["verifiable"]
92
+ },
93
+ "then": {
94
+ "required": ["expected"],
95
+ "properties": {
96
+ "expected": { "type": "string", "minLength": 1 }
97
+ }
87
98
  }
88
99
  }
89
100
  },