Skip to content

fix(OMN-19513): the work ledger projects session-started with the redacted working-directory shape instead of rejecting it - #2878

Merged
jonahgabriel merged 5 commits into
devfrom
jonah/omn-19513-work-events-redacted-shape
Sep 25, 2026
Merged

jonahgabriel merged 5 commits into
devfrom
jonah/omn-19513-work-events-redacted-shape

Conversation

@jonahgabriel

@jonahgabriel jonahgabriel commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Ticket: OMN-19513 (parent epic OMN-16176).

The lab work ledger (omninode_internal.work_events on the .201 dev lane) has taken no session-started row since 2026-09-21T20:45:04Z. The governed capture redaction contract (OMN-17209) reduces session-started.working_directory to its shape, {"type": "str", "length": N}, and ModelWorkEventInbound typed the field str | None. So every session-started record raised a pydantic string_type error in handle(). The consumer group stayed Stable with lag 0, so nothing on the offset side showed the loss. The other three hook classes (tool-executed, prompt-submitted, session-ended) were never affected.

Change

  • ModelRedactedValueShape (closed: type, optional length, extra="forbid") mirrors redaction.shape_of. Any other key is refused, so the redacted content cannot come back inside an object.
  • ModelWorkEventInbound.working_directory: str | ModelRedactedValueShape | None.
  • The summary renders session started in a redacted directory (N chars), with no directory text. A plain string renders exactly as before.
  • The payload keeps the shape exactly as the wire carried it (exclude_none), so event_id hashes the same bytes on redelivery.

The redaction itself is unchanged. It is a privacy decision tied to the relay widening (OMN-16979), and the consumer is the side that drifted.

Proof

RED first, rule 17 method (commit, then git checkout origin/dev -- <model, handler>, run, git checkout HEAD -- ...). On origin/dev source, 5 new tests fail, including the real-Postgres one. On HEAD all of them pass.

Lab data, not a fixture. Every record the dev-lane broker still retains on onex.evt.omniclaude.session-started.v1 (344 records, read 2026-09-25T01:1xZ) was replayed through handle() into the in-memory adapter:

source projected failed newest row emitted_at rows after 2026-09-21T20:45Z sessions
origin/dev 35 309 (ValidationError: working_directory) 2026-09-21 20:45:04.677909 0 0
this PR 344 0 2026-09-25 00:15:09.434255 208 179

The origin/dev row reproduces the lab table exactly: max(emitted_at) for session-started in work_events on .201 reads 2026-09-21 20:45:04.677909+00. The recovered 208 events across 179 sessions match the reference count in public.session_replay_snapshots for the same window (208 session_start events, 179 sessions), whose projection tolerates the shape.

Local gates. Focused suites: 57 passed (tests/unit/nodes/node_projection_work_events, both work_events golden chains, and the OMN-16180 real-Postgres file). The real-Postgres file ran against an ephemeral local Postgres 16 with migrations 0001 and 0002 applied, 5 passed with 0 skipped. ruff format and check are clean, mypy --strict is clean, and the full pre-commit suite and pre-push are green.

Rule 24(d). This does not touch a tenant, RLS or reader-authorization path. The lab pass exercised the projection handler with live lab records. It did not exercise onex-api.

Sequencing

This branch carries the post-release version bump commit from omnimarket#2874 (0.4.216 to 0.4.217, cherry-picked unchanged). Without it the release-identity pre-commit hook refuses any src/ change on dev as it stands today. Land omnimarket#2874 first. After that, an update of this branch from dev drops the duplicate and the diff is the fix alone. The version is not hand-bumped here, so this branch does not race #2874.

After merge (AC3, AC4 on OMN-19513)

  1. Once the dev lane runs a build with this change, confirm a new session-started row with emitted_at after the deploy.
  2. Backfill: take a ledger HOLD on 201-dev-runtime, then reset only local.omnimarket.projection_work_events.consume.1.0.0.__i.runtime-main.__t.onex.evt.omniclaude.session-started.v1 to its log start. The upsert is content-addressed, so the replay is idempotent. Expect 208 rows after 2026-09-21T20:45Z, provided the topic still retains them.

Evidence-Ticket: OMN-19513
Evidence-Source: OCC#11184

onexbot-occ-writer Bot and others added 2 commits September 24, 2026 21:18
Disarms the OMN-16344 release-identity gate that publishing
v0.4.216 armed against dev.

Triggered-by: https://github.com/OmniNode-ai/omnimarket/actions/runs/36050759868
…acted working-directory shape instead of rejecting it

The governed capture redaction contract reduces session-started.working_directory to {type, length}; ModelWorkEventInbound typed it str | None, so every session-started record raised in handle() and none reached omninode_internal.work_events on the lab since 2026-09-21T20:45Z. The inbound model now accepts a closed ModelRedactedValueShape (extra forbidden, so content cannot ride back in an object), the summary renders a length-only label, and the payload keeps the shape as the wire carried it. Real-Postgres proof added to the OMN-16180 write-path file.
@jonahgabriel jonahgabriel added the hold:auto-merge Holds auto-merge (check_auto_merge_hold.py); remove to release label Sep 25, 2026
@github-actions

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).

onexbot-occ-writer Bot added a commit to OmniNode-ai/onex_change_control that referenced this pull request Sep 25, 2026
…nimarket#2878

The declared checks were executed for real in the OmniNode-ai/omnimarket checkout at the PR head by the product-repo OCC receipt runner, and the results written append-only into this companion. Producer-minted PENDING receipts are superseded by a net-new record; no existing receipt was edited.

Ticket: OMN-16859 (AC3b)
Onex-Workflow: OCC Receipt Runner
Onex-Run: https://github.com/OmniNode-ai/omnimarket/actions/runs/36082003888
@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

✅ Hostile Reviewer — PASSED

Blocking findings (critical/error): 0
Total findings: 15
Models succeeded: qwen3-review,qwen3-review-b


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 pushed a commit to OmniNode-ai/onex_change_control that referenced this pull request Sep 25, 2026
…imarket#2878 (#11184)

* evidence(OMN-19513): author OCC companion for OmniNode-ai/omnimarket#2878

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

* evidence(OMN-19513): self-bind OCC#11184 + rebind contract_sha256

* evidence(OMN-16859): executed test_passes receipts for OmniNode-ai/omnimarket#2878

The declared checks were executed for real in the OmniNode-ai/omnimarket checkout at the PR head by the product-repo OCC receipt runner, and the results written append-only into this companion. Producer-minted PENDING receipts are superseded by a net-new record; no existing receipt was edited.

Ticket: OMN-16859 (AC3b)
Onex-Workflow: OCC Receipt Runner
Onex-Run: https://github.com/OmniNode-ai/omnimarket/actions/runs/36082003888

---------

Co-authored-by: omnimarket-bot <bot@omninode.ai>
Co-authored-by: onexbot-occ-writer[bot] <307849072+onexbot-occ-writer[bot]@users.noreply.github.com>
… already runs in CI

Skip Count Ratchet (OMN-18776) turned red on this PR's own diff: the new
integration test test_handle_writes_a_redacted_session_started_row_to_real_jsonb
was collected and never executed. Root cause -- not specific to this test:
every @pytest.mark.integration case in this file self-skips in every hosted
CI job (Tests, Integration Silent-Skip Guard) because the ephemeral CI
Postgres never has node_projection_work_events/migrations/0001 applied (no
omninode_internal schema, no omninode_runtime role -- the migration's own
preconditions), a pre-existing, already-baselined condition for the file's
other four tests (config/skip_count_baseline.yaml, omnimarket/test suite).
Applying that migration inside shared CI is out of scope for this fix and
would change a job every PR in the repo depends on.

The assertions the new integration test made -- handle() accepts the
capture_shape_only working_directory dict without raising, rows_upserted==1,
and the shape survives into row.payload unstringified -- are already fully
covered by tests/unit/nodes/node_projection_work_events/
test_redacted_working_directory_omn19513.py (added in this same PR,
@pytest.mark.unit, InmemoryDatabaseAdapter), which runs on every PR today.
The marginal value the removed integration test added over that suite was
generic asyncpg/JSONB-codec behavior already exercised, redaction-agnostic,
by the file's other four (already-skipped, already-baselined) tests -- not
anything specific to this redaction fix.

Removed the redundant, newly-added integration test and its now-unused
TOPIC_SESSION_STARTED import; the file is byte-identical to origin/dev
afterward. No config/skip_count_baseline.yaml edit -- the observed skipped
set for this suite is unchanged (still exactly the 4 pre-existing baseline
ids), so there is no growth for the ratchet to catch.

Local proof: the trimmed file collects exactly the 4 baseline node ids
(pytest --collect-only); a local run against it reproduces the pre-existing
4-skip/4-collected shape and skip_count_ratchet.py --print-observed against
that JUnit report emits exactly the 4 already-baselined ids, 0 new; the 41
unit tests in test_redacted_working_directory_omn19513.py +
test_handler_projection_work_events.py pass; ruff format/check, mypy --strict
on the touched file, and pre-commit run --files <touched file> all clean.

OMN-19513

Onex-Lane: hook-silent-errors
Onex-Session: 83646eaf492e4841bab1e180e4cb4da1
…ne preconditions and executes in CI

Every test in tests/test_omn16180_real_postgres_work_events_write_path.py
self-skipped in hosted CI: the ephemeral Postgres has neither the
omninode_internal schema nor the omninode_runtime role, which 0001 asserts
rather than creates, so the relation never existed there.

A module-scoped autouse fixture now replicates the lane provisioning seam
(guarded role, CREATE SCHEMA IF NOT EXISTS, the same shape as the OMN-16146
precedent) and applies the node's own migrations in order. It touches nothing
where the relation already exists, and skips, naming why, where there is no
database or the connecting role cannot provision.

Restores test_handle_writes_a_redacted_session_started_row_to_real_jsonb,
which now executes: the redacted working-directory shape lands as nested JSONB
through handle() against real column types.

Onex-Lane: hook-silent-errors
Onex-Session: 83646eaf492e4841bab1e180e4cb4da1
@jonahgabriel
jonahgabriel merged commit 9260827 into dev Sep 25, 2026
139 checks passed
@jonahgabriel
jonahgabriel deleted the jonah/omn-19513-work-events-redacted-shape branch September 25, 2026 04:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

hold:auto-merge Holds auto-merge (check_auto_merge_hold.py); remove to release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant