ci: give this repository a CI job, and fix what its first run found #9

Merged
peterswimm merged 1 commit from ci/spec-repo-has-ci into main 2026-09-23 19:07:07 +00:00
Owner

Phase 0 of the approved plan, for the repository where the gap was total: this
repo had no CI. The only workflow was pr-policy-check.yml, which reads the
PR description and never looks at the contents.

Its first run found six real defects. All are fixed here.

Scope

Five schemas that were never written. schemas/index.json advertised
actor_manifest, mode, ritual, rubric and ruse. git log --all -- schemas/<name>.json returns zero commits for every one of them. The
machine-readable registry of this contract has always described half a
repository that does not exist. Removed, along with their now-empty category
entries. The matching examples/*.yaml are left alone — they illustrate
concepts, not schemas.

A version two releases behind. index.json recorded frame_wire at 1.1
while the CHANGELOG had shipped 1.2.0 and then 1.3.0. Commit 3eae10c — the
1.3.0 amendment itself — edited index.json and left the version untouched. A
consumer reading the registry to learn which contract it was getting was told
1.1. Corrected, and now compared against the CHANGELOG so it cannot drift again.

tools/check_schema_registry.py enforces five properties, every one of which
has been false on main at some point
: a registry entry with no file, a file
with no registry entry, a version behind the CHANGELOG, a schema that stops
compiling, and a new file declaring no $schema without being recorded as an
entity descriptor.

That last one exists because schemas/ holds two kinds of file.
tool_manifest.json and wish.json are entity descriptors (name,
version, canonical, extensions) rather than JSON Schema documents, so
compiling them would be a category error. They are listed explicitly rather
than skipped by heuristic, so a real schema that merely forgets $schema is
still reported.

Testing

All five properties mutation-tested — broken one at a time, each watched to
fail with its own message, then restored to a clean tree:

mutation result
registry entry with no file FAIL, names the entry
file with no registry entry FAIL, names the file
version behind CHANGELOG FAIL, quotes both versions
schema stops compiling FAIL, quotes the compiler
new file, no $schema, not a descriptor FAIL, explains both remedies

Run in a clean python:3.12-slim container, as CI will, not just on my machine.

Two findings recorded rather than acted on

The Python suites are not run here, and that is a finding. As of today:
dynseal/test_security_scenarios.py and its byte-identical copy under docs/
collect zero tests — the file is a script whose class is not named Test* —
while their duplicate basename aborts pytest collection for the whole repo.
docs/test_prism.py collects zero. docs/test_schema_sync.py cannot import
schema_validator from the repo root. Only docs/test_api.py holds real tests:
32, of which 23 error without a live API. Switching those on today would be red
for reasons unrelated to the contract. Needs its own slice; I could not file the
wish because wish writes currently need ritual auth login.

Both ratified schemas declare a $schema URI that does not exist.
frame_wire.json and forge_block.json both say
"https://json-schema.org/draft/2020-12" — missing the trailing /schema, so
it resolves to nothing and every validator falls back to its newest supported
draft. That is 2020-12 today, which is why nothing has broken; Python's
jsonschema warns that it "will raise an error in the future". Rust never
noticed because ritual-core and frame-wire-contract both pass
.with_draft(Draft202012) explicitly and never consult the declared URI.

It is reported, not failed, because the same bytes are vendored into
ritual-engine (twice) and rituals-chat (once), each pinned by its own guard —
correcting the source alone would put three repositories in conflict. The fix is
three coordinated PRs. The checker names it in its output and in a note at the
bottom of the file, so it is tracked rather than tolerated, and says to tighten
the check to the exact canonical URI once the re-vendoring lands.

Worth noting what that cost demonstrates: a one-character correction to a
ratified document currently requires three reviewed PRs across three
repositories.
That is the argument for depending on a published version rather
than copying a file.

Impact Statement

  • Existing features: none. No schema content changes; index.json loses
    five entries that pointed at nothing and gains a correct version.
  • Public APIs/contracts: a consumer reading index.json now sees five
    schemas instead of ten — the five that exist. If anything depended on the
    fictional entries it was already broken.
  • Schemas: none modified. Only the registry that describes them.
  • Data/artifacts: index.json is reformatted by the fix (2-space JSON).
  • Integrations: adds one CI job. Installs jsonschema at job time.
  • Mappings: registry entry ↔ schema file, now enforced both directions.
  • Backward compatibility: the removed entries never resolved to a file, so
    nothing that worked stops working.

Deliberately not in scope

This does not check the contract's content. That is
crates/frame-wire-contract in ritual-engine — 60 tests driving the normative
fold against these exact bytes. A second opinion here about the same thing is
the failure this contract exists to prevent.

Phase 0 of the approved plan, for the repository where the gap was total: this repo had **no CI**. The only workflow was `pr-policy-check.yml`, which reads the PR description and never looks at the contents. Its first run found six real defects. All are fixed here. ## Scope **Five schemas that were never written.** `schemas/index.json` advertised `actor_manifest`, `mode`, `ritual`, `rubric` and `ruse`. `git log --all -- schemas/<name>.json` returns **zero commits for every one of them.** The machine-readable registry of this contract has always described half a repository that does not exist. Removed, along with their now-empty category entries. The matching `examples/*.yaml` are left alone — they illustrate concepts, not schemas. **A version two releases behind.** `index.json` recorded `frame_wire` at 1.1 while the CHANGELOG had shipped 1.2.0 and then 1.3.0. Commit `3eae10c` — the 1.3.0 amendment itself — edited `index.json` and left the version untouched. A consumer reading the registry to learn which contract it was getting was told 1.1. Corrected, and now compared against the CHANGELOG so it cannot drift again. `tools/check_schema_registry.py` enforces five properties, **every one of which has been false on `main` at some point**: a registry entry with no file, a file with no registry entry, a version behind the CHANGELOG, a schema that stops compiling, and a new file declaring no `$schema` without being recorded as an entity descriptor. That last one exists because `schemas/` holds two kinds of file. `tool_manifest.json` and `wish.json` are entity **descriptors** (`name`, `version`, `canonical`, `extensions`) rather than JSON Schema documents, so compiling them would be a category error. They are listed explicitly rather than skipped by heuristic, so a real schema that merely forgets `$schema` is still reported. ## Testing All five properties **mutation-tested** — broken one at a time, each watched to fail with its own message, then restored to a clean tree: | mutation | result | |---|---| | registry entry with no file | FAIL, names the entry | | file with no registry entry | FAIL, names the file | | version behind CHANGELOG | FAIL, quotes both versions | | schema stops compiling | FAIL, quotes the compiler | | new file, no `$schema`, not a descriptor | FAIL, explains both remedies | Run in a clean `python:3.12-slim` container, as CI will, not just on my machine. ## Two findings recorded rather than acted on **The Python suites are not run here, and that is a finding.** As of today: `dynseal/test_security_scenarios.py` and its byte-identical copy under `docs/` collect **zero** tests — the file is a script whose class is not named `Test*` — while their duplicate basename **aborts pytest collection for the whole repo**. `docs/test_prism.py` collects zero. `docs/test_schema_sync.py` cannot import `schema_validator` from the repo root. Only `docs/test_api.py` holds real tests: 32, of which 23 error without a live API. Switching those on today would be red for reasons unrelated to the contract. Needs its own slice; I could not file the wish because wish writes currently need `ritual auth login`. **Both ratified schemas declare a `$schema` URI that does not exist.** `frame_wire.json` and `forge_block.json` both say `"https://json-schema.org/draft/2020-12"` — missing the trailing `/schema`, so it resolves to nothing and every validator falls back to its newest supported draft. That is 2020-12 today, which is why nothing has broken; Python's `jsonschema` warns that it "will raise an error in the future". Rust never noticed because `ritual-core` and `frame-wire-contract` both pass `.with_draft(Draft202012)` explicitly and never consult the declared URI. It is **reported, not failed**, because the same bytes are vendored into ritual-engine (twice) and rituals-chat (once), each pinned by its own guard — correcting the source alone would put three repositories in conflict. The fix is three coordinated PRs. The checker names it in its output and in a note at the bottom of the file, so it is tracked rather than tolerated, and says to tighten the check to the exact canonical URI once the re-vendoring lands. Worth noting what that cost demonstrates: **a one-character correction to a ratified document currently requires three reviewed PRs across three repositories.** That is the argument for depending on a published version rather than copying a file. ## Impact Statement - **Existing features:** none. No schema content changes; `index.json` loses five entries that pointed at nothing and gains a correct version. - **Public APIs/contracts:** a consumer reading `index.json` now sees five schemas instead of ten — the five that exist. If anything depended on the fictional entries it was already broken. - **Schemas:** none modified. Only the registry that describes them. - **Data/artifacts:** `index.json` is reformatted by the fix (2-space JSON). - **Integrations:** adds one CI job. Installs `jsonschema` at job time. - **Mappings:** registry entry ↔ schema file, now enforced both directions. - **Backward compatibility:** the removed entries never resolved to a file, so nothing that worked stops working. ### Deliberately not in scope This does **not** check the contract's content. That is `crates/frame-wire-contract` in ritual-engine — 60 tests driving the normative fold against these exact bytes. A second opinion here about the same thing is the failure this contract exists to prevent.
ci: give this repository a CI job, and fix what its first run found
All checks were successful
PR policy check / policy-gate (pull_request) Successful in 0s
CI / schema-registry (pull_request) Successful in 8s
433f26c4a0
Until now rituals-api-spec had NO CI. The only workflow was pr-policy-check.yml,
which reads the PR description and never looks at the contents. Phase 0 of the
approved plan is making CI tell the truth; this is that, for the repo where the
gap was total.

The first run found six real defects, all fixed here:

FIVE FICTIONAL SCHEMAS. schemas/index.json advertised actor_manifest, mode,
ritual, rubric and ruse. `git log -- schemas/<name>.json` returns zero commits
for every one of them — they were never written, so the machine-readable
registry of this contract has always described half a repository that does not
exist. Removed, along with their now-empty category entries. (The matching
examples/*.yaml are left alone: they illustrate concepts, not schemas.)

A VERSION TWO RELEASES BEHIND. index.json recorded frame_wire 1.1 while the
CHANGELOG had shipped 1.2.0 and then 1.3.0. Worse, commit 3eae10c — the 1.3.0
amendment itself — edited index.json and left the version untouched. A consumer
reading the registry to learn which contract it is getting was told 1.1.
Corrected to 1.3, and the check now compares it to the CHANGELOG so it cannot
drift again.

tools/check_schema_registry.py enforces five properties, every one of which has
been false on main at some point: a registry entry with no file, a file with no
registry entry, a version behind the CHANGELOG, a schema that stops compiling,
and a new file that declares no $schema without being recorded as an entity
descriptor. All five were mutation-tested — broken one at a time, each watched
to fail with its own message, then restored to a clean tree.

That last property exists because schemas/ holds two kinds of file.
tool_manifest.json and wish.json are entity DESCRIPTORS (name, version,
canonical, extensions) rather than JSON Schema documents, so compiling them
would be a category error. They are listed explicitly rather than skipped by
heuristic, so a real schema that merely forgets $schema is reported.

WHAT THIS DELIBERATELY DOES NOT DO:

It does not check the contract's CONTENT. That is crates/frame-wire-contract in
ritual-engine, 60 tests driving the normative fold against these exact bytes.
A second opinion here about the same thing is the failure this contract exists
to prevent.

It does not run the Python suites, and that is a finding rather than an
omission. dynseal/test_security_scenarios.py and its byte-identical copy under
docs/ collect ZERO tests — the file is a script whose class is not named Test*
— while their duplicate basename aborts pytest collection for the whole repo.
docs/test_prism.py collects zero. docs/test_schema_sync.py cannot import
schema_validator from the repo root. Only docs/test_api.py holds real tests: 32,
of which 23 error without a live API. Switching those on today would be red for
reasons unrelated to the contract.

It reports, rather than fails on, the $schema URI both ratified schemas declare:
"https://json-schema.org/draft/2020-12" is missing the trailing "/schema" and
resolves to nothing, so validators fall back to their newest draft — which is
2020-12 today, which is why nothing has broken. Rust never noticed because
ritual-core and frame-wire-contract both pass .with_draft(Draft202012)
explicitly. It is not failed here because the same bytes are vendored into
ritual-engine twice and rituals-chat once, each pinned by its own guard, so
correcting the source alone would put three repositories in conflict. The
checker now names it in its output and in a note, so it is tracked rather than
tolerated.
peterswimm deleted branch ci/spec-repo-has-ci 2026-09-23 19:07:07 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Toilville/rituals-api-spec!9
No description provided.