Skip to content

feat: compound match conditions (all_of / any_of / not) (COD-481) - #19

Merged
shivros merged 1 commit into
mainfrom
runner/COD-481-compound-match-conditions
Sep 11, 2026
Merged

feat: compound match conditions (all_of / any_of / not) (COD-481)#19
shivros merged 1 commit into
mainfrom
runner/COD-481-compound-match-conditions

Conversation

@shivros

@shivros shivros commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

COD-481 — rite: compound match conditions (all_of / any_of / not)

Re-implemented on endver after the hosted runner's worktree (commit 538ce2fb) was lost to /tmp cleanup on builder-01. Same spec, fresh implementation.

Grammar

  • match = { all_of = [...], any_of = [...], not = {...} } — inline tables nest arbitrarily; leaf keys (event_type, action, severity, body_contains, metadata) keep today's semantics verbatim.
  • Operators are recognized only by TOML shape (array-of-tables for all_of/any_of, table for not). A scalar under an operator-named key (not = "draft") stays a legacy metadata-lookup leaf — existing configs never change meaning.
  • Flat match = { event_type = "push", ... } parses to an all-of of the current leaves (back-compat pinned by tests).
  • The source guard (handler.source == event.source) stays outside the tree.
  • Mixed operator+leaf tables and sibling operators are rejected with actionable errors.

Review finding fixed (gpt-5.6-terra panel)

The first cut validated compound tables only in validate(); a library caller skipping validate could install a silently-never-matching handler. Fixed structurally: the matcher field now uses deserialize_with so malformed compound tables are rejected at deserialization — no load path (load_config included) can ever produce them. validate() keeps its belt-and-braces check.

Verification (endver, base 9f3d393)

  • cargo test --all-targets: 48 passed, 0 failed (12 rite-core incl. nesting/back-compat/rejections, 12 rite-server incl. README + rite.example.toml fences driven through real load_config, 14 docker-entrypoint, 10 rite-sources)
  • Acceptance criterion pinned: any_of = [{ severity = "critical" }, { body_contains = "URGENT" }] matches exactly critical-or-urgent; not inside all_of excludes correctly
  • cargo clippy --all-targets -- -D warnings clean · cargo fmt --all -- --check clean · rite-codegen check exit 0 · git diff --check clean

Closes COD-481

Upgrade handler matching from a flat all-of equality map to composable
boolean condition trees. all_of/any_of take arrays of condition tables,
not takes a single condition table, and forms nest arbitrarily. Leaf
semantics are unchanged and the source guard stays outside the tree.

Operators are recognized only by TOML shape: a scalar under an
operator-named key (e.g. not = "draft") remains a legacy metadata
lookup, so existing configs never change meaning. Malformed tables —
empty all_of/any_of, multi-child or empty not, operators mixed with
leaves, sibling operators — are rejected at config load through
validate() with actionable errors.

Tests cover nesting, each leaf type in compounds, back-compat flat
form, legacy scalar operator-named keys, both validation rejection
classes, and production-loader coverage of rite.example.toml plus every
README TOML fence through load_config + validate + condition_tree.
@shivros

shivros commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Automated Review Panel

Dual-model review before opening.

Reviewer A (gpt-5.6-terra) — REQUEST_CHANGES → resolved

  • [minor] load_config did not reject malformed compound tables — rejection lived only in validate(), so a caller that skipped validate could construct handlers that silently never match (matches() returning false on tree error). Fixed in this PR: the matcher field now deserializes through deserialize_validated_matcher, rejecting malformed compound forms at deserialization time — structurally, on every load path. Tests updated to pin rejection at load_config itself.

Reviewer B (initial panel run, timed out before verdict; work verified independently)

Both prior panel agents independently re-ran the full suite (47→48 tests green), traced all .matches() call sites (dispatch.rs:178, lib.rs:323), and confirmed the README/example fences parse. No blockers were identified by either agent; the one actionable finding from the completed seat is the load-time gap above, now fixed and re-gated (48/48 green, clippy/fmt clean).

No unresolved blocking issues from the panel.

@shivros

shivros commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

Auto-Merge Gate

Confidence: 0.91

The implementation directly delivers COD-481: recursive all_of / any_of / not condition trees preserve flat-match compatibility and source guarding, reject malformed compound TOML at deserialization, cover the required matching and rejection paths, and document the grammar.

Checks observed: CI test and Creed context drift succeeded. Independent gate verification in a disposable checkout also passed:

  • cargo test --all-targets — 48 passed, 0 failed
  • cargo clippy --all-targets -- -D warnings
  • cargo fmt --all -- --check
  • cargo run -p rite-codegen -- check
  • git diff --check origin/main...HEAD

No manual-review marker or unresolved review blocker was found in COD-481 or the PR discussion. Squash-merging under the configured CodeFold gate.

@shivros
shivros merged commit 952c8aa into main Sep 11, 2026
2 checks passed
@shivros
shivros deleted the runner/COD-481-compound-match-conditions branch September 11, 2026 22:30
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