Skip to content

Stage 2d-1 — serializable matcher AST + fail-closed classifier (DAN-605) - #2

Merged
Danny-Devs merged 1 commit into
mainfrom
tackle/dan-605-matcher-ast
Jul 21, 2026
Merged

Stage 2d-1 — serializable matcher AST + fail-closed classifier (DAN-605)#2
Danny-Devs merged 1 commit into
mainfrom
tackle/dan-605-matcher-ast

Conversation

@Danny-Devs

@Danny-Devs Danny-Devs commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Goal

Query filters become serializable data: a JSON-pure matcher AST (eq/neq, ordered ops, in/nin, exists, and/or/not) with builder M, a strict coercion-free evaluator, canonical sorted-key serialization, and the fail-closed two-tier gate classifyFilter — the substrate live queries (roadmap 2.2) re-evaluate against change events, and the shape agent surfaces can safely accept. Closes audit blocker #7 (DAN-575 council audit).

Design law

encodable is returned ONLY for input that fully validates; the verdict carries the canonical frozen tree so consumers never re-trust raw input. Closures, unknown operators, malformed shapes, over-deep/over-budget trees → opaque (a re-run is always correct; a missed update never is). Normative semantics table in docs/design/matcher-semantics.md = the contract the DAN-579 worker SQL tier compiles against. ADR-009 filed.

Gate evidence

  • CI=true pnpm test238/238 (29 new matcher tests incl. adversarial catalog: trap bombs, revoked proxies, depth bombs, index-planted getters asserted UNREAD, prototype pollution, aggregate-cost DoS)
  • pnpm typecheck / pnpm lint / pnpm build → clean
  • Observe-run: drove the built dist/index.mjs through the agent-boundary path (JSON wire → classify → evaluate) — PASS

Review verdicts

  1. Adversarial pre-handoff review: 0 blocking behavioral findings (fail-closed, TOCTOU-freedom, depth cap, semantics coherence all held); 2 advisory + 4 nits fixed in-branch (Proxy-containment claim scoped honestly, -0 normalization, non-enumerable key refusal).
  2. Land-stage security gauntlet (escalated — parses untrusted input): LAND, 0 blocking; serialization proven injective (no query-key collision, 5000-tree fuzz); one ADVISORY fixed in-branch (MATCHER_MAX_COST aggregate budget — over-budget routes to opaque, zero correctness loss).

Linear: DAN-605 (parent DAN-579 / epic DAN-575)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a serializable matcher system supporting comparisons, membership checks, existence checks, grouping, and negation.
    • Added strict evaluation with canonical serialization for stable query representation.
    • Added fail-closed classification for unsupported or unsafe filters.
  • Security

    • Added validation, input-size limits, prototype-pollution protections, and safe handling of accessors and proxies.
  • Documentation

    • Added design documentation covering matcher semantics, serialization, and security behavior.
  • Tests

    • Added comprehensive tests for evaluation, parsing, security boundaries, classification, and serialization.

…605, ADR-009)

Filters become data: JSON-pure AST (eq/neq, ordered, in/nin, exists,
and/or/not) with builder M, strict coercion-free evaluator (own-props
only, SQL-NULL absence discipline), canonical sorted-key serialization,
and the two-tier classifyFilter gate — encodable ONLY for input that
fully validates; closures/unknown ops/malformed trees route opaque
(a re-run is always correct; a missed update never is).

Untrusted-input hardening: accessor properties rejected unread, proto
field names refused, depth cap 32, list cap 10K, frozen fresh copies.

Normative semantics table in docs/design/matcher-semantics.md = the
contract the DAN-579 worker SQL tier compiles against.

Unblocks live queries (roadmap 2.2). Audit blocker #7 closed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@linear

linear Bot commented Jul 21, 2026

Copy link
Copy Markdown
DAN-605 Stage 2d-1 — serializable matcher AST + fail-closed classifier (audit blocker #7)

First slice of DAN-579 (scope item 1 — "the real first deliverable"). Split out 2026-07-20 per Danny's lane pick: the AST is a pure additive module with unit-test verification; the worker query tier (ADR-003 amendment, real-browser verification) and pinned result sets stay in DAN-579.

Goal

Query filters become serializable data: a matcher AST with a strict in-memory evaluator and a classifier that fails CLOSED, so live queries (roadmap 2.2) can maintain filtered views against change-set events instead of re-running opaque closures.

Context

Constraints

  • Fail CLOSED is the design law: classify() returns encodable only when the filter is positively provable as fully evaluable. JS closures, unknown operators, malformed shapes, over-depth trees → opaque (or typed parse error). A false encodable = silent missed updates downstream — the audit's worst failure mode.
  • Additive only. New module (src/matcher.ts + spec). The Phase-0-frozen event shape, engines, worker protocol, transactions, and persist are untouched.
  • Strict semantics, zero coercion. null vs undefined vs missing-field behavior explicitly defined; no numeric-string coercion. Document a semantics table (in the ADR or docs/) precise enough that the future worker SQL tier can implement identical semantics — evaluator/SQL divergence is the known trap.
  • Untrusted-input safe. Parsing a JSON AST never executes code; depth cap; reject prototype-pollution-shaped keys.
  • New ADR for the matcher-AST design (append-only, next free number; note the roadmap's informal "ADR-009 = worker read tier" mapping if numbering shifts).
  • Spec-heavy methodology (trust-surface foundation), per house problem-class rule.
  • DO NOT TOUCH: sqlite-worker.ts, src/engines/*, transactions.ts, persist.ts, store.ts event emission, existing ADRs (append-only), anything in the plugin repo.

Discretion granted

Exact operator vocabulary (recommend a WatermelonDB-informed minimum: eq/neq/gt/gte/lt/lte/in/nin/and/or/not, field-exists; add more only with evaluator+test coverage), builder-API ergonomics, error shape (match existing repo patterns until the M6 error taxonomy exists), ADR number, file naming.

Acceptance criteria

  • AST types + builder exported from src/index.ts
  • Pure evaluate(ast, entity) → boolean, total over all supported ASTs, strict semantics per the documented table
  • classify(filter) → 'encodable' | 'opaque' failing CLOSED: closures → opaque; any unknown op/shape/depth → opaque or typed error; never falsely encodable
  • JSON round-trip stable; untrusted-AST validation rejects malformed input with a typed error and no code execution
  • Adversarial tests: unknown-operator injection, function-valued filters, depth bomb, __proto__-shaped keys, null-vs-missing-field, numeric-string non-coercion
  • ADR filed; CHANGELOG entry appended

Verification (DoR gate)

cd core/projects/colada-db-project/colada-db && CI=true pnpm test && pnpm typecheck && pnpm lint && pnpm build

Expected: all suites green including new matcher spec covering every adversarial case above; zero typecheck/lint errors; build clean. (No browser observe-run required — no storage-engine change; that requirement returns with the worker tier chip.)

Blast radius

This chip: additive, dev-only — no existing behavior touched. Downstream severity HIGH: a wrong classifier verdict later means silently stale live-query results, which is why the adversarial test set is done-defining now rather than deferred.

Review in Linear

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Free

Run ID: 1c0d3736-58b1-4ada-b766-2f8f70a1a87e

📥 Commits

Reviewing files that changed from the base of the PR and between 5914ab7 and 232bd2d.

📒 Files selected for processing (6)
  • CHANGELOG.md
  • docs/adr/009-serializable-matcher-ast.md
  • docs/design/matcher-semantics.md
  • src/index.ts
  • src/matcher.spec.ts
  • src/matcher.ts

📝 Walkthrough

Walkthrough

Adds a serializable JSON-pure matcher AST with fail-closed parsing and classification, strict evaluation semantics, canonical serialization, typed builders, public exports, design documentation, and adversarial Vitest coverage.

Changes

Matcher AST subsystem

Layer / File(s) Summary
Matcher contracts and semantics
src/matcher.ts, docs/design/matcher-semantics.md, docs/adr/009-serializable-matcher-ast.md
Defines matcher node types, parse limits and errors, strict operator semantics, serialization rules, and the fail-closed classification design.
Validation and fail-closed classification
src/matcher.ts, src/matcher.spec.ts
Validates untrusted inputs into fresh frozen ASTs, rejects unsafe shapes and resource overages, and classifies parse or inspection failures as opaque.
Evaluation, serialization, builders, and integration
src/matcher.ts, src/matcher.spec.ts, src/index.ts, CHANGELOG.md
Adds strict evaluation, canonical serialization, typed matcher builders, public exports, and tests for builder, evaluator, serializer, proxy, and round-trip behavior.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant classifyFilter
  participant parseMatcher
  participant evaluateMatcher
  participant serializeMatcher
  Client->>classifyFilter: Submit matcher input
  classifyFilter->>parseMatcher: Validate and normalize
  parseMatcher-->>classifyFilter: Encodable AST or parse failure
  classifyFilter-->>Client: AST classification
  Client->>evaluateMatcher: Evaluate AST against entity
  evaluateMatcher-->>Client: Boolean result
  Client->>serializeMatcher: Serialize validated AST
  serializeMatcher-->>Client: Canonical JSON string
Loading

Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login.

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

@Danny-Devs
Danny-Devs merged commit 99486f0 into main Jul 21, 2026
1 check was pending
@Danny-Devs
Danny-Devs deleted the tackle/dan-605-matcher-ast branch July 21, 2026 05:55
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