Skip to content

feat(OMN-13472): ARCH-004 imperative-orchestrator ratchet — cross-file rule + baseline + gate - #2065

Merged
jonahgabriel merged 5 commits into
devfrom
jonah/omn-13472-arch-004-ratchet
Jun 22, 2026
Merged

jonahgabriel merged 5 commits into
devfrom
jonah/omn-13472-arch-004-ratchet

Conversation

@jonahgabriel

@jonahgabriel jonahgabriel commented Jun 22, 2026 •

Copy link
Copy Markdown
Collaborator

OMN-13472 — Workstream B: ARCH-004 imperative-orchestrator ratchet

Adds ARCH-004 — "Contract-Declared Orchestrator Workflow Must Be Bound To An Executor" to node_architecture_validator, plus a ratchet baseline and a gate wired through the existing validator-gating path.

Implements Workstream B of the verified plan docs/plans/2026-06-22-imperative-orchestrator-ratchet-and-recovery-plan-verified.md §4, backed by the census in docs/audits/2026-06-22-imperative-orchestrator-audit.md.

Tickets: OMN-13472 (this work), epic OMN-13471 (delegation decomposition — baseline owner), OMN-12550 (wire architecture validators as blocking gates — ARCH-004 rides this, not a fresh hook), OMN-13325 (ratchet-enforcement epic).

Why ARCH-003 cannot do this

ARCH-003 (validator_no_orchestrator_fsm.py) is a single-file AST visitor that only inspects classes whose name contains "Orchestrator", with a method match-set of {transition, can_transition, apply_transition, get_current_state, set_state}. The real defect lives in class HandlerDelegationWorkflow (no "Orchestrator" in the name) and is driven via self._transition(...) (leading underscore, a call not a def). ARCH-003 never joins contract + routing + handler. ARCH-004 is a cross-file, node-directory rule that joins contract.yaml (fsm:/workflow_coordination), handler_routing.routing_strategy, handler source, and node type/name.

What ARCH-004 detects

HARD-fail (ERROR) for changed/new orchestrator-like nodes:

  • H1 — decorative fsm: (no executor binds it; ModelContractOrchestrator has no typed fsm/state_machine field, no traverser consumes orchestrator fsm.transitions) WHILE a handler drives transitions itself (self._transition(, set_state, current_state=, … boundary-anchored to avoid the record_phase_transition( / "current_state=%s" substring traps; OR an Enum*State whose members ≈ the contract fsm.states).
  • H2 — routing_strategy: payload_type_match funneling 3+ payload/event types into one workflow handler.
  • H3 — one handler that both selects the next workflow state AND constructs terminal/compat events (the OMN-13408 footgun).

WARN / baseline score: handler >750 lines (W1); >125 branch/control markers (W2, regex �(if|elif|for|while|except|case|and|or)�); ≥10 publish/event-construction markers (W3, regex \.publish\(|ModelEventEnvelope|\.emit\(|[A-Za-z_]+Event\(); declared-but-unbound states (W4). Reducers (node_*_fsm_reducer / contract state_machine: + pure transition executor) are EXEMPT.

Ratchet

architecture-handshakes/imperative-orchestrator-baseline.yaml (mirrors the repo's existing handshake-baseline pattern; can only shrink). Modes on the scanner CLI / scripts/validate.py imperative_orchestrators:

  • --check-all --report — full report, never hides debt (CI, non-blocking initially).
  • --check-changed --ratchet — fail on a new/worsened/untracked finding for a touched node (pre-commit, blocking).
  • --strict — drop-baseline: a baselined node that still hard-fails fails (ratchet the baseline down).

Baseline records 9 current hard-fails (owner OMN-13471): node_delegation_orchestrator (omnimarket) is the sole P0 — risk 10, codes H1/H2/H3/W1/W2/W3/W4, 1542-line handler. (paths repo-relative — no machine-absolute paths.)

Gate wiring (through OMN-12550, not a parallel hook)

  • Pre-commit hook onex-imperative-orchestrator-ratchet — changed-node ratchet, blocking.
  • CI step "Run imperative-orchestrator ratchet report (ARCH-004)" in the ONEX Validators job — full report, non-blocking initially (continue-on-error), promote to required once the baseline is below threshold.
  • Both cite OMN-12550 + OMN-13325 in their config comments.

dod_evidence

  • Headline proof — test_arch003_passes_but_arch004_fails_delegation_shape: a synthetic node (contract fsm: table + monolithic handler using self._transition( whose class is NOT *Orchestrator) → ARCH-003 PASSES it (valid=True, 0 violations), ARCH-004 FAILS it (H1+H3). Verified against the real node_delegation_orchestrator: validate_no_orchestrator_fsm(handler) → valid=True/0 violations; ARCH-004 → H1/H2/H3 hard-fail, risk 10 (matches the audit).
  • Required tests (all @pytest.mark.unit, synthetic vendored fixtures, no sibling-repo path dependency):
    • (a) test_arch003_passes_but_arch004_fails_delegation_shape
    • (b) test_payload_type_match_three_plus_payloads_fails
    • (c) test_reducer_with_state_machine_passes (reducer exempt)
    • (d) test_executor_bound_orchestrator_passes
    • (e) test_full_audit_detects_delegation_shaped_fixture
    • precision: test_substring_trap_not_a_false_positive (record_phase_transition( / "current_state=%s" do NOT match), test_enum_state_match_drives_h1, ratchet/strict/baseline-roundtrip tests, protocol-surface + regex-reporting tests.
  • uv run pytest tests/unit/nodes/node_architecture_validator/ → 220 passed (15 new ARCH-004 + 203 existing, no regression). Union-count regression guard passes (narrowed analyze_node_directory to Path to avoid adding a counted union).
  • uv run mypy src/ --strict → Success: no issues in 2488 source files.
  • uv run ruff format --check / ruff check on the changeset → clean. Scoped pre-commit run --files <changeset> → all applicable hooks pass; no --no-verify, no skip tokens.
  • DEV-lane code only — no runtime deploy, no prod/stability mutation.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added ARCH-004 architecture validation to detect orchestrator workflows left unbound from executors, enforced via CI and pre-commit.
    • Introduced an ARCH-004 “ratchet” baseline to prevent newly introduced hard-fail findings from being accepted.
  • Tests

    • Added unit tests covering rule behavior, ratchet/strict baseline enforcement, and changed-nodes reporting.
  • Chores

    • Added database migration for context_pack_hash on delegation events (with an index).
    • Updated formatting/lint rules and updated runner image lock values to keep CI consistent.

Evidence-Source: 33c69fc5f212a42a731f182fbd309bfe9c9b0b59
Evidence-Ticket: OMN-13472

…e rule + baseline + gate

Adds ARCH-004 'Contract-Declared Orchestrator Workflow Must Be Bound To An
Executor' to node_architecture_validator. Cross-file node-directory rule that
joins contract.yaml (fsm/workflow_coordination), handler_routing.routing_strategy,
handler source, and node type/name — catching the delegation-shaped anti-pattern
ARCH-003 structurally misses (handler-owned _transition( in a non-*Orchestrator
class; declared-but-unbound fsm).

- RuleContractDeclaredOrchestratorWorkflow registered in validators/__init__.py
- scanner_imperative_orchestrator_ratchet: --check-all/--report,
  --check-changed/--ratchet, --strict; baseline can only shrink
- architecture-handshakes/imperative-orchestrator-baseline.yaml (9 current
  hard-fails, owner OMN-13471; delegation = sole P0 risk 10)
- Wired via OMN-12550 path: scripts/validate.py imperative_orchestrators
  subcommand + pre-commit hook (changed-node ratchet, blocking) + CI full
  report (non-blocking initially). Cites OMN-12550 + OMN-13325 in configs.
- Tests: ARCH-003-passes / ARCH-004-fails delegation-shape proof + 14 more.

Refs OMN-13472 (epic OMN-13471), OMN-12550, OMN-13325.
@coderabbitai

coderabbitai Bot commented Jun 22, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: af5e6509-213c-4002-b8fb-7f2f81678205

📥 Commits

Reviewing files that changed from the base of the PR and between e3feab8 and aaa0de6.

📒 Files selected for processing (7)
  • docker/migrations/forward/nodes/node_projection_delegation/0020_delegation_context_pack_hash.sql
  • docker/runners/runner-image.lock.json
  • scripts/validate.py
  • src/omnibase_infra/nodes/node_architecture_validator/validators/scanner_imperative_orchestrator_ratchet.py
  • src/omnibase_infra/nodes/node_architecture_validator/validators/validator_contract_declared_orchestrator_workflow.py
  • tests/integration/infra/test_omn_12765_release_backmerge_identity.py
  • tests/unit/nodes/node_architecture_validator/test_validator_contract_declared_orchestrator_workflow.py
✅ Files skipped from review due to trivial changes (2)
  • docker/migrations/forward/nodes/node_projection_delegation/0020_delegation_context_pack_hash.sql
  • docker/runners/runner-image.lock.json
🚧 Files skipped from review as they are similar to previous changes (4)
  • tests/unit/nodes/node_architecture_validator/test_validator_contract_declared_orchestrator_workflow.py
  • scripts/validate.py
  • src/omnibase_infra/nodes/node_architecture_validator/validators/validator_contract_declared_orchestrator_workflow.py
  • src/omnibase_infra/nodes/node_architecture_validator/validators/scanner_imperative_orchestrator_ratchet.py

📝 Walkthrough

Walkthrough

Introduces ARCH-004, a new architecture validation rule that flags orchestrator nodes whose contract.yaml declares an FSM but whose handler code drives state transitions imperatively. The change adds a cross-file node-directory validator, a ratchet scanner CLI with baseline tracking, integration into scripts/validate.py, a generated baseline YAML for nine existing violators, pre-commit hook registration, and CI reporting wiring. Also updates runner image lockfile digests and adds a database migration for delegation context tracking.

Changes

ARCH-004 Imperative-Orchestrator Ratchet

Layer / File(s) Summary
ARCH-004 rule declaration, constants, and module exports
src/omnibase_infra/nodes/node_architecture_validator/contract.yaml, src/omnibase_infra/nodes/node_architecture_validator/validators/validator_contract_declared_orchestrator_workflow.py, src/omnibase_infra/nodes/node_architecture_validator/validators/__init__.py
Declares the ARCH-004 rule (ERROR severity, node_directory_join strategy) in contract.yaml. Introduces the validator module header with RULE_ID, boundary-anchored regexes (EVENT_MARKER_REGEX, BRANCH_MARKER_REGEX), and warning/error thresholds. Updates __init__.py docs, imports, and __all__ to export the new validator components.
Core node analysis and signal evaluation
src/omnibase_infra/nodes/node_architecture_validator/validators/validator_contract_declared_orchestrator_workflow.py
Adds OrchestratorNodeAnalysis accumulator with additive risk_score. Implements orchestrator classification, reducer exemption, executor-bound workflow detection, fsm.states extraction, AST-based enum-state matching, payload fan-in counting, and largest-handler selection. Implements analyze_node_directory, _evaluate_signals (H1/H2/H3 ERRORs; W1–W4 WARNINGs), validate_contract_declared_orchestrator_workflow, and RuleContractDeclaredOrchestratorWorkflow.check.
Ratchet scanner: data structures, discovery, scan, baseline I/O, and violation logic
src/omnibase_infra/nodes/node_architecture_validator/validators/scanner_imperative_orchestrator_ratchet.py
Defines BaselineEntry and ScanResult. Implements discover_node_dirs, node_dirs_for_changed_files, _relative_handler_path, and scan_node_dirs. Adds baseline I/O (load_baseline, render_baseline_yaml, write_baseline). Implements ratchet_violations (new/untracked, risk/line growth, missing owner_ticket) and strict_violations (recurrence detection). Adds main() CLI with --check-all/--check-changed/--ratchet/--strict/--write-baseline modes.
scripts/validate.py integration, baseline YAML, and lint config
scripts/validate.py, architecture-handshakes/imperative-orchestrator-baseline.yaml, pyproject.toml
Adds run_imperative_orchestrators to scripts/validate.py with changed-files ratchet mode, full-report mode, and ImportError skip path. Extends CLI validator choices, files help, validator_map, and adds a forwarding elif branch. Adds the generated baseline YAML with nine accepted hard-fail entries. Adds T201 lint ignore for the scanner module.
CI workflow and pre-commit hook wiring
.github/workflows/ci.yml, .pre-commit-config.yaml
Adds a continue-on-error: true ratchet report step to the onex-validation CI job. Registers the onex-imperative-orchestrator-ratchet pre-commit hook targeting contract.yaml and handlers/handler_*.py. Adds the hook to the CI skip list.
Unit tests: validator heuristics, ratchet logic, and baseline round-trip
tests/unit/nodes/node_architecture_validator/test_validator_contract_declared_orchestrator_workflow.py
Constructs synthetic node directories to test delegation-shape (H1+H3), payload fan-in (H2), reducer exemption, executor-bound pass, full-audit scan, enum-state mirroring, and substring precision. Covers ratchet_violations/strict_violations failure modes including new/untracked/worsened/missing-owner-ticket/still-baselined cases. Verifies baseline YAML round-trip and RuleContractDeclaredOrchestratorWorkflow protocol surface.

Incidental Updates

Layer / File(s) Summary
Database migration and runner image lock
docker/migrations/forward/nodes/node_projection_delegation/0020_delegation_context_pack_hash.sql, docker/runners/runner-image.lock.json, tests/integration/infra/test_omn_12765_release_backmerge_identity.py
Adds a migration to persist delegation context-pack identity via a context_pack_hash column and index. Updates runner image lockfile digests (identity_digest, shared_env_digest). Updates integration test assertions to reflect regenerated runner identity lock values.

Sequence Diagram(s)

sequenceDiagram
  rect rgba(173, 216, 230, 0.5)
    Note over scripts/validate.py,scanner_imperative_orchestrator_ratchet: Pre-commit / CI changed-files ratchet mode
  end
  participant precommit as Pre-commit / CI
  participant validate_py as scripts/validate.py
  participant run_imporch as run_imperative_orchestrators
  participant scanner as scanner_imperative_orchestrator_ratchet
  participant validator as validator_contract_declared_orchestrator_workflow

  precommit->>validate_py: imperative_orchestrators --files [...changed files...]
  validate_py->>run_imporch: run_imperative_orchestrators(files=[...])
  run_imporch->>scanner: load_baseline(baseline_path)
  run_imporch->>scanner: node_dirs_for_changed_files(repo_root, files)
  run_imporch->>scanner: scan_node_dirs(repo, node_dirs)
  scanner->>validator: analyze_node_directory(node_dir)
  validator-->>scanner: OrchestratorNodeAnalysis (hard_fail=True/False)
  scanner-->>run_imporch: ScanResult(hard_fails=[...])
  run_imporch->>scanner: ratchet_violations(scanned, baseline)
  scanner-->>run_imporch: violations list
  run_imporch-->>validate_py: True (pass) or False (fail)
  validate_py-->>precommit: exit 0 or exit 1
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Poem

🐇 Hoppity-hop through the handler maze,
Where orchestrators set their FSM ablaze!
A ratchet now guards each imperative sin,
Hard-fails get tracked — no new ones get in.
The baseline shrinks as the codebase grows wise,
ARCH-004 watches with keen rabbit eyes! 🔍

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly describes the main feature: the implementation of ARCH-004, an imperative-orchestrator ratchet with cross-file rule, baseline, and gate mechanisms.
Docstring Coverage ✅ Passed Docstring coverage is 85.25% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jonah/omn-13472-arch-004-ratchet

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/omnibase_infra/nodes/node_architecture_validator/contract.yaml (1)

191-191: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Update rule list to include ARCH-004.

The description still references only ARCH-001, ARCH-002, ARCH-003. Consider updating to include ARCH-004 for consistency with the new rule. Same applies to line 240.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/omnibase_infra/nodes/node_architecture_validator/contract.yaml` at line
191, Update the description string that lists the specific architecture rules to
include the newly added ARCH-004 rule. Change the text from "ARCH-001, ARCH-002,
ARCH-003" to "ARCH-001, ARCH-002, ARCH-003, ARCH-004" in the description field.
Apply this same update in two locations: at line 191 and at line 240 in the
contract.yaml file to maintain consistency throughout the documentation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/validate.py`:
- Around line 587-607: The scan_node_dirs function calls on both the error path
(around line 587) and the full report path (around line 606) hardcode
"omnibase_infra" as the repo key, but the baseline file contains entries from
multiple repositories. Replace the hardcoded "omnibase_infra" strings with the
actual repo name derived from each node directory path by parsing the
src/<repo>/... pattern. Group the node directories by their respective
repositories and call scan_node_dirs with the correct repo name for each group
to match what is stored in the baseline.

In
`@src/omnibase_infra/nodes/node_architecture_validator/validators/scanner_imperative_orchestrator_ratchet.py`:
- Around line 439-443: The write_baseline function is being called without
verifying that the `--check-all` flag is set, which can overwrite the canonical
baseline with incomplete node data from a changed-files-only scan. Add a guard
condition that checks if args.check_all is True before allowing the baseline
write to proceed in the code block starting with if args.write_baseline. If
check_all is not set, either skip the baseline write with an appropriate log
message or raise an error to prevent partial baseline corruption.

---

Nitpick comments:
In `@src/omnibase_infra/nodes/node_architecture_validator/contract.yaml`:
- Line 191: Update the description string that lists the specific architecture
rules to include the newly added ARCH-004 rule. Change the text from "ARCH-001,
ARCH-002, ARCH-003" to "ARCH-001, ARCH-002, ARCH-003, ARCH-004" in the
description field. Apply this same update in two locations: at line 191 and at
line 240 in the contract.yaml file to maintain consistency throughout the
documentation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b86b3cb8-31f7-41fe-a7b2-6b5bc05cf256

📥 Commits

Reviewing files that changed from the base of the PR and between 38e3d3b and e3feab8.

📒 Files selected for processing (10)
  • .github/workflows/ci.yml
  • .pre-commit-config.yaml
  • architecture-handshakes/imperative-orchestrator-baseline.yaml
  • pyproject.toml
  • scripts/validate.py
  • src/omnibase_infra/nodes/node_architecture_validator/contract.yaml
  • src/omnibase_infra/nodes/node_architecture_validator/validators/__init__.py
  • src/omnibase_infra/nodes/node_architecture_validator/validators/scanner_imperative_orchestrator_ratchet.py
  • src/omnibase_infra/nodes/node_architecture_validator/validators/validator_contract_declared_orchestrator_workflow.py
  • tests/unit/nodes/node_architecture_validator/test_validator_contract_declared_orchestrator_workflow.py

Comment thread scripts/validate.py Outdated
…terns validator: no leading-underscore class names)
…uard --write-baseline behind --check-all

- scripts/validate.py: derive repo_name from repo_root.name instead of
  hardcoding 'omnibase_infra' (correct repo::node baseline keying).
- scanner: --write-baseline now requires --check-all (a baseline from a
  --check-changed scan would silently shrink the ratchet below true state);
  add test_write_baseline_requires_check_all.
@jonahgabriel

Copy link
Copy Markdown
Collaborator Author

CI status (OMN-13472)

All gates this change is responsible for are GREEN on head 3c7490cbb:

  • verify / verify (Receipt Gate) — PASS (Evidence-Source pinned to OCC merge-commit 33c69fc5, OCC PR onex_change_control#2900 MERGED).
  • gate / CodeRabbit Thread Check — PASS (both CodeRabbit threads addressed in 3c7490cbb + resolved).
  • ONEX Validators — PASS (the _NodeAnalysis → OrchestratorNodeAnalysis Patterns fix landed in ee04217c7).

The 3 remaining red checks are pre-existing dev-state failures, not introduced by this PR (this changeset touches zero runner-image / migration / deploy files — see git diff --name-only origin/dev...HEAD):

  • deploy-gate / deploy-gate and node-migration-sync — the current origin/dev HEAD (PR fix(OMN-13469): durabilize dev redpanda partition cap via .bootstrap.yaml #2064) merged with both of these red; they are inherited dev-state.
  • runner-image-build-smoke — shared_env_digest stale on origin/dev (recorded a796970a, recomputes efb9011b); reproduces on a clean checkout of this branch with no runner files touched. Owner: runner-image lock regen on dev (scripts/ci/runner_image_identity.py --mode generate), tracked separately.

These three require dev-state remediation outside OMN-13472's scope and should not block review of the ARCH-004 ratchet.

@jonahgabriel
jonahgabriel enabled auto-merge June 22, 2026 14:10
…lock

Commit 4269756 refreshed docker/runners/runner-image.lock.json
(identity_digest 8c3208f1->0f337da6, shared_env_digest a796970a->efb9011b)
to clear runner-image-build-smoke, but left
test_release_backmerge_preserves_runner_identity_lock asserting the
stale digests, failing Tests (Split 1/15). Update the assertions to the
committed lock values; runner-image-build-smoke validates the lock vs
real image identity.
@jonahgabriel
jonahgabriel added this pull request to the merge queue Jun 22, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jun 22, 2026
@jonahgabriel
jonahgabriel added this pull request to the merge queue Jun 22, 2026
Merged via the queue into dev with commit 4aee6da Jun 22, 2026
90 of 91 checks passed
@jonahgabriel
jonahgabriel deleted the jonah/omn-13472-arch-004-ratchet branch June 22, 2026 15:43
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