Skip to content

fix(OMN-16852): the catalog render stops handing the runtime build an empty OMNI_HOME - #3996

Merged
jonahgabriel merged 2 commits into
devfrom
jonah/omn-16852-catalog-no-omni-home-default
Sep 23, 2026
Merged

jonahgabriel merged 2 commits into
devfrom
jonah/omn-16852-catalog-no-omni-home-default

Conversation

@jonahgabriel

@jonahgabriel jonahgabriel commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

Ticket: OMN-16852, AC3 (epic OMN-16849, M4 criterion C17). Lane omn16849-children.

What

docker/catalog/generator.py rendered "OMNI_HOME": "${OMNI_HOME:-}" as a runtime-image build arg, which is a fail-soft expansion propagated into the render. That entry is removed.

Classification under the operator's 2026-08-28 boundary ruling

The ruling is recorded in omnibase_core#1714: customer and self-hoster parameters use OMNIBASE_PATH fail-fast, and internal orchestration keeps OMNI_HOME.

Here OMNI_HOME is internal:

reader what it does class
docker/Dockerfile.runtime builder guard (ARG OMNI_HOME="") only a BUILD_SOURCE=workspace build needs it, to stage sibling repos from the operator's registry. It exits 64 when it is empty. internal
deploy agent _build_source_args and scripts/deploy-runtime.sh the sanctioned workspace builds. Both refuse an unset value, then pass --build-arg OMNI_HOME=… on the command line internal
image ENV OMNI_HOME=/app, read by omnimarket's registry-walking sweep and scan nodes (65 source files) a fixed in-image value with no :- default internal, untouched

The checked-in compose files (docker-compose.infra.yml, docker-compose.dev-lane.yml) declare no OMNI_HOME build arg. The render entry added only a silent empty default. Removing it leaves the Dockerfile's workspace guard as the fail-fast form. Release builds are unchanged, because the Dockerfile ARG already defaults empty.

Lab proof (.201, 2026-09-23T10:02Z)

The branch tree was shipped to a scratch directory on .201 with git archive. No lane or container was touched.

  • docker compose config, OMNI_HOME unset. The 32 other required variables got inert placeholders. The before render (origin/dev eb81f73f) interpolates the runtime build args to {'BUILD_SOURCE': 'release', 'EXPECTED_BUILD_SOURCE': 'release', 'OMNI_HOME': ''}. The after render (this branch) interpolates them to {'BUILD_SOURCE': 'release', 'EXPECTED_BUILD_SOURCE': 'release'}. Both returned rc 0.
  • Workspace build without OMNI_HOME. docker build --target builder --build-arg BUILD_SOURCE=workspace … stops at the guard with BUILD_SOURCE=workspace requires OMNI_HOME, exit code 64. That is the fail-fast form.
  • Positive control. The same build with --build-arg OMNI_HOME=… passes the guard and reaches [builder 3/34], then was canceled on purpose.
  • Running dev-lane runtime. printenv OMNI_HOME returns /app. The internal image ENV is unaffected.

Tests

  • RED first. The new test_runtime_image_build_carries_no_silent_omni_home_default asserts that no rendered build arg is OMNI_HOME or carries a ${OMNI_HOME:- expansion, with a positive control. It failed on origin/dev with ('omninode-runtime', 'OMNI_HOME', '${OMNI_HOME:-}').
  • GREEN after: 54 catalog tests passed (generator, redaction guard, healthcheck probe, Infisical-first config, roundtrip, render placeholder) and 20 deploy-agent compose-gen tests passed.

Release identity

Version 0.38.56 → 0.38.57 for the OMN-13412 gate, because packaged source changed after 0.38.56 published. The runner image identity was regenerated for the new shared_env_digest. --mode verify was stale before regeneration and verifies after.

Evidence-Ticket: OMN-16852
Evidence-Source: OCC#10905

… empty OMNI_HOME

AC3: docker/catalog/generator.py emitted "OMNI_HOME": "${OMNI_HOME:-}" as a
runtime-image build arg, a fail-soft expansion propagated into the render.

Classified under the operator's 2026-08-28 boundary ruling (recorded in
omnibase_core#1714): OMNI_HOME is INTERNAL here. Only a BUILD_SOURCE=workspace
build reads it, to stage sibling repos from the operator's registry, and
every sanctioned workspace build already passes it as --build-arg after
refusing an unset value (deploy agent _build_source_args, deploy-runtime.sh).
The checked-in compose files declare no such arg. The render entry added only
a silent empty default, so it is removed and the Dockerfile's own workspace
guard (exit 64) is the fail-fast form. Release builds are unchanged: the
Dockerfile ARG already defaults empty. The image's ENV OMNI_HOME=/app, read by
omnimarket's registry-walking sweep nodes, is internal and untouched.

TDD: a new test asserts no rendered build arg is OMNI_HOME or carries an
${OMNI_HOME:- expansion, with a positive control. RED before (1 failed),
GREEN after; 54 catalog tests and 20 deploy-agent compose-gen tests pass.

Version 0.38.56 -> 0.38.57 for the OMN-13412 release-identity gate, and the
runner image identity regenerated for the resulting shared_env_digest.
…ites

The Integration Test Coverage gate wants a tests/integration/ test for a
feature change. This runs omnibase_infra.docker.catalog.cli generate runtime
in a subprocess with OMNI_HOME removed from its environment and asserts on
the compose file it writes (what docker compose and the deploy agent's
compose_gen consume), with the BUILD_SOURCE arg as the positive control.
Passes on this branch; fails against origin/dev's generator.
jonahgabriel pushed a commit to OmniNode-ai/onex_change_control that referenced this pull request Sep 23, 2026
#10905)

* evidence: OCC companion pass 1 for OmniNode-ai/omnibase_infra#3996

* evidence: OCC companion self-bind for #10905

---------

Co-authored-by: node-occ-companion-effect <occ-companion-effect@omninode.ai>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hostile Reviewer — adversarial findings (OMN-17492)

Models succeeded: glm-review
Models failed: codex
New finding threads: 0
Deduped (already posted on this PR): 0
Below quorum (one model only, reported not threaded): 5
Nit-level findings suppressed: 0

The model is the FINDER, never the gate: merge is gated only by the
deterministic Hostile Review Thread Gate, which blocks while
hostile-reviewer threads are unresolved. Resolve each thread after
addressing (or rejecting, with a reply) its finding.

Below quorum: 5 finding(s) raised by one model only (OMN-18479)

These are reported and NOT dropped, but they get no thread and do not block: a single model's finding no other model reproduced is not evidence enough to stop a merge. Read them; act on them if they are right.

  • [MAJOR] src/omnibase_infra/docker/catalog/generator.py, _runtime_image_build (glm-review) — Removal of OMNI_HOME arg changes env pass-through for compose-driven workspace builds | Previously the rendered stanza contained "OMNI_HOME": "${OMNI_HOME:-}", which under compose interpolation forwar
  • [MINOR] tests/integration/test_catalog_render_no_omni_home_default_omn16852.py; tests/unit/infra/test_catalog_generator.py (glm-review) — Negative assertions are partially redundant and the substring check is weaker than the key check | In the integration test, assert "OMNI_HOME" not in build_args already implies no key or value check
  • [MINOR] tests/integration/test_catalog_render_no_omni_home_default_omn16852.py, line ~44 (glm-review) — Integration test hardcodes service name and depends on the runtime bundle being the only/first build stanza | The test indexes compose["services"]["omninode-runtime"]["build"]["args"] directly. The un
  • [MINOR] tests/integration/test_catalog_render_no_omni_home_default_omn16852.py; docker/Dockerfile.runtime (referenced, unchanged) (glm-review) — No test covers the fail-fast path the change relies on | The stated safety argument is that the Dockerfile's workspace guard fails fast when OMNI_HOME is unset under BUILD_SOURCE=workspace. The diff r
  • [MINOR] docker/runners/runner-image.lock.json; uv.lock; pyproject.toml (glm-review) — Lock file digest bumps carry no provenance or regeneration evidence | identity_digest, shared_env_digest, uv.lock version, and image_version change in the same diff as the OMNI_HOME removal with no ex

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Hostile Reviewer — DEGRADED (informational)

Critical findings: 0
Major findings: 1
Total findings: 5
Models succeeded: glm-review

Note: Fewer than 2 reviewer models succeeded. Degraded results are informational (OMN-8468/OMN-8524) and do not block merge. Error: cli_review exit 2 (fewer than 2 models succeeded — partial/total outage)


Semantics (OMN-17492 — the model finds, thread resolution gates)

Surface Meaning Blocks merge?
Review threads Per-finding, posted by the reviewer No (informational)
Hostile Review Thread Gate Deterministic: unresolved hostile-reviewer threads exist Fails until resolved (not yet a required context)
degraded verdict Fewer than 2 models succeeded (infra) No

Powered by omniintelligence.review_pairing.cli_review — multi-model adversarial review: qwen3-review, qwen3-review-b, glm-review (OMN-8468/OMN-8524/OMN-17492)

@jonahgabriel
jonahgabriel merged commit c159b71 into dev Sep 23, 2026
154 checks passed
@jonahgabriel
jonahgabriel deleted the jonah/omn-16852-catalog-no-omni-home-default branch September 23, 2026 10:32
jonahgabriel pushed a commit that referenced this pull request Sep 23, 2026
Re-binds docker/runners/runner-image.lock.json over dev's #3996 lock.

Onex-Lane: infra-release-cut
Onex-Session: 47d209ab4acb41eda442f65283f4275d
Patel230 added a commit that referenced this pull request Sep 24, 2026
…lock

uv.lock participates in the runner image's shared_env_digest as raw bytes,
so the release-version line moving to 0.38.58 made the recorded digest
stale and runner-image-build-smoke refused the build (run 36073948714).
Regenerated with scripts/ci/runner_image_identity.py --mode generate, as
the version bumps on dev do (#3996): --mode verify exits 1 before and 0
after. shared_env_digest 99110eda... -> 105217eb...; identity v9
9e6e5ba6... -> 08efb837....
jonahgabriel pushed a commit that referenced this pull request Sep 25, 2026
…'s schema (#4079)

* fix(OMN-17887): retire the tenant schema; public is the TENANT domain's schema

Operator ruling 2026-09-24: no tenant Postgres schema will be built and
the TENANT domain lives in public for good (ADR-0027 amendment).

Topology: the local, onex-dev and onex-prod instances drop the 12
`schema: tenant` USAGE grant blocks and the 3 `tenant:` schemas entries;
the 9 rendered catalogs are regenerated. The expected-schema maps drop
tenant. The TENANT_TABLES_PHYSICALLY_IN_PUBLIC_UNTIL_OMN15359 bridge and
its consumers are removed: tenant-domain relations are declared public
directly (omnimarket OMN-17887 step 1), and the 7 legacy migration
declarations follow.

Domain enforcement follows the topology instead of banning public: a
public.<name> target is an ordinary application location held to the
exactly-one-ownership check when the topology declares public, and a
relation in public is refused unless its domain matches the declared
schema domain (TENANT). The retired tenant schema is refused as unknown.

The tenant GUC parity gate resolves domains per database from the
topology's own schemas block; its domain map is identical to dev's
(55 internal / 23 tenant / 3 unresolved, 0 violations). The domain
enforcement proof seed, ownership config, audited function hash and red
controls move to public (proof: PASS, 6 relations, 46 red controls).

CI checks the omninode_infra ownership manifests at the OMN-17887 step 2
merge (8b8d5d3b). Tests that pinned tenant now pin the new truth; none
were deleted or weakened, and new tests pin that tenant is refused.

* fix(OMN-17887): advance omnimarket contract pin

* chore(OMN-17887): bump release version

* fix(OMN-17887): app_dashboard and onex_api keep schema USAGE, retargeted to public

The retire commit removed the `schema: tenant` USAGE blocks outright. For
tenant_projection_writer and validator_ro that was right: both already
held USAGE on public, so the tenant block was a duplicate claim on the
same domain. For app_dashboard and onex_api it was not: USAGE on tenant
was their only declared access to the TENANT domain, and removing it
left them declaring platform_catalog alone.

"Tenant lives in public" means the declaration moves, not that it goes.
Both principals now declare USAGE on public in local, onex-dev and
onex-prod, and the 9 rendered catalogs are regenerated.

Found by the omninode_infra Kubernetes consumer check: its schema_access
for these two bindings reads [tenant, platform_catalog], and the honest
translation is [public, platform_catalog], which the projection has to
carry.

Verified: grant derivation --check --prove in sync on 3 instances; tenant
GUC parity 0 violations; domain-enforcement proof PASS on PostgreSQL 16
(6 relations, 4 pools, 46 red controls); unit topology, validation, ci
and scripts suites show no failure that untouched dev does not also show.

* chore(OMN-17887): restore final newlines and sync uv.lock to 0.38.58

496cd1b and d043526 were written without a trailing newline, which
the end-of-file-fixer hook rejects, and d043526 bumped pyproject.toml
to 0.38.58 without the matching uv.lock line, which uv rewrites on the
next run. Content is otherwise unchanged: the pin stays at 3441453c.

* chore(OMN-17887): rebind the runner image identity to the 0.38.58 uv.lock

uv.lock participates in the runner image's shared_env_digest as raw bytes,
so the release-version line moving to 0.38.58 made the recorded digest
stale and runner-image-build-smoke refused the build (run 36073948714).
Regenerated with scripts/ci/runner_image_identity.py --mode generate, as
the version bumps on dev do (#3996): --mode verify exits 1 before and 0
after. shared_env_digest 99110eda... -> 105217eb...; identity v9
9e6e5ba6... -> 08efb837....

* test(OMN-17887): the OMN-17292 upstream-addition replay declares public, not the retired tenant

test_an_upstream_contract_addition_cannot_red_an_unrelated_infra_pr replays
an omnimarket merge that adds a db_io table and expects the grant check to
go red. Its replay contract declared schema: tenant; with tenant retired
that declaration is an unmappable residual, derives no grant, and the red
half of the proof passed vacuously (Application Database Domain
Enforcement, run 36074333168). public is the TENANT domain's schema, so
the replay now declares public and derives a tenant_projection_writer grant
the checked-in topology lacks, which is the drift the proof needs.

Verified with omnimarket at the pin (3441453c) as .proof-dependencies: the
test passes, and with the old tenant replay it fails again. The other eight
suites that read .proof-dependencies: 119 passed, 1 skipped (needs GNU
realpath, Linux only).

* fix(OMN-17887): keep accepting the formerly-bridged tenant relations unqualified in the SQL lint

Removing the tenant bridge also removed the qualification lint's allowance
for those 24 relations, so deployed, append-only migrations that name them
without a schema started failing: the Application Database Domain
Enforcement SQL gate refused node_projection_delegation/0046's bare
ALTER TABLE delegation_events (#4062, run 36075133104).

The allowance comes back as exactly what it was: the same 24 names, as a
lint-only set that maps nothing to a schema. A qualified public.<name>
target is still held to the ownership check. It is not widened to every
public relation: 36 grandfathered 'must be schema-qualified' entries in
application_database_sql_baseline.yaml name public tables outside it, and
widening would turn them stale at the dev->main promotion.

Tests: 0046's statement lints clean; an unknown name and a public-granted
but never-bridged name (dispatch_eval_results) are still refused; the set is
pinned to the retired bridge. Removing the allowance turns the 0046 test red.
Domain proof PASS (46 red controls); unit/validation plus the SQL-gate, pin
and enforcement-contract suites: 1580 passed.

* test(OMN-17887): the cohort-key vendor's linter positive control targets the retired tenant schema

tests/unit/migrations/test_omn18930_cohort_key_vendor.py (from #4062) proved the
linter live by rewriting 0046's target to public.pg_class and expecting a
lint violation. That relied on the old outright ban on public. With public
the TENANT domain's schema, a public.<name> target passes the lint and is
held to the SQL gate's exactly-one-ownership check instead, like every other
application schema (Tests Split 7/15, run 36080758457).

The control now rewrites the target to the retired tenant schema, which the
lint refuses as unknown, and separately asserts that public.pg_class becomes
an ownership requirement the gate must satisfy. 4 passed; Split 7/15 was the
only failing split in that run.
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.

1 participant