Skip to content

spec(ownir): add machine-readable JSON Schema, pinned to code with no new dep#175

Merged
PhysShell merged 4 commits into
mainfrom
claude/ownir-json-schema
Jul 4, 2026
Merged

spec(ownir): add machine-readable JSON Schema, pinned to code with no new dep#175
PhysShell merged 4 commits into
mainfrom
claude/ownir-json-schema

Conversation

@PhysShell

@PhysShell PhysShell commented Jul 4, 2026

Copy link
Copy Markdown
Owner

Что и зачем

Добавляет машинную грамматику OwnIR — spec/ownir.schema.json (JSON Schema draft 2020-12, ownir_version как const:0) — рядом с прозаической спекой spec/OwnIR.md. Схема зеркалит контракт, который проверяет валидатор ownlang/ownir.py::load(), и спроектирована как единый источник, из которого крейт own-ir (P-022, #170) генерирует serde-типы. Ядру нельзя тянуть jsonschema (zero-dependency), поэтому вместо валидации документов тест tests/test_ownir.py привязывает словарь схемы к авторитетным множествам в коде — так схема и валидатор не могут разъехаться без красной сборки, без новой зависимости.

Тип изменения

  • feat — новая возможность
  • fix — исправление бага
  • docs — документация
  • refactor / chore / test / ci — без изменения поведения

Как проверено

  • python tests/run_tests.py — вся сборка зелёная (ownir: 230/230)
  • ruff check . и mypy (mypy --strict над ownlang) — чисто
  • python3 -c "import json; json.load(open('spec/ownir.schema.json'))" — схема валидный JSON

Привязка схема↔код в тесте: resourceKind == _KNOWN_RESOURCE_KINDS, diLifetime == di.LIFETIMES, paramEffect == _PARAM_EFFECTS, flowOp consts == _FLOW_OPS (в обе стороны + drive-test лоуэрит каждый op), ownir_version const == OWNIR_VERSION.

Связанные issue

Refs #170 (крейт own-ir — второй набор type-ов, для которого схема станет источником генерации). Closes — none.

Чеклист

  • изменение покрыто тестом/селфтестом (новая секция schema↔code binding в tests/test_ownir.py)
  • README/docs обновлены при необходимости (spec/README.md, docs/notes/tech-debt-register.md п.3 → shipped)
  • коммиты в conventional-commit стиле (spec:, fix:, style:)

… new dep

OwnIR now has a machine grammar to sit alongside the prose spec
(spec/OwnIR.md): spec/ownir.schema.json (JSON Schema draft 2020-12,
ownir_version as const:0). It mirrors the contract the Python validator
(ownlang/ownir.py::load) enforces — envelope, owned-resource records,
flow bodies, DI graph, reactive effects — and is shaped for the P-022
`own-ir` Rust crate to generate serde types from: the flow-op vocabulary
is a `oneOf` discriminated on `op`, and the enums (resource kinds, DI
lifetimes, param effects) are closed.

The schema's trigger has fired: a second language-native type set (the
Rust crate in #170) is arriving, so a single source both are generated
from stops the two hand-maintained validators from drifting.

The core cannot import a jsonschema validator (zero-dependency
constraint), so instead of validating documents this binds the schema's
*vocabulary* to the code's authoritative sets in tests/test_ownir.py:

- resourceKind enum  == _KNOWN_RESOURCE_KINDS  (the load() routing set)
- diLifetime enum    == di.LIFETIMES
- ownir_version const == OWNIR_VERSION
- every flowOp const lowers through _lower_flow without the fail-loud
  "unknown op" raise; a bogus op the schema omits still raises

Schema and validator therefore cannot drift without a red build, no
dependency added. Registered in spec/README.md; tech-debt register item
3 updated to shipped.

228/228 bridge checks pass; full suite green.
@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a JSON Schema (spec/ownir.schema.json) formalizing the OwnIR document contract, exposes shared _FLOW_OPS and _PARAM_EFFECTS vocabulary constants in the Python core with corresponding validation changes, adds tests cross-checking schema enums against core constants, and updates documentation.

Changes

OwnIR schema formalization

Layer / File(s) Summary
Core flow-op and param-effect vocabulary
ownlang/ownir.py
Adds _FLOW_OPS and _PARAM_EFFECTS constants, updates parameter effect validation to use _PARAM_EFFECTS, and raises OwnIRError when a listed flow op lacks a lowering branch.
OwnIR JSON Schema definition
spec/ownir.schema.json
New schema defining OwnIR document shape and $defs for resourceKind, diLifetime, paramEffect, site, component, resourceRecord, function, param, flowOp, service, effect, and binding.
Schema-to-code binding tests and documentation
tests/test_ownir.py, spec/README.md, docs/notes/tech-debt-register.md
New test section validates schema enums/consts against core vocabularies and per-op lowering; README and tech-debt register updated to describe the shipped schema and remaining validation/type-generation work.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • PhysShell/Own.NET#36: Both PRs modify OwnIR parameter-effect validation and flow-op lowering logic in ownlang/ownir.py along with corresponding tests/test_ownir.py expectations.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding a machine-readable OwnIR JSON Schema with no new dependency.
Description check ✅ Passed The description fills all required template sections and includes purpose, change type, verification, related issue, and checklist items.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/ownir-json-schema

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 45ac5cc783

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread spec/ownir.schema.json Outdated
Comment thread tests/test_ownir.py
Comment thread tests/test_ownir.py
claude added 2 commits July 4, 2026 10:56
The schema-consistency imports (_KNOWN_RESOURCE_KINDS, DI_LIFETIMES)
tripped ruff's import-order check on the PR; merge them into the sorted
block.
…inds

Three P2 findings from the Codex review on the schema PR:

1. Null resource types. The reference loaders (Python load(), the Rust
   own-ir parity tests) accept and preserve `"type": null` — load() only
   rejects a non-null non-string. The schema made `type`/`source_type`
   string-only, so it (and generated serde types) would reject documents
   the pipeline accepts. Widen them to ["string","null"] — maps to
   Option<String>. `source` widened too (load() never constrained it).

2. paramEffect never pinned. The schema advertised a closed paramEffect
   enum but the drift test jumped straight from diLifetime to flow ops,
   so it never bound. Extract the effect tuple in load() to a named
   authority `_PARAM_EFFECTS` and assert schema.paramEffect == it.

3. Flow-op bind was one-directional. The test only proved schema-ops
   lower; a handled op the schema forgot stayed green. Extract the
   lowerer's vocabulary to `_FLOW_OPS` (the _lower_flow `else` now
   distinguishes "declared-but-unhandled" from "unknown vocabulary") and
   assert schema.flowOp consts == _FLOW_OPS — both directions. The
   drive-test still lowers every declared op so a phantom set entry fails.

Both _FLOW_OPS and _PARAM_EFFECTS now mirror _KNOWN_RESOURCE_KINDS: a
named frozenset used by load()/_lower_flow AND bound to the schema by
equality, so schema and validator cannot drift either way.

230/230 bridge checks; ruff + mypy --strict + full suite green.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with 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.

Inline comments:
In `@spec/ownir.schema.json`:
- Around line 85-108: Update the resourceRecord schema to include the missing
fact identifiers event, handler, and lambda alongside the existing
line/released/resource/type/source/source_type fields. Keep them additive and
optional like the other preserved fields so the generated own-ir Rust serde
types round-trip the full subscription/capture facts used in tests. Use
resourceRecord as the anchor in spec/ownir.schema.json so the schema remains the
source of truth for all core identifying fields.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 4e0f50d3-196e-4a1d-81e2-906158633435

📥 Commits

Reviewing files that changed from the base of the PR and between 174764a and 8702aa4.

📒 Files selected for processing (5)
  • docs/notes/tech-debt-register.md
  • ownlang/ownir.py
  • spec/README.md
  • spec/ownir.schema.json
  • tests/test_ownir.py

Comment thread spec/ownir.schema.json
CodeRabbit (Major): the resourceRecord schema omitted the event,
handler, and lambda fields that every subscription/timer/capture fact
carries — event/handler are core identifying fields (finding messages,
rendered output) read as strings via sub.get(), and lambda is the bool
flag (ownir.py:2171) marking an un-detachable inline-lambda handler.
Without them the schema-generated Rust own-ir serde types would drop the
fields and lose round-trip fidelity for real facts, undermining the
schema's single-source purpose. Added additive/optional, matching code
semantics (event/handler string, lambda bool).

230/230 bridge checks; full suite green.
@PhysShell
PhysShell merged commit db6f673 into main Jul 4, 2026
31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants