Skip to content

fix(OMN-20327): fix projection API cursor walks and lane-health contract - #3208

Merged
jonahgabriel merged 3 commits into
devfrom
jonah/omn-20327-bus-backed-exposures
Oct 1, 2026
Merged

jonahgabriel merged 3 commits into
devfrom
jonah/omn-20327-bus-backed-exposures

Conversation

@jonahgabriel

@jonahgabriel jonahgabriel commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Summary

OMN-20327 (parent OMN-19971). Four exposures failed on the .201 dev lane through the table-backed projection API: lab.lane-health.v1 answered 503 and the cursor walks of consumer-flow.v1, prod-promotion-gate.v1 and runtime-error-fingerprints.v1 returned partial key sets. A walk is: GET without since, follow next_cursor until null.

Lab: head 033331a run on host h201 against the .201 dev lane database, readback below. The commit message was drafted by the lab (run b2b46235); the lab refused the PR-body run, so this text is hand-written.

Cause per exposure

  • lab.lane-health.v1 (exposure contract wrong). 503 projection_column_missing. The contract declared census_status, health_status, receipt_status; omninode_internal.lab_lane_health stores only each fact's pre-decay verdict and observed-at (a stored decayed verdict needs a timer to stay true, as the runner's docstring says). The writer is running: 1 row, compose-dev, projected 17:53Z.
  • consumer-flow.v1 (window / cursor bug). A read without since served the newest limit*4 = 2000 rows by cursor. consumer_flow_windows keeps every window of a pair (23.8M rows), so the walk named 597 of 976 distinct (consumer_group, topic) pairs.
  • prod-promotion-gate.v1 (same bug). Window of the newest 200 rows (limit 50 x 4); the walk reached 200 of 470 keys (471 now).
  • runtime-error-fingerprints.v1 (ranked page, no cursor). First page is the top 200 by rank with next_cursor null, so a walk reached 200 of 4476 keys. The rank was also taken over a recency window rather than the whole table.

Fix

  • A keyed read takes the newest row of each key by a skip scan over the key index. New optional contract field projection_api.latest_by; consumer-flow declares window_start, the tail of its primary key (20 ms on the 23.8M-row table; without it the same query ran past 40 s).
  • A read without since is page one of the ascending cursor walk.
  • A ranked page ranks the whole table, and a truncated ranked page advertises the walk origin (min cursor - 1) so following it reaches every key (it repeats the top page and skips nothing).
  • The lane-health contract drops the three decayed columns; the published snapshot row still carries them.

Lab proof

Proof ran on the pre-rebase tree 40c72eb (identical source; the rebase onto dev only moved the base). Same image as the deployed omnimarket-projection-api, branch src mounted, same env, side container on :3012, against the .201 dev lane database.

exposure before (deployed :3002) after (branch :3012) table ground truth
lab.lane-health.v1 503 projection_column_missing 200, 1 row, 1 key 1 row
consumer-flow.v1 200, 4 pages, 2000 rows, 597 keys 200, 2 pages, 976 rows, 976 keys 976 pairs
prod-promotion-gate.v1 200, 4 pages, 200 keys 200, 10 pages, 471 rows, 471 keys 471
runtime-error-fingerprints.v1 200, 1 page, 200 keys 200, 24 pages, 4676 rows (200 repeated from the ranked page), 4476 keys 4476

Tests

  • test_projection_table_reader.py: two real-Postgres tests (every key reached once at its latest row; ranked walk reaches every key). On main they fail (4 keys of 12; top row fp-29 instead of fp-00); they pass here. Plus SQL-shape and contract-field unit tests.
  • test_omn20327_lane_health_exposure_columns.py: 3 tests, 2 fail on main.
  • test_omn19841_ranked_page_selection.py: the ranked-cursor test updated for the new cursor.
  • 465 passed, 2 skipped across the projection, read-effect, golden-chain and lane-health suites.

Evidence-Ticket: OMN-20327
Evidence-Source: OCC#12234

@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown

✅ Architectural Review — PASSED

Errors: 0
Warnings: 0

What this checks

Rule Description
ARCH-TOPIC-001 No hardcoded Kafka topic strings in handler code
ARCH-DI-001 No event_bus=None bypass
ARCH-DI-002 No direct Handler instantiation outside workflow_runner/adapters
ARCH-DI-003 No reinvented DI containers
ARCH-TOPIC-002 contract.yaml topics follow `onex.{cmd

No architectural violations found.

Static architectural lint — no model inference (OMN-14176).

Four bus-backed projection exposures failed on the .201 dev lane via the
table-backed projection API (OMN-20152).

- lab.lane-health.v1 returned 503 projection_column_missing. The contract
  listed census_status, health_status, and receipt_status, but
  omninode_internal.lab_lane_health stores only <fact>_original_status and
  <fact>_observed_at. Decayed verdicts are deliberately not stored. Removed
  those three columns from the exposure contract; the published snapshot
  row still carries them.

- consumer-flow.v1, prod-promotion-gate.v1, and
  runtime-error-fingerprints.v1 cursor walks returned partial key sets.
  A read without since served the newest limit*4 rows.
  consumer_flow_windows holds every window of a pair, so 2000 newest rows
  named 597 of 976 pairs. The prod gate window was 200 of 470 rows.
  Ranked fingerprints returned only the top 200 of 4476 with no cursor.

- Fixed in table_reader/read_page:
  - A mutable-grain keyed read takes the newest row of each key by a skip
    scan over the key index.
  - A read without since is page one of the ascending cursor walk.
  - A ranked page ranks the whole table and its truncated page advertises
    the walk origin cursor (min cursor minus one) so following it reaches
    every key.
  - Added new optional contract field projection_api.latest_by.
    consumer-flow declares window_start, the tail of its primary key.

- Tests:
  - Two real-Postgres tests (walk reaches every key once; ranked walk
    reaches every key) fail on main, pass here.
  - Unit tests for the SQL shape, the contract field, and lane-health
    columns being a subset of the table's columns.
@jonahgabriel
jonahgabriel force-pushed the jonah/omn-20327-bus-backed-exposures branch from 40c72eb to 033331a Compare October 1, 2026 18:19
@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

✅ Hostile Reviewer — PASSED

Blocking findings (critical/error): 0
Total findings: 12
Models succeeded: qwen3-review,gpt-oss-review


Gate semantics

Verdict Meaning Blocks merge?
passed >=2 models succeeded, no critical/error findings No
blocked CRITICAL or ERROR findings found Yes
degraded Fewer than 2 models succeeded (infra unavailable/timeout) Yes (OMN-15110)

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

@jonahgabriel
jonahgabriel changed the base branch from main to dev October 1, 2026 18:20
@jonahgabriel
jonahgabriel enabled auto-merge (squash) October 1, 2026 18:21
Comment on lines +652 to +654
(cfg,) = load_projection_exposures_from_contract(
contract, str(contract["name"]), path
)

async def walk_origin(
self, cfg: ProjectionTableConfig, *, tenant_id: str | None
) -> str | None: ...
jonahgabriel added a commit to OmniNode-ai/onex_change_control that referenced this pull request Oct 1, 2026
* evidence(OMN-20327): add hand-authored deploy probe for OmniNode-ai/omnimarket#3208

Added a falsifiable deploy probe to contracts/OMN-20327.yaml that verifies the presence of `def _latest_per_key_sql(` in src/omnimarket/projection/table_reader.py at commit b7cb51accc40cd272eb6e920b48415a993f41c9e. This resolves the deploy-gate failure by providing the required evidence companion and a corresponding PASS receipt under drift/dod_receipts/OMN-20327/.

* evidence(OMN-18921): mint the owed OCC self-bind for PR #12234

---------

Co-authored-by: onexbot-occ-writer[bot] <onexbot-occ-writer[bot]@users.noreply.github.com>
@jonahgabriel
jonahgabriel merged commit 777ca4d into dev Oct 1, 2026
146 of 147 checks passed
@jonahgabriel
jonahgabriel deleted the jonah/omn-20327-bus-backed-exposures branch October 1, 2026 23:59
jonahgabriel added a commit that referenced this pull request Oct 2, 2026
dev was excluded from the CI push triggers, and dev is not strict and has no
merge queue. #3212 merged on CI graded against a base older than #3208, so dev
head d471dcb broke typecheck and Tests (Split 14/20) with no check run on it
until the release fast-forward to main. This adds dev to push.branches, matching
omnibase_infra, omnibase_core and omniclaude; the push concurrency group is
ref-scoped and never cancels.
jonahgabriel added a commit that referenced this pull request Oct 2, 2026
dev was excluded from the CI push triggers, and dev is not strict and has no
merge queue. #3212 merged on CI graded against a base older than #3208, so dev
head d471dcb broke typecheck and Tests (Split 14/20) with no check run on it
until the release fast-forward to main. This adds dev to push.branches, matching
omnibase_infra, omnibase_core and omniclaude; the push concurrency group is
ref-scoped and never cancels.
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.

2 participants