Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@
/scripts/admit-maintenance-plan @loadinglucian
/scripts/seal-maintenance-patch @loadinglucian
/scripts/validate-codex-action-inputs @loadinglucian
/scripts/validate-structured-output-schemas @loadinglucian
/scripts/verify-merge-admission @loadinglucian
1 change: 1 addition & 0 deletions maintenance/protected-paths.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"scripts/admit-maintenance-plan",
"scripts/seal-maintenance-patch",
"scripts/validate-codex-action-inputs",
"scripts/validate-structured-output-schemas",
"scripts/verify-merge-admission",
"maintenance-events/*",
"readiness/*",
Expand Down
34 changes: 28 additions & 6 deletions schemas/agent-completion-assessment.schema.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,36 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"additionalProperties": false,
"required": ["contractVersion", "instructionDigests", "phaseStatus", "criteria", "goNoGo", "unresolved", "summary"],
"properties": {
"contractVersion": {"const": 1},
"instructionDigests": {"type": "object"},
"phaseStatus": {"enum": ["complete", "blocked", "needs_human"]},
"criteria": {"type": "array"},
"goNoGo": {"enum": ["go", "no_go"]},
"unresolved": {"type": "array"},
"contractVersion": {"type": "integer", "const": 1},
"instructionDigests": {
"type": "object",
"additionalProperties": false,
"required": ["shared", "phaseTemplate", "eventContract"],
"properties": {
"shared": {"type": "string", "pattern": "^sha256:[0-9a-f]{64}$"},
"phaseTemplate": {"type": "string", "pattern": "^sha256:[0-9a-f]{64}$"},
"eventContract": {"type": "string", "pattern": "^sha256:[0-9a-f]{64}$"}
}
},
"phaseStatus": {"type": "string", "enum": ["complete", "blocked", "needs_human"]},
"criteria": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["id", "status", "evidence"],
"properties": {
"id": {"type": "string"},
"status": {"type": "string", "enum": ["passed", "failed", "unresolved"]},
"evidence": {"type": "array", "items": {"type": "string"}}
}
}
},
"goNoGo": {"type": "string", "enum": ["go", "no_go"]},
"unresolved": {"type": "array", "items": {"type": "string"}},
"summary": {"type": "string"}
}
}
110 changes: 97 additions & 13 deletions schemas/maintenance-plan.schema.json
Original file line number Diff line number Diff line change
@@ -1,51 +1,135 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"additionalProperties": false,
"required": ["schemaVersion", "actionKey", "action", "agentContract", "evidence", "repositories", "preconditions", "editsRequired", "allowedPaths", "requiredChecks", "agentOperations", "budgets", "notification", "risk", "completionAssessment", "summary"],
"properties": {
"schemaVersion": {"const": 1},
"schemaVersion": {"type": "integer", "const": 1},
"actionKey": {"type": "string"},
"action": {"enum": ["no_change", "new_patch", "new_branch", "branch_eol", "repair", "reconcile_partial", "blocked", "needs_human"]},
"agentContract": {"type": "object"},
"action": {"type": "string", "enum": ["no_change", "new_patch", "new_branch", "branch_eol", "repair", "reconcile_partial", "blocked", "needs_human"]},
"agentContract": {
"type": "object",
"additionalProperties": false,
"required": ["contractVersion", "instructionDigests"],
"properties": {
"contractVersion": {"type": "integer", "const": 1},
"instructionDigests": {
"type": "object",
"additionalProperties": false,
"required": ["shared", "phaseTemplate", "eventContract"],
"properties": {
"shared": {"type": "string", "pattern": "^sha256:[0-9a-f]{64}$"},
"phaseTemplate": {"type": "string", "pattern": "^sha256:[0-9a-f]{64}$"},
"eventContract": {"type": "string", "pattern": "^sha256:[0-9a-f]{64}$"}
}
}
}
},
"evidence": {
"type": "array",
"minItems": 4,
"maxItems": 4,
"items": {
"type": "object",
"required": ["captureId", "digest", "locator"],
"additionalProperties": false,
"required": ["captureId", "digest", "claim", "locator"],
"properties": {
"captureId": {"enum": ["php_bin_policy_selector", "php_bin_state", "support_policy", "policy_invariants"]},
"captureId": {"type": "string", "enum": ["php_bin_policy_selector", "php_bin_state", "support_policy", "policy_invariants"]},
"digest": {"type": "string", "pattern": "^sha256:[0-9a-f]{64}$"},
"claim": {"type": "string"},
"locator": {
"type": "object",
"additionalProperties": false,
"required": ["kind", "value"],
"properties": {
"kind": {"const": "json_pointer"},
"kind": {"type": "string", "const": "json_pointer"},
"value": {"type": "string", "pattern": "^/"}
}
}
}
}
},
Comment on lines 28 to 51

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files | rg '(^|/)schemas/maintenance-plan\.schema\.json$|maintenance-plan' || true

echo "== schema excerpt =="
if [ -f schemas/maintenance-plan.schema.json ]; then
  cat -n schemas/maintenance-plan.schema.json | sed -n '1,120p'
fi

echo "== uniqueItems occurrences =="
if [ -f schemas/maintenance-plan.schema.json ]; then
  python3 - <<'PY'
import json
p='schemas/maintenance-plan.schema.json'
data=json.load(open(p))
for path, val in json_paths({'$': data}, '$'):
    if isinstance(val, dict) and val.get('uniqueItems') is True:
        print(path)
PY
fi

echo "== captureId occurrences in repo =="
rg -n '"captureId"|captureId' -S . || true

echo "== validator behavior probe with python jsonschema if available =="
python3 - <<'PY'
import json, sys
try:
    from jsonschema import validate, ValidationError
except Exception as e:
    print("jsonschema unavailable:", type(e).__name__, str(e))
    sys.exit(0)

schema = json.load(open('schemas/maintenance-plan.schema.json'))
duplicate = {
  "evidence": [
      {"captureId":"php_bin_policy_selector","digest":"sha256:0000000000000000000000000000000000000000000000000000000000000000","claim":"first","locator":{"kind":"json_pointer","value":"/a"}},
      {"captureId":"php_bin_policy_selector","digest":"sha256:0000000000000000000000000000000000000000000000000000000000000001","claim":"second","locator":{"kind":"json_pointer","value":"/a"}},
      {"captureId":"php_bin_policy_selector","digest":"sha256:0000000000000000000000000000000000000000000000000000000000000002","claim":"third","locator":{"kind":"json_pointer","value":"/a"}},
      {"captureId":"php_bin_policy_selector","digest":"sha256:0000000000000000000000000000000000000000000000000000000000000003","claim":"fourth","locator":{"kind":"json_pointer","value":"/a"}},
  ],
}
try:
    validate(duplicate, schema)
    print("duplicate-first captureId VALID without uniqueItems")
except ValidationError as err:
    print("duplicate-first captureId INVALID:", str(err))

with_unique = json.loads(json.dumps(schema))
with_unique['properties']['evidence']['uniqueItems'] = True
try:
    validate(duplicate, with_unique)
    print("duplicate-first captureId VALID with uniqueItems")
except ValidationError as err:
    print("duplicate-first captureId INVALID with uniqueItems:", str(err))

unique = duplicate.copy()
unique["evidence"] = [
      {"captureId":"php_bin_policy_selector","digest":"sha256:0000000000000000000000000000000000000000000000000000000000000000","claim":"first","locator":{"kind":"json_pointer","value":"/a"}},
      {"captureId":"php_bin_state","digest":"sha256:0000000000000000000000000000000000000000000000000000000000000001","claim":"second","locator":{"kind":"json_pointer","value":"/b"}},
      {"captureId":"support_policy","digest":"sha256:0000000000000000000000000000000000000000000000000000000000000002","claim":"third","locator":{"kind":"json_pointer","value":"/c"}},
      {"captureId":"policy_invariants","digest":"sha256:0000000000000000000000000000000000000000000000000000000000000003","claim":"fourth","locator":{"kind":"json_pointer","value":"/d"}},
]
try:
    validate(unique, with_unique)
    print("all-four distinct captureIds VALID with uniqueItems")
except ValidationError as err:
    print("all-four distinct captureIds INVALID with uniqueItems:", str(err))
PY

Repository: Bigpixelrocket/mise-php

Length of output: 6722


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import json

schema = json.load(open('schemas/maintenance-plan.schema.json'))

def scan(obj, path='$'):
    if isinstance(obj, dict):
        yield path, obj
        for k, v in obj.items():
            yield from scan(v, f'{path}.{k}')
    elif isinstance(obj, list):
        for i, v in enumerate(obj):
            yield from scan(v, f'{path}[{i}]')

for path, val in scan(schema):
    if isinstance(val, dict) and val.get('uniqueItems') is True:
        parent_path = path.rsplit('.', 1)[0] if '.' in path else '/properties/' + path
        print(path, 'in', parent_path)

required_capture = schema['properties']['evidence']['items']['properties']['captureId'].get('enum')
print(
    "evidence length:", schema['properties']['evidence'].get('minItems'), schema['properties']['evidence'].get('maxItems'),
    "captureIds:", required_capture,
    "evidence uniqueItems:", schema['properties']['evidence'].get('uniqueItems')
)
items_props = schema['properties']['evidence']['items']['properties']
print("evidence item required:", schema['properties']['evidence']['items']['required'])
PY

echo
if python3 - <<'PY'
import importlib.util
print(importlib.util.find_spec("jsonschema"))
PY
then
  python3 - <<'PY'
from jsonschema import validate, ValidationError
import json

schema = json.load(open('schemas/maintenance-plan.schema.json'))

duplicate = {
  "evidence": [
      {"captureId":"php_bin_policy_selector","digest":"sha256:0000000000000000000000000000000000000000000000000000000000000000","claim":"first","locator":{"kind":"json_pointer","value":"/a"}},
      {"captureId":"php_bin_policy_selector","digest":"sha256:0000000000000000000000000000000000000000000000000000000000000001","claim":"second","locator":{"kind":"json_pointer","value":"/a"}},
      {"captureId":"php_bin_policy_selector","digest":"sha256:0000000000000000000000000000000000000000000000000000000000000002","claim":"third","locator":{"kind":"json_pointer","value":"/a"}},
      {"captureId":"php_bin_policy_selector","digest":"sha256:0000000000000000000000000000000000000000000000000000000000000003","claim":"fourth","locator":{"kind":"json_pointer","value":"/a"}},
  ],
}
try:
    validate(duplicate, schema)
    print("duplicate-first captureId VALID without uniqueItems")
except ValidationError as err:
    print("duplicate-first captureId INVALID:", str(err))

with_unique = json.loads(json.dumps(schema))
with_unique['properties']['evidence']['uniqueItems'] = True
try:
    validate(duplicate, with_unique)
    print("duplicate-first captureId VALID with uniqueItems")
except ValidationError as err:
    print("duplicate-first captureId INVALID with uniqueItems:", str(err))

unique = duplicate.copy()
unique["evidence"] = [
      {"captureId":"php_bin_policy_selector","digest":"sha256:0000000000000000000000000000000000000000000000000000000000000000","claim":"first","locator":{"kind":"json_pointer","value":"/a"}},
      {"captureId":"php_bin_state","digest":"sha256:0000000000000000000000000000000000000000000000000000000000000001","claim":"second","locator":{"kind":"json_pointer","value":"/b"}},
      {"captureId":"support_policy","digest":"sha256:0000000000000000000000000000000000000000000000000000000000000002","claim":"third","locator":{"kind":"json_pointer","value":"/c"}},
      {"captureId":"policy_invariants","digest":"sha256:0000000000000000000000000000000000000000000000000000000000000003","claim":"fourth","locator":{"kind":"json_pointer","value":"/d"}},
]
try:
    validate(unique, with_unique)
    print("all-four distinct captureIds VALID with uniqueItems")
except ValidationError as err:
    print("all-four distinct captureIds INVALID with uniqueItems:", str(err))
  PY
else
  echo "jsonschema library not available"
  echo "Behavioral summary remains deterministic: JSON Schema uniqueItems compares array items by JSON equality; two evidence objects differ because their non-captureId fields (digest/claim/locator) differ, so uniqueItems alone does not enforce distinct captureIds. It only prevents exact duplicate evidence entries."
fi

Repository: Bigpixelrocket/mise-php

Length of output: 687


Prevent duplicate evidence captureIds in the schema.

evidence is bounded to 4 items from a 4-value captureId enum, but without uniqueItems plus capture-level uniqueness it still accepts entries that omit a required evidence type. Add uniqueItems: true and another check that forces distinct captureId values, for example via a separate requiresCaptureIds keyword, enum constraint on each item, or an unevaluatedProperties rule.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@schemas/maintenance-plan.schema.json` around lines 28 - 51, Update the
evidence schema to reject duplicate captureId values, not merely duplicate full
evidence objects. Add uniqueItems: true and a capture-level uniqueness
constraint using the project’s supported schema mechanism, while preserving the
existing four-item bound and allowed captureId enum.

"repositories": {"type": "array"},
"preconditions": {"type": "object"},
"repositories": {"type": "array", "items": {"type": "string", "enum": ["php-bin", "mise-php"]}, "uniqueItems": true},
"preconditions": {
"type": "object",
"additionalProperties": false,
"required": ["misePhpHead", "phpBinPolicyCommit", "supportPolicyDigest", "policyInvariantsDigest", "phpBinOperatorCommit", "operatorState"],
"properties": {
"misePhpHead": {"type": "string", "pattern": "^[0-9a-f]{40}$"},
"phpBinPolicyCommit": {"type": "string", "pattern": "^[0-9a-f]{40}$"},
"supportPolicyDigest": {"type": "string", "pattern": "^sha256:[0-9a-f]{64}$"},
"policyInvariantsDigest": {"type": "string", "pattern": "^sha256:[0-9a-f]{64}$"},
"phpBinOperatorCommit": {"type": "string", "pattern": "^[0-9a-f]{40}$"},
"operatorState": {"type": "string", "const": "enabled"}
}
},
"editsRequired": {"type": "boolean"},
"allowedPaths": {"type": "object"},
"requiredChecks": {"const": ["Plugin contract"]},
"allowedPaths": {
"type": "object",
"additionalProperties": false,
"required": ["mise-php"],
"properties": {
"mise-php": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
}
},
"requiredChecks": {"type": "array", "items": {"type": "string"}, "const": ["Plugin contract"]},
"agentOperations": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
"budgets": {
"type": "object",
"additionalProperties": false,
"required": ["maxModelCalls", "maxRetries", "timeoutMinutes"],
"properties": {
"maxModelCalls": {"type": "integer", "minimum": 1, "maximum": 5},
"maxRetries": {"type": "integer", "minimum": 1, "maximum": 3},
"timeoutMinutes": {"type": "integer", "minimum": 1, "maximum": 60}
}
},
"notification": {"type": "object"},
"risk": {"type": "string"},
"completionAssessment": {"type": "object"},
"notification": {
"type": "object",
"additionalProperties": false,
"required": ["suggestedSeverity", "summary", "humanActionRequired"],
"properties": {
"suggestedSeverity": {"type": "string", "enum": ["info", "warning", "critical"]},
"summary": {"type": "string"},
"humanActionRequired": {"type": "boolean"}
}
},
"risk": {"type": "string", "enum": ["routine", "compatibility", "lifecycle", "recovery", "policy-sensitive"]},
"completionAssessment": {
"type": "object",
"additionalProperties": false,
"required": ["contractVersion", "instructionDigests", "phaseStatus", "criteria", "goNoGo", "unresolved", "summary"],
"properties": {
"contractVersion": {"type": "integer", "const": 1},
"instructionDigests": {
"type": "object",
"additionalProperties": false,
"required": ["shared", "phaseTemplate", "eventContract"],
"properties": {
"shared": {"type": "string", "pattern": "^sha256:[0-9a-f]{64}$"},
"phaseTemplate": {"type": "string", "pattern": "^sha256:[0-9a-f]{64}$"},
"eventContract": {"type": "string", "pattern": "^sha256:[0-9a-f]{64}$"}
}
},
"phaseStatus": {"type": "string", "enum": ["complete", "blocked", "needs_human"]},
"criteria": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["id", "status", "evidence"],
"properties": {
"id": {"type": "string"},
"status": {"type": "string", "enum": ["passed", "failed", "unresolved"]},
"evidence": {"type": "array", "items": {"type": "string"}}
}
}
},
"goNoGo": {"type": "string", "enum": ["go", "no_go"]},
"unresolved": {"type": "array", "items": {"type": "string"}},
"summary": {"type": "string"}
}
},
"summary": {"type": "string"}
}
}
1 change: 1 addition & 0 deletions scripts/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"

"$SCRIPT_DIR/check-public-language.sh"
"$SCRIPT_DIR/validate-codex-action-inputs"
"$SCRIPT_DIR/validate-structured-output-schemas"

if [[ "$(uname -s)" != "Darwin" || "$(uname -m)" != "arm64" ]]; then
echo "Plugin installation tests require macOS arm64." >&2
Expand Down
75 changes: 75 additions & 0 deletions scripts/validate-structured-output-schemas
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#!/usr/bin/env python3
"""Validate every static Codex Structured Outputs schema used by workflows."""

from __future__ import annotations

import json
import pathlib
import re
import sys
from typing import Any


ROOT = pathlib.Path(__file__).resolve().parents[1]
OUTPUT_SCHEMA_RE = re.compile(r'--output-schema","([^"]+\.json)"')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Make workflow schema discovery format-complete.

The .yml-only glob and exact --output-schema","…" regex can miss .yaml workflows or normally spaced command arguments; validation can still pass if another schema is discovered. Scan both extensions and support the static argument forms used by workflows.

Also applies to: 53-55

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/validate-structured-output-schemas` at line 14, Make workflow schema
discovery format-complete by updating OUTPUT_SCHEMA_RE to accept normal
whitespace around the --output-schema argument and the static argument forms
used by workflows, not only the exact comma-quote sequence. Update the workflow
globbing logic at the related discovery sites to scan both .yml and .yaml files,
while preserving schema validation across all discovered files.



def fail(message: str) -> None:
print(f"Structured output schema error: {message}", file=sys.stderr)
raise SystemExit(1)


def validate_node(node: Any, location: str) -> None:
if isinstance(node, list):
for index, item in enumerate(node):
validate_node(item, f"{location}[{index}]")
return
if not isinstance(node, dict):
return

declared_type = node.get("type")
types = {declared_type} if isinstance(declared_type, str) else set(declared_type or [])
if "object" in types:
if node.get("additionalProperties") is not False:
fail(f"{location} must set additionalProperties to false")
properties = node.get("properties")
if not isinstance(properties, dict):
fail(f"{location} must declare object properties")
required = node.get("required")
if not isinstance(required, list) or set(required) != set(properties):
fail(f"{location} must require every declared property exactly once")
if len(required) != len(set(required)):
fail(f"{location} contains duplicate required properties")

Comment on lines +30 to +43

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Detect object-shaped schemas that omit type: object.

At Line 32, validation runs only for explicitly typed objects. JSON Schema allows properties, required, or additionalProperties without type, so an open object can bypass this strictness check. Reject object keywords unless type includes object.

Proposed fix
+OBJECT_KEYWORDS = frozenset(
+    {"properties", "required", "additionalProperties", "patternProperties",
+     "propertyNames", "dependentRequired", "dependentSchemas",
+     "unevaluatedProperties", "minProperties", "maxProperties"}
+)
+
 def validate_node(node: Any, location: str) -> None:
     ...
     declared_type = node.get("type")
     types = {declared_type} if isinstance(declared_type, str) else set(declared_type or [])
+    if OBJECT_KEYWORDS.intersection(node) and "object" not in types:
+        fail(f"{location} uses object keywords without type: object")
     if "object" in types:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
declared_type = node.get("type")
types = {declared_type} if isinstance(declared_type, str) else set(declared_type or [])
if "object" in types:
if node.get("additionalProperties") is not False:
fail(f"{location} must set additionalProperties to false")
properties = node.get("properties")
if not isinstance(properties, dict):
fail(f"{location} must declare object properties")
required = node.get("required")
if not isinstance(required, list) or set(required) != set(properties):
fail(f"{location} must require every declared property exactly once")
if len(required) != len(set(required)):
fail(f"{location} contains duplicate required properties")
OBJECT_KEYWORDS = frozenset(
{
"properties",
"required",
"additionalProperties",
"patternProperties",
"propertyNames",
"dependentRequired",
"dependentSchemas",
"unevaluatedProperties",
"minProperties",
"maxProperties",
}
)
declared_type = node.get("type")
types = {declared_type} if isinstance(declared_type, str) else set(declared_type or [])
if OBJECT_KEYWORDS.intersection(node) and "object" not in types:
fail(f"{location} uses object keywords without type: object")
if "object" in types:
if node.get("additionalProperties") is not False:
fail(f"{location} must set additionalProperties to false")
properties = node.get("properties")
if not isinstance(properties, dict):
fail(f"{location} must declare object properties")
required = node.get("required")
if not isinstance(required, list) or set(required) != set(properties):
fail(f"{location} must require every declared property exactly once")
if len(required) != len(set(required)):
fail(f"{location} contains duplicate required properties")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/validate-structured-output-schemas` around lines 30 - 43, Update the
schema validation logic around the declared_type/types handling so any schema
containing properties, required, or additionalProperties is treated as
object-shaped and must explicitly include "object" in types. Reject such schemas
when "object" is absent, while preserving the existing strict
additionalProperties, properties, and required validations for explicitly typed
objects.

if ("const" in node or "enum" in node) and "type" not in node:
fail(f"{location} uses const or enum without an explicit type")

for key, value in node.items():
validate_node(value, f"{location}.{key}")


def main() -> int:
schema_paths: set[pathlib.Path] = set()
for workflow in sorted((ROOT / ".github/workflows").glob("*.yml")):
for relative in OUTPUT_SCHEMA_RE.findall(workflow.read_text()):
schema_paths.add(ROOT / relative)

if not schema_paths:
fail("no static Codex output schemas were discovered")

for path in sorted(schema_paths):
if not path.is_file() or not path.resolve().is_relative_to(ROOT):
fail(f"unsafe or missing schema path: {path}")
try:
document = json.loads(path.read_text())
except json.JSONDecodeError as error:
fail(f"{path.relative_to(ROOT)} is invalid JSON: {error}")
validate_node(document, str(path.relative_to(ROOT)))

print(f"Validated {len(schema_paths)} Codex Structured Outputs schemas.")
return 0


if __name__ == "__main__":
raise SystemExit(main())

Loading