Skip to content

docs(arch): formalize registration FSM contract [OMN-938] - #221

Merged
jonahgabriel merged 10 commits into
mainfrom
jonah/omn-938-formalize-registration-fsm-contract
Dec 20, 2025
Merged

jonahgabriel merged 10 commits into
mainfrom
jonah/omn-938-formalize-registration-fsm-contract

Conversation

@jonahgabriel

@jonahgabriel jonahgabriel commented Dec 19, 2025 •

Copy link
Copy Markdown
Contributor

Summary

  • Formal FSM contract for Dual Registration Reducer (OMN-889)
  • 10 states covering full registration lifecycle
  • 16 state transitions with triggers and guards
  • Complete Mermaid state diagram

Acceptance Criteria

  • All states documented (10 states across 4 categories)
  • All valid transitions documented (16 transitions)
  • Transition guards defined (12 guard conditions)
  • State diagram included (Mermaid stateDiagram-v2)

Dependencies

Ticket Title Status
OMN-912 Discriminated Union for Core Intents ✅ Done
OMN-913 Registration Payload Models ✅ Done

Blocks

Ticket Title
OMN-889 Dual Registration Reducer
OMN-942 Comprehensive Reducer Test Suite

Test Plan

  • Mermaid diagram renders correctly in GitHub
  • Document structure follows existing architecture docs patterns
  • Contract is implementable for OMN-889

Summary by CodeRabbit

  • Documentation
    • Added Registration FSM Contract v1.0.0 describing a deterministic node registration lifecycle with states, transitions, guards, triggers, intents, checkpointing, timeouts, error handling, recovery flows, retries (with backoff guidance), transition definitions, diagrams, and a configuration example.
    • Added an agent guideline: do not run agents in background mode; run parallel polymorphic agents in the foreground by dispatching multiple agents in a single message.

✏️ Tip: You can customize this high-level summary in your review settings.

Define formal FSM contract for Dual Registration Reducer (OMN-889):

- 10 states: unregistered, validating, registering_postgres,
  postgres_registered, registering_consul, registered,
  partial_registered, deregistering, deregistered, failed
- 16 state transitions with triggers and priorities
- 12 guard conditions with expressions
- Complete Mermaid state diagram
- Intent emission mapping for Consul and PostgreSQL
- Implementation notes and error handling strategies

Dependencies: OMN-912 (Core Intents), OMN-913 (Payload Models)
Blocks: OMN-889 (Reducer Implementation), OMN-942 (Test Suite)
@coderabbitai

coderabbitai Bot commented Dec 19, 2025 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Review was skipped as selected files did not have any reviewable changes.

💤 Files selected but had no reviewable changes (1)
  • docs/architecture/REGISTRATION_FSM_CONTRACT.md

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Adds a formal Registration FSM Contract (v1.0.0) specifying a deterministic Dual Registration reducer for node registration to PostgreSQL (source of truth) and Consul (service discovery): defines 10 states, 17 transitions, triggers, guards, intents, timeouts, checkpointing, error/recovery flows, and implementation guidance for OMN-889. Also adds an Agent Instructions guideline discouraging running agents in background mode (CLAUDE.md).

Changes

Cohort / File(s) Change Summary
Registration FSM Contract Documentation
docs/architecture/REGISTRATION_FSM_CONTRACT.md
Added new v1.0.0 specification detailing a 10-state, 17-transition Dual Registration FSM: state metadata (types, terminal/recoverable flags, entry/exit actions), per-state data, timeouts, trigger catalog (external/internal/effect/orchestrator), guard syntax/validations, intent catalog (Postgres/Consul intents), retry/backoff and correlation guidance, transition table (names, from/to, triggers, priorities, conditions, actions), synthetic CONTINUE trigger semantics, error/timeout handling, recovery/checkpoint behavior, Mermaid diagrams and subcontract example.
Agent Instruction Update
CLAUDE.md
Adds guideline to avoid running agents in background mode (run_in_background: true) and to run parallel polymorphic agents in the foreground by dispatching multiple agents in a single message.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor Operator
    participant FSM as Registration FSM
    participant Executor as Intent Executor
    participant PG as PostgreSQL
    participant CONS as Consul

    Operator->>FSM: startRegistration()
    FSM->>Executor: emit ModelPostgresUpsertRegistrationIntent
    Executor->>PG: upsert registration
    alt Postgres success
        PG-->>Executor: success
        Executor-->>FSM: intentOutcome(success)
        Note right of FSM: synthetic CONTINUE triggers Consul step
        FSM->>Executor: emit ModelConsulRegisterIntent (CONTINUE)
        Executor->>CONS: register service
        alt Consul success
            CONS-->>Executor: success
            Executor-->>FSM: intentOutcome(success)
            FSM->>FSM: transition -> RegisteredOperational
        else Consul failure
            CONS-->>Executor: failure
            Executor-->>FSM: intentOutcome(failure)
            FSM->>FSM: transition -> PartialRegistration
            FSM->>Executor: schedule retry/compensate intents (backoff)
        end
    else Postgres failure
        PG-->>Executor: failure
        Executor-->>FSM: intentOutcome(failure)
        FSM->>FSM: transition -> Error / Retry (with backoff)
    end

    Note over FSM,Executor: Timeouts and orchestrator-managed recovery can trigger Deregister/rollback paths
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Attention points:
    • Verify consistency between the documented 10 states and 17 transitions and the transition table.
    • Confirm exact intent names and CONTINUE trigger semantics match executor/orchestrator implementations.
    • Review timeout semantics, retry/backoff guidance, and checkpoint/recovery behavior for edge cases.

Possibly related PRs

Poem

🐰
I hop through states both calm and spry,
Checkpoints kept as intents fly,
Postgres nods, Consul rings,
Retries, timeouts, hopeful springs—
A rabbit signs this FSM with a sigh. 🥕

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and specifically describes the main change: formalizing the registration FSM contract document for OMN-938, which aligns with the primary objective and the core file added (REGISTRATION_FSM_CONTRACT.md).
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

@claude

claude Bot commented Dec 19, 2025

Copy link
Copy Markdown

Pull Request Review: Registration FSM Contract [OMN-938]

Overall Assessment

Status: ✅ APPROVED with minor suggestions

This is an excellent, comprehensive FSM contract specification that follows ONEX architectural patterns. The document is well-structured, detailed, and provides clear implementation guidance for OMN-889. The Mermaid diagrams and extensive documentation make this highly maintainable.


Strengths

1. Architectural Alignment ✅

  • Correctly follows the ONEX Reducer pattern: delta(state, event) -> (new_state, intents[])
  • Pure FSM semantics with Intent emission for side effects
  • Aligns with CONTRACT_DRIVEN_NODEREDUCER_V1_0.md specification
  • Proper separation of concerns (Reducer describes, Effects execute)

2. Comprehensive State Coverage ✅

  • 10 well-defined states across 4 categories (initial, operational, snapshot, terminal, error)
  • Clear distinction between terminal states (registered, deregistered) and recoverable error states (partial_registered, failed)
  • Proper timeout specifications for operational states (5s-15s)
  • Recovery paths from all error states

3. Dual Registration Logic ✅

  • PostgreSQL-first approach (source of truth) is correct
  • Consul-second for service discovery is appropriate
  • partial_registered state elegantly handles asymmetric failures
  • Targeted retry mechanisms (RETRY vs RETRY_POSTGRES) enable granular recovery

4. Documentation Quality ✅

  • Excellent YAML specifications for each state and transition
  • Both Mermaid and ASCII diagrams for visualization
  • Complete Intent emission matrix
  • Clear validation rules and error handling strategies
  • Proper versioning (v1.0.0) and metadata

Issues & Concerns

1. Terminal State Inconsistency ⚠️ CRITICAL

Issue: Line 646 states registered is "functionally terminal until a shutdown signal is received", but line 59 marks it as terminal: Yes.

Per CONTRACT_DRIVEN_NODEREDUCER_V1_0.md:189-195:

Terminal states represent completed workflows that cannot be re-entered. Contract validation MUST detect and raise ModelOnexError if transitions are defined from terminal states.

Contradiction: Transition 12 (initiate_deregistration) allows registered -> deregistering.

Resolution Options:

Option A (Recommended): Mark registered as non-terminal in the state definition

state_name: registered
is_terminal: false  # Not truly terminal - allows DEREGISTER transition

Option B: Remove the registered -> deregistering transition entirely and require external orchestration for deregistration.

Recommendation: Use Option A. The registered state is a stable success state but not truly terminal since graceful shutdown is a valid lifecycle operation. Update line 59 and line 200 to is_terminal: false.


2. Retry Counter Placement 🔵 MODERATE

Lines 554-556, 584-586, 681-684: Guard retry_count < 3 appears in multiple transitions.

Issue: The document doesn't specify:

  • Where retry_count is stored (FSM state context? External?)
  • Who increments it (Reducer? Orchestrator?)
  • When it's reset (on success? on state change?)

Recommendation: Add a "Retry Counter Management" section specifying:

retry_counter:
  storage: fsm_state_context.retry_count
  increment_on: [RETRY, RETRY_POSTGRES] triggers
  reset_on: [POSTGRES_SUCCEEDED, CONSUL_SUCCEEDED, VALIDATION_PASSED]
  max_value: 3

3. Missing Trigger Definitions 🔵 MODERATE

Transition Table (lines 321-338): Uses triggers like REGISTER, VALIDATION_PASSED, POSTGRES_SUCCEEDED, etc.

Issue: No formal enum or trigger catalog is defined.

Recommendation: Add a "Trigger Catalog" section:

triggers:
  - REGISTER              # IntrospectionEvent received
  - VALIDATION_PASSED     # Payload validation succeeded
  - VALIDATION_FAILED     # Payload validation failed
  - POSTGRES_SUCCEEDED    # PostgreSQL effect returned success
  - POSTGRES_FAILED       # PostgreSQL effect returned failure
  - CONSUL_SUCCEEDED      # Consul effect returned success
  - CONSUL_FAILED         # Consul effect returned failure
  - CONTINUE              # Automatic transition (no external event)
  - RECOVERY_COMPLETE     # Both components now registered
  - DEREGISTER            # Shutdown signal received
  - DEREGISTRATION_COMPLETE  # Both deregistrations succeeded
  - RETRY                 # Retry after failure
  - RETRY_POSTGRES        # Retry PostgreSQL specifically
  - ABANDON               # Give up on registration
  - FATAL_ERROR           # Unrecoverable error

4. Wildcard Transition Edge Case 🟢 MINOR

Line 721: from_state: "*" for global_error_handler.

Issue: Document doesn't clarify if * includes terminal states.

Per CONTRACT_DRIVEN_NODEREDUCER_V1_0.md: Terminal states cannot have outgoing transitions.

Recommendation: Clarify in transition 16:

from_state: "*"  # All non-terminal states

5. CONTINUE Trigger Semantics 🟢 MINOR

Line 328 (Transition 6): postgres_registered -> registering_consul uses CONTINUE trigger.

Issue: Unclear if this is:

  • Automatic (no event needed)
  • Self-triggered by Reducer
  • Triggered by Orchestrator after aggregating PostgreSQL result

Recommendation: Clarify in Implementation Notes:

CONTINUE Trigger: Automatically triggered by the Reducer after entering 
postgres_registered state. No external event required. This enables 
immediate progression to Consul registration without waiting for another event.

6. Deregistration Intent Ordering 🟢 MINOR

Lines 256-257: deregistering entry emits both Consul and PostgreSQL deregister intents.

Question: Should deregistration be ordered (Consul first, then PostgreSQL) like registration was ordered (PostgreSQL first, then Consul)?

Recommendation: Add note in line 266:

Note: Deregistration intents are emitted in parallel. Unlike registration, 
the order doesn't matter since both systems are being cleaned up.

Security Considerations

✅ No Security Issues Detected

  • No credential handling in FSM (delegated to Effects)
  • No SQL injection risks (Pydantic models for payload)
  • Proper correlation_id tracking for audit trails
  • Error messages don't leak sensitive data

Performance Considerations

✅ Efficient Design

  • Timeouts: Appropriate values (5s validation, 10s per registration, 15s deregistration)
  • Parallel Deregistration: Correct approach (line 995)
  • Snapshot State: postgres_registered enables quick Consul retry without re-running PostgreSQL

Suggestion: Consider adding performance benchmarks in OMN-942 test suite:

  • Full happy path latency budget (< 20s)
  • Partial failure retry overhead (< 15s)
  • Validation failure fast-fail (< 5s)

Test Coverage Recommendations

For OMN-942 (Comprehensive Reducer Test Suite):

Required Test Scenarios

  1. Happy Path: unregistered -> ... -> registered (16 tests)
  2. Validation Failure: validating -> failed -> validating -> ... (3 tests)
  3. PostgreSQL Failure: registering_postgres -> failed (2 tests)
  4. Consul Failure: registering_consul -> partial_registered -> registered (5 tests)
  5. Partial Recovery: All partial_registered transitions (4 tests)
  6. Deregistration: registered -> deregistering -> deregistered (2 tests)
  7. Retry Exhaustion: retry_count >= 3 guard failure (3 tests)
  8. Wildcard Error: FATAL_ERROR from each state (10 tests)
  9. Intent Emission: Verify correct intents at each transition (16 tests)
  10. Guard Validation: Test all 12 guards (12 tests)

Total Minimum: ~70 test cases


Documentation Improvements

Suggested Additions

  1. Sequence Diagram: Add a sequence diagram showing Reducer-Effect-Orchestrator interaction
  2. Error Code Reference: Link to EnumCoreErrorCode definitions for the error codes in line 978-984
  3. Correlation ID Propagation: Add example showing how correlation_id flows through the entire workflow
  4. PostgreSQL Record Schema: Reference or define postgres_record structure
  5. Consul Health Check Format: Document expected consul_health_check format

Code Quality

YAML Specifications ✅

  • Consistent formatting
  • Complete field coverage
  • Proper indentation
  • Valid YAML syntax

Mermaid Diagram ✅

  • Renders correctly (line 784-830)
  • All states present
  • All transitions labeled
  • Notes add clarity

Markdown Structure ✅

  • Proper heading hierarchy
  • Complete table of contents
  • Cross-references work
  • Code blocks properly formatted

Alignment with CLAUDE.md

✅ Follows All Required Patterns

  1. Architecture: Pure FSM Reducer with Intent emission (CLAUDE.md line 17)
  2. Error Handling: Uses ModelOnexError with error codes (not shown but implied)
  3. Documentation: Follows docs/architecture/ pattern
  4. Versioning: Proper SemVer (1.0.0)
  5. Terminology: Aligns with docs/standards/onex_terminology.md

Final Recommendations

Before Merge

  1. Fix Terminal State Definition (line 200): is_terminal: false for registered state ⚠️
  2. Add Retry Counter Management section 🔵
  3. Add Trigger Catalog section 🔵

Post-Merge (OMN-889 Implementation)

  1. Verify retry counter implementation matches spec
  2. Add sequence diagram to docs
  3. Create enum for triggers (EnumRegistrationFSMTrigger)
  4. Implement all 70+ test cases in OMN-942

Summary

This is a high-quality, production-ready FSM contract that demonstrates deep understanding of ONEX architecture. The only critical issue is the registered state terminal classification, which contradicts the transition definition. Fixing this and adding the suggested sections will make this contract implementation-ready.

Estimated Implementation Effort: 3-5 days for FSM executor, 2-3 days for tests (OMN-942)

Great work! 🚀


Reviewed by: Claude Code
Review Date: 2025-12-19
Contract Version: 1.0.0
Architecture Compliance: ✅ Passed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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)
docs/architecture/REGISTRATION_FSM_CONTRACT.md (1)

740-779: Clarify guard expression syntax and validation rules.

The guard definitions use a simple 3-token syntax (field operator value), but the implementation details are sparse. Consider adding:

  1. Type system: How are field types (boolean, string, number) determined at runtime?
  2. Field resolution: How are guard field names (e.g., postgres_applied, retry_count) resolved from FSM context?
  3. Operator semantics: Document whether operators like exists check for null/undefined, key presence in objects, or array membership.
  4. Error cases: What happens if a guard field is undefined or an operator is unsupported?

This will help implementers of OMN-889 validate guards correctly.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 87be33c and 7b7b35b.

📒 Files selected for processing (1)
  • docs/architecture/REGISTRATION_FSM_CONTRACT.md (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-19T18:46:12.150Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T18:46:12.150Z
Learning: Applies to **/*reducer*.py : Use ModelIntent pattern for REDUCER nodes with pure FSM transitions

Applied to files:

  • docs/architecture/REGISTRATION_FSM_CONTRACT.md
🪛 LanguageTool
docs/architecture/REGISTRATION_FSM_CONTRACT.md

[style] ~311-~311: To form a complete sentence, be sure to include a subject.
Context: ... **Purpose**: Complete failure state. Can be retried viaRETRY` trigger. --- #...

(MISSING_IT_THERE)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: Tests (Split 20/20)
  • GitHub Check: Tests (Split 16/20)
  • GitHub Check: Tests (Split 18/20)
  • GitHub Check: Tests (Split 15/20)
  • GitHub Check: Tests (Split 13/20)
  • GitHub Check: Tests (Split 10/20)
  • GitHub Check: Tests (Split 17/20)
  • GitHub Check: Tests (Split 19/20)
  • GitHub Check: Tests (Split 9/20)
  • GitHub Check: Tests (Split 8/20)
  • GitHub Check: Tests (Split 11/20)
  • GitHub Check: Tests (Split 2/20)
  • GitHub Check: Tests (Split 14/20)
  • GitHub Check: Tests (Split 5/20)
  • GitHub Check: Tests (Split 12/20)
  • GitHub Check: Tests (Split 3/20)
  • GitHub Check: Tests (Split 4/20)
  • GitHub Check: Tests (Split 1/20)
  • GitHub Check: Tests (Split 6/20)
  • GitHub Check: claude-review
🔇 Additional comments (4)
docs/architecture/REGISTRATION_FSM_CONTRACT.md (4)

344-363: Clarify intent emission semantics in transition actions.

Transition details include action_type: emit_intent with nested action_config (e.g., lines 359–361), but the relationship to the explicit Intent Emission section (lines 883–950) is unclear.

Ambiguity: Are the log_event/log_metric intents in transition actions actually emitted by the Reducer, or are they examples of what could be emitted? The Intent Emission Matrix seems to define the canonical intents, yet transition details also specify intent emissions.

Recommendation:

  1. Clarify whether action_config describes emitted intents or is pseudo-code for documentation.
  2. If they are emitted, ensure they don't conflict with the Intent Emission Matrix.
  3. If they are documentation only, move them to an "Associated Actions" section to avoid confusion.

Also applies to: 419-440


721-721: Validate wildcard transition scope and priority.

Transition 16 (global_error_handler) uses from_state: "*" with priority: 0 (lowest). This enables transitions from any non-terminal state, but the contract doesn't explicitly:

  1. Exclude terminal states: Do registered and deregistered allow FATAL_ERROR transitions? The note says "from any non-terminal state," but this should be explicit in the transition definition.
  2. Priority ordering: Clarify how priority interacts with wildcards. If multiple transitions are valid, is priority the tiebreaker?
  3. Atomicity: Are wildcard transitions atomic, or can other transitions fire concurrently?
🔎 Suggested clarification

Update the transition definition to explicitly exclude terminal states:

transition_name: global_error_handler
from_state: "*" # Any state EXCEPT terminal states (registered, deregistered)
to_state: failed
trigger: FATAL_ERROR
priority: 0
note: "Wildcard wildcard does not apply to terminal states"
is_atomic: true

And add a subsection to the Transitions section:

**Wildcard Transition Semantics**:
- Wildcard `from_state: "*"` matches all states EXCEPT terminal states (`registered`, `deregistered`).
- When multiple transitions are valid, priority determines precedence (highest first).
- Atomic transitions cannot be preempted.

Also applies to: 736-736


784-830: Verify Mermaid diagram renders correctly in GitHub.

The Mermaid stateDiagram-v2 diagram is well-formed, but GitHub's Mermaid rendering has evolving support and occasional edge-case rendering issues (e.g., with note blocks, special characters, or very large diagrams).

Action: After merging, verify in the GitHub PR preview that:

  1. The diagram renders without errors or warnings.
  2. All states and transitions are visible.
  3. Note annotations render correctly.

If rendering issues occur, consider providing an alternative ASCII flow diagram (which you've already included at lines 832–879, which is excellent).


1-1107: Excellent comprehensive FSM specification document.

This document meets all acceptance criteria and provides clear, detailed guidance for OMN-889 implementation:

✓ All 10 states documented across 4 categories with YAML metadata
✓ All 16 transitions with triggers, guards, and actions
✓ 12 guard definitions with expression syntax
✓ Mermaid diagram + simplified ASCII flow for clarity
✓ Intent emission semantics and correlation tracking
✓ Validation rules and error handling scenarios
✓ Implementation guidance including purity constraints and FSM configuration
✓ Cross-references to dependencies (OMN-912, OMN-913) and related architecture docs

Alignment with ONEX patterns: The document correctly emphasizes Reducer purity (line 991) and Intent emission for side effects, aligning with the ModelIntent pattern. The separation of retry logic to Effect nodes (line 1008) is architecturally sound.

Structure quality: The YAML metadata for each state and transition makes the contract machine-readable and implementable. The recovery scenarios (lines 1053–1080) are instructive.

Minor refinements needed: Guard expression syntax, retry limit standardization, intent emission semantics, and wildcard transition scope (flagged in separate comments above).

Comment thread docs/architecture/REGISTRATION_FSM_CONTRACT.md Outdated
required: true
- condition_name: retry_count_valid
condition_type: expression
expression: "retry_count < 3"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Standardize and document retry limit enforcement.

The retry limit < 3 is hardcoded in three guard definitions across different transition contexts (retry_consul, retry_postgres, retry_from_failed). This creates maintenance risk if the limit changes.

Recommendation:

  1. Define a global constant (e.g., MAX_RETRY_COUNT = 3) in a configuration or constants section.
  2. Reference it consistently across all guards.
  3. Document whether exceeding the limit triggers the RETRY_EXHAUSTED error code (line 982).
🔎 Proposed structure

Add a new section before "Guards" to define FSM constants:

## FSM Constants

| Constant | Value | Description |
|----------|-------|-------------|
| MAX_RETRY_COUNT | 3 | Maximum number of retry attempts before giving up |
| VALIDATION_TIMEOUT_MS | 5000 | Timeout for validation state |
| REGISTRATION_TIMEOUT_MS | 10000 | Timeout for registration states |
| DEREGISTRATION_TIMEOUT_MS | 15000 | Timeout for deregistration state |

Then update guard expressions to reference the constant:

-expression: "retry_count < 3"
+expression: "retry_count < MAX_RETRY_COUNT"

Also applies to: 585-585, 683-683

🤖 Prompt for AI Agents
In docs/architecture/REGISTRATION_FSM_CONTRACT.md around lines 556, 585, and 683
(and note line 982 for the error code), the retry limit "retry_count < 3" is
hardcoded in multiple guards; add a new "FSM Constants" section immediately
before the "Guards" section that defines MAX_RETRY_COUNT = 3 (and any related
timeouts if desired), update each guard expression to reference MAX_RETRY_COUNT
instead of the literal 3, and update the documentation at line 982 to explicitly
state whether exceeding MAX_RETRY_COUNT triggers the RETRY_EXHAUSTED error code
so all references stay consistent and maintainable.

…OMN-938]

Fixes identified in PR #221 code review:

MAJOR fixes:
- Fix terminal state inconsistency: `registered` now correctly marked as
  `is_terminal: false` to allow graceful shutdown transition
- Add Retry Counter Management section with YAML spec (storage, increment_on,
  reset_on, max_value)

MINOR fixes:
- Fix sentence fragment at line 311 ("Can be retried" -> "The registration
  can be retried")
- Add Trigger Catalog section documenting all 15 FSM triggers with sources
- Add CONTINUE trigger semantics note explaining auto-emission
- Add deregistration ordering note (parallel intent emission)
- Update wildcard transition note clarifying terminal state exclusion

NITPICK fixes:
- Add Guard Implementation Details section with:
  - Type Resolution (boolean, numeric, string fields)
  - Field Resolution (FSM context structure)
  - Operator Semantics table (all operators documented)
  - Error Handling (undefined field, unsupported operator, type mismatch)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
docs/architecture/REGISTRATION_FSM_CONTRACT.md (2)

311-311: Fix sentence fragment in failed state description.

This was previously flagged and needs correction: rewrite "Can be retried via RETRY trigger." as a complete sentence.

🔎 Proposed fix
-**Purpose**: Complete failure state. Can be retried via `RETRY` trigger.
+**Purpose**: Complete failure state. It can be retried via `RETRY` trigger.

560-560: Standardize and document retry limit enforcement.

The retry limit < 3 is hardcoded across three transitions (lines 560, 585, 687), and line 982 references RETRY_EXHAUSTED error without explicitly documenting its trigger condition. This was flagged previously and should be refactored to use a constant for maintainability.

Recommendation: Add an FSM Constants section before "Guards" (around line 744) to centralize these values, then update all guard expressions and error documentation to reference the constant consistently.

🔎 Proposed structure

Add this section before line 744:

## FSM Constants

| Constant | Value | Description |
|----------|-------|-------------|
| MAX_RETRY_COUNT | 3 | Maximum number of retry attempts before giving up |

Then update each guard at lines 560, 585, and 687:

-    expression: "retry_count < 3"
+    expression: "retry_count < MAX_RETRY_COUNT"

And clarify line 1055 to explicitly tie the constant to the error:

-| `RETRY_EXHAUSTED` | Maximum retry count exceeded | Manual intervention |
+| `RETRY_EXHAUSTED` | retry_count >= MAX_RETRY_COUNT | Manual intervention or `ABANDON` |

Also applies to: 585-585, 687-687, 982-982

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7b7b35b and b619d39.

📒 Files selected for processing (1)
  • docs/architecture/REGISTRATION_FSM_CONTRACT.md (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-19T18:46:12.170Z
Learnt from: CR
Repo: OmniNode-ai/omnibase_core PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T18:46:12.170Z
Learning: Applies to **/*reducer*.py : Use ModelIntent pattern for REDUCER nodes with pure FSM transitions

Applied to files:

  • docs/architecture/REGISTRATION_FSM_CONTRACT.md
🪛 LanguageTool
docs/architecture/REGISTRATION_FSM_CONTRACT.md

[style] ~801-~801: To form a complete sentence, be sure to include a subject.
Context: ...xternal/Internal | Retry after failure. Can be triggered by operator intervention o...

(MISSING_IT_THERE)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: Tests (Split 20/20)
  • GitHub Check: Tests (Split 19/20)
  • GitHub Check: Tests (Split 12/20)
  • GitHub Check: Tests (Split 13/20)
  • GitHub Check: Tests (Split 15/20)
  • GitHub Check: Tests (Split 18/20)
  • GitHub Check: Tests (Split 2/20)
  • GitHub Check: Tests (Split 3/20)
  • GitHub Check: Tests (Split 16/20)
  • GitHub Check: Tests (Split 9/20)
  • GitHub Check: Tests (Split 7/20)
  • GitHub Check: Tests (Split 11/20)
  • GitHub Check: Tests (Split 14/20)
  • GitHub Check: Tests (Split 8/20)
  • GitHub Check: Tests (Split 4/20)
  • GitHub Check: Tests (Split 17/20)
  • GitHub Check: Tests (Split 1/20)
  • GitHub Check: Tests (Split 6/20)
  • GitHub Check: Tests (Split 5/20)
  • GitHub Check: claude-review

Comment thread docs/architecture/REGISTRATION_FSM_CONTRACT.md Outdated
@claude

claude Bot commented Dec 19, 2025

Copy link
Copy Markdown

PR Review: Registration FSM Contract [OMN-938]

Overview

This PR introduces a comprehensive FSM contract for dual registration (Consul + PostgreSQL) management. The document is well-structured, thorough, and follows ONEX architectural patterns.

✅ Strengths

1. Excellent Documentation Structure

  • Clear table of contents with 9 major sections
  • Comprehensive state definitions (10 states) with YAML specifications
  • Detailed transition table (16 transitions) with full YAML schemas
  • Beautiful Mermaid state diagrams for visualization
  • Related documentation cross-references

2. Architectural Alignment

  • Follows ONEX Reducer pattern
  • Pure FSM semantics - no I/O in Reducer
  • Intent-based design for Effect node execution
  • Proper separation of concerns
  • Aligns with CLAUDE.md guidelines for v0.4.0

3. Comprehensive Coverage

  • All 10 states properly categorized
  • Guard definitions with expression syntax
  • Trigger catalog with 15 triggers
  • Intent emission matrix
  • Validation rules
  • Error handling scenarios
  • Retry counter management

4. Implementation Guidance

  • Clear notes for OMN-889 implementation
  • FSM subcontract YAML configuration
  • Guard implementation details
  • Correlation tracking guidance
  • Recovery flows with concrete scenarios

🔍 Issues & Recommendations

CRITICAL: State Type Inconsistency

Location: Line 59, 199-201

The registered state has conflicting metadata - state_type says terminal but is_terminal is false.

Impact: Creates confusion about state behavior. Documentation says success state allowing graceful shutdown but YAML contradicts itself.

Recommendation: Change state_type from terminal to success, OR document why terminal is used despite is_terminal being false.


MAJOR: Missing Transition from partial_registered to failed

Location: Transition table (lines 322-341)

The FSM lacks a transition from partial_registered to failed when retry count is exhausted.

Current behavior: When retry_count >= 3 in partial_registered, guards block transitions 9 and 10. Only options are RECOVERY_COMPLETE or FATAL_ERROR.

Problem: If retries exhausted but recovery not complete, FSM is stuck with no natural path to failed state.

Recommendation: Add transition 17 for retry_exhausted, OR document that operators must use FATAL_ERROR trigger when retries exhausted.


MAJOR: Guard Expression Validation Gap

Location: Lines 765-782, 812-852

The guard expression syntax allows complex operators (matches, contains, in) but implementation details do not specify:

  1. Array syntax - How are arrays specified?
  2. Regex syntax - What regex flavor for matches operator?
  3. String literals - Are quotes required?

Recommendation: Add subsection under Guard Expression Syntax documenting literal syntax for strings, booleans, numbers, arrays, and regex patterns.


MINOR: Ambiguous CONTINUE Trigger Semantics

Location: Lines 330, 471-491, 797

The CONTINUE trigger is described as automatically emitted by the Reducer but lacks clarity:

  1. WHEN is it emitted? Immediately on entering postgres_registered?
  2. Can it fail? What happens if CONTINUE emission fails?
  3. Is it logged? Should CONTINUE appear in FSM event logs?

Recommendation: Expand note at line 490 to clarify timing, error handling, and logging requirements.


MINOR: Incomplete Error Code Mapping

Location: Lines 1050-1057

Validation errors table lists 5 error codes but document references additional codes like GUARD_EVALUATION_ERROR and GUARD_TYPE_ERROR.

Recommendation: Add complete error code appendix including all FSM-specific error codes with descriptions and context fields.


MINOR: Missing Timeout Handling

Location: Lines 97, 131, 176, 254

Several states define timeout_ms but no timeout transition defined. What happens when state timeout expires?

Recommendation: Add timeout transition or document timeout behavior in Implementation Notes.


🎯 Final Verdict

Code Quality: ⭐⭐⭐⭐⭐ (5/5)

Architectural Alignment: ⭐⭐⭐⭐⭐ (5/5)

Completeness: ⭐⭐⭐⭐ (4/5)

Implementability: ⭐⭐⭐⭐ (4/5)

✅ Approval Recommendation

Status: APPROVE with minor revisions

This is excellent work that will unblock OMN-889. Issues identified are mostly clarifications and edge cases that can be addressed in follow-up commit or PR.

Must-Fix Before Merge:

  1. Resolve registered state type inconsistency (line 199)
  2. Document timeout handling behavior

Should-Fix Soon (can be follow-up):

  1. Add missing partial_registered to failed transition for retry exhaustion
  2. Clarify guard expression literal syntax
  3. Complete error code mapping

Nice-to-Have:

  1. Reorganize trigger catalog by source
  2. Add test scenario documentation
  3. Standardize Reducer vs FSM terminology

Great work on this comprehensive specification! The FSM contract is well-designed and will provide solid guidance for the Dual Registration Reducer implementation.


Reviewed by: Claude Code (Sonnet 4.5)
Review Date: 2025-12-19
Based on: omnibase_core CLAUDE.md v0.4.0

…tract [OMN-938]

Critical:
- Fix state type inconsistency: change `registered` from terminal to success type
- Add `success_states` field to FSM subcontract configuration

Major:
- Add Transition 17 (retry_exhausted) from partial_registered to failed
- Add RETRY_EXHAUSTED trigger for retry limit enforcement
- Document guard expression validation rules (load-time and runtime)
- Add comprehensive retry limit enforcement documentation

Minor:
- Fix sentence fragments in failed state and RETRY trigger descriptions
- Clarify CONTINUE trigger semantics as internal auto-progression
- Add complete error code mapping (6 new codes)
- Add timeout handling documentation with recovery strategies
- Update state diagrams to show retry exhaustion path

Nitpick:
- Add guard validation examples and error codes
@claude

claude Bot commented Dec 19, 2025

Copy link
Copy Markdown

PR Review: Registration FSM Contract [OMN-938]

Summary

This is an excellent architectural documentation PR that formalizes the FSM contract for dual registration (Consul + PostgreSQL). The document is comprehensive, well-structured, and adheres to ONEX framework conventions. I have some suggestions to further improve clarity and correctness.


✅ Strengths

1. Comprehensive Coverage

  • 10 states covering the full registration lifecycle (initial → operational → success/error → terminal)
  • 17 transitions with clear triggers and guards
  • Detailed guard validation with both contract load-time and runtime phases
  • Excellent Mermaid state diagrams for visualization

2. Architecture Alignment

  • Correctly follows ONEX Reducer pattern: pure FSM with intent emission
  • Proper separation of concerns (Reducer emits intents, Effects execute)
  • Validated against actual implementation models (ModelConsulRegisterIntent, ModelPostgresUpsertRegistrationIntent, ModelRegistrationPayload)

3. Error Handling & Recovery

  • Well-designed partial registration handling (partial_registered state)
  • Comprehensive retry semantics with limit enforcement (max 3 retries)
  • Clear timeout specifications for operational states
  • Multiple recovery paths documented

4. Documentation Quality

  • Clear YAML state/transition definitions
  • Excellent trigger catalog with source attribution
  • Implementation notes for OMN-889
  • Related documentation cross-references

🔍 Issues & Recommendations

CRITICAL: State Transition Inconsistency

Issue: Transition Table shows 17 transitions, but the document states "The Registration FSM defines 17 transitions" everywhere, yet the transition count is correct at 17. However, there's a logical inconsistency in the RETRY trigger.

Lines 333-334, 551, 580: Transitions 9 and 10 both use the RETRY trigger from partial_registered, but with different guards:

  • Transition 9: retry_consul uses RETRY with guard postgres_applied == true
  • Transition 10: retry_postgres uses RETRY_POSTGRES with guard consul_applied == true

Problem: Line 551 shows Transition 9 uses RETRY trigger, but how does the FSM distinguish whether to retry Consul or PostgreSQL when both failed? The guard postgres_applied == true only tells us PostgreSQL succeeded (so retry Consul), but what if both failed?

Recommendation:
Either:

  1. Use distinct triggers (which you already do for Transition 10 with RETRY_POSTGRES), so Transition 9 should perhaps use RETRY_CONSUL instead of RETRY
  2. Clarify in the documentation that RETRY from partial_registered is only valid when exactly one component succeeded (XOR condition enforced by guards)

Suggested Fix (Option 1):

# Transition 9
trigger: RETRY_CONSUL  # Make it explicit
conditions:
  - postgres_applied == true
  - consul_applied == false  # Make XOR explicit

# Trigger Catalog update
| `RETRY_CONSUL` | External | Retry Consul specifically when PostgreSQL succeeded |

HIGH: Guard Expression Ambiguity

Lines 557-558, 586-587: The guards use postgres_applied == true and consul_applied == true, but per the validation rules (line 239), the partial_registered state requires:

validation_rules:
  - "postgres_applied != consul_applied"  # XOR condition

Issue: The XOR validation rule should mean exactly one is true and one is false when entering partial_registered. However, the transition guards don't explicitly check the inverse condition.

Recommendation: Add explicit negative checks to guards for clarity:

# Transition 9 (retry Consul)
conditions:
  - postgres_applied == true
  - consul_applied == false  # Explicit check
  - retry_count < 3

# Transition 10 (retry PostgreSQL)  
conditions:
  - consul_applied == true
  - postgres_applied == false  # Explicit check
  - retry_count < 3

This makes the XOR condition explicit and prevents potential runtime confusion.


MEDIUM: Timeout Trigger Inconsistency

Lines 1349-1353: The timeout trigger mapping recommends:

State Recommended Trigger
registering_postgres timeout POSTGRES_FAILED
registering_consul timeout CONSUL_FAILED

Issue: These triggers have guards checking postgres_applied == false and consul_applied == false respectively (lines 457-460, 529-532). But if a timeout occurs, no Effect result was received, so these fields may be undefined in the FSM context.

Recommendation:

  1. Document the Orchestrator's responsibility to populate these fields as false when emitting timeout triggers
  2. Or add a note in the Timeout Handling section (line 1313+) clarifying that the Orchestrator must set postgres_applied = false before emitting POSTGRES_FAILED on timeout

Suggested Addition (after line 1351):

**Orchestrator Timeout Responsibilities**:
When emitting a timeout trigger, the Orchestrator must:
1. Set the corresponding `*_applied` field to `false` in FSM context
2. Optionally set a `*_error` field with timeout details
3. Log the timeout event with duration and state context

MEDIUM: CONTINUE Trigger Semantics

Lines 491, 883: The CONTINUE trigger is described as "automatically emitted by the Reducer when entering the postgres_registered state."

Issue: This is a bit ambiguous - does the Reducer emit CONTINUE as part of its delta function return, or is this implicit FSM behavior?

Recommendation: Clarify the implementation pattern:

Option A: Reducer explicitly returns CONTINUE intent

def delta(state, event):
    if event == POSTGRES_SUCCEEDED:
        return ("postgres_registered", [ContinueIntent()])

Option B: FSM runtime auto-progresses (epsilon transition)

The `CONTINUE` trigger is an **epsilon transition** - the FSM runtime 
automatically evaluates this transition without waiting for an external 
event when entering `postgres_registered` state.

I suspect Option B is the intent, but the document should clarify this. If so, add to line 491:

**Note**: The `CONTINUE` trigger represents an **epsilon transition** 
(automatic progression). The FSM runtime automatically attempts this 
transition when entering `postgres_registered` without waiting for an 
external event. No external trigger source is required.

MINOR: Inconsistent Terminology

Lines 59, 216: State types use "success" vs "terminal":

  • registered is type "success" but is_terminal = false
  • deregistered is type "terminal" but is_terminal = true

Issue: The terminology might confuse readers. registered is described as "functionally terminal but has a single outgoing transition" (line 741).

Recommendation: Clarify the state type taxonomy:

### State Type Classification

| Type | Meaning | Terminal? | Outgoing Transitions |
|------|---------|-----------|---------------------|
| **initial** | Starting state | No | Many |
| **operational** | Active processing | No | Many |
| **snapshot** | Checkpoint state | No | Few (1-2) |
| **success** | Goal achieved, awaiting shutdown | No | One (DEREGISTER only) |
| **error** | Recoverable failure | No | Few (retry/abandon) |
| **terminal** | Final state, no exit | Yes | Zero |

Add this table after line 64 for clarity.


MINOR: Guard Operator Example Missing

Line 932: The operator semantics table includes matches for regex, but no example guard expression uses it.

Recommendation: Add a regex example to the Guard Definitions table (line 778) or Guard Expression Syntax examples (line 806):

service_name matches ^onex-.*$       # Regex pattern for service naming convention
node_type matches ^(compute|effect|reducer)$  # Enum validation via regex

MINOR: Missing Error Code Context

Lines 1140-1151: Validation Errors table includes error codes like TIMEOUT_EXCEEDED, INTENT_EMISSION_FAILED, but these aren't referenced elsewhere in the error handling section.

Recommendation: Add a subsection documenting these error scenarios:

### Intent Emission Errors

**Error**: `INTENT_EMISSION_FAILED`

**Cause**: Failed to construct or emit an intent during a state transition entry action.

**Examples**:
- Missing required field in intent payload
- Target Effect service not registered in container
- Intent serialization failure

**Recovery**: 
- Trigger `FATAL_ERROR` to transition to `failed` state
- Log full context including state, transition, and failed intent

🎨 Code Quality & Best Practices

✅ Follows ONEX Conventions

  • Pure Reducer semantics (no I/O)
  • Intent-based side effect emission
  • Discriminated union pattern for intents
  • Correlation ID for distributed tracing

✅ Type Safety

  • All intent models exist and match specification
  • ModelRegistrationPayload has required fields documented in state definitions
  • Pydantic validation ensures runtime safety

✅ Thread Safety

  • Intent models are frozen (immutable)
  • FSM is stateless (state passed through ModelReducerInput)
  • No shared mutable state

🔒 Security Considerations

✅ Good

  • No sensitive data in state diagram or examples
  • Correlation IDs for audit trails
  • Health check configuration sanitized (dict[str, Any] is appropriate)

⚠️ Recommendation

Add a security note to the Implementation Notes section:

### Security Considerations

1. **Sensitive Data**: Never log full `consul_health_check` or `postgres_record` 
   contents as they may contain credentials or PII. Use redacted logging.

2. **Retry Limits**: The 3-retry limit prevents infinite loops but consider 
   alerting on `RETRY_EXHAUSTED` events for security monitoring (potential DoS).

3. **Correlation IDs**: Ensure correlation IDs are UUIDs, not user-controlled 
   input, to prevent injection attacks in logging systems.

📊 Test Coverage Recommendations

While this is a specification document, when implementing OMN-889:

  1. FSM State Coverage: Test all 10 states can be entered
  2. Transition Coverage: Test all 17 transitions fire correctly
  3. Guard Coverage: Test each guard condition (true/false paths)
  4. Error Paths: Especially partial_registered recovery flows
  5. Timeout Scenarios: Mock Effect delays exceeding timeout_ms
  6. Retry Exhaustion: Verify retry_count >= 3 blocks transitions

Suggested test file structure:

tests/unit/nodes/reducers/test_dual_registration_reducer.py
tests/integration/registration/test_dual_registration_fsm_flows.py

📝 Minor Typos & Formatting

  1. Line 240: postgres_applied != consul_applied - Consider adding a comment: # XOR: exactly one succeeded
  2. Line 883: "Automatic progression trigger" - Could be "Epsilon transition trigger" for standard FSM terminology
  3. Line 1194: fsm_state_context.retry_count - Clarify if this is a dict key or object attribute

✅ Final Verdict

Recommendation: Approve with minor changes

This is high-quality architectural documentation that will significantly help the implementation team. The issues raised are mostly clarifications and edge case handling - the core FSM design is sound.

Required Changes (before merge):

  1. ✅ Clarify RETRY vs RETRY_CONSUL trigger distinction (CRITICAL)
  2. ✅ Add explicit negative guards for XOR conditions (HIGH)
  3. ✅ Document Orchestrator timeout responsibilities (MEDIUM)

Nice-to-Have:

  • Clarify CONTINUE epsilon transition semantics
  • Add state type taxonomy table
  • Add regex guard example
  • Add security considerations section

Great work on this comprehensive FSM specification! Once the trigger naming is clarified, this will be an excellent reference for OMN-889 implementation.

Reviewed by: Claude Code Assistant
Date: 2025-12-19
PR: #221 (OMN-938)

…MN-938]

- Add detailed CONTINUE trigger semantics with implementation patterns
- Expand guard expression syntax with comprehensive examples
- Add cross-references from retry transitions to enforcement section
- Clarify terminal vs success state distinction for registered state
- Add Phase column to validation errors table
- Document GUARD_FIELD_UNDEFINED error and strict mode behavior
- Add 4 additional workflow scenarios (validation failure, retry exhaustion, fatal error, timeout recovery)
@claude

claude Bot commented Dec 19, 2025

Copy link
Copy Markdown

Registration FSM Contract Review [OMN-938]

Executive Summary

Verdict: ✅ APPROVE with Minor Recommendations

This is an exceptionally well-crafted FSM contract specification that demonstrates deep understanding of the ONEX architecture. The document provides comprehensive coverage of the dual registration lifecycle with excellent implementation guidance for OMN-889.

Quality Score: 9.5/10

  • Architecture Alignment: 10/10
  • Documentation Quality: 10/10
  • Completeness: 9/10
  • Implementation Readiness: 9/10

✅ Major Strengths

1. Architectural Excellence

  • Pure FSM Semantics: Correctly implements delta(state, event) -> (new_state, intents[]) pattern
  • Intent-Based Design: Proper separation between Reducer (pure) and Effects (I/O)
  • Dual Registration Strategy: PostgreSQL-first (source of truth) followed by Consul (service discovery) is architecturally sound
  • Recovery Patterns: The partial_registered state with targeted retry mechanisms (RETRY vs RETRY_POSTGRES) is elegant

2. Comprehensive State Machine Design

  • 10 Well-Defined States: Clear categorization (initial, operational, snapshot, success, error, terminal)
  • 17 Transitions: Complete coverage of happy paths, error paths, and recovery flows
  • Guard System: Robust 3-token expression syntax with clear validation semantics
  • Timeout Management: Appropriate timeouts for each operational state (5s validation, 10s registration, 15s deregistration)

3. Documentation Quality

  • YAML Specifications: Every state and transition has complete YAML contract
  • Mermaid Diagrams: Beautiful state diagram for visualization (lines 787-1082)
  • Implementation Notes: Clear guidance on CONTINUE trigger semantics, retry counter management, and guard evaluation
  • Cross-References: Excellent linkage to related docs (REGISTRATION_TRIGGER_DESIGN.md, CONTRACT_DRIVEN_NODEREDUCER_V1_0.md)

4. Error Handling & Observability

  • Error Categories: Well-defined error codes (VALIDATION_ERROR, REGISTRATION_TIMEOUT, etc.)
  • Recovery Flows: Clear paths from every error state back to operational states
  • Retry Semantics: Explicit retry limits (max 3), backoff strategies, and exhaustion handling
  • Correlation Tracking: Proper correlation_id propagation for debugging

🔍 Issues & Recommendations

1. CRITICAL: State Terminal Classification ⚠️

Location: Lines 59, 199-226

Issue: The registered state has conflicting terminal classification:

  • Line 59: is_terminal: false (correct)
  • Line 209: state_type: success (correct)
  • Line 227: Note says "Success state. Node is fully discoverable and registered. Allows transition to deregistering for graceful shutdown via DEREGISTER trigger."

Analysis: This is actually CORRECT. The registered state is:

  • NOT terminal (is_terminal: false) ✅
  • A success state (state_type: success) ✅
  • Allows one outgoing transition to deregistering (Transition 12) ✅

Per ONEX patterns: Terminal states have NO outgoing transitions. registered has one valid outgoing transition for graceful shutdown, so it's a stable success state, not a terminal state.

Status: ✅ No issue - design is correct as-is.


2. MODERATE: Retry Counter Management 🔵

Location: Lines 554, 584, 681, 1226-1285

Issue: Guard retry_count < 3 appears in transitions 9, 10, and 14, but the retry counter lifecycle is not fully specified in the main document.

Missing Details:

  1. Where is retry_count stored? (FSM context? External state?)
  2. Who increments it? (Reducer on transition? Orchestrator?)
  3. When is it reset? (On success? On state change?)

Good News: Lines 1226-1285 provide excellent detail in the "Retry Limit Enforcement" section, including:

  • Storage location (FSM context)
  • Increment semantics (on transition entry)
  • Reset events (on success transitions)
  • Exhaustion handling (Transition 17)

Recommendation: Add forward reference in the Guards section (line 765):

### Guard Definitions

| Guard Name | Expression | Required? | Description |
|------------|------------|-----------|-------------|
| ...
| `retry_count_valid` | `retry_count < 3` | Yes | Haven't exceeded retry limit (see [Retry Limit Enforcement](#retry-limit-enforcement)) |

Status: 🔵 Minor improvement - add cross-reference for discoverability.


3. MODERATE: Missing Transition for Retry Exhaustion 🔵

Location: Lines 322-341 (Transition Table), Lines 723-735 (Transition 17)

Observation: The document defines Transition 17 (retry_exhausted) to handle retry_count >= 3 in partial_registered state:

transition_name: retry_exhausted
from_state: partial_registered
to_state: failed
trigger: RETRY_EXHAUSTED

Issue: The note at lines 739-743 says:

"When retry_count >= 3, the guard on the retry_consul (Transition 9) and retry_postgres (Transition 10) transitions will fail. The Reducer should then emit a RETRY_EXHAUSTED trigger to move to the failed state..."

Question: How does the Reducer "emit" the RETRY_EXHAUSTED trigger?

Per ONEX patterns: Reducers are pure functions that only return (new_state, intents[]). They don't emit triggers - triggers come from external events or Effects.

Recommendation: Clarify the exhaustion mechanism. Options:

Option A (Automatic): When guards fail on RETRY in partial_registered, Reducer automatically transitions to failed with no trigger needed.

Option B (Orchestrator-driven): Orchestrator detects failed guards and sends RETRY_EXHAUSTED event.

Option C (Effect-driven): After failed retry, Effect checks retry count and emits RETRY_EXHAUSTED event if limit reached.

Suggested Fix (lines 739-743):

**Note**: When retry_count >= 3, the guards on transitions 9 and 10 will fail. 
The **Orchestrator** should detect this condition and emit a RETRY_EXHAUSTED 
trigger to transition the FSM to the failed state. Alternatively, the Reducer 
MAY implement automatic transition to failed when all retry transitions are 
guard-blocked and no other valid transitions exist.

Status: 🔵 Clarification needed - specify who/how RETRY_EXHAUSTED trigger is generated.


4. MINOR: CONTINUE Trigger Implementation Detail 🟢

Location: Lines 471-541 (Transition 6 note)

Observation: Excellent explanation of CONTINUE trigger semantics! The note clearly explains:

  • It's a synthetic internal trigger (not external)
  • Single-step vs two-step execution patterns
  • Implementation example with code snippet
  • Observability considerations

Suggestion: Add one clarification about error handling:

Line 528 (after "observability note"):

**Error Handling**: If the Reducer encounters an error while processing CONTINUE 
(e.g., invalid context state), it should emit a FATAL_ERROR trigger rather than 
silently failing or returning to postgres_registered.

Status: 🟢 Optional enhancement - already excellent, minor addition for completeness.


5. MINOR: Guard Expression Syntax Examples 🟢

Location: Lines 765-889 (Guard Expression Syntax)

Observation: Outstanding documentation of guard syntax! The 3-token format is well-specified with clear examples.

Minor Suggestion: Add example of nested field access (if supported):

# Nested field access (if supported)
context.postgres_result.applied == true
metadata.retry_attempts < 3

# If nested access NOT supported, add note:
**Note**: Field names must be top-level context fields. Nested field access 
(e.g., `context.field.subfield`) is NOT supported. Use flat context keys instead.

Status: 🟢 Nice-to-have - clarify nested field support.


🎯 Code Quality Assessment

Architecture Compliance ✅

  • ONEX Four-Node Pattern: Correct (Reducer emits intents, Effects execute)
  • Pure FSM Semantics: Correct (no I/O in delta function)
  • Protocol-Driven DI: Assumed correct in implementation (not shown in spec)
  • Error Handling: Uses structured errors (implied by error code references)

Documentation Standards ✅

  • Versioning: Proper SemVer (v1.0.0) with change history placeholder
  • Metadata: Complete (ticket references, related docs, status)
  • Table of Contents: Comprehensive with deep linking
  • Code Examples: Excellent YAML specifications

Testing Considerations ✅

  • Test Blocks: Document blocks OMN-942 (Comprehensive Reducer Test Suite) ✅
  • Test Coverage Guidance: Implicit - all 17 transitions should be tested
  • Edge Cases: Well documented (retry exhaustion, partial failures, timeouts)

Suggested Test Categories for OMN-942:

  1. Happy path: unregistered → registered (7 transitions)
  2. Validation failure: validating → failed → validating (retry flow)
  3. PostgreSQL failure: registering_postgres → failed
  4. Consul failure: registering_consul → partial_registered → registered
  5. Retry exhaustion: partial_registered → failed (via RETRY_EXHAUSTED)
  6. Deregistration: registered → deregistering → deregistered
  7. Global error: FATAL_ERROR from any operational state
  8. Guard validation: All 12 guards tested
  9. Timeout handling: All timeouts triggered
  10. Wildcard transition: * → failed from each state

Estimated Test Count: ~60-80 test cases


🔐 Security Review

✅ No Security Issues Detected

  • Input Validation: Payload validation in validating state ✅
  • Guard Security: Field names validated (no underscore prefix per fsm_expression_parser.py) ✅
  • Credential Handling: Delegated to Effects (not in FSM) ✅
  • SQL Injection: Protected by Pydantic models ✅
  • Audit Trail: correlation_id tracking for all transitions ✅

⚡ Performance Considerations

✅ Efficient Design

  • Timeout Budgets: Reasonable (5s validation, 10s per registration)
  • Parallel Deregistration: Correct approach (line 266)
  • Snapshot State: postgres_registered enables incremental retry ✅
  • Intent Batching: Not applicable (single intent per transition)

Performance Budget Recommendation:

  • Happy Path: < 20s (5s validation + 10s Postgres + 10s Consul, parallel possible)
  • Partial Recovery: < 15s (reuse Postgres result, retry Consul only)
  • Validation Failure: < 5s (fast fail)

📋 Pre-Merge Checklist

Must Address Before Merge:

  • ✅ None - document is ready to merge as-is

Recommended Improvements (can be follow-up commits):

  • 🔵 Add cross-reference from Guards section to Retry Limit Enforcement section
  • 🔵 Clarify RETRY_EXHAUSTED trigger emission mechanism (who generates it?)
  • 🟢 Add error handling note for CONTINUE trigger failures
  • 🟢 Clarify nested field support (or lack thereof) in guard expressions

Post-Merge (OMN-889 Implementation):

  • Create EnumRegistrationFSMTrigger enum with all 15 triggers
  • Implement retry counter in FSM context with proper lifecycle
  • Add sequence diagram showing Reducer-Effect-Orchestrator interaction
  • Implement all 60-80 test cases in OMN-942

🎓 Learning Highlights

This document is an exemplary FSM contract that other teams should reference. Key patterns to replicate:

  1. Snapshot States: Using postgres_registered as a checkpoint enables granular recovery without re-running expensive operations.

  2. Targeted Retry Triggers: RETRY vs RETRY_POSTGRES allows the Orchestrator to retry exactly what failed, not everything.

  3. CONTINUE Trigger Pattern: Enables automatic progression through checkpoint states while maintaining state machine purity.

  4. Guard 3-Token Syntax: Simple, parseable, and type-safe guard expressions prevent complex logic in FSM specifications.


📊 Final Assessment

Category Score Comments
Architecture Alignment 10/10 Perfect adherence to ONEX patterns
Completeness 9/10 Minor clarifications needed
Code Quality 10/10 Excellent YAML/Markdown formatting
Documentation 10/10 Outstanding explanatory notes
Test Coverage 9/10 Blocks OMN-942, guidance provided
Security 10/10 No issues detected
Performance 10/10 Efficient design
Maintainability 10/10 Clear structure, good cross-refs

Overall: 9.5/10


✅ Recommendation

APPROVE - This is production-ready documentation. The minor recommendations are enhancements, not blockers. Excellent work! 🚀

The FSM contract provides clear, implementable guidance for OMN-889. The dual registration pattern with partial recovery is well-designed and will serve as a template for future FSM contracts in the ONEX framework.


Reviewer: Claude Code (Sonnet 4.5)
Review Date: 2025-12-19
Time Spent: 25 minutes
Lines Reviewed: 1749
Files Changed: 1

…MN-938]

- Add error handling note for CONTINUE trigger failures
- Add cross-reference from Guards section to Retry Limit Enforcement
- Clarify nested field support (top-level only) in guard expressions
- Clarify RETRY_EXHAUSTED trigger is generated by Orchestrator
@claude

claude Bot commented Dec 19, 2025

Copy link
Copy Markdown

Pull Request Review: Registration FSM Contract

Overall Assessment: EXCELLENT ✅

This is an exceptionally well-crafted formal FSM specification that demonstrates mastery of the ONEX Reducer pattern and provides comprehensive implementation guidance. The document quality is production-ready.


Strengths

1. Architectural Adherence ⭐⭐⭐⭐⭐

  • Correctly follows ONEX Four-Node Architecture with pure FSM semantics
  • Proper delta(state, event) -> (new_state, intents[]) pattern
  • Clear separation of concerns: Reducer (pure) vs Effects (I/O)
  • Intent-based side effect emission aligns with ModelIntent architecture

2. Comprehensive State Modeling ⭐⭐⭐⭐⭐

  • 10 states with clear categorization (initial, operational, snapshot, success, error, terminal)
  • 17 transitions covering all edge cases including:
    • Happy path (unregistered → registered)
    • Partial registration recovery
    • Retry exhaustion handling
    • Graceful deregistration
    • Global error handling
  • Excellent distinction between success state (registered) and terminal state (deregistered)

3. Guard Expression System ⭐⭐⭐⭐⭐

  • Well-defined 3-token syntax: <field> <operator> <value>
  • Complete operator set covering equality, comparison, existence, containment, pattern matching
  • Two-phase validation: contract load-time (syntax) + runtime (data)
  • Clear error code taxonomy (GUARD_SYNTAX_ERROR, GUARD_TYPE_ERROR, etc.)
  • Excellent examples for edge cases (whitespace, case sensitivity, boundary values)

4. CONTINUE Trigger Semantics ⭐⭐⭐⭐⭐

Lines 491-527 provide exemplary documentation of internal trigger mechanics:

  • Clearly distinguishes synthetic internal triggers from external events
  • Documents single-step vs two-step execution patterns with trade-offs
  • Provides implementation example with code snippet
  • Explains observability implications and error handling
  • This level of detail prevents common FSM implementation errors

5. Retry Logic & Timeout Handling ⭐⭐⭐⭐⭐

  • Retry limit enforcement (lines 1326-1430): Exhaustive documentation of counter semantics, increment triggers, reset events, and guard failure behavior
  • Timeout handling (lines 1432-1578): Complete timeout semantics including:
    • Per-state timeout values with rationale
    • Orchestrator responsibility for detection
    • State-specific recovery strategies
    • Timeout vs failure distinction
    • Metrics guidance
  • Both sections include working code examples

6. Error Recovery Flows ⭐⭐⭐⭐

  • Seven concrete scenarios (lines 1644-1728) cover:
    • PostgreSQL fails first
    • Consul fails after PostgreSQL
    • Graceful shutdown
    • Validation failure
    • Retry exhaustion
    • Fatal errors
    • Timeout recovery
  • Visual flow diagrams aid comprehension

7. Documentation Quality ⭐⭐⭐⭐⭐

  • Excellent structure: TOC, cross-references, related docs section
  • Mermaid state diagram + simplified text diagram
  • Comprehensive YAML examples for state definitions and transitions
  • Intent emission matrix with target mapping
  • Implementation notes with code snippets
  • Version tracking and ticket cross-references

Issues & Recommendations

Critical Issues: None ✅

Moderate Issues

1. Guard Expression Limitation - Nested Field Access (Line 899)

Issue: The contract states nested field access is NOT supported (context.retry_count), requiring context flattening.

Recommendation: Consider adding an optional nested field syntax for v1.1:

# Current limitation
retry_count < 3  # Only works if retry_count is top-level

# Proposed for v1.1
context.retry_count < 3  # Nested access with dot notation
payload.consul_service_id exists true

Rationale: Flattening context adds boilerplate and potential naming collisions. If the guard evaluator can walk object graphs, the FSM contract becomes cleaner.

Action: Document this as a future enhancement or provide clear guidance on context flattening patterns.


2. Unclear RETRY_EXHAUSTED Trigger Source (Lines 1005, 809)

Issue: The trigger catalog (line 1005) states RETRY_EXHAUSTED is "Generated by the Orchestrator", but Transition 17 documentation (line 809) says "The Reducer should then emit a RETRY_EXHAUSTED trigger".

Conflict: Who emits this trigger - Reducer or Orchestrator?

Recommendation: Clarify the trigger source. Suggested resolution:

RETRY_EXHAUSTED:
  source: Orchestrator
  condition: Detects Reducer returned without state change due to guard failure (retry_count >= 3)
  action: Emits RETRY_EXHAUSTED to transition partial_registered -> failed

Update line 809 transition note to match this clarification.


3. Missing Guard on Transition 11 (RECOVERY_COMPLETE)

Issue: Transition 11 (partial_recovery_success) checks postgres_applied == true AND consul_applied == true, but doesn't verify HOW the recovery happened.

Edge Case: What if an external event sets both flags to true without actually going through retry transitions 9 or 10?

Recommendation: Add a guard condition to verify the recovery path:

conditions:
  - condition_name: both_applied
    condition_type: expression
    expression: "postgres_applied == true"
    required: true
  - condition_name: consul_now_applied
    condition_type: expression
    expression: "consul_applied == true"
    required: true
  # Add this guard:
  - condition_name: recovery_via_retry
    condition_type: expression
    expression: "recovery_attempt exists true"
    required: true

Or document that the Orchestrator is responsible for only emitting RECOVERY_COMPLETE after observing successful retry.


Minor Issues

4. XOR Validation on partial_registered State (Line 239)

Issue: Validation rule postgres_applied != consul_applied is documented but not enforced at contract load time.

Recommendation: Document that this XOR check is a runtime invariant enforced by the Reducer when entering partial_registered. Add to Implementation Notes:

# Reducer should enforce XOR invariant on entry to partial_registered
assert postgres_applied != consul_applied, "partial_registered requires exactly one component failed"

5. Timeout Metric Labels (Line 1574)

Issue: Histogram metric registration_state_duration_ms uses label outcome with values success/timeout/failure. This creates high cardinality if failure reasons are included.

Recommendation: Use separate metrics or bounded enum for outcome:

metrics:
  - name: registration_state_duration_ms
    type: histogram
    labels: [state]  # Remove outcome to reduce cardinality
  
  - name: registration_state_outcome_total
    type: counter
    labels: [state, outcome]  # Separate counter for outcomes
    values: [success, timeout, validation_failure, postgres_failure, consul_failure]

6. Guard Validation Error Handling (Line 979)

Issue: Runtime behavior states "No exception is raised for undefined fields (fail-safe behavior)", but Error Handling section lists GUARD_FIELD_UNDEFINED error code.

Clarification Needed: When is GUARD_FIELD_UNDEFINED raised?

Recommendation: Update line 979 to:

**Runtime Behavior for Undefined Fields**:
- For `exists`/`not_exists` operators: Undefined fields return appropriate boolean
- For other operators: 
  - Default mode: Guard evaluation returns `false`, no exception raised
  - Strict mode (`strict_validation_enabled: true`): Raises `GUARD_FIELD_UNDEFINED`

This matches the note at line 1270.


7. Missing Correlation ID Propagation Examples

Issue: Implementation Notes (line 1296) mention correlation_id tracking but no examples show propagation through context updates.

Recommendation: Add correlation ID handling to retry counter example:

# Current example (line 1389)
new_context = context.model_copy(
    update={"retry_count": retry_count + 1}
)

# Enhanced example
new_context = context.model_copy(
    update={
        "retry_count": retry_count + 1,
        "correlation_id": context.correlation_id,  # Preserve correlation
        "retry_timestamp": datetime.utcnow(),  # Add for debugging
    }
)

Suggestions for Future Enhancement

  1. Backoff Strategy Documentation: Document exponential backoff coefficients for retry delays (mentioned at line 1295 but not specified)

  2. Dead Letter Queue: Consider adding a dead_letter terminal state for truly unrecoverable failures that should be logged but not retried

  3. Metrics Dashboard Templates: Provide example Grafana dashboard JSON for visualizing FSM state transitions

  4. Contract Validation Tool: Reference or build a CLI tool that validates FSM contracts against this specification

  5. Performance Benchmarks: Document expected state transition latencies for SLA planning


Code Quality Assessment

Type Safety: ✅ Excellent

  • All models use Pydantic with proper types
  • Guard expression syntax prevents injection attacks
  • YAML configuration follows CONTRACT_DRIVEN_NODEREDUCER_V1_0.md patterns

Error Handling: ✅ Comprehensive

  • 9 distinct error codes covering contract load and runtime phases
  • Clear recovery strategies for each error category
  • Fail-safe defaults (undefined fields → guard returns false)

Testing Considerations: ⭐⭐⭐⭐⭐

This contract provides excellent test coverage guidance:

  • Unit Tests: Guard expression parser (valid/invalid syntax)
  • Integration Tests: Each of 17 transitions with guard evaluation
  • Recovery Tests: All 7 error recovery scenarios
  • Timeout Tests: State timeout behavior for 4 operational states
  • Chaos Tests: Retry exhaustion, partial failures, fatal errors

Documentation: ✅ Production-Ready

  • 1755 lines of comprehensive specification
  • Code examples, diagrams, cross-references
  • Version tracking and maintainer attribution
  • Follows ONEX documentation standards

Security Considerations

✅ No security issues identified

The guard expression syntax is properly constrained:

  • No arbitrary code execution (strict 3-token format)
  • No SQL injection risk (guards operate on in-memory context)
  • No command injection (values are parsed, not evaluated)

Performance Considerations

Positive:

  • Pure FSM with no I/O keeps Reducer fast
  • Intent batching (line 1282) reduces round-trips
  • Retry limit prevents infinite loops

Watch:

  • Guard evaluation happens on every transition (could optimize with guard caching if needed)
  • Large context objects may cause memory pressure (document max context size limits)

Test Coverage Recommendations

Based on CLAUDE.md requirement (60% minimum):

Essential Tests (must have):

  1. All 17 transitions with valid inputs
  2. Guard expression parser (valid + invalid syntax)
  3. Retry limit enforcement (counter increment, reset, exhaustion)
  4. Timeout detection (4 operational states)
  5. Error recovery flows (scenarios 1-7)

Recommended Tests (should have):
6. Concurrent transition attempts (verify concurrent_transitions_allowed: false)
7. State persistence and recovery
8. Correlation ID propagation
9. Intent emission verification
10. XOR invariant on partial_registered


Alignment with CLAUDE.md

✅ Fully compliant with project standards:

  • Uses Pydantic models with proper validation
  • Follows ONEX Four-Node Architecture
  • Protocol-driven DI (ProtocolLogger, ProtocolEventBus)
  • Structured errors with ModelOnexError
  • Intent-based side effects
  • Pure Reducer pattern (no I/O)

Final Recommendation

APPROVE with minor clarifications

This PR is ready to merge after addressing the 2 moderate issues:

  1. Clarify RETRY_EXHAUSTED trigger source (Reducer vs Orchestrator)
  2. Add guard or documentation for RECOVERY_COMPLETE edge case

The minor issues and future enhancements can be tracked in follow-up tickets.

Congratulations on an exceptional specification document! This will serve as an excellent blueprint for OMN-889 implementation.


Estimated Implementation Effort

Based on this contract:

  • Reducer Implementation (OMN-889): 3-5 days
  • Effect Nodes (Consul + PostgreSQL): 2-3 days
  • Orchestrator Integration: 2-3 days
  • Comprehensive Test Suite (OMN-942): 4-6 days
  • Total: ~11-17 days for full implementation

Reviewed by: Claude Sonnet 4.5
Review Date: 2025-12-19
Contract Version: 1.0.0

…tract [OMN-938]

- Add State Type Terminology section clarifying success vs terminal states
- Add Dual Registration Order section documenting PostgreSQL-first ordering
- Add Intent Emission Error Details with CONTINUE trigger failure handling
- Improve state descriptions (failed, RETRY trigger, validating, etc.)
- Add no-background-agents rule to CLAUDE.md Agent Instructions
@claude

claude Bot commented Dec 19, 2025

Copy link
Copy Markdown

Code Review - PR #221: Registration FSM Contract

Summary

This PR introduces a comprehensive FSM (Finite State Machine) contract for the Dual Registration Reducer (OMN-889), providing formal specification for managing node registration to both Consul and PostgreSQL. The documentation is exceptionally thorough and well-structured.


✅ Strengths

1. Outstanding Documentation Quality

  • Comprehensive coverage: 1,959 lines covering states, transitions, guards, error handling, timeouts, and recovery flows
  • Clear structure: Excellent table of contents, cross-references, and navigation
  • Multiple perspectives: State diagrams (Mermaid), flow diagrams (ASCII), tables, and YAML specifications
  • Implementation guidance: Concrete Python examples for critical patterns

2. Architectural Excellence

  • Pure FSM semantics: Properly follows the ONEX Reducer pattern delta(state, event) -> (new_state, intents[])
  • Dual registration order: Well-justified PostgreSQL-first approach (source of truth before service discovery)
  • Checkpoint pattern: postgres_registered snapshot state enables targeted retry of Consul without re-registering PostgreSQL
  • State type taxonomy: Clear distinction between initial/operational/snapshot/success/terminal/error states

3. Robust Error Handling

  • 17 transitions covering happy path, partial failures, timeouts, retry exhaustion, and fatal errors
  • Guard validation: Two-phase validation (contract load-time + runtime) with clear error codes
  • Timeout semantics: Well-defined timeout behavior with Orchestrator-driven monitoring
  • Recovery flows: 8 recovery scenarios documented with ASCII flow diagrams

4. CONTINUE Trigger Innovation

  • Synthetic internal trigger for automatic progression from postgres_registered to registering_consul
  • Single-step vs two-step execution: Clear implementation guidance with tradeoffs
  • Error handling: Comprehensive failure scenarios including CONTINUE processing failures (Scenario 8)

5. Retry Limit Enforcement

  • Clear semantics: Maximum 3 retries with counter increment, reset, and exhaustion handling
  • Guard-based enforcement: retry_count < 3 guard blocks transitions when limit reached
  • Orchestrator coordination: Proper separation of concerns (Reducer checks guards, Orchestrator emits RETRY_EXHAUSTED)

6. Alignment with ONEX Conventions

  • Protocol-driven DI: Intents target services by protocol (e.g., postgres_effect, consul_effect)
  • Structured errors: Uses ModelOnexError with EnumCoreErrorCode
  • Event-driven: ModelEventEnvelope pattern for inter-service communication
  • Correlation tracking: All intents include correlation_id for distributed tracing

🔍 Areas for Improvement

1. State Recoverability Inconsistency

Issue: Table at line 82 shows registered as not recoverable, but this seems inconsistent with allowing DEREGISTER transition.

State Type Terminal? Recoverable?
registered success No No

Concern: If registered is not recoverable, does that mean crash recovery wouldn't restore this state? Or does "recoverable" mean "can retry after failure"? The term needs clarification.

Recommendation: Add a "Recoverability" section explaining what this field means:

  • Does it refer to crash recovery (FSM persistence)?
  • Does it refer to retry-after-failure capability?
  • Why is registered non-recoverable but allows DEREGISTER (which implies the state can be exited)?

2. Guard Expression Syntax - Missing Whitespace Edge Cases

Issue: Line 990-991 states:

"retry_count< 3"  # GUARD_SYNTAX_ERROR: operator attached to field

Concern: This conflicts with line 867-869 which says expressions are split into "exactly 3 tokens" separated by whitespace. If retry_count< 3 is split by whitespace, it would become 2 tokens: ["retry_count<", "3"], not a syntax error.

Recommendation: Clarify the tokenization rules:

  • Are operators required to be whitespace-delimited?
  • Or is the parser more lenient (splits on operators even without spaces)?
  • Update the edge case example to match the actual parsing behavior

3. CONTINUE Trigger - Two-Step Observability Tradeoff

Issue: Line 528 mentions two-step execution "makes the checkpoint state observable but adds latency."

Concern: The tradeoff isn't fully explored:

  • How much latency does two-step execution add (milliseconds? seconds)?
  • Are there scenarios where observability is critical enough to justify the latency?
  • What happens if a crash occurs between postgres_registered and registering_consul in two-step mode?

Recommendation: Add a subsection under "CONTINUE Trigger Semantics" titled "When to Use Two-Step Execution" with guidance on:

  • Debugging/development environments (observability > latency)
  • Production environments (latency > observability, use single-step)
  • Crash recovery implications for each approach

4. Timeout Recovery - Deregistering Timeout Semantics

Issue: Line 1593 states that deregistering timeout should emit DEREGISTRATION_COMPLETE and proceed to deregistered.

Concern: This creates orphaned resources if cleanup fails:

  • Consul may still advertise the service
  • PostgreSQL may still have the node record
  • No automatic retry mechanism for cleanup

Recommendation: Consider adding:

  • A partial_deregistered error state for cleanup failures
  • A retry mechanism for deregistration (e.g., RETRY_DEREGISTRATION)
  • Or document the expected orphan cleanup process (garbage collection job?)

5. Guard Field Resolution - Nested Fields

Issue: Line 943 states "Nested field access (e.g., context.retry_count) is NOT supported."

Concern: This limits guard expressiveness. What if context structure changes and fields become nested?

Recommendation:

  • Add a migration guide for flattening nested context before guard evaluation
  • OR provide examples of how the Reducer should pre-process context
  • Document whether this is a permanent limitation or a v1.0 simplification

6. Error Code Table Completeness

Issue: Lines 1297-1313 define validation error codes, but some errors mentioned in the document don't appear in the table:

  • INTENT_PAYLOAD_ERROR (line 1699)
  • EFFECT_NOT_FOUND (line 1701)
  • INTENT_SERIALIZATION_ERROR (line 1702)
  • INTERNAL_TRANSITION_ERROR (line 1703)

Recommendation: Add a comprehensive "Error Code Reference" appendix listing ALL error codes with:

  • Error code name
  • Phase (contract load / runtime)
  • Severity (warning / error / critical)
  • Recovery action
  • Example scenario

7. Metrics Section Missing

Issue: The document mentions metrics at lines 1610-1622 (timeout metrics) but doesn't provide a comprehensive metrics catalog.

Recommendation: Add a "Metrics & Observability" section documenting ALL emitted metrics:

  • registration_postgres_success (line 469)
  • registration_postgres_failure (line 494)
  • registration_complete (line 577)
  • registration_partial (line 601)
  • registration_recovery_success (line 693)
  • deregistration_complete (line 736)
  • registration_internal_error (line 1795)
  • registration_state_timeout_total (line 1614)
  • registration_state_duration_ms (line 1619)

Each metric should include:

  • Metric type (counter/histogram/gauge)
  • Labels
  • When it's emitted
  • Alerting thresholds (if applicable)

8. CLAUDE.md Change - Minor Wording

Issue: Line 79 in CLAUDE.md states:

- **NEVER** run agents in background mode (`run_in_background: true`) - run parallel polymorphic agents in the foreground instead by dispatching multiple agents in a single message

Concern: The phrase "parallel polymorphic agents" might be unclear to new contributors.

Recommendation: Rephrase for clarity:

- **NEVER** run agents in background mode (`run_in_background: true`) - instead, dispatch multiple agents in parallel by making multiple Task tool calls in a single message (all calls run in foreground)

🛡️ Security Considerations

✅ No security concerns identified:

  • No hardcoded credentials or secrets
  • Proper separation of concerns (Reducer is pure, Effects handle I/O)
  • Correlation IDs for distributed tracing (no PII exposure)
  • Timeout protections prevent resource exhaustion

🧪 Test Coverage Assessment

Missing Test Guidance

Observation: The "Test Plan" in the PR description only covers documentation validation:

  • Mermaid diagram renders correctly in GitHub
  • Document structure follows existing architecture docs patterns
  • Contract is implementable for OMN-889

Recommendation: Add a "Testing Strategy" section to the document covering:

  1. Unit test requirements for the Reducer:
    • All 17 transitions
    • All 13 guards
    • Retry counter increment/reset logic
    • CONTINUE trigger processing
  2. Integration test scenarios:
    • Happy path (full registration)
    • Partial failures with recovery
    • Retry exhaustion
    • Timeout handling
    • Fatal error from each state
  3. Property-based testing opportunities:
    • FSM determinism (same input always produces same output)
    • No orphan states (all states reachable)
    • Terminal state invariants (no outgoing transitions from deregistered)

📊 Performance Considerations

✅ Well-designed for performance:

  • Pure Reducer (no blocking I/O)
  • Parallel deregistration (emit_consul_deregister_intent + emit_postgres_deregister_intent)
  • Reasonable timeouts (5s validation, 10s registration, 15s deregistration)
  • Single-step CONTINUE execution avoids extra round-trips

Minor concern: Retry backoff is mentioned (line 1339) but not specified:

  • Exponential backoff parameters? (initial delay, multiplier, max delay)
  • Jitter to prevent thundering herd?

Recommendation: Add a "Retry Backoff Configuration" section with recommended parameters.


✨ Best Practices Observed

  1. Idempotency: PostgreSQL upsert is idempotent (line 1928)
  2. Correlation tracking: All intents include correlation_id
  3. Graceful degradation: Timeout during deregistering doesn't block shutdown
  4. Fail-safe defaults: Undefined guard fields return false (no exception)
  5. Comprehensive logging: Entry/exit actions for all critical states

🎯 Final Verdict

APPROVE ✅

This is an exemplary architectural document that sets a high bar for FSM contract specifications. The level of detail, clarity, and implementation guidance is outstanding.

Blockers: None

The issues identified are minor clarifications and enhancements that can be addressed in follow-up PRs or inline edits.

Recommended Actions (Priority Order):

  1. High Priority (before merge):

  2. Medium Priority (next PR):

    • Add metrics catalog section (Issue #7)
    • Add testing strategy section (Test Coverage)
    • Document retry backoff configuration (Performance)
  3. Low Priority (future enhancement):


🙏 Kudos

Exceptional work on this contract, @jonahgabriel! The postgres_registered checkpoint pattern and CONTINUE trigger semantics are particularly elegant solutions. This document will serve as a reference implementation for future FSM contracts in the ONEX framework.

Lines reviewed: 1,961 (1,959 new + 2 modified)
Estimated review time: 45 minutes
Review confidence: High (comprehensive architectural understanding)


Ready to merge pending minor clarifications above. 🚀

…tract [OMN-938]

Comprehensive documentation enhancements addressing all review feedback:

CRITICAL:
- Clarified state type terminology (success vs terminal states)
- Added whitespace-delimited operator requirements with valid/invalid examples
- Documented observability vs latency tradeoffs for CONTINUE trigger
- Added severity levels (Warning/Error/Critical) to validation errors table

MAJOR:
- Added missing error codes: INTENT_PAYLOAD_ERROR, EFFECT_NOT_FOUND,
  INTENT_SERIALIZATION_ERROR, INTERNAL_TRANSITION_ERROR
- Added context preprocessing guidance for nested field limitation
- Enhanced retry limit enforcement documentation with guard semantics
- Added error handling best practices section (6 best practices)

MINOR:
- Added comprehensive metrics reference with types, emission points, thresholds
- Documented nested field limitation as v1.0 scope with workaround
- Added deregistration failure handling and orphan cleanup guidance
- Clarified is_recoverable semantics for success states

NITPICK:
- Added guard validation flow diagram
- Enhanced token separation rules documentation
- Added valid/invalid format examples for guard expressions
@claude

claude Bot commented Dec 19, 2025

Copy link
Copy Markdown

Pull Request Review: Registration FSM Contract [OMN-938]

Summary

This PR introduces a comprehensive 2,735-line FSM (Finite State Machine) contract specification for the Dual Registration Reducer. The document is exceptionally thorough, well-structured, and demonstrates strong technical depth in formal state machine design for ONEX node registration.


✅ Strengths

1. Outstanding Documentation Quality

  • Comprehensive coverage: 10 states, 17 transitions, detailed guard specifications, error handling, metrics, and implementation patterns
  • Excellent structure: Clear TOC, consistent formatting, logical progression from states → transitions → guards → diagrams
  • Professional tone: Technical precision without verbosity; appropriate use of tables, YAML blocks, and diagrams

2. Thoughtful Architecture Design

  • Clear state semantics: The distinction between success states (registered) and terminal states (deregistered) is well-explained with comprehensive tables and rationale
  • Dual registration order: PostgreSQL-first strategy is well-justified (source of truth before service discovery)
  • Partial failure handling: The partial_registered state enables targeted retry of only the failed component - excellent design for resilience

3. Exceptional Error Handling

  • Multiple error categories: Validation, PostgreSQL, Consul, partial failures, guard errors, timeout errors, intent emission errors
  • Detailed recovery flows: 8 scenario-based recovery flows with ASCII diagrams (including CONTINUE processing failure)
  • Severity levels: Warning/Error/Critical distinction with clear guidance on actions required

4. Guard Expression System

  • Strict 3-token format: Simple, parseable, and well-documented with extensive examples
  • Clear validation phases: Contract load-time vs runtime validation with specific error codes
  • Comprehensive operator support: Equality, comparison, existence, containment, pattern matching
  • Flow diagram: Visual representation of validation logic aids understanding

5. Observability & Operations

  • Comprehensive metrics: 9 metrics with labels, emission points, and alerting thresholds
  • Correlation ID tracking: Emphasized throughout for distributed tracing
  • Timeout handling: Detailed semantics for each state with recommended recovery strategies
  • Retry limit enforcement: Well-defined counter semantics with implementation examples

6. Practical Implementation Guidance

  • Python code examples: Realistic, runnable implementations for retry handling, timeout monitoring, CONTINUE processing
  • YAML configurations: FSM subcontract example provides concrete starting point
  • Implementation notes: Section specifically for OMN-889 with outcome handling patterns

🔍 Observations & Minor Suggestions

1. Document Length (2,735 lines)

Observation: While comprehensive, the document is very long. This may impact maintainability and discoverability.

Suggestions:

  • ✅ Keep as-is for v1.0: The length is justified for a formal specification contract. Every section adds value.
  • 💡 Future enhancement: Consider extracting some subsections into separate docs if the pattern is reused across multiple FSMs:
    • docs/patterns/GUARD_EXPRESSION_REFERENCE.md (reusable guard syntax guide)
    • docs/patterns/FSM_TIMEOUT_PATTERNS.md (reusable timeout handling patterns)
    • docs/patterns/RETRY_LIMIT_ENFORCEMENT.md (reusable retry patterns)

2. CONTINUE Trigger Complexity

Observation: The CONTINUE trigger implementation section (lines 608-678) is highly detailed with single-step vs two-step execution patterns, latency comparisons, and crash recovery considerations.

Strength: This level of detail is excellent for implementers who need to make informed tradeoff decisions.

Suggestion: Consider adding a TL;DR summary at the top of the CONTINUE section:

**TL;DR**: Use single-step execution (recommended for production, <10ms latency). Use two-step only for debugging (adds 50-100ms but makes checkpoint observable).

3. Nested Field Limitation (v1.0)

Observation: Lines 1096-1148 document that guard expressions don't support nested field access (context.retry_count), requiring context flattening.

Strength: The limitation is well-documented with clear workarounds and rationale.

Question: Is there a plan to implement nested field support in v2.0? If so, consider adding a reference to a tracking ticket.

4. Mermaid Diagram Rendering

Observation: The state diagram (lines 1393-1440) uses stateDiagram-v2 syntax.

Verification needed: Confirm this renders correctly in GitHub's Markdown renderer. The simplified ASCII flow diagram (lines 1444-1489) is an excellent fallback.

Suggestion: Add a note in the PR description or commit message confirming successful rendering.

5. Deregistration Best-Effort Cleanup

Observation: Lines 339-383 document that deregistration failures proceed to terminal state with best-effort cleanup, potentially leaving orphaned resources.

Strength: The garbage collection recommendation (lines 356-379) is excellent - provides concrete SQL query example.

Suggestion: Consider referencing this pattern in the "Implementation Notes" section (lines 1621-1646) so implementers don't miss it.


🔐 Security Considerations

✅ No Security Concerns Identified

  • No hardcoded credentials or secrets
  • No SQL injection vectors (uses parameterized queries in examples)
  • Timeout values are reasonable and prevent DoS scenarios
  • Error messages don't leak sensitive information
  • Correlation IDs are UUIDs (no PII)

🧪 Test Coverage Recommendations

While this is a specification document, here are testing suggestions for the implementation (OMN-889):

  1. State transition matrix tests: Verify all 17 transitions with valid/invalid triggers
  2. Guard evaluation tests: Test all 13 guards with edge cases (boundary values, type mismatches)
  3. Timeout behavior tests: Verify timeout detection for all 4 states with timeout_ms
  4. Retry exhaustion tests: Verify guard blocks at retry_count >= 3
  5. CONTINUE failure tests: Verify FATAL_ERROR handling when CONTINUE processing fails
  6. Partial registration recovery: Test all paths from partial_registered state
  7. Idempotency tests: Verify PostgreSQL upsert and Consul registration are idempotent

📊 Performance Considerations

✅ Well-Designed for Performance

  • Single-step CONTINUE execution: <10ms latency vs 50-100ms for two-step (lines 647-652)
  • Timeout values are reasonable:
    • Validation: 5s (fast operation)
    • Registration: 10s each (network + database)
    • Deregistration: 15s (dual cleanup)
  • Intent batching: Deregistration emits both intents atomically (line 1628)

💡 Optimization Opportunity

Parallel deregistration: Lines 325-327 note that emit_consul_deregister_intent and emit_postgres_deregister_intent are emitted "in parallel". Confirm that the Orchestrator actually executes these Effects concurrently (not sequentially) to achieve the 15s timeout budget.


🎯 Code Quality Assessment

Criterion Rating Notes
Documentation Clarity ⭐⭐⭐⭐⭐ Exceptional - clear, comprehensive, well-structured
Architecture Design ⭐⭐⭐⭐⭐ Strong FSM design with partial failure handling
Error Handling ⭐⭐⭐⭐⭐ Comprehensive error categories, recovery flows, and best practices
Observability ⭐⭐⭐⭐⭐ Excellent metrics, logging guidance, correlation tracking
Implementability ⭐⭐⭐⭐⭐ Provides Python code examples, YAML configs, and detailed semantics
Maintainability ⭐⭐⭐⭐☆ Very long document (2,735 lines) - consider future modularization

✅ Recommendation: APPROVE

This PR represents excellent technical documentation work. The Registration FSM Contract is comprehensive, well-reasoned, and provides strong guidance for implementation. The minor suggestions above are enhancements for future iterations, not blockers for v1.0.

Specific approval criteria met:

  • ✅ All acceptance criteria checked off in PR description
  • ✅ Document structure follows existing architecture docs patterns
  • ✅ Contract is implementable for OMN-889 (detailed Python examples provided)
  • ✅ Mermaid diagram syntax is correct (pending rendering verification)
  • ✅ No security concerns identified
  • ✅ Aligns with ONEX architecture patterns (pure Reducer, Intent emission, Orchestrator aggregation)
  • ✅ CLAUDE.md update is appropriate (agent background mode guidance)

Next steps:

  1. ✅ Merge after confirming Mermaid rendering in GitHub
  2. 📝 Create follow-up tickets for suggested enhancements:
    • Extract reusable pattern docs (guards, timeouts, retry limits) if pattern emerges across multiple FSMs
    • Track nested field support for v2.0 (if planned)
    • Add garbage collection implementation ticket

📚 Related CLAUDE.md Guidance Followed

  • ✅ Uses Pydantic models for structured data
  • ✅ Error handling uses ModelOnexError with structured error codes
  • ✅ Protocol-driven DI pattern referenced (container.get_service)
  • ✅ Node purity emphasized (Reducer is pure, no I/O)
  • ✅ Intent emission pattern aligns with ONEX four-node architecture

Excellent work on this specification! 🎉


Review completed by Claude Code on 2025-12-19

…OMN-938]

- Add TL;DR summary to CONTINUE trigger section for quick reference
- Add v2.0 tracking note for nested field access enhancement
- Add Mermaid rendering note with fallback reference
- Cross-reference garbage collection in Implementation Notes
- Add Document Organization section with future extraction guidance
@claude

claude Bot commented Dec 19, 2025

Copy link
Copy Markdown

Pull Request Review: Registration FSM Contract [OMN-938]

Overview

This PR introduces a comprehensive 2,752-line FSM contract specification for the Dual Registration Reducer. This is an architectural specification document, not an implementation. Overall, this is excellent work - comprehensive, well-structured, and thoughtful. The document demonstrates deep understanding of FSM patterns and ONEX architecture.


✅ Strengths

1. Exceptional Documentation Quality

  • Comprehensive Coverage: 10 states, 17 transitions, guard expressions, error handling, metrics, and recovery flows
  • Clear Structure: Excellent TOC, logical flow, well-defined sections
  • Rich Examples: Concrete YAML configurations, Python code examples, and flow diagrams
  • Mermaid Diagrams: State diagram and guard validation flow are excellent visual aids

2. Thoughtful Architecture

  • State Type Terminology (lines 72-92): Excellent clarification of success vs terminal states. The registered vs deregistered distinction is well-explained
  • Dual Registration Order (lines 39-50): Clear rationale for PostgreSQL-first ordering
  • CONTINUE Trigger Semantics (Transition 6): Thorough treatment of single-step vs two-step execution patterns with latency analysis

3. Robust Error Handling

  • 7 Error Categories: Validation, PostgreSQL, Consul, Partial, Fatal, Guard, Timeout, Intent Emission
  • CONTINUE Failure Handling (lines 2316-2430): Exceptional detail on internal trigger failures
  • Recovery Flows: 8 scenarios with ASCII diagrams (Scenarios 1-8, lines 2615-2725)
  • Error Handling Best Practices (lines 2431-2612): Production-ready patterns with correlation IDs, metrics, structured logging

4. Guard Expression Design

  • Strict 3-Token Format (lines 1001-1041): Clear, parseable, predictable
  • Two-Phase Validation: Contract load-time + runtime (lines 1163-1331)
  • Nested Field Limitation (lines 1107-1161): Acknowledged with clear workaround and future enhancement path
  • Comprehensive Operator Set: Equality, comparison, existence, containment, pattern matching

5. Observability & Metrics

  • 8 Metrics Defined (lines 1995-2168): Counters + histogram with alerting thresholds
  • Correlation ID Requirements (lines 2471-2498): Distributed tracing support
  • Structured Logging: Best practices section with examples

🔍 Code Quality Assessment

Adherence to CLAUDE.md Standards

✅ PASS - All project conventions followed:

  • Pure FSM semantics (delta(state, event) -> (new_state, intents[]))
  • Protocol-driven DI references
  • ModelOnexError for structured errors
  • Intent emission pattern for Reducer → Effect communication
  • Thread safety considerations documented

Type Safety

✅ PASS - YAML contracts use Pydantic models:

  • ModelFSMContext, ModelRegistrationPayload
  • ModelIntent subclasses referenced throughout
  • Type annotations in Python examples follow PEP 604 (e.g., dict[str, Any])

Documentation Standards

✅ EXCELLENT - Exceeds expectations:

  • Version tracking (v1.0.0)
  • Related ticket references (OMN-889, OMN-912, OMN-913, OMN-938)
  • Structured metadata (version, status, last updated)
  • Cross-references to related docs (9 links in Related Documentation section)

⚠️ Issues & Recommendations

1. CRITICAL: Deregistration Best-Effort Cleanup May Create Orphaned Resources

Location: Lines 348-393 (Deregistration Failure Handling)

Issue: The FSM proceeds to deregistered even if cleanup fails or times out. While the rationale ("node needs to stop") is valid, this can leave orphaned resources in production:

  • Consul advertisements for dead nodes → traffic routing failures
  • PostgreSQL records for non-existent nodes → stale data

Current Mitigation: Garbage collection recommendation (lines 365-388) is code only, not integrated. There's no guarantee operators will implement this.

Recommendation:

  1. Add to Implementation Notes: Require garbage collection job as part of OMN-889 acceptance criteria
  2. Alerting: Define deregistration_failure_total metric (currently missing from metrics catalog)
  3. Partial Deregistration State (acknowledged at line 390): Consider prioritizing this for v1.1 if deregistration failures occur frequently in practice

Severity: Medium (operational risk in production)


2. Guard Expression Nested Field Limitation May Cause Implementation Pain

Location: Lines 1107-1161 (Nested Field Limitation)

Issue: The v1.0 guard limitation (top-level fields only) requires manual flattening of all context data. This creates:

  • Boilerplate burden: Every Reducer must implement flatten_context() (example at lines 2264-2287)
  • Maintainability risk: Guard fields and flattening logic can drift out of sync
  • Debugging complexity: Flattened field names may not match domain model

Example Pain Point:

# Domain model has payload.node_id, but guards must use node_id
# Developers must remember to flatten AND update guards when model changes

Recommendation:

  1. Accept for v1.0: The design decision is sound for simplicity and performance
  2. Track Enhancement: File issue for v2.0 dot notation support (already mentioned at line 1159)
  3. Add Contract Linter: Consider a contract validation tool that checks guard field references against expected context schema to catch flattening bugs early

Severity: Low (design tradeoff, not a bug)


3. CONTINUE Trigger Failure Handling is Excellent But May Be Over-Engineered

Location: Lines 2316-2430 (CONTINUE Trigger Failure Handling)

Observation: The CONTINUE failure handling is exceptionally thorough (logging, metrics, correlation IDs, exception handling). However:

  • How often will CONTINUE fail? If intent construction is deterministic and the context is validated, CONTINUE failures should be extremely rare (code bugs only)
  • Is 114 lines of documentation justified for an edge case that may never occur in practice?

Counter-Argument: This is a specification document, not implementation code. Comprehensive failure documentation is appropriate for a contract that will guide implementers.

Recommendation:

  • Keep as-is for v1.0 specification
  • Monitor in practice: If OMN-889 implementation shows CONTINUE never fails, consider simplifying in future revisions
  • Add Implementation Note: Clarify that CONTINUE failures indicate code bugs, not operational issues, to help implementers prioritize

Severity: Informational (not an issue, just an observation)


4. Retry Limit Enforcement is Asymmetric

Location: Transition 17 (retry_exhausted, line 467), Trigger Catalog (RETRY_EXHAUSTED, line 1353)

Issue: The retry limit guard (retry_count < 3) is enforced differently across states:

State Trigger Guard Limit Enforcement
failed RETRY retry_count < 3 Transition silently blocked (guard returns false)
partial_registered RETRY retry_count < 3 Orchestrator emits RETRY_EXHAUSTED → failed

Why the Difference?

  • From partial_registered: Orchestrator detects guard failure and emits RETRY_EXHAUSTED trigger (Transition 17)
  • From failed: Guard failure blocks transition, no RETRY_EXHAUSTED trigger

This asymmetry is confusing and may cause implementation bugs.

Recommendation:

  1. Add Transition 18: failed → deregistered on RETRY_EXHAUSTED (analogous to Transition 17)
  2. Update Orchestrator Behavior: Emit RETRY_EXHAUSTED when guard fails on ANY retry transition, not just from partial_registered
  3. Rationale: Consistent behavior is easier to test and reason about

Severity: Medium (consistency issue that may cause bugs)


5. Missing Metrics for Deregistration Failures

Location: Lines 1995-2168 (Metrics Catalog)

Issue: The metrics catalog defines success metrics (deregistration_complete) but no failure metrics:

  • Missing: deregistration_consul_failure_total
  • Missing: deregistration_postgres_failure_total
  • Missing: deregistration_timeout_total

These are critical for alerting on orphaned resources (see Issue #1).

Recommendation:
Add 3 metrics to the catalog:

deregistration_consul_failure_total:
  type: counter
  labels: [node_id, deployment_id, error_type]
  description: "Consul deregistration failed during graceful shutdown"

deregistration_postgres_failure_total:
  type: counter
  labels: [node_id, deployment_id, error_type]
  description: "PostgreSQL deregistration failed during graceful shutdown"

deregistration_timeout_total:
  type: counter
  labels: [node_id, deployment_id, phase]
  description: "Deregistration exceeded 15s timeout (best-effort cleanup applied)"

Severity: Medium (observability gap for production operations)


6. Minor: CLAUDE.md Change is Unrelated

Location: CLAUDE.md line 79

Change: Added instruction "NEVER run agents in background mode"

Issue: This is a process improvement, not related to FSM contract work. Ideally should be a separate PR for cleaner git history.

Recommendation: Accept for this PR (it's a one-line change), but future process improvements should be separate PRs.

Severity: Trivial (process suggestion)


🧪 Test Coverage Considerations

Current Status: This PR is documentation only (no implementation).

For OMN-889 Implementation, ensure test coverage for:

  1. All 17 Transitions: Unit tests for each transition with valid guards
  2. Guard Failures: Test retry limit enforcement (Issue TKT-003: Validate Import Resolution and Eliminate Unused Dependencies #4 above)
  3. Timeout Handling: Integration tests with mock delays
  4. CONTINUE Failures: Test internal transition error path (Issue feat(tests): Complete TKT-002 ONEXContainer validation with comprehensive test suite #3)
  5. Deregistration Failures: Test best-effort cleanup (Issue Complete SPI Migration and Core Stabilization: LogLevel Standardization + ONEX Architecture #1)
  6. Metrics Emission: Verify all 8+ metrics are emitted correctly

Recommendation: Add "Test Plan" section to FSM contract document listing required test scenarios for implementers.


🔐 Security Concerns

✅ NO SECURITY ISSUES IDENTIFIED

  • No credentials or secrets in YAML configs
  • Correlation IDs for audit logging (good practice)
  • Idempotent operations resist replay attacks
  • Structured errors don't leak sensitive data

📊 Performance Considerations

Positive:

  • Single-Step CONTINUE Execution (lines 537-566): <10ms latency vs 50-100ms for two-step
  • Timeout Values: Reasonable (5s validation, 10s registration, 15s deregistration)
  • Guard Evaluation: O(1) for 3-token expressions

Potential Issues:

  • No Batch Registration: Each node registers individually (may be bottleneck at scale)
  • Retry Backoff Missing: The contract mentions "backoff guidance" (line 29) but doesn't specify exponential backoff parameters

Recommendation:

  1. Add Retry Backoff Section: Document recommended backoff strategy (e.g., 1s, 2s, 4s for 3 retries)
  2. Batch Registration: File future enhancement issue if node registration at scale becomes a bottleneck

Severity: Low (performance is likely adequate for v1.0)


🎯 Acceptance Criteria Review

Comparing against PR description:

Criterion Status Evidence
All states documented (10 states) ✅ PASS Lines 96-440
All transitions documented (16 transitions) ⚠️ 17 transitions (one more than claimed) Lines 449-467
Transition guards defined (12 guards) ✅ PASS Guard expressions throughout
State diagram included (Mermaid) ✅ PASS Lines 1408-1455

Note: PR description says "16 state transitions" but the Transition Table (line 449) lists 17 transitions. This is likely because Transition 17 (retry_exhausted) was added during authoring. Update PR description to reflect 17 transitions.


📝 Documentation Suggestions

1. Add Quick Reference Table

Add a 1-page "Quick Reference" section at the end with:

  • State summary table (10 states with 1-line descriptions)
  • Trigger summary table (14 triggers with sources)
  • Common recovery paths (3-4 most frequent scenarios)

2. Add Implementation Checklist

For OMN-889 implementers, add:

## Implementation Checklist
- [ ] Implement all 10 states with entry/exit actions
- [ ] Implement all 17 transitions with guards
- [ ] Implement flatten_context() for guard evaluation
- [ ] Emit all 8+ metrics
- [ ] Handle CONTINUE failures with FATAL_ERROR path
- [ ] Implement idempotent PostgreSQL upsert
- [ ] Implement idempotent Consul registration
- [ ] Add garbage collection job for orphaned resources
- [ ] Test all 8 recovery scenarios
- [ ] Verify correlation_id propagation

3. Cross-Reference to ONEX Terminology

Line 37 references docs/standards/onex_terminology.md, but the FSM uses terms like "Intent", "Reducer", "Effect" without defining them. Add:

> **Terminology**: For canonical definitions of Intent, Reducer, Effect, and other ONEX concepts, see [ONEX Terminology Guide](../standards/onex_terminology.md).

Severity: Trivial (usability improvements)


🚀 Final Verdict

Overall Assessment: APPROVE with Recommendations

This is high-quality work that demonstrates:

  • Deep FSM expertise
  • Thoughtful error handling design
  • Production-ready observability patterns
  • Excellent documentation skills

Required Changes (Before Merge):

  1. ⚠️ Add deregistration failure metrics (Issue feat: implement complete 4-node canary deployment architecture #5) - 10 minutes
  2. ⚠️ Fix retry limit asymmetry (Issue TKT-003: Validate Import Resolution and Eliminate Unused Dependencies #4) - Add Transition 18 or document rationale - 15 minutes
  3. ⚠️ Update PR description (17 transitions, not 16) - 2 minutes

Recommended Changes (Can Be Follow-Up PRs):

  1. Add garbage collection job requirement to OMN-889 acceptance criteria (Issue Complete SPI Migration and Core Stabilization: LogLevel Standardization + ONEX Architecture #1)
  2. File enhancement issue for nested guard field access (Issue Add Claude Code GitHub Workflow #2)
  3. Add retry backoff parameters to FSM configuration (Performance section)
  4. Add Quick Reference and Implementation Checklist (Documentation suggestions)

Blocking Issues: None


🏆 Kudos

Exceptional work on:

  • CONTINUE trigger semantics (single-step vs two-step execution analysis)
  • State type terminology (success vs terminal state distinction)
  • Error handling best practices (correlation IDs, structured logging, graceful degradation)
  • Guard validation flow diagram (clear 2-phase validation process)

This document will serve as an excellent reference for future FSM contracts in the ONEX framework.


Reviewer: Claude Sonnet 4.5 (Code Review Agent)
Review Date: 2025-12-19
Contract Version: 1.0.0
Recommendation: Approve after addressing required changes

Address 50+ PR review issues across 8 categories:

CRITICAL (5):
- Add three-dimensional state classification system
- Document mandatory whitespace in guard expressions
- Add latency decision matrix for CONTINUE trigger
- Define severity levels (warning/error/critical)
- Document deregistration cleanup with GC examples

MAJOR (14):
- Add retry limit enforcement cross-references
- Clarify RETRY_EXHAUSTED trigger source (Orchestrator)
- Add guard error recovery scenarios (9, 10)
- Complete error code table with all codes
- Add 11 new metrics with alerting thresholds

MINOR (30+):
- Fix 16 sentence fragments in Trigger Catalog
- Add recovery terminology disambiguation section
- Enhance Table of Contents with subsections
- Add comprehensive edge case examples

NITPICK (1):
- Extensively document guard expression syntax

+937 lines of documentation enhancements
@claude

claude Bot commented Dec 20, 2025

Copy link
Copy Markdown

PR Review: Registration FSM Contract v1.0.0

Overview

This PR introduces a comprehensive FSM (Finite State Machine) contract for the Dual Registration Reducer (OMN-889), managing node registration to both Consul and PostgreSQL. The documentation is exceptionally thorough and well-structured.


✅ Strengths

1. Exceptional Documentation Quality

  • Comprehensive coverage: 3,570 lines of detailed specification covering all aspects of the FSM
  • Clear organization: Excellent table of contents, cross-references, and section structure
  • Practical examples: Extensive code examples, error cases, and edge case handling
  • Three-dimensional state classification: The distinction between state_type, is_terminal, and is_recoverable is brilliant and well-explained

2. Architectural Alignment with ONEX Principles

  • Pure FSM semantics: Correctly follows the delta(state, event) -> (new_state, intents[]) pattern
  • Intent-based design: Reducer emits intents rather than performing I/O directly ✅
  • Clear separation: Reducer (pure logic) → Effects (I/O) → Orchestrator (aggregation) workflow is well-defined
  • Dual registration order: PostgreSQL-first (source of truth) then Consul (discovery) is sound architectural reasoning

3. Robust Error Handling

  • Partial failure recovery: The partial_registered state with targeted retry (RETRY vs RETRY_POSTGRES) is excellent design
  • Retry limits: Guard-based retry count enforcement with clear exhaustion handling
  • Timeout handling: Comprehensive timeout specifications for all operational states
  • Deregistration best-effort: Well-reasoned approach with orphaned resource handling via garbage collection

4. Guard Expression System

  • Strict 3-token format: The mandatory whitespace requirement is good for parser simplicity
  • Comprehensive validation: Contract load-time vs runtime validation separation is well thought out
  • Extensive examples: The guard syntax section (lines 1310-1699) with error codes is exemplary
  • Future-proof: Acknowledges v1.0 limitations (no nested fields) with clear workaround and v2.0 consideration

5. State Diagram & Transitions

  • 17 transitions: All critical paths covered (happy path, failures, retries, abandonment, deregistration)
  • CONTINUE trigger semantics: Excellent explanation of single-step vs two-step execution with latency tradeoffs
  • Crash recovery: Thoughtful consideration of recovery scenarios with persistence

6. Production Readiness

  • Garbage collection: Concrete Python implementations for both PostgreSQL and Consul cleanup jobs
  • Observability: Logging, metrics, and monitoring guidance throughout
  • Kubernetes integration: CronJob examples for GC jobs

🔍 Areas for Improvement

1. Minor Inconsistencies

Recovery Terminology (Lines 148-172)

The "Recovery Terminology Disambiguation" section is excellent but could be positioned earlier. Consider moving it immediately after "State Type Terminology" (around line 77) since it clarifies concepts used throughout the document.

Recommendation: Move lines 148-172 to precede "State Definitions" (before line 174) for better information flow.


2. Guard Expression Whitespace - UX Consideration

The mandatory whitespace requirement for guard expressions is correctly implemented but may cause friction for users accustomed to compact syntax. While the decision is sound (simpler parser, predictable behavior), consider these enhancements:

Recommendation 1 - Helpful Error Messages:
Ensure error messages suggest the fix:

❌ Expression 'retry_count<3' has 1 token(s), expected 3
✅ Expression 'retry_count<3' has 1 token(s), expected 3
   Did you mean: 'retry_count < 3' (add spaces around operator)?

Recommendation 2 - Auto-formatting Tool (Future Enhancement):
Consider a contract linting/formatting tool that auto-fixes common mistakes:

poetry run onex-lint --fix registration_fsm.yaml
# Auto-corrects: retry_count<3 → retry_count < 3

Not a blocker - The current specification is clear and well-justified.


3. CONTINUE Trigger - Crash Recovery Ambiguity

Lines 961-973 discuss crash recovery during CONTINUE processing but leave some ambiguity:

"In single-step mode, crashes during CONTINUE processing result in:

  • FSM state remains at registering_postgres (pre-transition)"

Question: What if the crash occurs after PostgreSQL registration completes but before the state transition is persisted? This could lead to:

  • PostgreSQL has the registration (applied = true)
  • FSM state persisted at registering_postgres
  • On replay: PostgreSQL upsert re-attempted (though idempotent)

Recommendation: Add a subsection clarifying atomicity of state persistence:

### Crash Recovery - State Persistence Atomicity

**Critical Assumption**: This contract assumes the Orchestrator persists FSM state 
transitions **atomically** with intent emission acknowledgment. If state persistence 
is NOT atomic with effect application:

1. Use **event sourcing** - persist the trigger/event, replay on recovery
2. Or use **two-phase commit** - ensure state + effect results are atomic
3. Or accept **at-least-once semantics** - rely on Effect idempotency

See: `docs/architecture/ORCHESTRATOR_PERSISTENCE_GUARANTEES.md`

Impact: Medium - This affects correctness guarantees during crashes. Should be clarified for implementers.


4. Deregistration Failure - Operational Concerns

Lines 411-700+ cover deregistration failure handling with best-effort cleanup and GC jobs. This is excellent and necessary, but raises operational questions:

Missing Guidance:

  1. GC job failure handling: What if the GC job itself fails? (e.g., PostgreSQL connection timeout, Consul API unavailable)
  2. Alerting thresholds: When should operators be paged? (e.g., >100 orphaned nodes, GC job failing for >30 minutes)
  3. Manual remediation: If GC can't auto-resolve, what are the manual steps?

Recommendation: Add a subsection:

### Garbage Collection - Operational Runbook

#### GC Job Failure Handling
- **Transient errors** (network timeouts): Retry with exponential backoff (max 3 retries)
- **Persistent errors** (DB unavailable): Alert ops team, job will retry on next schedule
- **Batch size tuning**: If GC times out, reduce `batch_size` from 100 to 50/25

#### Alerting Thresholds
| Metric | Warning | Critical | Action |
|--------|---------|----------|--------|
| Orphaned nodes | >50 | >200 | Review node churn rate; check for mass failures |
| GC job failure | 2 consecutive | 5 consecutive | Check database/Consul health |
| GC duration | >60s | >120s | Reduce batch size or optimize query |

#### Manual Remediation
If automated GC fails, use these manual commands:
```sql
-- PostgreSQL: Manually delete orphaned registration
DELETE FROM node_registrations WHERE node_id = '<orphaned_node_id>';
# Consul: Manually deregister service
consul services deregister -id=<service_id>

Impact: Low-Medium - Operational guidance helps production teams handle failures.


5. Test Coverage - Not Included

The PR is documentation-only (no implementation). The test plan checklist is minimal:

- [ ] Mermaid diagram renders correctly in GitHub
- [ ] Document structure follows existing architecture docs patterns
- [ ] Contract is implementable for OMN-889

Recommendation: When OMN-889 (implementation) is created, ensure:

  1. FSM unit tests: All 17 transitions with guard conditions
  2. Integration tests: End-to-end registration flow with real Consul/PostgreSQL (use testcontainers)
  3. Chaos tests: Crash recovery, timeout handling, partial failure scenarios
  4. Property-based tests: Use Hypothesis to generate random state/trigger sequences

Suggested Test Structure:

# tests/unit/reducer/test_registration_fsm.py
class TestRegistrationFSMTransitions:
    @pytest.mark.parametrize("state,trigger,expected_state,guards", [
        ("unregistered", "REGISTER", "validating", {"payload_valid": True}),
        ("validating", "VALIDATION_SUCCESS", "registering_postgres", {}),
        # ... 17 transitions
    ])
    def test_transition(self, state, trigger, expected_state, guards):
        """Test each FSM transition follows contract."""
        ...

# tests/integration/reducer/test_registration_e2e.py  
@pytest.mark.integration
async def test_full_registration_happy_path():
    """Test complete registration flow with real dependencies."""
    # Uses testcontainers for Consul + PostgreSQL
    ...

Impact: High - Tests are critical for FSM correctness. Not a blocker for this doc PR, but critical for OMN-889.


6. Observability - Metrics & Logging

The contract mentions metrics and logging throughout but lacks a centralized reference:

Recommendation: Add a section (or link to external doc):

## Observability & Monitoring

### Metrics Catalog

| Metric | Type | Labels | Description |
|--------|------|--------|-------------|
| `registration_attempts_total` | Counter | `environment`, `deployment_id` | Total registration attempts |
| `registration_success_total` | Counter | `environment` | Successful registrations (reached `registered` state) |
| `registration_partial_total` | Counter | `component` (postgres/consul) | Partial failures by component |
| `registration_retry_total` | Counter | `from_state`, `retry_count` | Retry attempts |
| `registration_duration_seconds` | Histogram | `outcome` (success/failed/partial) | Time from `unregistered` to terminal state |
| `fsm_state_transitions_total` | Counter | `from_state`, `to_state`, `trigger` | All FSM transitions (debugging) |

### Logging Standards

All FSM state transitions should emit structured logs:
```json
{
  "timestamp": "2025-12-19T10:15:30Z",
  "level": "INFO",
  "fsm": "registration",
  "event": "state_transition",
  "from_state": "validating",
  "to_state": "registering_postgres",
  "trigger": "VALIDATION_SUCCESS",
  "correlation_id": "reg-abc123",
  "node_id": "node-prod-001",
  "duration_ms": 42
}

Impact: Medium - Improves production debugging and alerting.


7. CLAUDE.md Change - Minor Enhancement

The change to CLAUDE.md (line 79) is good but could be more specific:

- **NEVER** run agents in background mode (`run_in_background: true`) - run parallel polymorphic agents in the foreground instead by dispatching multiple agents in a single message

Recommendation: Add rationale for clarity:

- **NEVER** run agents in background mode (`run_in_background: true`) - Background agents 
  can fail silently and complicate error handling. Instead, run parallel polymorphic agents 
  in the foreground by dispatching multiple agents in a single message (see Agent SDK docs).

Impact: Low - Helps developers understand why, not just what.


🔒 Security Considerations

1. Garbage Collection - Race Conditions

The PostgreSQL GC job (lines 465-700) uses FOR UPDATE SKIP LOCKED:

SELECT node_id FROM node_registrations
WHERE last_heartbeat < NOW() - $1::interval
FOR UPDATE SKIP LOCKED

Good: Prevents lock contention with concurrent GC jobs ✅

Potential Issue: If a node is legitimately down but heartbeat hasn't stopped (e.g., heartbeat process stuck), the GC won't clean it up until heartbeat stops.

Recommendation: Add a secondary condition:

WHERE (
  last_heartbeat < NOW() - $1::interval 
  OR (status = 'deregistering' AND updated_at < NOW() - INTERVAL '1 hour')
)

Impact: Low - Edge case, but worth considering.


2. Consul Deregistration - Missing Health Check Cleanup

Lines 430-434 mention orphaned health check definitions:

"Health check definition | Consul | Consul continues polling non-existent endpoint"

The document states: "Automatic cleanup with service deregistration"

Question: What if service deregistration times out but the health check was registered separately? Consul may continue polling.

Recommendation: Clarify in the Consul GC job example:

# Deregister service (includes associated health checks)
await client.put(f"{consul_addr}/v1/agent/service/deregister/{service_id}")

# Explicitly deregister health checks if service deregistration may have failed
checks = await client.get(f"{consul_addr}/v1/agent/checks")
orphaned_checks = [c for c in checks if c["ServiceID"] == service_id]
for check_id in orphaned_checks:
    await client.put(f"{consul_addr}/v1/agent/check/deregister/{check_id}")

Impact: Low - Consul usually handles this, but explicit cleanup is safer.


📋 Code Quality & Best Practices

✅ Adherence to ONEX Standards

Based on CLAUDE.md:

Standard Compliance Notes
Pure FSM semantics ✅ Excellent Reducer emits intents, no I/O
Intent-based design ✅ Excellent Clear intent definitions throughout
Four-node architecture ✅ Excellent EFFECT → COMPUTE → REDUCER → ORCHESTRATOR
Error handling (ModelOnexError) ⚠️ N/A (Doc only) Ensure OMN-889 uses structured errors
Protocol-driven DI ⚠️ N/A (Doc only) Implementation should use container.get_service()
Thread safety ⚠️ To be verified FSM state must be thread-safe (use locks or single-writer)

Action Item for OMN-889: Ensure implementation follows ONEX container/DI patterns.


✅ Documentation Quality Metrics

Metric Score Notes
Completeness 10/10 Covers all FSM aspects comprehensively
Clarity 9/10 Excellent, minor improvements suggested above
Examples 10/10 Extensive code examples, edge cases, error codes
Maintainability 9/10 Well-structured, good cross-references
Accessibility 8/10 Could benefit from a "Quick Start" summary section

🎯 Recommendations Summary

Must-Have (Before Merge)

None - the PR is merge-ready as-is.

Should-Have (Nice to Have)

  1. Move "Recovery Terminology Disambiguation" earlier in the document (before "State Definitions")
  2. Add crash recovery atomicity clarification for state persistence
  3. Add GC operational runbook (alerting, failure handling, manual remediation)
  4. Enhance guard error messages with suggested fixes (e.g., "Did you mean 'retry_count < 3'?")

Could-Have (Future Enhancements)

  1. Metrics & logging catalog section (or link to external doc)
  2. Contract linting/formatting tool for guard expressions
  3. Quick Start / TL;DR section at the top for experienced users
  4. Nested field access in guards (tracked for v2.0)

✅ Final Verdict

APPROVED ✅

This is an exemplary FSM contract specification. The documentation quality exceeds typical architecture documents by a significant margin. The three-dimensional state classification system, comprehensive error handling, and production-ready GC implementations demonstrate deep architectural thinking.

Why Approve?

  1. ✅ Architecturally sound: Aligns perfectly with ONEX principles
  2. ✅ Production-ready: Handles edge cases, failures, and operational concerns
  3. ✅ Well-documented: 3,570 lines of clear, comprehensive specification
  4. ✅ Future-proof: Acknowledges v1.0 limitations with clear upgrade paths
  5. ✅ No blocking issues: All concerns are minor enhancements or future work

Post-Merge Actions

  1. OMN-889 implementation should reference this contract as the source of truth
  2. Comprehensive testing (17 transitions, chaos tests, property-based tests)
  3. Consider extracting reusable patterns to docs/patterns/ as document suggests (lines 25-29)

Great work! This sets a high bar for FSM contract specifications in the ONEX ecosystem. 🎉


Reviewed by Claude Code | Following ONEX architecture standards from CLAUDE.md v0.4.0

Comprehensive documentation improvements for Registration FSM Contract:

**CRITICAL Issues:**
- Add CONTINUE trigger crash recovery vs retry-after-failure disambiguation
- Enhance deregistration best-effort cleanup with orphan resource table
- Fix state type inconsistency with consistent trigger names

**MAJOR Issues:**
- Standardize retry limit enforcement with RETRY_EXHAUSTED source clarified
- Document Orchestrator (not Reducer) generates RETRY_EXHAUSTED trigger
- Add missing partial_registered -> failed transition via RETRY_EXHAUSTED
- Complete error code table with phase and severity info
- Add comprehensive FATAL_ERROR path documentation (~175 lines)
- Add guard validation error handling subsection (~100 lines)

**MINOR Issues:**
- Add Guard Syntax Quick Reference section (~52 lines)
- Document all 15+ metrics with type, emission timing, alerting thresholds
- Add correlation ID propagation examples (~200 lines)
- Fix incomplete ASCII diagrams and duplicate separators
- Verify document structure matches project patterns

**NITPICK:**
- Clarify guard expression syntax and validation rules

Document grew from ~2,735 to 4,308 lines (+749 insertions, -11 deletions)
@claude

claude Bot commented Dec 20, 2025

Copy link
Copy Markdown

Pull Request Review: Registration FSM Contract [OMN-938]

Overall Assessment

Verdict: ✅ Approve with Minor Suggestions

This is an exceptionally well-crafted FSM contract specification. The document demonstrates deep architectural thinking, comprehensive edge case handling, and outstanding documentation quality. The 4,308-line document is thorough without being redundant, providing a complete reference for implementing the Dual Registration Reducer (OMN-889).


Strengths 🎯

1. Exceptional State Modeling

  • Three-dimensional state classification (state_type, is_terminal, is_recoverable) is brilliant and eliminates common confusion between success/terminal/recoverable states
  • The distinction between registered (success, non-terminal) and deregistered (terminal) is clearly explained with detailed tables
  • Recovery terminology disambiguation section prevents confusion between retry-after-failure, crash recovery, and partial recovery

2. Comprehensive Error Handling

  • Deregistration failure handling is extraordinarily thorough with production-ready garbage collection code examples (PostgreSQL + Consul)
  • Best-effort cleanup strategy is well-justified (shutdown context, no retry mechanisms)
  • Failure scenario matrix clearly shows impact of each timeout combination

3. Outstanding Documentation Quality

  • Guard expression reference with comprehensive edge cases and exact error codes
  • Complete metrics reference with emission points and alerting thresholds
  • Mermaid state diagram and simplified ASCII flow diagram complement the formal specification

4. Practical Implementation Guidance

  • Context flattening pattern solves the nested field access limitation
  • Timeout handling strategies with concrete recommendations for each state
  • 10 detailed error flow scenarios showing recovery paths
  • Intent emission matrix clearly maps states to intents, targets, and descriptions

5. Strong Architectural Alignment

  • Pure FSM semantics: delta(state, event) -> (new_state, intents[])
  • Dual registration order (PostgreSQL first, Consul second) is well-justified
  • Follows ONEX Reducer pattern correctly (no I/O, only intent emission)

Potential Issues & Suggestions

1. Minor: Postgres vs PostgreSQL Consistency 🟡

Issue: The document uses both "Postgres" and "PostgreSQL" inconsistently.

Suggestion: Standardize on "PostgreSQL" in prose, "postgres" in identifiers (state names, transitions already follow this).

Impact: Low - does not affect correctness, just consistency.


2. Minor: CLAUDE.md Change Needs Context 🟡

File: CLAUDE.md (line 79)

Change: Added guideline about not running agents in background mode.

Issue: This is valuable but appears unrelated to the FSM contract. PR description doesn't mention CLAUDE.md updates.

Suggestion: Add a note to PR description explaining why this guideline was added.


3. Enhancement: Guard Expression Cookbook 🔵

The guard expression reference is comprehensive for validation errors but could benefit from more positive examples showing common patterns (retry limits, boolean checks, XOR conditions).

Impact: Low priority - current documentation is already excellent.


4. Question: Validation State Timeout Strategy ❓

Location: validating state (timeout_ms: 5000)

Suggestion: Add a note clarifying that validation MUST be local (no I/O) to justify the 5-second timeout. This prevents implementers from accidentally adding blocking I/O to validation.


Security Considerations 🔒

1. Orphaned Resource Cleanup ✅

  • Documents orphaned resource types, detection, and remediation
  • Provides production-ready GC jobs with Kubernetes CronJob specs
  • Recommendation: Emphasize that GC jobs are not optional - required for system integrity

2. PostgreSQL Injection Risk ✅

  • Correctly uses parameterized queries
  • No SQL injection risk ✅

3. Consul API Security ⚠️

  • Example uses http://localhost:8500 without TLS/authentication
  • Recommendation: Add security note recommending HTTPS + ACL tokens in production
  • Impact: Medium - example code might be copy-pasted without hardening

Performance Considerations

1. Timeout Values Are Appropriate ✅

State Timeout Analysis
validating 5s ✅ Generous for local validation
registering_postgres 10s ✅ Reasonable for DB upsert
registering_consul 10s ✅ Reasonable for Consul API
deregistering 15s ✅ Extra buffer for dual cleanup

2. PostgreSQL GC Performance ✅

  • Excellent use of FOR UPDATE SKIP LOCKED (prevents lock contention)
  • Batch size limiting (prevents unbounded deletions)
  • Transaction-scoped (atomic cleanup)
  • Status: Production-ready ✅

3. Retry Limit of 3 ✅

  • Maximum 3 attempts is appropriate
  • Recommendation: Consider adding exponential backoff guidance

Test Coverage

The PR test plan covers rendering, structure, and implementability. For the implementation ticket (OMN-889), suggest adding:

  1. Contract Load-Time Validation Tests

    • All guard expressions parse correctly
    • Invalid guards fail with correct error codes
  2. Transition Coverage Tests

    • All 16 transitions are reachable
    • Terminal state has no outgoing transitions
  3. Error Flow Tests

    • All 10 error scenarios
    • Retry exhaustion triggers correct transition
  4. Garbage Collection Tests

    • PostgreSQL GC with mocked stale records
    • Consul GC with mocked critical services
    • Idempotency verification

Adherence to CLAUDE.md ✅

Project Standards Compliance:

  • ✅ Uses proper Pydantic v2 patterns
  • ✅ Follows ONEX Reducer purity guarantees
  • ✅ Intent emission follows ModelIntent pattern
  • ✅ Error handling uses ModelOnexError (implied by error codes)
  • ✅ No backwards-incompatible changes (new document)

Documentation Standards:

  • ✅ Version number and last updated date present
  • ✅ Related ticket numbers cross-referenced
  • ✅ Clear purpose and scope definition
  • ✅ Comprehensive table of contents

Architecture Alignment:

  • ✅ Follows ONEX four-node architecture (Reducer → Effect → Orchestrator)
  • ✅ Pure FSM semantics (no I/O in Reducer)
  • ✅ Protocol-driven DI implied
  • ✅ Structured errors with error codes

Recommendations Summary

Must Fix (Blockers) 🔴

None - document is ready to merge.

Should Fix (High Value) 🟡

  1. Standardize PostgreSQL vs Postgres terminology in prose (5 min)
  2. Add context for CLAUDE.md change in PR description (2 min)

Nice to Have (Enhancements) 🔵

  1. Add Guard Expression Cookbook subsection (15 min)
  2. Add validation I/O prohibition note (5 min)
  3. Add security note to Consul GC example (10 min)
  4. Add retry backoff recommendation (10 min)

Future Work (Post-Merge) ⚪

  1. Extract reusable patterns to standalone guides if FSM pattern is reused
  2. Link to registration payload models once OMN-913 merges

Conclusion

This is exceptional work. The Registration FSM Contract demonstrates:

  • Deep understanding of FSM theory and ONEX architecture
  • Comprehensive edge case analysis (orphaned resources, crash recovery, timeout handling)
  • Outstanding documentation quality (4,308 lines, zero fluff)
  • Production-ready operational guidance (GC jobs, metrics, alerting)

The minor suggestions above are truly minor - they would make a great document even better, but none are blockers.

Recommendation: ✅ Approve and merge after addressing items #1 and #2 (terminology + PR description context). Items #3-6 can be addressed in follow-up commits or during implementation (OMN-889).


Reviewed by: Claude Code (Sonnet 4.5)
Review Date: 2025-12-20
Reviewed Commits: fe316dd (PR #221)

Great work, @jonahgabriel! 🎉

@jonahgabriel
jonahgabriel merged commit 46a70b1 into main Dec 20, 2025
29 checks passed
@jonahgabriel
jonahgabriel deleted the jonah/omn-938-formalize-registration-fsm-contract branch December 20, 2025 03:20
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