Repository navigation
fix(OMN-16852): the catalog render stops handing the runtime build an empty OMNI_HOME - #3996
Conversation
… 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.
#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>
There was a problem hiding this comment.
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_argsalready 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
|
| 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)
Re-binds docker/runners/runner-image.lock.json over dev's #3996 lock. Onex-Lane: infra-release-cut Onex-Session: 47d209ab4acb41eda442f65283f4275d
…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....
…'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.
Ticket: OMN-16852, AC3 (epic OMN-16849, M4 criterion C17). Lane
omn16849-children.What
docker/catalog/generator.pyrendered"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_PATHfail-fast, and internal orchestration keepsOMNI_HOME.Here
OMNI_HOMEis internal:docker/Dockerfile.runtimebuilder guard (ARG OMNI_HOME="")BUILD_SOURCE=workspacebuild needs it, to stage sibling repos from the operator's registry. It exits 64 when it is empty._build_source_argsandscripts/deploy-runtime.sh--build-arg OMNI_HOME=…on the command lineENV OMNI_HOME=/app, read by omnimarket's registry-walking sweep and scan nodes (65 source files):-defaultThe checked-in compose files (
docker-compose.infra.yml,docker-compose.dev-lane.yml) declare noOMNI_HOMEbuild 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_HOMEunset. The 32 other required variables got inert placeholders. The before render (origin/deveb81f73f) 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.OMNI_HOME.docker build --target builder --build-arg BUILD_SOURCE=workspace …stops at the guard withBUILD_SOURCE=workspace requires OMNI_HOME, exit code 64. That is the fail-fast form.--build-arg OMNI_HOME=…passes the guard and reaches[builder 3/34], then was canceled on purpose.printenv OMNI_HOMEreturns/app. The internal image ENV is unaffected.Tests
test_runtime_image_build_carries_no_silent_omni_home_defaultasserts that no rendered build arg isOMNI_HOMEor carries a${OMNI_HOME:-expansion, with a positive control. It failed on origin/dev with('omninode-runtime', 'OMNI_HOME', '${OMNI_HOME:-}').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 verifywas stale before regeneration and verifies after.Evidence-Ticket: OMN-16852
Evidence-Source: OCC#10905