Skip to content

fix(OMN-15379): lane-scope the node-migration fence release to the lab lane (operator ruling 15) - #2540

Merged
jonahgabriel merged 1 commit into
devfrom
jonah/omn-15379-lane-scoped-fence-release
Jul 29, 2026
Merged

jonahgabriel merged 1 commit into
devfrom
jonah/omn-15379-lane-scoped-fence-release

Conversation

@jonahgabriel

@jonahgabriel jonahgabriel commented Jul 29, 2026 •

Copy link
Copy Markdown
Collaborator

Implements operator ruling 15 (2026-07-29) for OMN-15379: node_service_registry FORCE ROW LEVEL SECURITY extends to the lab lane only. The lab (compose dev lane, project omnibase-infra) applies node_projection_registration 0000/0001/0002 in full — CREATE + heartbeat columns + ENABLE/FORCE RLS — as the FORCE proving ground. The staging k8s fence (omninode_infra FENCED_NODE_MIGRATION_IDS) is untouched by this PR.

Ticket: OMN-15379. Related: OMN-15336 (the fence this extends), OMN-15343 / OMN-15335 / OMN-15088 (the FORCE hold), OMN-15332 (ruling trail).

Two changes

1. registration 0002 joins the compose fence — a parity fix, not a cosmetic one

0002_node_service_registry_tenant_rls.sql was in the k8s list from the start and missing from the compose list shipped in #2532. With 0000 (the CREATE) fenced and 0002 (the dependent ALTER) unfenced, every cold compose lane ran 0002 against a table that had never been created:

psql:.../0002_node_service_registry_tenant_rls.sql:41
ERROR:  relation "node_service_registry" does not exist

…taking the forward-migration one-shot to exit 3 and the whole lane with it. Measured live on .201 2026-07-29 — that is what makes OMN-15379 a blocker rather than a corpus curiosity. A fence that gates a CREATE but not its dependent ALTER is worse than no fence. This also restores exact cross-repo parity at seven ids.

Net effect for every non-dev lane (stability-test / prod / judge / CI / fresh volumes): strictly safer. 0002 goes from applied unattended to fenced, and cold bring-up on those lanes stops dying at exit 3.

2. A lane-scoped release, fail-closed on three independent axes

scripts/run-forward-migrations.sh gains a committed, lane-keyed release policy consulted inside the fenced-id branch:

  • Default is fully fenced. ONEX_MIGRATION_LANE unset → release nothing. An unknown value → release nothing, and say so on stderr. There is no value that widens the fence relative to today.
  • The release set is committed, not supplied. The env var selects among policies literal in the runner; it never carries ids. And because the release is only consulted for an id the fence already covers, a lane can only ever un-gate a strict subset of the fence.
  • Not inheritable. The indicator is deliberately not in docker-compose.infra.yml. Every non-dev lane overlay merges that base — stability-test's forward-migration override is a lone container_name: line, so it inherits the base environment: block wholesale. A value there would be inherited by stability-test, prod, judge, and any lane added later: fail-OPEN, silently. It lives in the new docker/docker-compose.dev-lane.yml, which only the dev/lab project loads.

delegation 0023-0026 are not releasable on any lane — ruling 15 is scoped to node_service_registry; the delegation tenant-RLS hold is a separate, still-pending ruling. Asserted by test.

SEAMS (declared, matched field-by-field)

Seam A — the fence list (cross-repo, omnibase_infra ↔ omninode_infra). Unchanged in mechanism from OMN-15336, now at seven ids:

field k8s Job compose runner
id grammar node:${node_name}:$(basename "$sql_file") node:${node_name}:${filename}
list contents AND order FENCED_NODE_MIGRATION_IDS=(...) bash array, 7 ids newline-delimited string, same 7 ids, same order
match exact string equality per element grep -Fxq (exact whole-line)
position in loop before the already-applied probe before the already-applied probe
ledger row on skip NO NO
env-overridable NO NO

Live source of truth read at authoring time: omninode_infra@dev 966463a; the cross-repo diff leg was additionally run against a freshly-fetched 662a366 and passes.

Seam B — the lane indicator (compose → runner, new).

field producer consumer
name ONEX_MIGRATION_LANE ONEX_MIGRATION_LANE
type / grammar string; the single recognised value is dev case over exactly dev / "" / *
producer file docker/docker-compose.dev-lane.yml, services.forward-migration.environment —
absent value not set anywhere else ${ONEX_MIGRATION_LANE:-} → "" arm → release nothing
unknown value — * arm → release nothing + stderr warning
loaded by resolve_compose_file_args() (deploy-runtime.sh) and _LANE_CONFIGS[DEV] (deploy_agent/executor.py) —

Seam C — the release set. LANE_RELEASED_NODE_MIGRATION_IDS for lane dev == exactly the three node_projection_registration ids, asserted to be a strict subset of the fence and asserted to contain no delegation id on any lane.

Cross-boundary test — drives the actual runner, both ways

tests/scripts/test_node_migration_fence_parity.py grows 20 tests (14 static, 3 live, plus the existing 2 live and the cross-repo leg). The live half executes the shipped scripts/run-forward-migrations.sh against a real Postgres over the real vendored registration SQL (copied verbatim from docker/migrations/forward/nodes, not marker stubs — marker stubs cannot prove FORCE landed), with the app_dashboard role created by a flat migration so 0002's real precondition is exercised:

  • test_dev_lane_applies_the_registration_trio_with_force_rls — ONEX_MIGRATION_LANE=dev → all three applied + recorded; pg_class.relrowsecurity = true and relforcerowsecurity = true; last_heartbeat_at/uptime_seconds/tenant_id columns present; tenant_isolation policy present. Negative control in the same run: a fenced delegation id rides along and must still be skipped.
  • test_default_lane_skips_the_trio_and_the_registry_table_is_absent — indicator genuinely unset (popped, so an ambient value on the test host cannot leak in through the os.environ splat) → all three reported operator-gated, node_service_registry does not exist, no ledger rows.
  • test_unknown_lane_value_fails_closed_to_the_full_fence — value stability-test (a real lane name, so loose prefix/glob matching betrays itself) → fenced, plus the stderr warning.

The pair is the discriminator: a runner that ignored the indicator entirely fails one leg whichever way it defaulted, so neither leg can pass vacuously.

Static half additionally asserts the compose seam directly: the indicator is absent from docker-compose.infra.yml and from docker/catalog/services/forward-migration.yaml (so a catalog regenerate cannot reintroduce it), absent from all three non-dev overlays, present as exactly dev in the dev overlay, and wired into both lane→compose-file mappings.

RED-vs-origin/dev (proven by execution, not asserted)

Same tests against the pre-change artifacts (shipped files reverted to origin/dev, tests kept): 10 fail / 18 pass.

FAILED test_fence_is_exactly_the_expected_ids_in_order          <- 0002 missing from the fence
FAILED test_lane_release_arms_are_exactly_dev_default_and_unknown
FAILED test_unset_and_unknown_lane_are_fully_fenced
FAILED test_dev_lane_releases_exactly_the_registration_trio
FAILED test_no_lane_can_release_anything_outside_the_fence
FAILED test_delegation_ids_are_not_releasable_on_any_lane
FAILED test_release_is_checked_inside_the_fenced_branch_not_beside_it
FAILED test_only_the_dev_lane_overlay_carries_the_indicator
FAILED test_dev_lane_overlay_is_wired_into_both_lane_mappings
FAILED test_dev_project_layers_only_the_dev_lane_overlay

Also note: test_fence_matches_omninode_infra_k8s_runner (opt-in cross-repo diff) was a latent RED on origin/dev — compose had 6 ids, k8s had 7. This PR is what makes it green.

Gates — all on .200 per rule 11a, sha256-verified patch-transfer both directions

All six changed files verified byte-identical between this Mac and the .200 worktree before and after gating (797e2b98… runner, c87528f4… overlay, 2877c0b4… parity test, …).

  • ruff check + ruff format --check on scripts/ + tests/scripts/: clean.
  • Governed change-aware selector (scripts/ci/detect_test_paths.py) resolved this diff to tests/scripts/ tests/unit/scripts/ → 1346 passed, 5 skipped (skips all pre-existing/environmental: 4× GNU realpath -m OMN-15134, 1× the opt-in cross-repo leg).
  • Fence + lane suites with live Postgres and REQUIRE_MIGRATION_LOCK_DB=1 (so a missing DB fails rather than silently skips) and REQUIRE_CROSS_REPO_FENCE_PARITY=1: 34 passed, 0 skipped.
  • Fence-consumer suites (tests/ci/test_node_migration_shape_reconciliation.py, tests/unit/migrations/, tests/integration/migrations/, test_judge_compose_profile.py): 214 passed, 1 failed — test_sync_check_reports_in_sync, proven pre-existing by running the identical test on a pristine origin/dev worktree on the same host (same failure). Cause is .200's omnimarket clone sitting at 69a576c4 (feat(OMN-12957): wire runtime-profile validator + core registry parity guard #1945) while infra's vendored tree was synced from f4f27a98 (fix(OMN-12989): workspace-mode image build must honor sibling lock pins #1947); no vendored SQL is touched by this diff.
  • pre-commit run --all-files: 4 hooks fail — URL Authority Gate, Node Migration Vendor Sync Check, SPDX Header Requirement (2026 year in two files this diff does not touch), check-required-env-vars (GITHUB_TOKEN absent from that host's env). A pristine origin/dev worktree on the same host fails 5, a strict superset. Every failure is pre-existing; none is introduced here.
  • The commit itself ran the staged-file pre-commit hooks and passed — no --no-verify, no --no-gpg-sign, no skip token.

Deliberately NOT done

  • The staging k8s fence is untouched. No edit to omninode_infra. Ruling 15 is lab-only and the staging un-fence is waiting on the evidence this lab lane will now generate.
  • No rollback of the already-breached lanes, no DDL by hand, no RLS/FORCE change on prod, stability-test or judge — those remain outstanding operator decisions (OMN-15088).
  • The fence is not widened beyond k8s parity. 0003/081/0027 belong to OMN-15088.
  • Single-sourcing the fence list across the two repos is still not done — that is the cross-repo architecture change filed as OMN-15349, and this PR keeps the same always-on pinned-tuple + opt-in live-diff shape rather than re-architecting unilaterally.

Honest limit, stated rather than papered over

A deliberate -e ONEX_MIGRATION_LANE=dev on another lane's forward-migration container would release the trio there. That is an explicit operator act on the same footing as editing the fence list itself, and it is not defended against: the container has no un-forgeable lane fact to corroborate against — compose does not inject the project name, and a service cannot read its own container_name from inside itself. What is defended against is the realistic failure — silent inheritance through the base compose file — and that is closed by construction plus test.

Evidence-Source: OCC#5469

Evidence-Ticket: OMN-15379

…b lane

Operator ruling 15 (2026-07-29): node_service_registry FORCE ROW LEVEL
SECURITY extends to the LAB LANE ONLY. The lab (compose dev lane, project
omnibase-infra) applies node_projection_registration 0000/0001/0002 in full
as the FORCE proving ground; the omninode_infra k8s fence is UNCHANGED.

1. registration 0002 joins the compose fence, restoring exact parity with the
   k8s list at seven ids. It was in the k8s list from the start and missing
   here. Not cosmetic: with 0000 (the CREATE) fenced and 0002 (the dependent
   ALTER) not, every COLD compose lane ran 0002 against a table that had never
   been created and died at exit 3 (measured on .201 2026-07-29).

2. A lane-scoped release, fail-closed on three independent axes: unset or
   unknown ONEX_MIGRATION_LANE releases nothing; the release SET is committed
   in the runner and is only ever consulted for already-fenced ids; and the
   indicator lives in a dev-only overlay, never in the base compose that every
   lane overlay merges.
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 37 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3ffbcea6-4dd8-475e-83c5-91300790f610

📥 Commits

Reviewing files that changed from the base of the PR and between e0d1530 and 7964da7.

📒 Files selected for processing (6)
  • docker/docker-compose.dev-lane.yml
  • scripts/deploy-agent/deploy_agent/executor.py
  • scripts/deploy-runtime.sh
  • scripts/run-forward-migrations.sh
  • tests/scripts/test_deploy_runtime_lane_overlay.py
  • tests/scripts/test_node_migration_fence_parity.py

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

jonahgabriel added a commit to OmniNode-ai/onex_change_control that referenced this pull request Jul 29, 2026
Hand-authored because the machine mint is STRANDED, not bypassed: the
occ-companion-effect publisher SUCCEEDED on #2540, but live readback of the
.201 dev broker shows onex.cmd.omnimarket.occ-companion-effect-requested.v1 at
HIGH-WATERMARK 25 with ZERO consumer groups. The consuming runtime is down, and
it is down because of the cold-bring-up failure #2540 fixes. OMN-15247 class.

Five dod_evidence items. Every check_value was round-tripped out of the YAML
and EXECUTED before this commit; none is a shape-only declaration.

  fence carries registration 0002 (pinned head SHA): head 2 / origin-dev 0
    -> RED proven against the pre-change artifact
  indicator absent from base compose: base 0 / dev-lane overlay 2
    -> discriminates; it is not a file-presence check
  FORCE RLS live on the lab lane: exit 1 (table absent, cold lane)
    -> DECLARED acceptance, NOT satisfied. Admissibility proven both ways:
       the identical probe against stability-test, where node_service_registry
       IS forced, exits 0. So the RED is a state fact, not a broken command.
  non-dev lanes unchanged: exit 0 against the true baseline 060
    -> asserts UNCHANGED, not zero. stability-test's six forced tables are the
       pre-existing OMN-15336 breach, deliberately not rolled back here; a
       zero-expectation probe would go RED for a condition this PR did not
       cause. A 000 expectation exits 1 (RED control).

No Done flip. Net-new file only.
jonahgabriel added a commit to OmniNode-ai/onex_change_control that referenced this pull request Jul 29, 2026
Hand-authored because the machine mint is STRANDED, not bypassed: the
occ-companion-effect publisher SUCCEEDED on #2540, but live readback of the
.201 dev broker shows onex.cmd.omnimarket.occ-companion-effect-requested.v1 at
HIGH-WATERMARK 25 with ZERO consumer groups. The consuming runtime is down, and
it is down because of the cold-bring-up failure #2540 fixes. OMN-15247 class.

Five dod_evidence items. Every check_value was round-tripped out of the YAML
and EXECUTED before this commit; none is a shape-only declaration.

  fence carries registration 0002 (pinned head SHA): head 2 / origin-dev 0
    -> RED proven against the pre-change artifact
  indicator absent from base compose: base 0 / dev-lane overlay 2
    -> discriminates; it is not a file-presence check
  FORCE RLS live on the lab lane: exit 1 (table absent, cold lane)
    -> DECLARED acceptance, NOT satisfied. Admissibility proven both ways:
       the identical probe against stability-test, where node_service_registry
       IS forced, exits 0. So the RED is a state fact, not a broken command.
  non-dev lanes unchanged: exit 0 against the true baseline 060
    -> asserts UNCHANGED, not zero. stability-test's six forced tables are the
       pre-existing OMN-15336 breach, deliberately not rolled back here; a
       zero-expectation probe would go RED for a condition this PR did not
       cause. A 000 expectation exits 1 (RED control).

No Done flip. Net-new file only.
jonahgabriel added a commit to OmniNode-ai/onex_change_control that referenced this pull request Jul 29, 2026
Hand-authored because the machine mint is STRANDED, not bypassed: the
occ-companion-effect publisher SUCCEEDED on #2540, but live readback of the
.201 dev broker shows onex.cmd.omnimarket.occ-companion-effect-requested.v1 at
HIGH-WATERMARK 25 with ZERO consumer groups. The consuming runtime is down, and
it is down because of the cold-bring-up failure #2540 fixes. OMN-15247 class.

Five dod_evidence items. Every check_value was round-tripped out of the YAML
and EXECUTED before this commit; none is a shape-only declaration.

  fence carries registration 0002 (pinned head SHA): head 2 / origin-dev 0
    -> RED proven against the pre-change artifact
  indicator absent from base compose: base 0 / dev-lane overlay 2
    -> discriminates; it is not a file-presence check
  FORCE RLS live on the lab lane: exit 1 (table absent, cold lane)
    -> DECLARED acceptance, NOT satisfied. Admissibility proven both ways:
       the identical probe against stability-test, where node_service_registry
       IS forced, exits 0. So the RED is a state fact, not a broken command.
  non-dev lanes unchanged: exit 0 against the true baseline 060
    -> asserts UNCHANGED, not zero. stability-test's six forced tables are the
       pre-existing OMN-15336 breach, deliberately not rolled back here; a
       zero-expectation probe would go RED for a condition this PR did not
       cause. A 000 expectation exits 1 (RED control).

No Done flip. Net-new file only.
jonahgabriel added a commit to OmniNode-ai/onex_change_control that referenced this pull request Jul 29, 2026
…ped fence release (ruling 15) (#5469)

* evidence(OMN-15379): OCC companion for OmniNode-ai/omnibase_infra#2540

Hand-authored because the machine mint is STRANDED, not bypassed: the
occ-companion-effect publisher SUCCEEDED on #2540, but live readback of the
.201 dev broker shows onex.cmd.omnimarket.occ-companion-effect-requested.v1 at
HIGH-WATERMARK 25 with ZERO consumer groups. The consuming runtime is down, and
it is down because of the cold-bring-up failure #2540 fixes. OMN-15247 class.

Five dod_evidence items. Every check_value was round-tripped out of the YAML
and EXECUTED before this commit; none is a shape-only declaration.

  fence carries registration 0002 (pinned head SHA): head 2 / origin-dev 0
    -> RED proven against the pre-change artifact
  indicator absent from base compose: base 0 / dev-lane overlay 2
    -> discriminates; it is not a file-presence check
  FORCE RLS live on the lab lane: exit 1 (table absent, cold lane)
    -> DECLARED acceptance, NOT satisfied. Admissibility proven both ways:
       the identical probe against stability-test, where node_service_registry
       IS forced, exits 0. So the RED is a state fact, not a broken command.
  non-dev lanes unchanged: exit 0 against the true baseline 060
    -> asserts UNCHANGED, not zero. stability-test's six forced tables are the
       pre-existing OMN-15336 breach, deliberately not rolled back here; a
       zero-expectation probe would go RED for a condition this PR did not
       cause. A 000 expectation exits 1 (RED control).

No Done flip. Net-new file only.

* evidence(OMN-15379): make every check_value hermetic and self-evidence-free

Three gates fired and all three were right; this commit obeys them rather than
working around them.

OMN-14051 (hermeticity): two check_values invoked ssh. A CI runner has no ssh
and no route to .201, so they could never pass there. Both removed.

OMN-12791 (receipt honesty) + OMN-13060 (receipt hardening): the sanctioned
substitute for a live probe is a committed receipt that the contract greps. I
wrote one - and the gates correctly rejected it: SELF_ATTESTATION, because
runner and verifier were the same single lane, and missing contract_entry_sha256
because receipts must be tool-generated. Inventing a second verifier identity or
hand-forging the hash would be exactly the self-authored evidence those gates
exist to stop, so the receipt is deleted, not patched.

What remains are three items, all hermetic, all executed before commit, none
reading anything this PR authors:
  - PR bind for omnibase_infra#2540
  - fence carries registration 0002 at the pinned head SHA (head 2 / dev 0)
  - lane indicator absent from the base compose (base 0 / dev overlay 2)

The two live measurements are now recorded as PROSE OBSERVATIONS in the
contract summary, not as evidence claims:
  - blast radius 0/6/0 (prod/stability-test/judge), unchanged from baseline
  - the ruling-15 FORCE readback, NOT yet satisfied (lab lane is cold)
Both land as append-only dod_evidence with tool-generated, independently
verified receipts once the lab bring-up is done.

* evidence(OMN-15379): add missing lane-fence receipts

* evidence(OMN-15379): self-bind OCC companion PR 5469

* evidence(OMN-15379): bind OCC companion final head
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Hostile Reviewer — DEGRADED (informational)

Blocking findings (critical): 0
Total findings: 0
Models succeeded: none

Note: All reviewer models failed or were unavailable. Degraded results are informational during the pilot phase (OMN-8468/OMN-8524) and do not block merge. Error: all review endpoints [192.168.86.201:8000 192.168.86.201:8001 ] unreachable — preflight short-circuit (no models available)


Gate semantics (pilot phase)

Verdict Meaning Blocks merge?
passed No critical findings No
blocked CRITICAL findings found Yes
degraded All models unavailable (infra) No (pilot)

Powered by omniintelligence.review_pairing.cli_review — node-based adversarial review via HandlerLlmCliSubprocess (OMN-8468/OMN-8524)

@jonahgabriel
jonahgabriel merged commit bcbaa4e into dev Jul 29, 2026
161 of 182 checks passed
@jonahgabriel
jonahgabriel deleted the jonah/omn-15379-lane-scoped-fence-release branch July 29, 2026 16:12
jonahgabriel added a commit that referenced this pull request Sep 14, 2026
Rule 24(a) says every runtime-affecting merge to dev produces an automatic
lab pass. In practice the Runtime Rebuild Trigger fires only on omnibase_infra
merges, so an omnimarket merge reached the lane only when some unrelated
omnibase_infra merge happened to carry it.

MEASURED 2026-09-14. The lane's effects container vendored omnimarket at
65c237cd (omnimarket#2538, merged 07:53:57Z), recorded in the image's own
/app/build-provenance.json, while its org.opencontainers.image.revision label
read 732fd29 (omnibase_infra). omnimarket#2537 (09:11:34Z) and #2540
(10:09:57Z) had merged since and were not on it. Nothing was red: the
omnimarket revision was never CHOSEN. stage_workspace.sh resolves each sibling
from origin/dev at staging time (DEPLOY_SIBLING_FALLBACK_REF, OMN-17135), so
the lane runs whatever that pointed at when another repository's merge last
fired a rebuild. omnimarket's own trigger had been dispatch-only since
2026-07-21, disabled for absent SASL/HMAC secrets; the SASL pair is now an org
secret with ALL visibility and no HMAC secret is a precondition of this
producer at all.

WHAT LANDS

A reusable workflow, runtime-rebuild-trigger-reusable.yml, called by a sibling
repository's CI on merge. There is no second publisher and no second copy of
the classifier: the caller supplies facts and holds no logic. The runtime-path
decision stays the canonical omniclaude deploy-gate classifier, which already
owns omnimarket's runtime paths, so a docs-only merge declines.

resolve_publish_ref() separates two facts the single-publisher shape conflated.
The deploy agent resets its BUILD-CONTEXT clone -- omnibase_infra -- with
git reset --hard <git_ref>, and a sibling merge SHA names no commit there, so a
sibling trigger publishes the omnibase_infra ref and carries its own SHA
separately. Every ambiguous combination is refused rather than defaulted.

check_lane_sibling_revision.py is the convergence guard. check_dev_lane_
staleness.py cannot answer here: the infra revision label does not move on a
sibling-only rebuild, so it would report converged on its first poll having
proven nothing. This reads per_repo_vcs_provenance.siblings.<repo>.vcs_ref out
of the image's build-provenance manifest and asserts CONTAINMENT, not equality
-- a build that starts after a later merge legitimately vendors a descendant.

The compose-dev lab-pass receipt is keyed by the sibling sha, since that is the
revision the lane was proven to carry, and is emitted under always() so a
failed lab pass is as recorded as a passing one.

STATED RATHER THAN IMPLIED. A called reusable workflow runs inside the caller's
repository, so the receipt lands on the caller's run and is deliberately not
visible to omnibase_infra's delivery lab-pass gate, which queries omnibase_infra
artifacts for an omnibase_infra sha. Those are different claims about different
commits and no artifact name collides. Rule 24(d) applies unchanged: this lane
has no onex-api and no RLS, so no check claims a tenant-scoped path.

Wired as CI in the same change: the reusable workflow is held to the repo's
shared-CI-env invariant, its uploader is declared in ci_evidence_policy.yaml,
and the new guard ships with a real incident replay driven over the manifest
bytes read from the running container and GitHub's own compare response.
Patel230 pushed a commit that referenced this pull request Sep 16, 2026
Rule 24(a) says every runtime-affecting merge to dev produces an automatic
lab pass. In practice the Runtime Rebuild Trigger fires only on omnibase_infra
merges, so an omnimarket merge reached the lane only when some unrelated
omnibase_infra merge happened to carry it.

MEASURED 2026-09-14. The lane's effects container vendored omnimarket at
65c237cd (omnimarket#2538, merged 07:53:57Z), recorded in the image's own
/app/build-provenance.json, while its org.opencontainers.image.revision label
read 732fd29 (omnibase_infra). omnimarket#2537 (09:11:34Z) and #2540
(10:09:57Z) had merged since and were not on it. Nothing was red: the
omnimarket revision was never CHOSEN. stage_workspace.sh resolves each sibling
from origin/dev at staging time (DEPLOY_SIBLING_FALLBACK_REF, OMN-17135), so
the lane runs whatever that pointed at when another repository's merge last
fired a rebuild. omnimarket's own trigger had been dispatch-only since
2026-07-21, disabled for absent SASL/HMAC secrets; the SASL pair is now an org
secret with ALL visibility and no HMAC secret is a precondition of this
producer at all.

WHAT LANDS

A reusable workflow, runtime-rebuild-trigger-reusable.yml, called by a sibling
repository's CI on merge. There is no second publisher and no second copy of
the classifier: the caller supplies facts and holds no logic. The runtime-path
decision stays the canonical omniclaude deploy-gate classifier, which already
owns omnimarket's runtime paths, so a docs-only merge declines.

resolve_publish_ref() separates two facts the single-publisher shape conflated.
The deploy agent resets its BUILD-CONTEXT clone -- omnibase_infra -- with
git reset --hard <git_ref>, and a sibling merge SHA names no commit there, so a
sibling trigger publishes the omnibase_infra ref and carries its own SHA
separately. Every ambiguous combination is refused rather than defaulted.

check_lane_sibling_revision.py is the convergence guard. check_dev_lane_
staleness.py cannot answer here: the infra revision label does not move on a
sibling-only rebuild, so it would report converged on its first poll having
proven nothing. This reads per_repo_vcs_provenance.siblings.<repo>.vcs_ref out
of the image's build-provenance manifest and asserts CONTAINMENT, not equality
-- a build that starts after a later merge legitimately vendors a descendant.

The compose-dev lab-pass receipt is keyed by the sibling sha, since that is the
revision the lane was proven to carry, and is emitted under always() so a
failed lab pass is as recorded as a passing one.

STATED RATHER THAN IMPLIED. A called reusable workflow runs inside the caller's
repository, so the receipt lands on the caller's run and is deliberately not
visible to omnibase_infra's delivery lab-pass gate, which queries omnibase_infra
artifacts for an omnibase_infra sha. Those are different claims about different
commits and no artifact name collides. Rule 24(d) applies unchanged: this lane
has no onex-api and no RLS, so no check claims a tenant-scoped path.

Wired as CI in the same change: the reusable workflow is held to the repo's
shared-CI-env invariant, its uploader is declared in ci_evidence_policy.yaml,
and the new guard ships with a real incident replay driven over the manifest
bytes read from the running container and GitHub's own compare response.
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