Files
paliad/internal/services
mAi 9940dd8216 feat(deadline-system): P2 — condition_expr write-validator (m/paliad#149)
Phase 2 P2 (design §4.1). Locks the condition_expr grammar to:

  CondExpr := { "flag": "<known_flag>" }
            | { "op": "and"|"or", "args": [<CondExpr>, ...] }

Where <known_flag> must exist in paliad.scenario_flag_catalog (today:
with_ccr / with_amend / with_cci; editorial adds via the catalog
table as needed).

Wire-time validation in RuleEditorService.Create and UpdateDraft —
the rule editor surfaces a 400 with a friendly message before the row
hits the DB. Empty / JSON null inputs pass through (the "no gate"
shape; stored as NULL column).

The validator:
  * walks the JSON tree once, collecting every leaf flag name
  * rejects mutually-exclusive shapes (leaf + composite in one node)
  * rejects empty args, bad op values, empty flag strings
  * does ONE batch lookup of the collected leaf names against the
    catalog (regardless of expression depth)

Tests:
  * 9 shape-only unit tests covering every reject path (no DB needed)
  * TestValidateConditionExpr_LiveCatalog covers 6 good shapes + 2
    unknown-flag cases against the live catalog
  * TestConditionExpr_AllLiveRowsValidate runs the validator over
    every active+published condition_expr in paliad.sequencing_rules
    to enforce the §4.1 invariant on every deploy (today's 18 rows
    all conform — verified via Supabase MCP pre-flight)

Live-DB tests skip cleanly when TEST_DATABASE_URL is unset (same
posture as sibling live tests in this package).

Design: docs/design-deadline-system-revision-2026-05-27.md §4.1
(grammar formalisation). t-paliad-331.
2026-05-27 15:22:53 +02:00
..