Skip to content

fix(OMN-18114): carry the tenant-projection profile on the lab lanes, and gate lane profile carriage - #3385

Merged
jonahgabriel merged 6 commits into
devfrom
jonah/omn-18114-tenant-projection-lane-carriage
Sep 10, 2026
Merged

jonahgabriel merged 6 commits into
devfrom
jonah/omn-18114-tenant-projection-lane-carriage

Conversation

@jonahgabriel

@jonahgabriel jonahgabriel commented Sep 10, 2026 •

Copy link
Copy Markdown
Collaborator

OMN-18114

Eight omnimarket contracts declare runtime_profiles: [tenant-projection]. filter_manifest_for_runtime_profile admits a contract to a profile only when that profile is named in the contract's own list, so main and effects skip all eight — and no service on any .201 compose lane bound RUNTIME_PROFILE=tenant-projection. All eight were therefore discovered and consumed by nothing, permanently, with no error on any process, because the manifest each process sees simply never contained them.

Root cause, proven live and read-only before any edit

Inside the running omninode-runtime container on the .201 dev lane, 2026-09-10:

total_discovered 499
main owned 301
effects owned 177
tenant-projection owned 8
['canary_score_reducer', 'node_hook_event_capture', 'node_projection_cost_summary',
 'node_projection_delegation_inference_response', 'node_projection_dep_health',
 'projection_context_roi', 'projection_pattern_learning', 'projection_routing_decision']
main skipped_target True   effects skipped_target True

Off the broker, same lane:

GROUP    local.omnimarket.node_projection_delegation_inference_response.consume.1.0.0
           .__i.runtime-main.__t.onex.evt.omnibase-infra.inference-response.v1
STATE    Empty      MEMBERS 0
TOPIC    onex.evt.omnibase-infra.inference-response.v1
         PARTITION 0  CURRENT-OFFSET 644  LOG-END-OFFSET 840  LAG 196

Last commit on that group was 2026-09-04T09:46Z; lag was 126 on 09-06 and 143 on 09-08, so it grows monotonically. All eight contracts read Empty on the lane — uniform, so systemic. Positive control: local.runtime_config.delegation-orchestrator... on the same topic and partition reads Stable, so the probe works and the zero is a real absence.

The mechanism is src/omnibase_infra/runtime/auto_wiring/profile_ownership.py:88. The pin was made for onex-dev (OMN-17641, ratified permanent by OMN-17556), where no pod binds ONEX_TENANT_DB_URL and strict-mode wiring was crash-looping the runtime plane. It is a global contract declaration, and it collaterally orphaned the same eight on the compose lanes — which do bind that DSN (RUNTIME_PROFILE=main, ONEX_WIRING_STRICT_MODE=1, ONEX_TENANT_DB_URL=postgresql://tenant_projection_writer:***@postgres:5432/omnidash_analytics) and whose local topology resolves tenant_projection by dsn_env, not secret_ref.

The only carrier that exists anywhere is the onex-dev Deployment omnimarket-tenant-projection-writer.

Why no gate saw it

tests/ci/test_lane_projection_writer_coverage_omn17562.py measures the adjacent class: contracts the kernel subscribes and never dispatches. A contract removed from the manifest by the profile filter is never subscribed at all, so it cannot appear in that gate's 16-name registry, and it does not. OMN-17641 scoped compose out in as many words ("no compose-lane change, the .201 lab lanes are OMN-17562's"); OMN-17562 scoped profile-filtered contracts out implicitly. The class fell between the two.

RED then GREEN, same working tree

tests/ci/test_lane_runtime_profile_carriage_omn18114.py at the parent commit:

3 failed, 2 passed, 1 skipped
  test_carrier_profiles_are_carried_on_every_mutable_lab_lane
    dev: no started service binds RUNTIME_PROFILE=tenant-projection ... 8 contract(s)
    stability-test: no started service binds RUNTIME_PROFILE=tenant-projection ... 8 contract(s)
  test_lane_uncarried_profile_count_is_shrink_only
  test_the_carrier_is_inert_on_every_lane_this_repo_does_not_deploy
    No service in the base compose binds a carrier RUNTIME_PROFILE.

With the change: 5 passed, 1 skipped. The two that passed at the parent are the registration and unregistered-profile controls, both negative-controlled, so the three reds are the defect and not a broken harness.

The gate

Per governed lane it asserts: every profile a contract pins itself to resolves through the real load_runtime_profile and is consumer-attached in core, negative-controlled against a name the registry must refuse; dev and stability-test each start a service binding it; the per-lane uncarried count is pinned shrink-only; no started service names a profile the runtime cannot resolve (OMN-17985's k8s finding, applied at the compose layer where it never was); and the carrier stays inert on prod, judge and lakshman. A sixth test re-derives each profile's contract set through discover_contracts + filter_manifest_for_runtime_profile when omnimarket is importable, so the registry cannot drift from the ownership filter it describes; it skips loudly in the CI job where omnimarket is deliberately absent, the same limit the sibling gate records.

The carrier

The ONEX runtime kernel under a different RUNTIME_PROFILE — same image, same entrypoint, same wire_from_manifest path, no command: override — mirroring the onex-dev Deployment exactly. It is not a BaseProjectionRunner like the six standalone writers: that class builds a topology-blind adapter, which handler_hook_event_capture's own docstring records as the reason it stopped extending it.

It is declared in the base compose because the runtime env anchors live there and YAML anchors do not cross compose files; declaring it in an overlay would mean re-declaring ~340 lines of environment. It carries a compose profile no lane's bring-up requests, so prod, judge and lakshman render it and never start it, and the two lab overlays opt in by overriding profiles:. That inertness is asserted, not assumed. Its role identity is its own, not main's: a second kernel reusing main's capabilities would put a second claimant on main-owned topics and start duplicate heartbeat loops for the same deterministic node IDs.

The proof lane gets it too. stability-proven is resolved from that lane, so a proof lane carrying fewer contracts than the mutable one proves less than the lane it certifies.

Verification

  • Governed pre-push impacted-test selector escalated to the full suite and placed it on the lab host h201.2. Its first run was RED on three tests I had not hit locally, all real: the proof lane's carrier was sharing a concrete log-volume name with the dev lane's, and the catalog manifest's ONEX_TENANT_DB_URL made the carrier a discovered application-DSN consumer that docker/catalog/database-consumers.yaml did not declare. Both fixed in the second commit; that is what the selector is for.
  • uv run mypy src/ --strict — Success: no issues found in 3002 source files.
  • uv run ruff format / ruff check clean; pre-commit run --all-files clean.
  • No lane was mutated by this change. Every .201 probe was read-only: rpk group describe / group list through the credential-on-stdin helper with no SASL value printed, docker ps, and docker exec python running discovery plus the ownership filter. No compose action, no restart, no group or topic touched, no credential work.

Deliberately out of scope, stated rather than left absent

What the eight can write once carried. projection_delegation_inference_response_text has FORCE RLS with no writer policy (OMN-17298, In Progress) and the no-authority SET LOCAL app.tenant_id defect is OMN-17422. This change makes the contracts carried; whether their INSERTs are then accepted belongs to those tickets. onex-dev already has the carrier and is unchanged. lakshman is measured and not deployed here. The cross-repo leg — proving carriage against onex-dev's manifest set from this repo's CI — remains OMN-17456's honest limit.

A related but distinct defect, so the two are not conflated

A peer lane is fixing node_omnigate_projection, which looks similar and is not: that contract is in main's manifest and its attach raised InfraTimeoutError after readiness passed, which the boot reconciliation never retries. Transient, and it self-heals on the next boot. This one is never in the manifest at all and does not self-heal. None of its four blocker contracts is one of these eight.

Evidence-Ticket: OMN-18114
Evidence-Source: OCC#8881

… and gate lane profile carriage

Eight omnimarket contracts declare `runtime_profiles: [tenant-projection]`.
`filter_manifest_for_runtime_profile` admits a contract to a profile only when
that profile is named in the contract's own list, so `main` and `effects` skip
all eight -- and NO service on any .201 compose lane bound
RUNTIME_PROFILE=tenant-projection. All eight were therefore discovered and
consumed by nothing, permanently, with no error on any process, because the
manifest each process sees simply never contained them.

Measured read-only on the .201 dev lane 2026-09-10, inside the running
omninode-runtime container:

    total_discovered 499
    main owned 301
    effects owned 177
    tenant-projection owned 8
    main skipped_target True   effects skipped_target True

and off the broker:

    local.omnimarket.node_projection_delegation_inference_response.consume.1.0.0
      .__i.runtime-main.__t.onex.evt.omnibase-infra.inference-response.v1
    STATE Empty  MEMBERS 0  CURRENT-OFFSET 644  LOG-END-OFFSET 840  LAG 196

last commit 2026-09-04T09:46Z, lag 126 on 09-06 and 143 on 09-08. All eight
contracts read Empty on that lane; the delegation-orchestrator group on the SAME
topic reads Stable, which is the positive control that the probe works.

The profile pin was made for onex-dev (OMN-17641, ratified permanent by
OMN-17556), where no pod binds ONEX_TENANT_DB_URL and strict-mode wiring was
crash-looping the runtime plane. It is a GLOBAL contract declaration, and it
collaterally orphaned the same eight on the compose lanes -- which do bind that
DSN and whose local topology resolves `tenant_projection` by `dsn_env`.

RED then GREEN, same working tree:
  * before: 3 failed, 2 passed, 1 skipped -- the two lab lanes named, and the
    base compose reported as declaring no carrier at all.
  * after:  5 passed, 1 skipped.

WHY NO GATE SAW IT. tests/ci/test_lane_projection_writer_coverage_omn17562.py
measures the adjacent class -- contracts the kernel SUBSCRIBES and never
dispatches. A contract removed from the manifest by the profile filter is never
subscribed, so it cannot appear in that gate's 16-name registry, and it does not.
OMN-17641 scoped compose out in as many words; OMN-17562 scoped profile-filtered
contracts out implicitly. The class fell between the two.

THE GATE (tests/ci/test_lane_runtime_profile_carriage_omn18114.py) asserts, per
governed lane: every profile a contract pins itself to resolves through the real
load_runtime_profile and is consumer-attached in core (negative-controlled
against a name the registry must refuse); dev and stability-test each START a
service binding it; the per-lane uncarried count is pinned shrink-only; no
started service names a profile the runtime cannot resolve (OMN-17985's k8s
finding, applied at the compose layer where it never was); and the carrier stays
inert on prod, judge and lakshman. A sixth test re-derives each profile's
contract set through discover_contracts + filter_manifest_for_runtime_profile
when omnimarket is importable, so the registry cannot drift from the ownership
filter it describes.

THE CARRIER is the ONEX runtime kernel under a different RUNTIME_PROFILE -- same
image, same entrypoint, same wire_from_manifest path, no `command:` override --
mirroring the onex-dev Deployment exactly. It is NOT a BaseProjectionRunner like
the six standalone writers: that class builds a topology-blind adapter, which
handler_hook_event_capture's own docstring records as the reason it stopped
extending it.

It is declared in docker/docker-compose.infra.yml, because the runtime env
anchors live there and YAML anchors do not cross compose files -- declaring it in
an overlay would mean re-declaring ~340 lines of environment. It carries a
compose profile no lane's bring-up requests, so prod, judge and lakshman RENDER
it and never START it; the two lab overlays opt in by overriding `profiles:`.
That inertness is asserted, not assumed.

Its role identity is its own, not main's: a second kernel reusing main's
capabilities would put a second claimant on main-owned topics and start duplicate
heartbeat loops for the same deterministic node IDs. The runtime policy contract
gains a fourth, OPTIONAL process name; the three shared kernels stay mandatory on
every lane, so no lane is forced to declare a carrier it does not deploy.

Changes:
  * contracts/services/runtime_policy.contract.yaml + docker/runtime-policy.env:
    a `tenant-projection` process on the dev and stability-test lanes only.
    Rendered, not hand-edited; --check-env-file passes.
  * model_runtime_process_policy.py / model_runtime_profile_policy.py: the
    fourth name, and REQUIRED_RUNTIME_PROCESSES so "a name a lane MAY use" and
    "a name every lane MUST supply" stay two different statements.
  * docker-compose.infra.yml: the carrier, its volumes, and the inert profile.
  * docker-compose.dev-lane.yml / docker-compose.stability-test.yml: the opt-in.
    The proof lane gets it too -- `stability-proven` is resolved from that lane,
    so a proof lane carrying fewer contracts than the mutable one proves less
    than the lane it certifies.
  * docker/catalog/services/tenant-projection-writer.yaml + bundles.yaml: the
    catalog render is validated on every deploy, and an overlay stanza with no
    base definition there makes that render invalid.
  * scripts/deploy-runtime.sh: the two lane-only restart arrays. Deliberately
    NOT RUNTIME_SERVICES -- that array is lane-agnostic, and since the service
    IS declared in the base, a prod `up -d --no-deps` naming it would succeed
    and start the carrier on a lane that never opted in.
  * deploy/lane-census/lane-manifest.yaml, the two required-env manifests, and
    the four counting assertions that now count four runtime kernels instead of
    three, each with the reason recorded rather than the number bumped.

Out of scope, stated: what the eight can WRITE once carried.
projection_delegation_inference_response_text has FORCE RLS with no writer
policy (OMN-17298) and the no-authority GUC defect is OMN-17422. This makes them
carried; whether their INSERTs are accepted is those tickets'. onex-dev already
has the carrier and is unchanged. No lane was mutated by this change.

OMN-18114
…enant_projection binding

Both found by the governed pre-push full suite on the lab host, not locally.

  * tests/unit/infra/test_stability_test_runtime_lane.py
    ::test_stability_lane_volumes_do_not_reuse_production_names -- the proof
    lane's carrier was sharing the base's concrete log volume name with the dev
    lane's carrier. Both volumes are now lane-scoped, which the lane's own
    invariant requires and which also keeps two processes' output attributable.
  * tests/unit/topology/test_application_database_topology.py -- the catalog
    manifest declares ONEX_TENANT_DB_URL, so the carrier is a discovered
    application-DSN consumer and must be declared in
    docker/catalog/database-consumers.yaml. It binds tenant_projection for the
    same reason every other entry there does; the inventory is exact by design,
    so an undeclared consumer is drift rather than an omission.

OMN-18114

@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: 1
Deduped (already posted on this PR): 0
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.

Findings not anchored to a changed file

  • [MAJOR] hostile-reviewer (glm-review)

    Dev lane inherits shallow curl healthcheck; only stability-test gets the strict probe | The base declaration in docker-compose.infra.yml ships curl -sf http://localhost:8085/health, which the diff's own commentary (OMN-17883, OMN-15217) identifies as incapable of failing on a DEGRADED runtime. The stability-test overlay replaces it with the semantic probe, but the dev lane overlay adds only profiles, environment, and labels. The dev lane therefore runs the carrier with exactly the healthcheck the diff cal

    Resolve this thread when addressed — the Hostile Review Thread Gate blocks while hostile-reviewer threads are unresolved (OMN-17492).

  • [MAJOR] hostile-reviewer (glm-review)

    STABILITY_TEST tenant-projection env vars absent from required-env manifests | The stability-test overlay requires six STABILITY_TEST_RUNTIME_TENANT_PROJECTION_* variables with :? / ? interpolation, but the diff adds only the three DEV_ spellings to docker/required-env-vars.manifest.txt and generated-compose-required-env.manifest.txt. If the manifest gates completeness (its name and sorted structure indicate it does), the proof lane's requirements are undeclared, and a deployment that validates against

    Resolve this thread when addressed — the Hostile Review Thread Gate blocks while hostile-reviewer threads are unresolved (OMN-17492).

  • [MAJOR] hostile-reviewer (glm-review)

    Backlog on stranded consumer groups is abandoned, not drained or reset | The carrier deliberately uses new group ids so the eight contracts do not resume the stranded __i.runtime-main groups. That decision is stated but its consequence is not handled: the existing lag (documented at 196 and climbing for node_projection_delegation_inference_response, plus whatever backlog the other seven groups hold) is orphaned on the old groups with no offset-reset, migration, or monitoring plan. The defect that motivate

    Resolve this thread when addressed — the Hostile Review Thread Gate blocks while hostile-reviewer threads are unresolved (OMN-17492).

  • [MINOR] hostile-reviewer (glm-review)

    Proof lane mounts the dev delegation bifrost config | The stability-test overlay's volume list mounts ./lane-overlays/dev.bifrost.yaml into the proof-lane carrier. The lane sets BIFROST_VERIFY_ENDPOINTS=0 (verify disabled) per its contract profile, yet receives the dev lane's delegation configuration. Cross-lane config bleed on a lane whose purpose is to certify prod behavior undermines the proof premise the diff itself argues for ('a proof lane that carries fewer contracts than the mutable dev lane prove

    Resolve this thread when addressed — the Hostile Review Thread Gate blocks while hostile-reviewer threads are unresolved (OMN-17492).

  • [MINOR] hostile-reviewer (glm-review)

    Merged-services test helper diverges from real compose merge semantics | _merged_services in the new CI gate implements a hand-rolled merge: {**base_body, **overlay_body} plus special cases for environment and profiles. Real compose merge has per-key rules for many other keys (ports, volumes append sequences, labels append sequences, depends_on merges per-service). The gate's carriage and inertness decisions read only profiles and environment, so the special cases cover what it uses today, but the helpe

    Resolve this thread when addressed — the Hostile Review Thread Gate blocks while hostile-reviewer threads are unresolved (OMN-17492).

Findings demoted from threads (anchor rejected)

  • [MINOR] hostile-reviewer (glm-review)

    Strict-probe and labels-override tests assert global regex counts | The updated assertions count regex matches across the entire stability-test compose file: len(strict_blocks) == 4 and override_count == 4. These counts include every service in the file, so the test fails spuriously when any unrelated service adds a strict probe or an override label block, and it does not verify WHICH services carry the probe, only that the file-wide total is 4. The same brittleness the comment criticizes in >= 3 appl

    Resolve this thread when addressed — the Hostile Review Thread Gate blocks while hostile-reviewer threads are unresolved (OMN-17492).

@github-actions

github-actions Bot commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

⚠️ Hostile Reviewer — DEGRADED (informational)

Critical findings: 0
Major findings: 0
Total findings: 0
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)

Four counting/coverage assertions that describe the lab lanes by exact count and
now describe four runtime kernels instead of three. Each is updated with the
reason recorded, never the number bumped:

  * tests/ci/test_bifrost_lane_overlay.py — the carrier overrides `volumes:`
    wholesale (it needs a lane-scoped state root), so it would LOSE the pinned
    bifrost overlay mount silently if the override omitted it. The exact count
    is the point: an inequality would not see a service dropping out of the pin.
  * tests/ci/test_runtime_policy_contract.py — the carrier is the same kernel
    under a different RUNTIME_PROFILE, so its auto-wired consume boundary makes
    the same DLQ-routing decision and must read the same lane-scoped stance.
    Leaving it unbound would give one process on the lane an implicit position
    on a flag every other process declares explicitly.
  * tests/scripts/test_deploy_runtime_rt6_scoped_readback.py, both lane cases —
    RT-6 refuses to certify a deploy whose in-scope service has no running
    container, so a new member of either lane-only recreate array must appear in
    the readback fixture or the fixture stops describing the real lane. The
    proof-lane case needs no ps_map special case, which is itself the check that
    the overlay's lane-scoped container_name follows the convention.

WHY THE GOVERNED PRE-PUSH DID NOT CATCH THESE, stated because it matters more
than the four fixes. The heavy fail-closed escalation runs
`pytest ${FULL_SUITE_TARGET}` with FULL_SUITE_TARGET="tests/unit/"
(scripts/hooks/prepush_smart_tests.sh:1337) — a positional path. pytest then
ignores `testpaths`, so that run collects tests/unit/ ONLY and never
tests/ci/, tests/scripts/, tests/integration/, scripts/ci/tests or
scripts/tests. That is why h105 reported 27468 tests green on this exact tree
while CI failed four tests in tests/ci/ and tests/scripts/. It is the OMN-15410
defect class — a hardcoded positional path making the invocation, not
pyproject's testpaths, the real definition of "the full suite" — which
OMN-15410 closed in ci.yml and not in this hook. Reported separately; not fixed
here, because widening the pre-push target is a cost decision about every lane's
push, not a change to smuggle into a projection-carriage PR.

OMN-18114
…cope

OMN-18108 landed on dev while this branch was open and bound the shell array to
its Python twin: scripts/deploy-runtime.sh's DEV_LANE_ONLY_RUNTIME_SERVICES and
deploy_agent.events.DEV_LANE_ONLY_RUNTIME_SERVICES are now asserted equal in
both directions by test_dev_lane_only_scope_omn18108.py, so editing one alone is
a red test. That is the property the binding was added for, and it fired here
exactly as intended.

The carrier belongs in that scope for the OPPOSITE reason the six writers do,
stated separately so the two do not merge. Those services exist only in the
dev-lane overlay, so a prod deploy naming one fails outright. This service IS
declared in docker-compose.infra.yml, so every lane resolves the name — which is
precisely why it cannot be lane-agnostic: a prod or judge
`up -d --no-deps tenant-projection-writer` would SUCCEED and start the carrier on
a lane that never opted in, defeating the compose profile that keeps it inert.

It also matters more than most members. It is the only process that owns the
eight contracts pinned to `runtime_profiles: [tenant-projection]`, so an agent
scope that could not reach it would leave those eight running an image the rest
of the lane had moved past — the exact defect OMN-18108 measured, on the one
service where nothing else would ever notice.

Also merges origin/dev, which brings in OMN-18110's failed-attach retry. That is
the peer lane's fix for node_omnigate_projection and is a DIFFERENT mechanism
from this one: that contract is in main's manifest and its attach raised after
readiness passed, so a retry reaches it. The eight here are never in the
manifest at all, so no retry can.

OMN-18114
jonahgabriel added a commit to OmniNode-ai/onex_change_control that referenced this pull request Sep 10, 2026
#8881)

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

* evidence: OCC companion self-bind for #8881

* evidence(OMN-18114): bind AC1-AC4 to the RED-first carriage gate and the two lab-lane carriers

Additive only. No generated item is edited and the autobind-authored checks are
byte-identical.

AC1 — the gate is RED against the tree as it stood at parent 170474981: 3 failed,
2 passed, 1 skipped, naming dev and stability-test and all eight contracts, and
reporting that no service in the base compose bound a carrier profile at all.
GREEN after the change: 5 passed, 1 skipped. The two that passed at the parent
are the registration and unregistered-profile controls, so the three reds are the
defect and not a broken harness.

AC2 — the obligation is re-derived through the kernel's own discover_contracts
then filter_manifest_for_runtime_profile path and through the real
load_runtime_profile, and carriage is computed from the base plus overlay merge
rather than from a hand-maintained list.

AC3 — the gate lives under tests/ci/, collected by the full-suite step through
pyproject testpaths (that step passes no positional path by design), and the
per-lane count is pinned in BOTH directions so a landed carrier that did not
lower the pin fails too.

AC4 — both lab lanes carry it and prod does not.

THE THREE ABSENCE CONTROLS ARE COMPOUND, not bare. Each first requires a needle
that must be present at that ref, then requires the absent one to be absent, so a
failed fetch reads as a failure instead of as a green control. Proven both ways:
all 17 bound checks exit 0, and the two absence controls were re-run against
inputs where the needle IS present (the dev overlay, and tests/ci at the merged
head) and correctly exited non-zero.

AC5 is deliberately left unbound. It is the live consumer-group readback after
the deploy agent rebuilds the dev lane from the merged head, which has not
happened yet. It will be bound once real, or its absence named, rather than
asserted early.

OMN-18114

* evidence(OMN-18114): count with awk in the three absence controls, not grep

Two separate defects in the same three lines, both caught by this repo's own
gates rather than by review.

1. SIGPIPE. The re-run after the installation's API quota recovered went from
   0/22 to 19/22 PASS with a single BLOCK, and that one exited 141. `grep -q`
   stops at its first match and closes the pipe, killing the `printf` feeding
   it; under `pipefail` the command is then 141, not 0. A false red this gate
   has produced before.

2. Fail-open. Rewriting it as `grep -c ... || true` is the exact shape the
   OMN-9350 check_value linter rejects, and it is right to: the `|| true`
   swallows a failed fetch.

`awk` closes both. It reads all of its input, so nothing SIGPIPEs it; it always
exits 0, so no `|| true` is needed; and it prints an integer, so the comparison
is explicit. The result is fail-CLOSED, which is the property that matters:
proven by running the check with an empty body, where PRESENT counts 0 and the
check exits 1 rather than reporting a clean absence.

Verified under `set -o pipefail`: the live control exits 0 with present=1
absent=0, and the empty-body control exits 1.

OMN-18114

* fix(OMN-18114): bind AC receipts for OCC companion

---------

Co-authored-by: node-occ-companion-effect <occ-companion-effect@omninode.ai>
Co-authored-by: jonahgabriel <jonah@omninode.ai>
@jonahgabriel
jonahgabriel merged commit b2b9ca8 into dev Sep 10, 2026
140 of 141 checks passed
@jonahgabriel
jonahgabriel deleted the jonah/omn-18114-tenant-projection-lane-carriage branch September 10, 2026 09:39
jonahgabriel added a commit that referenced this pull request Sep 10, 2026
The committed census snapshot (deploy/lane-census/census-snapshot.json)
was emitted 2026-09-02T12:28:09Z and crossed the Lane Census Staleness
gate's 7-day MAX_AGE_DAYS boundary at ~2026-09-09T12:28Z, so
`Lane Census Staleness + E-5 Env Drift (OMN-13034)` has been red on
every open omnibase_infra PR since. This is the fourth occurrence of the
same treadmill; prior refreshes were #2691, #2846 and #2998.

Regenerated READ-ONLY against 192.168.86.201's live Docker Engine API
inventory (GET /containers/json, GET /networks — 166 containers, 23
networks, inventory_source=engine_api, no stderr, no compose mutation,
no lane touched), following the process the prior refresh PRs used:
capture the inventory with lane_census_inventory.py, then run the pure
planner (lane_census_plan.py) and the event builder
(lane_census_event.py) against this branch's lane-manifest.yaml. The
inventory script was shipped to a scratch path on .201 and checksum-
matched to this branch's copy (md5 5c85a052ce1bbee97fff2893316284f4)
rather than run from a checkout there, because .201 carries no canonical
omnibase_infra clone and the desired-state definition must match this PR
exactly.

New snapshot: emitted_at=2026-09-10T10:06:57.003308+00:00, drift_count=6
(up from 2). Nothing here is hand-edited; the four added findings are
what live state actually is.

Two of the four are the direct, expected consequence of #3385
(OMN-18114), which merged as the current dev tip and added
`omnimarket-stability-test-tenant-projection-writer` and
`omnimarket-tenant-projection-writer` to the manifest's desired state.
Neither lane has been rebuilt since that merge, so both read
container_absent. The census is correct: desired moved, actual has not
caught up yet. This snapshot documents that gap; it does not remediate
it, and remediation is out of scope for a doc-freshness refresh.

The other two are dev-lane unexpected_container warnings for `onex-api`
and `omninode-infra-routing-decisions-consumer` — both running and
healthy on the dev lane, both carrying com.omninode.lane='dev', neither
declared in the manifest. Also documented, not remediated.

The two findings already in the prior snapshot are unchanged: the prod
`omninode-prod-runtime-worker` container_absent and the dev
`omnibase-infra-forward-migration` unexpected_container warning.

Verified locally before commit, all three of the gate's own steps:
- check_lane_census_age.py --max-age-days 7 -> OK, 0h old
- check_lane_env_drift.py (E-5) -> OK, no drift across all five lanes
- generate_claude_lane_block.py -> renders clean
- 73/73 lane_census unit tests pass

Ticket: OMN-13034
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