@dogfood-lab/schemas 1.6.0 → 1.8.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dogfood-lab/schemas",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.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",
|
|
@@ -100,7 +100,8 @@
|
|
|
100
100
|
|
|
101
101
|
"ci_checks": {
|
|
102
102
|
"type": "array",
|
|
103
|
-
"
|
|
103
|
+
"maxItems": 200,
|
|
104
|
+
"description": "Machine-evaluated CI results. Optional — not all dogfood runs include CI. Capped at 200 (resource guard; a real run reports a handful of checks).",
|
|
104
105
|
"items": {
|
|
105
106
|
"type": "object",
|
|
106
107
|
"required": ["id", "kind", "status"],
|
|
@@ -130,7 +131,8 @@
|
|
|
130
131
|
"scenario_results": {
|
|
131
132
|
"type": "array",
|
|
132
133
|
"minItems": 1,
|
|
133
|
-
"
|
|
134
|
+
"maxItems": 1000,
|
|
135
|
+
"description": "Dogfood evidence. At least one scenario required. Capped at 1000 (resource guard for the policy engine, which evaluates per-scenario rules synchronously; a real run reports far fewer).",
|
|
134
136
|
"items": {
|
|
135
137
|
"type": "object",
|
|
136
138
|
"required": ["scenario_id", "product_surface", "execution_mode", "verdict", "step_results"],
|
|
@@ -167,13 +169,15 @@
|
|
|
167
169
|
},
|
|
168
170
|
"tags": {
|
|
169
171
|
"type": "array",
|
|
172
|
+
"maxItems": 100,
|
|
170
173
|
"items": { "type": "string" },
|
|
171
|
-
"description": "Free-form scenario tags (e.g. 'smoke', 'release', 'wip', 'flaky', 'skip-ci'). Optional. Policy may gate on these via a surface's evidence_requirements.forbidden_tags / required_tags."
|
|
174
|
+
"description": "Free-form scenario tags (e.g. 'smoke', 'release', 'wip', 'flaky', 'skip-ci'). Optional. Capped at 100 (resource guard). Policy may gate on these via a surface's evidence_requirements.forbidden_tags / required_tags."
|
|
172
175
|
},
|
|
173
176
|
"step_results": {
|
|
174
177
|
"type": "array",
|
|
175
178
|
"minItems": 1,
|
|
176
|
-
"
|
|
179
|
+
"maxItems": 500,
|
|
180
|
+
"description": "Per-step execution results. Bridges scenario steps to record evidence. Capped at 500 (resource guard).",
|
|
177
181
|
"items": {
|
|
178
182
|
"type": "object",
|
|
179
183
|
"required": ["step_id", "status"],
|
|
@@ -194,6 +198,7 @@
|
|
|
194
198
|
},
|
|
195
199
|
"evidence": {
|
|
196
200
|
"type": "array",
|
|
201
|
+
"maxItems": 100,
|
|
197
202
|
"items": {
|
|
198
203
|
"type": "object",
|
|
199
204
|
"required": ["kind", "url"],
|
|
@@ -54,7 +54,8 @@
|
|
|
54
54
|
|
|
55
55
|
"defaults": {
|
|
56
56
|
"$ref": "#/$defs/surface_policy",
|
|
57
|
-
"description": "Default surface policy. Used when a surface has no explicit policy."
|
|
57
|
+
"description": "Default surface policy. Used when a surface has no explicit policy. VERIFY-F1: `custom_rules` is NOT permitted here — it is a repo-surface concern only. Global, non-overridable declarative rules belong in `global_rules` (which fail operationally, not submission-bad). Allowing custom_rules under defaults would make a global-origin predicate fault classify as submission-bad.",
|
|
58
|
+
"properties": { "custom_rules": false }
|
|
58
59
|
},
|
|
59
60
|
|
|
60
61
|
"global_rules": {
|
|
@@ -74,6 +75,19 @@
|
|
|
74
75
|
"type": "string",
|
|
75
76
|
"enum": ["reject", "warn", "info"],
|
|
76
77
|
"description": "reject: fails verification. warn: accepted with warning. info: logged only."
|
|
78
|
+
},
|
|
79
|
+
"scope": {
|
|
80
|
+
"type": "string",
|
|
81
|
+
"enum": ["submission", "scenario_result"],
|
|
82
|
+
"description": "VERIFY-F1: evaluation scope of the `when` predicate. submission (default) evaluates against the whole submission and emits one reason if matched. scenario_result evaluates each scenario_result independently and emits one reason per offending element, in array order. Ignored when `when` is absent (the rule falls to the built-in id switch). See docs/policy-dsl.md."
|
|
83
|
+
},
|
|
84
|
+
"when": {
|
|
85
|
+
"$ref": "#/$defs/predicate",
|
|
86
|
+
"description": "VERIFY-F1: optional declarative VIOLATION predicate. The rule fires (per its severity) when this evaluates true. Absent => the rule is enforced by the built-in id switch instead. See docs/policy-dsl.md."
|
|
87
|
+
},
|
|
88
|
+
"reason_template": {
|
|
89
|
+
"type": "string",
|
|
90
|
+
"description": "VERIFY-F1: optional per-rule reason body. {slot} placeholders interpolate fields of the matched element (scenario_result scope) or submission (submission scope), raw and unescaped. The engine prepends `[<id>] `. Absent => the body is the rule's `description`."
|
|
77
91
|
}
|
|
78
92
|
}
|
|
79
93
|
}
|
|
@@ -238,6 +252,138 @@
|
|
|
238
252
|
"description": "Scenario tags that EVERY scenario_result on this surface must carry. A scenario_result missing any of these (or carrying no tags at all) fails policy. Enforced per scenario_result independently."
|
|
239
253
|
}
|
|
240
254
|
}
|
|
255
|
+
},
|
|
256
|
+
"custom_rules": {
|
|
257
|
+
"type": "array",
|
|
258
|
+
"description": "VERIFY-F1: declarative per-scenario_result rules for this surface. Each rule's `when` predicate is evaluated against every scenario_result whose product_surface matches; a match fires the rule per its severity. Custom rules can only ADD constraints (reject/warn/info) — there is no accept/except verb, so a repo policy can never weaken a non-overridable global gate. See docs/policy-dsl.md.",
|
|
259
|
+
"items": {
|
|
260
|
+
"type": "object",
|
|
261
|
+
"required": ["id", "severity", "when"],
|
|
262
|
+
"additionalProperties": false,
|
|
263
|
+
"properties": {
|
|
264
|
+
"id": {
|
|
265
|
+
"type": "string",
|
|
266
|
+
"description": "Stable rule ID for reporting and diagnostics."
|
|
267
|
+
},
|
|
268
|
+
"description": { "type": "string" },
|
|
269
|
+
"severity": {
|
|
270
|
+
"type": "string",
|
|
271
|
+
"enum": ["reject", "warn", "info"],
|
|
272
|
+
"description": "reject: fails verification. warn: accepted with warning. info: logged only. There is intentionally no 'accept' value — custom rules are additive-only (non-weakening)."
|
|
273
|
+
},
|
|
274
|
+
"when": {
|
|
275
|
+
"$ref": "#/$defs/predicate",
|
|
276
|
+
"description": "The declarative VIOLATION predicate, evaluated per scenario_result on this surface. The rule fires when this evaluates true."
|
|
277
|
+
},
|
|
278
|
+
"reason_template": {
|
|
279
|
+
"type": "string",
|
|
280
|
+
"description": "Optional per-rule reason body. {slot} placeholders interpolate the matched scenario_result's fields, raw and unescaped. The engine prepends `[<id>] `. Absent => the body is the rule's `description`."
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
|
|
288
|
+
"predicate": {
|
|
289
|
+
"description": "VERIFY-F1: a bounded, no-eval predicate node. Exactly one form per node: a leaf {field, op, value?} or a combinator (all / any / not / implies). Combinator depth is additionally capped at 5 by the evaluator (the DoS floor; not expressible in JSON Schema). See docs/policy-dsl.md.",
|
|
290
|
+
"oneOf": [
|
|
291
|
+
{ "$ref": "#/$defs/predicate_leaf" },
|
|
292
|
+
{ "$ref": "#/$defs/predicate_all" },
|
|
293
|
+
{ "$ref": "#/$defs/predicate_any" },
|
|
294
|
+
{ "$ref": "#/$defs/predicate_not" },
|
|
295
|
+
{ "$ref": "#/$defs/predicate_implies" }
|
|
296
|
+
]
|
|
297
|
+
},
|
|
298
|
+
|
|
299
|
+
"predicate_leaf": {
|
|
300
|
+
"type": "object",
|
|
301
|
+
"additionalProperties": false,
|
|
302
|
+
"required": ["field", "op"],
|
|
303
|
+
"properties": {
|
|
304
|
+
"field": {
|
|
305
|
+
"type": "string",
|
|
306
|
+
"description": "Dotted field path into the evaluated data; a '[]' segment means 'any element'. The segments __proto__, constructor, and prototype are forbidden (prototype-pollution guard).",
|
|
307
|
+
"pattern": "^(?!.*(?:^|\\.)(?:__proto__|constructor|prototype)(?:$|\\.|\\[))[A-Za-z_][A-Za-z0-9_]*(?:\\[\\])?(?:\\.[A-Za-z_][A-Za-z0-9_]*(?:\\[\\])?)*$"
|
|
308
|
+
},
|
|
309
|
+
"op": {
|
|
310
|
+
"type": "string",
|
|
311
|
+
"enum": ["equals", "not_equals", "in", "not_in", "contains", "not_contains", "exists", "not_exists", "gt", "gte", "lt", "lte"],
|
|
312
|
+
"description": "Closed operator set. exists/not_exists are truthiness (not strict key-presence) and take no value."
|
|
313
|
+
},
|
|
314
|
+
"value": {
|
|
315
|
+
"description": "Comparand. Omitted for exists/not_exists. An array for in/not_in. A scalar otherwise. Numeric for gt/gte/lt/lte (a non-numeric resolved field is an eval-time policy-config: fault)."
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
"allOf": [
|
|
319
|
+
{
|
|
320
|
+
"if": { "required": ["op"], "properties": { "op": { "enum": ["exists", "not_exists"] } } },
|
|
321
|
+
"then": { "not": { "required": ["value"] } }
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
"if": { "required": ["op"], "properties": { "op": { "enum": ["equals", "not_equals", "in", "not_in", "contains", "not_contains", "gt", "gte", "lt", "lte"] } } },
|
|
325
|
+
"then": { "required": ["value"] }
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
"if": { "required": ["op"], "properties": { "op": { "enum": ["in", "not_in"] } } },
|
|
329
|
+
"then": { "properties": { "value": { "type": "array" } } }
|
|
330
|
+
}
|
|
331
|
+
]
|
|
332
|
+
},
|
|
333
|
+
|
|
334
|
+
"predicate_all": {
|
|
335
|
+
"type": "object",
|
|
336
|
+
"additionalProperties": false,
|
|
337
|
+
"required": ["all"],
|
|
338
|
+
"properties": {
|
|
339
|
+
"all": {
|
|
340
|
+
"type": "array",
|
|
341
|
+
"minItems": 1,
|
|
342
|
+
"maxItems": 64,
|
|
343
|
+
"description": "Logical AND. True when every child predicate is true. Width capped at 64 (DoS guard; the evaluator additionally enforces a node-visit budget).",
|
|
344
|
+
"items": { "$ref": "#/$defs/predicate" }
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
|
|
349
|
+
"predicate_any": {
|
|
350
|
+
"type": "object",
|
|
351
|
+
"additionalProperties": false,
|
|
352
|
+
"required": ["any"],
|
|
353
|
+
"properties": {
|
|
354
|
+
"any": {
|
|
355
|
+
"type": "array",
|
|
356
|
+
"minItems": 1,
|
|
357
|
+
"maxItems": 64,
|
|
358
|
+
"description": "Logical OR. True when at least one child predicate is true. Width capped at 64 (DoS guard; the evaluator additionally enforces a node-visit budget).",
|
|
359
|
+
"items": { "$ref": "#/$defs/predicate" }
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
},
|
|
363
|
+
|
|
364
|
+
"predicate_not": {
|
|
365
|
+
"type": "object",
|
|
366
|
+
"additionalProperties": false,
|
|
367
|
+
"required": ["not"],
|
|
368
|
+
"properties": {
|
|
369
|
+
"not": {
|
|
370
|
+
"description": "Logical NOT.",
|
|
371
|
+
"$ref": "#/$defs/predicate"
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
},
|
|
375
|
+
|
|
376
|
+
"predicate_implies": {
|
|
377
|
+
"type": "object",
|
|
378
|
+
"additionalProperties": false,
|
|
379
|
+
"required": ["implies"],
|
|
380
|
+
"properties": {
|
|
381
|
+
"implies": {
|
|
382
|
+
"type": "array",
|
|
383
|
+
"minItems": 2,
|
|
384
|
+
"maxItems": 2,
|
|
385
|
+
"description": "Sugar for [antecedent, consequent]: 'antecedent requires consequent'. As a VIOLATION predicate it desugars to all(antecedent, not(consequent)) — it matches the inputs that break the implication (antecedent holds but consequent does not).",
|
|
386
|
+
"items": { "$ref": "#/$defs/predicate" }
|
|
241
387
|
}
|
|
242
388
|
}
|
|
243
389
|
}
|