Skip to content

feat(js): auto-config verification layer (preflight + postflight) for goldenmatch-js v0.3.0#48

Merged
benzsevern merged 26 commits intomainfrom
feature/ts-autoconfig-verify
Apr 15, 2026
Merged

feat(js): auto-config verification layer (preflight + postflight) for goldenmatch-js v0.3.0#48
benzsevern merged 26 commits intomainfrom
feature/ts-autoconfig-verify

Conversation

@benzsevern
Copy link
Copy Markdown
Owner

Summary

Ports the Python v1.5.0 auto-config verification layer to the TypeScript port at `packages/goldenmatch-js/`. Ships as `goldenmatch-js-v0.3.0` on npm.

What shipped

Preflight (6 checks) — `preflight(rows, config, opts)` at end of `autoConfigureRows`:

  1. Column resolution + domain auto-repair (DBLP-ACM-class crashes become structured findings).
  2. Cardinality-high drop on exact matchkeys.
  3. Cardinality-low drop on exact matchkeys.
  4. Block-size sanity warning.
  5. Remote-asset demotion (`embedding` → `ensemble`, drop `record_embedding`, clear `rerank`). Offline-safe by default.
  6. Confidence-gated weight cap.

Postflight (4 signals) — `postflight(rows, config, {pairScores})` inside `runDedupePipeline` and `runMatchPipeline`:

  • Score histogram + bimodality detector → optional threshold auto-nudge.
  • Blocking recall — `"deferred"` sentinel (full implementation reserved for future iterative auto-config spec).
  • Preliminary cluster sizes + bottleneck pair via union-find.
  • Threshold-band overlap — advisory when > 20 % in band + LLM not enabled.

Four classifier fixes (Phase 0 — prerequisite):

  • Cardinality guard: unique-value columns → `id`, not `phone`/`zip`/`numeric`.
  • Extended `ID_NAME_PATTERNS` for `voter_reg_num`, `account_no`, `guid_`, `uuid_`.
  • New `year` col_type (routes to blocking, not scoring).
  • New `multi_name` col_type for delimited author-style fields (token_sort weight 1.0).
  • Confidence-gated weight cap (`weight <= 0.3` for fields with profile confidence < 0.5).

Config escape hatch: `GoldenMatchConfig` gains three non-readonly optional underscore fields (`_preflightReport`, `_strictAutoconfig`, `_domainProfile`). All existing public fields stay `readonly`. `stripConventionPrivate` utility strips them for YAML/JSON export. Scope-creep warning in-code.

Public API adds (top-level `goldenmatch` exports):

  • Functions: `preflight`, `postflight`, `makePreflightReport`, `stripConventionPrivate`, `ConfigValidationError`.
  • Types: `PreflightCheckName`, `Severity`, `PreflightFinding`, `PreflightReport`, `PostflightSignals`, `ScoreHistogram`, `BlockSizePercentiles`, `ClusterSizePercentiles`, `OversizedCluster`, `PostflightAdjustment`, `PostflightReport`.
  • Options: `autoConfigureRows(..., { strict?, allowRemoteAssets? })` new kwargs.
  • Result field: `DedupeResult.postflightReport?: PostflightReport` and same on `MatchResult`.

Breaking changes

  1. `autoConfigureRows` may now throw `ConfigValidationError` on unrepairable preflight findings (consistent with Python v1.5.0).
  2. `autoConfigureRows` on unique-email / unique-phone columns no longer emits an `exact_email` / `exact_phone` matchkey (Check 2 drops it as `cardinality_high`). The preflight report surfaces the drop; two pre-existing tests updated to assert the new contract.
  3. Remote-asset scorers (`embedding`, `record_embedding`) and `rerank: true` are demoted by default. Pass `allowRemoteAssets: true` to opt in.

Verification

  • 590 vitest tests pass (up from 478 pre-change; 112 new).
  • `npx tsc --noEmit` clean.
  • `npm run build` clean (tsup, 5 entrypoints).
  • Edge-safe: zero `node:*` imports in `src/core/autoconfigVerify.ts`.
  • Parity harness vs Python: 4 fixtures exported from `goldenmatch/core/autoconfig_verify.py`. Error-vs-success parity holds on all 4. Finding-key parity holds on 3; `all_same_state` documented in `KNOWN_PARITY_GAPS` (TS profiler doesn't yet flag single-value blocking columns — deferred).
  • Property-based invariants: 60+ assertions across 6 invariants × 10 seeds (histogram shape, percentile ordering, strict-mode zero adjustments, bottleneck-pair edge membership).
  • `dist/index.js` bundle size: 283,935 bytes (~277 KiB).

Planning artifacts

  • Spec: `docs/superpowers/specs/2026-04-15-ts-autoconfig-verification-design.md` (local-only per repo convention).
  • Plan: `docs/superpowers/plans/2026-04-15-ts-autoconfig-verification-implementation.md` (6 phases, 26 commits).
  • Python source spec (v1.5.0): `docs/superpowers/specs/2026-04-14-autoconfig-verification-design.md`.

Test plan

  • `npx tsc --noEmit` — zero errors.
  • `npx vitest run` — 590/590 pass.
  • `npm run build` — clean dist.
  • `examples/verificationInspection.ts` runs end-to-end.
  • `examples/strictModeParity.ts` runs end-to-end.
  • Reviewer: regenerate fixtures (`python tests/parity/export_ts_fixtures.py`) and re-run parity test to confirm.

🤖 Generated with Claude Code

Prerequisite for upcoming autoconfig verification layer (Check 6 + weight
cap in classifier fixes). Matches Python's confidence semantics:
0.9 (both heuristics agree), 0.7 (one heuristic), 0.3 (string fallthrough).
Types, dataclasses, ConfigValidationError, makePreflightReport factory,
stripConventionPrivate utility. preflight/postflight bodies stubbed;
real implementations land in Phase 2 and Phase 3.
…t to results

GoldenMatchConfig gains _preflightReport / _strictAutoconfig /
_domainProfile as non-readonly optional fields (minimum escape hatch for
strict-readonly config; see spec \u00a77). DedupeResult and MatchResult gain
optional readonly postflightReport. List is closed \u2014 future internal
state uses side-table pattern instead.
…repair

- Add DOMAIN_EXTRACTED_COLS constant to domain.ts (__brand__, __model__,
  __version__) so preflight can distinguish 'missing but producible' column
  references from hard errors.
- Replace preflight stub with a real implementation that walks every
  matchkey + blocking key reference, flags anything not present in the
  first row, and auto-repairs config.domain when _domainProfile is stashed
  on the config and a __<col>__ reference turns up.
- Keep types.ts -> autoconfigVerify.ts runtime cycle broken by using only
  'import type' from types.ts inside autoconfigVerify.ts.
… matchkeys

Drop exact matchkeys whose referenced column is either near-unique
(cardinality_ratio >= 0.99 — every row its own block) or near-constant
(ratio <= 0.01 — one giant block). Both drops are repaired warnings.
If the drops empty the matchkey list, emit a no_matchkeys_remain error
so ConfigValidationError fires downstream instead of producing a silent
no-op run.
Walks every BlockingKeyConfig, groups a <= 10k-row sample by raw field
concatenation, and warns when the p99 block size exceeds 5000 (scoring
will be slow) or the p50 is <2 (blocking is too selective, most records
end up alone). Raw-value grouping is a coarse proxy for the real
transform-applied blocker but catches the typical 'everyone has the
same state' / 'ID column used as blocker' failures.
Walks every matchkey field and demotes/drops scorers that depend on
remote model downloads:
- 'embedding' -> 'ensemble' (in-place scorer swap)
- 'record_embedding' fields dropped entirely
- weighted matchkey rerank=true -> false

Skipped entirely when allowRemoteAssets=true or llmScorer.enabled=true
(caller has already committed to remote round-trips). Matchkeys that
lose all their fields to the demotion emit remote_asset_matchkey_empty
and are removed.
…elds

Looks up each weighted matchkey field against the passed-in profiles
(optional — no-op when profiles absent). If the classifier confidence
is <0.5 and the configured weight is >0.5, cap the weight at 0.5. This
keeps a field that was classified with low confidence from dominating
the weighted score just because it happened to land with a high weight
during autoconfig construction.
- Extend AutoconfigOptions with optional strict + allowRemoteAssets flags.
- Detect domain from columns and stash _domainProfile on the config when
  confidence >0.7, so preflight Check 1 can auto-repair __<col>__ references.
- Run preflight at the end of autoConfigureRows with the profiled
  ColumnProfile list as 'profiles' and the caller's allowRemoteAssets
  setting; throw ConfigValidationError on unrepairable errors.
- Stamp _preflightReport on the returned config; stamp _strictAutoconfig
  only when strict=true.
- Update two pre-existing autoconfig tests to reflect preflight's new
  effect: exact_email / exact_phone matchkeys built from 100%-unique
  fixtures are now dropped as cardinality_high (repaired warning in
  the preflight report, weighted_identity still present).
_applyPostflight helper shared between both pipelines (mirrors Python's
_apply_postflight). isPreflightReport guard rejects stale/wrong-type
objects. Threshold adjustments apply to pairScores before clustering;
empty-pair case logs an advisory. runMatchPipeline threads postflightReport
through from the delegated runDedupePipeline result.
README: new Verification section.
Version: 0.1.0 -> 0.3.0.
JSDoc: every exported symbol in autoconfigVerify.ts documented.
Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@benzsevern benzsevern merged commit cc8ba4c into main Apr 15, 2026
@benzsevern benzsevern deleted the feature/ts-autoconfig-verify branch April 15, 2026 18:51
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 15, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

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