Skip to content

spec(#130): add on_result expression: matcher (§5.4)#155

Merged
AlexChesser merged 4 commits intomainfrom
claude/investigate-confidence-scores-NhS78
Apr 15, 2026
Merged

spec(#130): add on_result expression: matcher (§5.4)#155
AlexChesser merged 4 commits intomainfrom
claude/investigate-confidence-scores-NhS78

Conversation

@AlexChesser
Copy link
Copy Markdown
Owner

Adds a general-purpose expression matcher to on_result that reuses the
§12.2 condition grammar verbatim — no new syntax. Unlocks branching on
step fields other than response and exit_code (e.g. stdout,
stderr, cross-step references) without inventing a parallel grammar.

Decouples producing confidence signals (#128 native runner, #130
logprobs — deferred) from consuming them: once the grammar gains numeric
operators, expression: "{{ step.id.confidence }} >= 0.75" works
without further spec work on on_result.

Scope kept small deliberately:

  • expression: supports §12.2 operators only (==, !=, contains,
    starts_with, ends_with). Numeric comparisons and boolean combinators
    are explicitly out of scope for v0.3 and tracked as a joint §12.2 +
    §5.4 extension.
  • Unresolved template variables abort the pipeline (matches §11
    contract); silent non-match is deliberately not an option.
  • Named matchers (contains:, exit_code:, field:+equals:) remain the
    recommended form when they fit.

Cross-reference added in §12.2 so the shared grammar is discoverable
from both sides.

claude added 4 commits April 15, 2026 01:25
Adds a general-purpose expression matcher to `on_result` that reuses the
§12.2 condition grammar verbatim — no new syntax. Unlocks branching on
step fields other than `response` and `exit_code` (e.g. `stdout`,
`stderr`, cross-step references) without inventing a parallel grammar.

Decouples producing confidence signals (#128 native runner, #130
logprobs — deferred) from consuming them: once the grammar gains numeric
operators, `expression: "{{ step.id.confidence }} >= 0.75"` works
without further spec work on on_result.

Scope kept small deliberately:
- `expression:` supports §12.2 operators only (==, !=, contains,
  starts_with, ends_with). Numeric comparisons and boolean combinators
  are explicitly out of scope for v0.3 and tracked as a joint §12.2 +
  §5.4 extension.
- Unresolved template variables abort the pipeline (matches §11
  contract); silent non-match is deliberately not an option.
- Named matchers (contains:, exit_code:, field:+equals:) remain the
  recommended form when they fit.

Cross-reference added in §12.2 so the shared grammar is discoverable
from both sides.
Extends the shared condition/expression grammar with a `matches`
operator so `expression:` (§5.4) is a true superset of the named
`matches:` matcher — consistent with how `contains` is available at
both sites.

Regex semantics chosen for predictability:
- Rust `regex` crate syntax (linear-time; no backreferences/lookaround)
- Unanchored by default — `matches 'PASS'` fires on "tests PASSED";
  users add `^`/`$` for exact matching
- Case-sensitive by default (differs from `contains`/`starts_with`)
  because regex gives users explicit control via `(?i)`; this matches
  standard regex expectations rather than inventing an asymmetric rule
- Invalid regex fails at pipeline load, not at match time
- YAML single-quoting recommended to avoid backslash escaping traps

The named `matches:` in §5.4 is clarified as shorthand for the
expression form, locking down its previously-underspecified case
sensitivity before implementation work begins.
Replaces inline `(?i)`-style flags with conventional `/pattern/flags`
regex literals, matching JavaScript/Perl/Ruby convention. `/warn|error/i`
reads as what it is; `(?i)warn|error` demands the reader know a
Rust-regex-specific inline-flag grammar.

Design choices:
- Flags supported: i (case-insensitive), m (multiline), s (dotall).
- g (global) rejected at parse time — matching is boolean, "global"
  is meaningless.
- Unsupported Perl flags (x, etc.) rejected — users can still reach
  inline (?x) if they really need verbose mode.
- Parse rule: first / opens, last / followed by [ims]* closes — so
  forward slashes inside the pattern rarely need escaping.
- Single-quoted YAML strings strongly recommended so \d/\b/\s work
  as written (no YAML double-escape trap).

No change to engine choice (still Rust `regex` crate, linear-time, no
backrefs/lookaround) — just the surface syntax.
The regex spec was living inside §12.2's `matches` operator subsection,
which meant any future consumer (template filters, output_schema
patterns, etc.) would either redefine it or link into a subsection
header. Promoting it to §12.3 makes it directly referenceable.

§12.2 `matches` row: thin pointer to §12.3.
§5.4 `matches:` named-matcher row: thin pointer to §12.3.
§12.3: literal form, flags, parsing rule, matching semantics, YAML
       quoting — all in one place.

spec/README.md: §12 index entry updated to surface §12.3.

No behavior change — pure reorganisation.
@AlexChesser AlexChesser merged commit f8db27d into main Apr 15, 2026
6 checks passed
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.

2 participants