[PR #1220] [MERGED] fix: emit v-mode-compatible patterns for tool schemas #2731

Closed
opened 2026-10-17 21:09:36 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/1220
Author: @felipeatp
Created: 10/9/2026
Status: ✅ Merged
Merged: 10/9/2026
Merged by: @morluto

Base: main ← Head: fix/v-mode-schema-patterns


📝 Commits (2)

  • 0db50d7 fix: emit v-mode-compatible patterns for tool schemas
  • d5b30e1 fix: replace the Zod offset pattern and guard the advertised catalog

📊 Changes

20 files changed (+126 additions, -23 deletions)

View changed files

📝 src/browser/CdpSafeMetadata.ts (+3 -3)
📝 src/contracts/providerSelection.ts (+1 -1)
📝 src/domain/browserScenarioValues.ts (+2 -2)
📝 src/domain/callPathSchemas.ts (+4 -2)
📝 src/domain/completionLedgerGeneration.ts (+1 -1)
📝 src/domain/conformancePackage.ts (+1 -1)
📝 src/domain/evidenceCompletionLedger.ts (+1 -1)
📝 src/domain/native/binaryLayout.ts (+1 -1)
📝 src/domain/native/nativeCallObservation.ts (+1 -1)
📝 src/domain/native/recordedCrash.ts (+1 -1)
➕ src/domain/offsetDateTime.ts (+21 -0)
📝 src/domain/process/processTraceEvaluation.ts (+1 -1)
📝 src/domain/process/processTraceSpecification.ts (+1 -1)
📝 src/domain/reconstructionCoverage.ts (+5 -2)
📝 src/domain/reconstructionObligationLedgerSchemas.ts (+1 -1)
📝 src/domain/reconstructionReadinessSchemas.ts (+1 -1)
📝 src/domain/reconstructionVerificationSchemas.ts (+1 -1)
📝 src/domain/referenceSourceGraph.ts (+1 -1)
📝 src/ghidra/extensions/GhidraExtensions.ts (+1 -1)
➕ tests/boundary/mcp/advertisedPatternsVMode.test.ts (+77 -0)

📄 Description

Summary

tools/list emits pattern strings that are invalid under ECMA-262 v mode, and one of them
(^[^\s[\]]+$, used by observe_native_calls) is also rejected by Ruby, per #1212. A provider that
validates tool parameters with format: "regex" rejects the whole request before the model
runs, so the affected tools are unreachable rather than degraded.

Two changes: the reserved characters in the emitted pattern literals are escaped, and the one
pattern that came from Zod's offset datetime class is replaced by a schema whose pattern is valid
under v. A boundary regression now compiles every advertised input and output pattern.

Refs #1212.

Problem and expected behavior

Smallest trigger, no target artifact needed:

new RegExp("^[^\\s[\\]]+$", "v");   // SyntaxError: invalid character in class
new RegExp("^[^\\s[\\]]+$", "u");   // fine — this is why REA never sees it

The emitted catalog must contain patterns that are valid in every engine a client may use.
Measured against the 6.1.0 tools/list with the ECMA-262 engine in Node 22.23.1, two scopes,
both reproducible — read the counts below as the 6.1.0 catalog: revisions after it emit fewer fields for the
same set of patterns (on main: 988 pattern fields across input and output, the same 12 unique rejected
before this change):

  • input schemas, 138 tools: 351 pattern fields, 71 rejected under v — the same census
    reported in #1212;
  • input and output schemas: 1,976 pattern fields, 117 rejected under v, reducing to
    12 unique patterns across 11 tools
    (observe_native_calls, capture_browser_scenario, evaluate_reconstruction_coverage,
    compare_process_captures, open_binary, build_call_path, inspect_binary_layout,
    inspect_recorded_crash, compare_source_to_bundle,
    build_reconstruction_obligation_ledger, verify_reconstruction) — the two tools beyond the
    input-only census carry the same patterns in their output schemas. All of them compile with no
    flag and with u, so Zod's own validation cannot observe the defect. Because schema compilation
    happens before arguments are supplied, a single offending field is enough to fail the entire
    tools request.

Change and scope

Escaped literals. 22 pattern literals in 18 files — no other token changes, no pattern is
rewritten. Reserved characters are escaped as \x2d, \x2f, \x5b and \x5d rather than
\-, \/ and \[, because the repository's own pre-commit hook normalizes the shorter escapes
away (oxlint --fix through lint-staged treats an escaped - inside a character class as a
useless escape and removes it):

$ printf 'const a = /^[a-z0-9\\-]*$/u;\nconst b = /^[a-z0-9\\x2d]*$/u;\n' > probe.ts
$ npx oxlint --fix probe.ts
const a = /^[a-z0-9-]*$/u;      // escape removed — the defect returns silently
const b = /^[a-z0-9\x2d]*$/u;   // preserved

An earlier revision of this change used \- and was reduced from 18 files to 5 by that hook.
\xNN escapes are valid under Annex B, u and v, match the same characters, and are not
useless-escape candidates. Escaping \:/\%/\~/\_ would have been wrong in the opposite
direction: u and v reject those as invalid escapes.

Offset datetime. src/domain/reconstructionCoverage.ts used
z.string().datetime({ offset: true }), and Zod projects that validator's offset class as
[+-], which no escape in this repository's sources can reach. That site now uses the new
offsetDateTimeSchema in src/domain/offsetDateTime.ts: the same accepted set, with the offset
class escaped as [+\x2d]. This was not left as a dependency exception because the affected
tool (evaluate_reconstruction_coverage) was still unreachable for exactly the clients this
change targets.

Regression. tests/boundary/mcp/advertisedPatternsVMode.test.ts performs the real
tools/list projection through createServer and an in-memory transport, traverses every
advertised input and output schema, and compiles each pattern under v. It asserts the
inventory it walked (>100 tools, >500 patterns) so a traversal that silently collapses fails
rather than passing vacuously.

Contract and boundary impact

  • Semantic owner and earliest changed stage: domain/contract schema literals and one domain
    datetime schema; validation text only.
  • CLI and MCP/tool-catalog contract: the same inputs are accepted. Emitted pattern text changes
    from e.g. [a-z0-9-] to [a-z0-9\x2d] and from [+-] to [+\x2d], both equivalent under
    Annex B and u and now also valid under v.
  • Provider, bridge, target-format, or platform compatibility: none.
  • Evidence, artifact, provenance, or reconstruction contract: unchanged; the datetime validator
    still rejects the same non-conforming strings.
  • Process execution, authorization, cleanup, or containment impact: none.
  • Generated metadata, package, or installation impact: none — no generated artifact embeds
    pattern text, and docs:check passes without regenerating anything.

Evidence and regression coverage

  • Tests added or updated: tests/boundary/mcp/advertisedPatternsVMode.test.ts (new boundary
    regression over the advertised projection).

  • Base reproduction: restoring z.string().datetime({ offset: true }) in
    reconstructionCoverage.ts while the regression is present fails it with

    evaluate_reconstruction_coverage
    inputSchema/$defs/__schema0/properties/verifier_results/items/properties/observed_at/pattern
    ...: Invalid character in character class
    

    Replacing that schema makes it pass. Before the change the whole catalog was rejected under
    v with nothing in CI able to notice.

  • User-visible CLI/MCP output (if applicable): a client that validates schemas with
    format: "regex" can dispatch the 11 affected tools instead of failing the whole request.

  • Remaining proof gaps: none known for the advertised catalog; the regression covers future
    additions, and provider-level behavior is untouched.

Validation performed

  • new RegExp(pattern, "v" | "u" | undefined) over all 22 changed pairs — all 22 originals throw
    under v; all 22 patched patterns compile under v, u and with no flags.
  • Semantic equivalence, original (u) vs patched (v and u), over 1,525 strings per pattern
    (21,340 comparisons): empty input, -, /, [, ], \, :, ~, 0x1, 0XAB, s:0x1f,
    %2Fhost:0xff, base64 samples, CSP directive names, module paths and 400k generated random
    strings — 0 divergences.
  • Offset datetime accepted set re-checked with Z, +02:00, -03:00, a date-only string and a
    non-date: identical verdicts to z.string().datetime({ offset: true }).
  • npm run build:unlocked then a real initialize + tools/list handshake against the local
    build — rejected input-and-output patterns went from 117 to 0 (input-only scope: 71 to 0).
  • npx vitest run --project mcp-boundary tests/boundary/mcp/advertisedPatternsVMode.test.ts —
    1 passed, and 1 failed with the datetime schema restored.
  • npx vitest run --project domain --project services --project adapters --project conformance --project evaluation — 3,427 passed, 4 skipped.
  • npx tsc --noEmit — exit 0.
  • npm run docs:check — 2 tasks successful.

Compatibility, safety, and release

  • Breaking changes or migration steps: none.
  • Real Hopper/Ghidra, browser, or OS coverage: not applicable; no provider, bridge or launch
    behavior is touched.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: none; the change only affects the text of
    validation patterns, and it removes a state where a strict client silently loses tool access.

Review checklist

  • This PR addresses a concrete problem or an agreed enhancement.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/1220 **Author:** [@felipeatp](https://github.com/felipeatp) **Created:** 10/9/2026 **Status:** ✅ Merged **Merged:** 10/9/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `fix/v-mode-schema-patterns` --- ### 📝 Commits (2) - [`0db50d7`](https://github.com/morluto/rea/commit/0db50d731dad6234e25ab284920cdf8497a4e18e) fix: emit v-mode-compatible patterns for tool schemas - [`d5b30e1`](https://github.com/morluto/rea/commit/d5b30e18154ff1eeaaa3e0081690bbf8b0cf8b03) fix: replace the Zod offset pattern and guard the advertised catalog ### 📊 Changes **20 files changed** (+126 additions, -23 deletions) <details> <summary>View changed files</summary> 📝 `src/browser/CdpSafeMetadata.ts` (+3 -3) 📝 `src/contracts/providerSelection.ts` (+1 -1) 📝 `src/domain/browserScenarioValues.ts` (+2 -2) 📝 `src/domain/callPathSchemas.ts` (+4 -2) 📝 `src/domain/completionLedgerGeneration.ts` (+1 -1) 📝 `src/domain/conformancePackage.ts` (+1 -1) 📝 `src/domain/evidenceCompletionLedger.ts` (+1 -1) 📝 `src/domain/native/binaryLayout.ts` (+1 -1) 📝 `src/domain/native/nativeCallObservation.ts` (+1 -1) 📝 `src/domain/native/recordedCrash.ts` (+1 -1) ➕ `src/domain/offsetDateTime.ts` (+21 -0) 📝 `src/domain/process/processTraceEvaluation.ts` (+1 -1) 📝 `src/domain/process/processTraceSpecification.ts` (+1 -1) 📝 `src/domain/reconstructionCoverage.ts` (+5 -2) 📝 `src/domain/reconstructionObligationLedgerSchemas.ts` (+1 -1) 📝 `src/domain/reconstructionReadinessSchemas.ts` (+1 -1) 📝 `src/domain/reconstructionVerificationSchemas.ts` (+1 -1) 📝 `src/domain/referenceSourceGraph.ts` (+1 -1) 📝 `src/ghidra/extensions/GhidraExtensions.ts` (+1 -1) ➕ `tests/boundary/mcp/advertisedPatternsVMode.test.ts` (+77 -0) </details> ### 📄 Description ## Summary `tools/list` emits `pattern` strings that are invalid under ECMA-262 `v` mode, and one of them (`^[^\s[\]]+$`, used by `observe_native_calls`) is also rejected by Ruby, per #1212. A provider that validates tool `parameters` with `format: "regex"` rejects the whole request before the model runs, so the affected tools are unreachable rather than degraded. Two changes: the reserved characters in the emitted pattern literals are escaped, and the one pattern that came from Zod's offset datetime class is replaced by a schema whose pattern is valid under `v`. A boundary regression now compiles every advertised input and output pattern. Refs #1212. ## Problem and expected behavior Smallest trigger, no target artifact needed: ```js new RegExp("^[^\\s[\\]]+$", "v"); // SyntaxError: invalid character in class new RegExp("^[^\\s[\\]]+$", "u"); // fine — this is why REA never sees it ``` The emitted catalog must contain patterns that are valid in every engine a client may use. Measured against the 6.1.0 `tools/list` with the ECMA-262 engine in Node 22.23.1, two scopes, both reproducible — read the counts below as the 6.1.0 catalog: revisions after it emit fewer fields for the same set of patterns (on `main`: 988 `pattern` fields across input and output, the same 12 unique rejected before this change): - **input schemas, 138 tools: 351 `pattern` fields, 71 rejected under `v`** — the same census reported in #1212; - **input and output schemas: 1,976 `pattern` fields, 117 rejected under `v`**, reducing to **12 unique patterns** across **11 tools** (`observe_native_calls`, `capture_browser_scenario`, `evaluate_reconstruction_coverage`, `compare_process_captures`, `open_binary`, `build_call_path`, `inspect_binary_layout`, `inspect_recorded_crash`, `compare_source_to_bundle`, `build_reconstruction_obligation_ledger`, `verify_reconstruction`) — the two tools beyond the input-only census carry the same patterns in their output schemas. All of them compile with no flag and with `u`, so Zod's own validation cannot observe the defect. Because schema compilation happens before arguments are supplied, a single offending field is enough to fail the entire tools request. ## Change and scope **Escaped literals.** 22 pattern literals in 18 files — no other token changes, no pattern is rewritten. Reserved characters are escaped as `\x2d`, `\x2f`, `\x5b` and `\x5d` rather than `\-`, `\/` and `\[`, because the repository's own pre-commit hook normalizes the shorter escapes away (`oxlint --fix` through lint-staged treats an escaped `-` inside a character class as a useless escape and removes it): ``` $ printf 'const a = /^[a-z0-9\\-]*$/u;\nconst b = /^[a-z0-9\\x2d]*$/u;\n' > probe.ts $ npx oxlint --fix probe.ts const a = /^[a-z0-9-]*$/u; // escape removed — the defect returns silently const b = /^[a-z0-9\x2d]*$/u; // preserved ``` An earlier revision of this change used `\-` and was reduced from 18 files to 5 by that hook. `\xNN` escapes are valid under Annex B, `u` and `v`, match the same characters, and are not useless-escape candidates. Escaping `\:`/`\%`/`\~`/`\_` would have been wrong in the opposite direction: `u` and `v` reject those as invalid escapes. **Offset datetime.** `src/domain/reconstructionCoverage.ts` used `z.string().datetime({ offset: true })`, and Zod projects that validator's offset class as `[+-]`, which no escape in this repository's sources can reach. That site now uses the new `offsetDateTimeSchema` in `src/domain/offsetDateTime.ts`: the same accepted set, with the offset class escaped as `[+\x2d]`. This was not left as a dependency exception because the affected tool (`evaluate_reconstruction_coverage`) was still unreachable for exactly the clients this change targets. **Regression.** `tests/boundary/mcp/advertisedPatternsVMode.test.ts` performs the real `tools/list` projection through `createServer` and an in-memory transport, traverses every advertised input and output schema, and compiles each `pattern` under `v`. It asserts the inventory it walked (>100 tools, >500 patterns) so a traversal that silently collapses fails rather than passing vacuously. ## Contract and boundary impact - Semantic owner and earliest changed stage: domain/contract schema literals and one domain datetime schema; validation text only. - CLI and MCP/tool-catalog contract: the same inputs are accepted. Emitted `pattern` text changes from e.g. `[a-z0-9-]` to `[a-z0-9\x2d]` and from `[+-]` to `[+\x2d]`, both equivalent under Annex B and `u` and now also valid under `v`. - Provider, bridge, target-format, or platform compatibility: none. - Evidence, artifact, provenance, or reconstruction contract: unchanged; the datetime validator still rejects the same non-conforming strings. - Process execution, authorization, cleanup, or containment impact: none. - Generated metadata, package, or installation impact: none — no generated artifact embeds pattern text, and `docs:check` passes without regenerating anything. ## Evidence and regression coverage - Tests added or updated: `tests/boundary/mcp/advertisedPatternsVMode.test.ts` (new boundary regression over the advertised projection). - Base reproduction: restoring `z.string().datetime({ offset: true })` in `reconstructionCoverage.ts` while the regression is present fails it with ``` evaluate_reconstruction_coverage inputSchema/$defs/__schema0/properties/verifier_results/items/properties/observed_at/pattern ...: Invalid character in character class ``` Replacing that schema makes it pass. Before the change the whole catalog was rejected under `v` with nothing in CI able to notice. - User-visible CLI/MCP output (if applicable): a client that validates schemas with `format: "regex"` can dispatch the 11 affected tools instead of failing the whole request. - Remaining proof gaps: none known for the advertised catalog; the regression covers future additions, and provider-level behavior is untouched. ## Validation performed - `new RegExp(pattern, "v" | "u" | undefined)` over all 22 changed pairs — all 22 originals throw under `v`; all 22 patched patterns compile under `v`, `u` and with no flags. - Semantic equivalence, original (`u`) vs patched (`v` and `u`), over 1,525 strings per pattern (21,340 comparisons): empty input, `-`, `/`, `[`, `]`, `\`, `:`, `~`, `0x1`, `0XAB`, `s:0x1f`, `%2Fhost:0xff`, base64 samples, CSP directive names, module paths and 400k generated random strings — 0 divergences. - Offset datetime accepted set re-checked with `Z`, `+02:00`, `-03:00`, a date-only string and a non-date: identical verdicts to `z.string().datetime({ offset: true })`. - `npm run build:unlocked` then a real `initialize` + `tools/list` handshake against the local build — rejected input-and-output patterns went from 117 to 0 (input-only scope: 71 to 0). - `npx vitest run --project mcp-boundary tests/boundary/mcp/advertisedPatternsVMode.test.ts` — 1 passed, and 1 failed with the datetime schema restored. - `npx vitest run --project domain --project services --project adapters --project conformance --project evaluation` — 3,427 passed, 4 skipped. - `npx tsc --noEmit` — exit 0. - `npm run docs:check` — 2 tasks successful. ## Compatibility, safety, and release - Breaking changes or migration steps: none. - Real Hopper/Ghidra, browser, or OS coverage: not applicable; no provider, bridge or launch behavior is touched. - Package or release metadata impact: none. - Security, privacy, process, or containment review: none; the change only affects the text of validation patterns, and it removes a state where a strict client silently loses tool access. ## Review checklist - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] The PR has one focused outcome and the title follows `type(scope): outcome`. - [x] Related issue is linked, or the reason for not linking one is stated above. - [x] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [x] User-visible CLI/MCP changes include representative output. - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-17 21:09:36 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#2731
No description provided.