Skip to content

feat(OMN-17057): a sibling merge delivers its own named revision to onex-dev staging - #3611

Merged
jonahgabriel merged 2 commits into
devfrom
jonah/omn-17057-named-revision-sibling-delivery
Sep 16, 2026
Merged

jonahgabriel merged 2 commits into
devfrom
jonah/omn-17057-named-revision-sibling-delivery

Conversation

@jonahgabriel

@jonahgabriel jonahgabriel commented Sep 16, 2026 •

Copy link
Copy Markdown
Collaborator

Closes OMN-17057 (LD-02).

The gap, and a premise from the ticket that is now false

The ticket's evidence says omnimarket carries no workflow that can trigger a rebuild. That was true when it was written and is not true now: OMN-18268 gave omnimarket a runtime-rebuild-trigger.yml that rebuilds the .201 compose dev lane on merge and emits a compose-dev lab-pass receipt for the merged sha. What still has no implementation is the second half of the title, which is the half this PR is about.

An omnimarket merge cannot cause a delivery to staging. It rides along on whatever the next omnibase_infra push happens to build, and that candidate clones every sibling at branch dev at that instant. So the sibling revision baked into the image is never chosen, cannot be predicted, and no sibling merge can prove delivery of its own sha.

Measured on delivery run 35013603978 (2026-09-15T19:26Z, success), reading its build manifest artifact:

field value
commit_sha (omnibase_infra) 034b9a0a8d74fac8bc9ebb4fa9de9ce290fd58c3
sibling_ref dev
per_sibling_vcs_provenance.omnimarket.vcs_ref 3038d1dabadea4973707f0228862b4aef4e09242

3038d1da merged to omnimarket dev at 17:08Z. It reached a candidate at 19:26Z because an unrelated commit landed here, not because anything chose it. Twelve omnimarket commits had landed on top of it by the time this was read.

The primitive already existed and CI was not reaching it. scripts/runtime_build/deploy_source_ref.py has accepted --ref plus --fallback-ref since OMN-17135 — it resolves the primary in whichever repository carries it, takes the fallback everywhere else, and records fallback_from. stage_workspace.sh already refuses an unpinned build (OMN-17291) and already asserts the vendored sha equals what RT-1 checked out. The candidate build passed the same branch name as both values, which threw all of that away. Nothing new is invented here; the existing pin is reached.

What changed

Trigger. runtime-rebuild-trigger-reusable.yml gains a terminal job that needs: the existing convergence job, so it fires only after the .201 dev lane was proven to vendor the merged sibling sha and the compose-dev receipt for it was emitted. It sends one repository_dispatch carrying the repository and the revision. It lives in the reusable rather than in any caller, so omnimarket, omnibase_core and omnibase_compat each acquire the path with no per-repo code and no per-repo configuration — a copy per caller is how this org ended up with two divergent trigger_rebuild_on_merge.py, as that file's own header records. omniclaude is deliberately outside the family: it is not in SIBLING_REPOS and is not vendored into the image, so there is nothing for it to pin.

Naming the revision. build-workspace-candidate-runtime.yml gains pinned_repo and pinned_revision, both empty by default. When set, DEPLOY_REF becomes the commit and DEPLOY_SIBLING_FALLBACK_REF the branch, which is exactly the pair OMN-17135 built. sibling_ref is untouched and still governs the other three repositories, so the constraint its comments describe — one value cannot be a commit in four repositories — is preserved rather than worked around.

Proving it. The pinned commit is fetched by sha into its own clone, failing there with the repository named rather than later inside RT-1 with an unresolvable ref. After staging, the vendored sha in sibling-vcs-provenance.json is compared against the requested one. That file is the one the image bakes and omninode_infra's freshness gate later reads out of a running pod (per_repo_vcs_provenance.siblings.<repo>.vcs_ref, OMN-17926), so it is the artefact the ticket's own done-proof probe names. The build manifest records pinned_sibling, so a chosen revision is distinguishable from a branch that happened to drift to the same commit.

Gating. lab-pass-gate keeps asserting a PASS receipt for this repository's sha. When a pin is present it additionally reads the sibling repository's compose-dev receipt, using a short-lived installation token scoped actions: read on that one repository — the receipt is published by the reusable running as that repository's workflow, so this repository's own token cannot see it. dispatch-to-staging already needs: lab-pass-gate under default success(), so a missing, unreadable, malformed, name/payload-disagreeing or FAIL sibling receipt leaves the announcement unfired.

Concurrency. github.ref on a repository_dispatch resolves to the default branch, so without a second segment the next ordinary push would cancel a sibling's delivery silently — the exact class of invisible non-delivery this workflow exists to end. The group now names the pinned repository. Two pinned deliveries of the same sibling still collapse, which is wanted: the later revision contains the earlier one.

The announcement carries pinned_sibling to omninode_infra. That side already resolves what each running image actually vendors; what it has never had is the other half of the comparison, which revision was intended.

What fails closed

A half-specified pin, a revision that is not a 40-character sha, a repository the build does not stage, a commit its source does not contain, a vendored sha that disagrees with the request, an absent sibling receipt, and absent announcement credentials are each a refusal rather than a warning or a skip. No force input, no skip input and no if: override is added anywhere on the path, and a test over the parsed inputs of all three workflows refuses one being added later.

Rollback

Both new inputs default to empty and every new step is conditioned on a pin being present, so the push path is unchanged. Reverting the announcement job alone restores today's behaviour. Nothing is deleted, no existing input changes meaning.

Tests

tests/ci/test_named_revision_sibling_delivery_omn17057.py parses the workflow graph and asserts properties rather than matching text, because every one of these would survive a rename: the delivery workflow accepts the sibling announcement and only that type; the pin is declared on both trigger blocks with empty defaults; sibling_ref still defaults to dev; the pin reaches DEPLOY_REF while the fallback keeps the branch; the build verifies what it vendored; the manifest records the pin; the pin is passed from the announcement and nowhere else; the sibling receipt is read, conditioned on a pin, naming compose-dev; the announcement still needs: the lab gate with no if:; the concurrency group separates the two delivery kinds; the announcement needs: convergence; it refuses a non-sha; it refuses rather than skips without credentials; and no trigger input on any of the three workflows reads as an override.

uv run pytest tests/ci/test_named_revision_sibling_delivery_omn17057.py -q
16 passed

Positive control. With the three workflow files restored to their origin/dev content, 12 of the 16 fail. The 4 that pass are invariants that were already true — the push branches, the sibling_ref default, dispatch-to-staging needing the lab gate, and the absence of override inputs. The zero is a measurement, not an empty sweep.

Pushed through the governed pre-push hook; no local gate was bypassed.

Lab proof and what remains

This change alters which revision a candidate is built from, and its correctness is provable from the parsed workflow graph, which is what the tests do. The live proof is the ticket's own done-proof probe and needs the chain to run end to end: a runtime-path omnimarket merge, a PASS compose-dev receipt for that sha, a candidate whose build manifest names it, and a deploy-onex-staging run id carrying it. That readback is recorded on the ticket, not asserted here — a merge is not on staging until a deploy run id carries the sha.

Two things this PR deliberately does not do. It does not change what omninode_infra asserts from the new pinned_sibling field; that side already reads live provenance out of the pod, and widening its assertion is a separate change on the verification half (LD-03), which the ticket says not to duplicate here. And it does not make omnimarket pass the announcement credentials to the reusable: that is a one-line change in omnimarket that also advances its pin to this commit, and it has to land after this one or the reusable it calls does not yet declare the secrets.

The stale-receipt defect found while verifying this path is fixed separately under its own ticket, because a sibling sha that cannot receipt PASS blocks this gate.

Evidence-Ticket: OMN-17057
Evidence-Source: OCC#9756

…taging

An omnimarket merge could not cause a delivery at all. It rode along on
whatever the next omnibase_infra push happened to build, and that candidate
clones every sibling at branch dev at that instant -- so the sibling revision
in the image was never CHOSEN, could not be predicted, and no sibling merge
could prove delivery of its own sha. Measured on delivery run 35013603978: the
candidate vendored omnimarket 3038d1da, simply where that branch pointed 18
minutes after an unrelated commit merged here. Twelve omnimarket commits had
landed since by the time this was read.

The primitive already existed and CI was not reaching it. deploy_source_ref.py
has taken --ref plus --fallback-ref since OMN-17135, resolving the primary in
whichever repository carries it and taking the fallback everywhere else. The
candidate build passed the same branch name as both.

Trigger. runtime-rebuild-trigger-reusable.yml gains a terminal job that needs
the existing convergence job, so it fires only after the .201 dev lane was
proven to vendor the merged sibling sha and its compose-dev lab-pass receipt
was emitted. It sends one repository_dispatch carrying that repo and revision.
Because it lives in the reusable, every repository the runtime image vendors --
omnimarket, omnibase_core, omnibase_compat -- acquires the path with no
per-repo code. omniclaude is deliberately outside the family: it is not
vendored into the image, so there is nothing for it to pin.

Naming. build-workspace-candidate-runtime.yml gains pinned_repo and
pinned_revision, both empty by default so the push path is unchanged. When set,
DEPLOY_REF becomes the commit and DEPLOY_SIBLING_FALLBACK_REF the branch. The
pinned commit is fetched by sha into its clone, and after staging the vendored
sha in sibling-vcs-provenance.json is compared against the requested one -- the
same file the image bakes and the staging deploy later reads out of a running
pod (OMN-17926). The build manifest records pinned_sibling so a chosen revision
is distinguishable from a branch that drifted to the same commit.

Gating. lab-pass-gate keeps asserting a PASS receipt for this repo's sha, and
when a pin is present additionally reads the sibling repository's own
compose-dev receipt with a token scoped actions:read on that one repository.
dispatch-to-staging already needs lab-pass-gate under default success(), so a
missing, unreadable, malformed, name/payload-disagreeing or FAIL sibling
receipt leaves the announcement unfired.

Fail-closed throughout: a half-specified pin, a non-sha revision, a repository
the build does not stage, a commit its source does not contain, a vendored sha
that disagrees with the request, an absent sibling receipt and absent
announcement credentials are each a refusal. No force input, no skip input and
no if: override is added anywhere, and a test over the parsed inputs refuses
one later.

Concurrency. The delivery group gains a segment naming the pinned repo.
github.ref on a repository_dispatch resolves to the default branch, so without
it the next ordinary push would silently cancel a sibling's delivery.

The announcement payload carries pinned_sibling to omninode_infra, which
already resolves what each running image actually vendors; what it never had
was the other half of that comparison -- which revision was intended.

Tests parse the workflow graph and pin properties, not spellings. Positive
control: 12 of the 16 fail against the pre-change workflows, and the 4 that
pass are invariants that were already true.
@github-actions

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)

jonahgabriel pushed a commit to OmniNode-ai/onex_change_control that referenced this pull request Sep 16, 2026
…ibase_infra#3611 (#9756)

* evidence(OMN-17057): author OCC companion for OmniNode-ai/omnibase_infra#3611

OCC companion by node_pr_lifecycle_fix_effect (OMN-13317 F1 / OMN-13990 / OMN-14285). Product PR head c7b8461f48409e5116a07353182502c2c24b54b3.

* evidence(OMN-17057): self-bind OCC#9756 + rebind contract_sha256

---------

Co-authored-by: omnimarket-bot <bot@omninode.ai>
…w delivery job

Three existing guards asserted properties of a two-job workflow and failed on
the third. Each is extended by deciding what the new job needs, never by
excluding it.

  * the secrets guard now asserts the App pair alongside the SASL pair, and
    asserts the REQUIREDNESS of each: SASL required, the App pair optional at
    the interface so an existing caller keeps compiling, and refused at run
    time by the announcement job itself. Marking them required would break
    every caller in the commit that adds them; skipping on their absence would
    report a delivery that did not happen.

  * the self-checkout guard now maps the new job to None -- it runs gh and jq
    and nothing from the tree, so a checkout would be ceremony that later reads
    as a dependency. Listed rather than excluded, so adding a job still forces
    the decision the test exists to force, and a None job is asserted to
    reference no local action and no repository script.

  * the CI-python-environment guard now scopes the setup-action requirement to
    jobs that RUN Python. The pip-install prohibition stays universal. The
    discriminating half is the else branch: a job with no setup step is
    asserted to invoke no Python at all, so "forgot the setup action" cannot
    pass as "does not need it". Verified against the live graph -- all six
    pre-existing jobs across the three workflows classify as Python jobs with
    setup present, and only the new job classifies as neither.
@jonahgabriel
jonahgabriel merged commit 43bd275 into dev Sep 16, 2026
133 checks passed
@jonahgabriel
jonahgabriel deleted the jonah/omn-17057-named-revision-sibling-delivery branch September 16, 2026 02:01
Patel230 pushed a commit that referenced this pull request Sep 16, 2026
…nex-dev staging (#3611)

* feat(OMN-17057): a sibling merge delivers its own named revision to staging

An omnimarket merge could not cause a delivery at all. It rode along on
whatever the next omnibase_infra push happened to build, and that candidate
clones every sibling at branch dev at that instant -- so the sibling revision
in the image was never CHOSEN, could not be predicted, and no sibling merge
could prove delivery of its own sha. Measured on delivery run 35013603978: the
candidate vendored omnimarket 3038d1da, simply where that branch pointed 18
minutes after an unrelated commit merged here. Twelve omnimarket commits had
landed since by the time this was read.

The primitive already existed and CI was not reaching it. deploy_source_ref.py
has taken --ref plus --fallback-ref since OMN-17135, resolving the primary in
whichever repository carries it and taking the fallback everywhere else. The
candidate build passed the same branch name as both.

Trigger. runtime-rebuild-trigger-reusable.yml gains a terminal job that needs
the existing convergence job, so it fires only after the .201 dev lane was
proven to vendor the merged sibling sha and its compose-dev lab-pass receipt
was emitted. It sends one repository_dispatch carrying that repo and revision.
Because it lives in the reusable, every repository the runtime image vendors --
omnimarket, omnibase_core, omnibase_compat -- acquires the path with no
per-repo code. omniclaude is deliberately outside the family: it is not
vendored into the image, so there is nothing for it to pin.

Naming. build-workspace-candidate-runtime.yml gains pinned_repo and
pinned_revision, both empty by default so the push path is unchanged. When set,
DEPLOY_REF becomes the commit and DEPLOY_SIBLING_FALLBACK_REF the branch. The
pinned commit is fetched by sha into its clone, and after staging the vendored
sha in sibling-vcs-provenance.json is compared against the requested one -- the
same file the image bakes and the staging deploy later reads out of a running
pod (OMN-17926). The build manifest records pinned_sibling so a chosen revision
is distinguishable from a branch that drifted to the same commit.

Gating. lab-pass-gate keeps asserting a PASS receipt for this repo's sha, and
when a pin is present additionally reads the sibling repository's own
compose-dev receipt with a token scoped actions:read on that one repository.
dispatch-to-staging already needs lab-pass-gate under default success(), so a
missing, unreadable, malformed, name/payload-disagreeing or FAIL sibling
receipt leaves the announcement unfired.

Fail-closed throughout: a half-specified pin, a non-sha revision, a repository
the build does not stage, a commit its source does not contain, a vendored sha
that disagrees with the request, an absent sibling receipt and absent
announcement credentials are each a refusal. No force input, no skip input and
no if: override is added anywhere, and a test over the parsed inputs refuses
one later.

Concurrency. The delivery group gains a segment naming the pinned repo.
github.ref on a repository_dispatch resolves to the default branch, so without
it the next ordinary push would silently cancel a sibling's delivery.

The announcement payload carries pinned_sibling to omninode_infra, which
already resolves what each running image actually vendors; what it never had
was the other half of that comparison -- which revision was intended.

Tests parse the workflow graph and pin properties, not spellings. Positive
control: 12 of the 16 fail against the pre-change workflows, and the 4 that
pass are invariants that were already true.

* test(OMN-17057): bring the three reusable-workflow guards onto the new delivery job

Three existing guards asserted properties of a two-job workflow and failed on
the third. Each is extended by deciding what the new job needs, never by
excluding it.

  * the secrets guard now asserts the App pair alongside the SASL pair, and
    asserts the REQUIREDNESS of each: SASL required, the App pair optional at
    the interface so an existing caller keeps compiling, and refused at run
    time by the announcement job itself. Marking them required would break
    every caller in the commit that adds them; skipping on their absence would
    report a delivery that did not happen.

  * the self-checkout guard now maps the new job to None -- it runs gh and jq
    and nothing from the tree, so a checkout would be ceremony that later reads
    as a dependency. Listed rather than excluded, so adding a job still forces
    the decision the test exists to force, and a None job is asserted to
    reference no local action and no repository script.

  * the CI-python-environment guard now scopes the setup-action requirement to
    jobs that RUN Python. The pip-install prohibition stays universal. The
    discriminating half is the else branch: a job with no setup step is
    asserted to invoke no Python at all, so "forgot the setup action" cannot
    pass as "does not need it". Verified against the live graph -- all six
    pre-existing jobs across the three workflows classify as Python jobs with
    setup present, and only the new job classifies as neither.
Patel230 pushed a commit to OmniNode-ai/onex_change_control that referenced this pull request Sep 16, 2026
…ibase_infra#3611 (#9756)

* evidence(OMN-17057): author OCC companion for OmniNode-ai/omnibase_infra#3611

OCC companion by node_pr_lifecycle_fix_effect (OMN-13317 F1 / OMN-13990 / OMN-14285). Product PR head c7b8461f48409e5116a07353182502c2c24b54b3.

* evidence(OMN-17057): self-bind OCC#9756 + rebind contract_sha256

---------

Co-authored-by: omnimarket-bot <bot@omninode.ai>
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