Repository navigation
feat(OMN-16882): theme instances + theme catalog — give a design-token value somewhere to live - #1608
Merged
Conversation
…n value somewhere to live ModelRendererThemeContract is a SCHEMA: every token field is Field(...)-required under frozen=True, extra="forbid", and the class holds no values. The emitter emits JSON Schema. Nothing in the shipped pipeline could carry a token VALUE to a consumer, so "edit the Python class to change color_accent_primary" was never a workflow the code supported. This lands the instance layer the schema was always missing. - ModelThemeInstance — a schema-validated token value set plus the document header a lifecycle needs. Header (theme_id, schema_version) is cross-checked against the embedded token set and fails closed on disagreement. - ModelThemeCatalogEntry / ModelThemeCatalog — published, immutable, digested revisions, unique per (theme_id, instance_revision), resolved by exact revision only (there is deliberately no "latest"). - ModelThemeActivation — the active pointer: which surface renders which revision, carrying the digest it resolved and a monotonic sequence. - util_theme_catalog — load, digest, build, activate, roll back. - Instance documents for light / dark / warm under src/omnibase_core/contracts/themes/<theme_id>/<revision>.yaml, already covered by the wheel's artifacts glob. - Emitter registers the theme schema plus the whole instance layer. Three version axes, three distinct fields: schema_version (token FIELDS), instance_revision (token VALUES), content digest (published bytes). Conflating any two is how a stale artifact passes on a matching version number. OD-2 resolved: published revisions are packaged immutable data; only the activation pointer is runtime state. That split is what makes rollback provable — revisions cannot move, so a restored pointer carrying the same digest proves the old bytes came back rather than a lookalike recompile. Gates: GC.1 (value change -> publish -> activate -> rollback with no Python class edit) and GC.2 (two loads of one entry report one digest) are covered by tests/unit/models/dashboard/test_theme_catalog.py.
Contributor
|
Warning Review limit reachedNext included review available in 8 seconds. View limit detailsLimit details: You’ve used the included review currently available. Your 131 included PR review attempts over the past 7 days set your current allowance at 1 review per hour. Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. Review configuration: ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (12)
Comment |
jonahgabriel
added a commit
to OmniNode-ai/onex_change_control
that referenced
this pull request
Aug 28, 2026
…#7466) * evidence: OCC companion pass 1 for OmniNode-ai/omnibase_core#1608 * evidence: OCC companion self-bind for #7466 * fix(OMN-16882): scope product proof to local done gate * evidence(OMN-16882): add missing test_passes receipt --------- Co-authored-by: node-occ-companion-effect <occ-companion-effect@omninode.ai> Co-authored-by: jonahgabriel <jonah@omninode.ai>
4 of 5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes OMN-16882 (Phase C1 of epic OMN-16879, per
omni_home/docs/plans/2026-08-27-omnidash-component-rework-plan.md§1.4.1 / §2.1 / Phase C item C1).ModelRendererThemeContractis a schema: every token field isField(...)-required underfrozen=True, extra="forbid", and the class holds no values.scripts/emit_ts_types.pyemitsmodel_json_schema(). So the shipped pipeline had no way to carry a token value to a consumer, and "edit the Python class to changecolor_accent_primary" was never a workflow the code supported — there is no value in the class to edit.This lands the instance layer the schema was always missing. It is purely additive; no existing model changes shape.
What lands
ModelThemeInstancetheme_id/schema_versionin the header are cross-checked against the embedded token set and fail closed on disagreement — a header that can drift from its body is worse than no header.ModelThemeCatalogEntrysha256:<hex>content digest, source path. The digest lives here, not on the instance, because an object cannot contain a hash that covers itself.ModelThemeCatalog(theme_id, instance_revision), resolved by exact revision — there is deliberately nolatest.ModelThemeActivationactivation_sequence.utils/util_theme_catalog.pyload/digest/build/activate/roll_back.contracts/themes/<theme_id>/<revision>.yamlThe three version axes are three fields
schema_version(token fields) ·instance_revision(token values) · content digest (published bytes). Conflating any two is how a stale artifact passes on a matching version number, which is the precise failure §2.1 records. The digest issha256over the instance's canonical JSON (compute_canonical_hash), not over raw file bytes, so two surfaces loading one entry through different YAML writers still agree and formatting churn cannot read as a value change.OD-2 resolved (plan §5.1)
Published revisions are a compiled artifact shipped with the library; the active pointer is runtime state. Revisions must be immutable, digested, and byte-reproducible offline so two surfaces agree on a digest with no service in the path — a registry projection cannot give that property to a build input. Which surface renders which revision is mutable, per-surface, and reported: that half is
ModelThemeActivation. Splitting them is what makes "rollback moves a pointer and the digest proves the old bytes came back" mechanically true rather than asserted — revisions cannot move, so a restored pointer carrying the same digest bytes proves the old bytes returned rather than a fresh compile that happens to look similar.Gates
test_a_token_value_changes_without_editing_a_python_class,test_rollback_moves_the_pointer_and_restores_the_original_digesttest_digest_is_deterministic_across_independent_loads,test_digest_is_stable_across_a_serialize_reload_round_triptest_light_dark_warm_instances_exist_and_validate,test_every_shipped_instance_document_validates_against_the_schematest_instance_declares_schema_version_and_instance_revision_separately,test_a_value_change_moves_the_digest_but_not_the_schema_versiontest_emitter_registers_the_theme_schema_and_instance_layer,test_instance_json_schema_embeds_the_token_schemadod_evidence
uv run pytest tests/unit/models/dashboard/ -q→ 327 passed (26 new intest_theme_catalog.py).uv run ruff format --checkclean;uv run ruff checkclean on every changed file.uv run mypy src/ --strict→ 14 errors, all pre-existing in unrelated files (backend_cache_redis,service_protocol_auditor,model_onex_container, …); zero in any file this PR touches.Phase C1 exit unblocks Phase 1B's theme binding; Phase 1A is unaffected and may run in parallel.
Evidence-Ticket: OMN-16882
Evidence-Source: OCC#7466