Skip to main content

v3.x

Major release with breaking changes, declarative constraints, and full PICT-format model support.

Breaking Changes

  • The length option has been renamed to strength.
  • The seed option (used by sorters.hash) has been renamed to salt.
  • The default export has been removed. Use import { make } from "covertable" instead of import make from "covertable".
  • PictConstraintsLexer is no longer exported. Use the new PictModel class instead.
  • PictModel.errors (string[]) has been replaced with PictModel.issues (PictModelIssue[]). Each issue carries severity, source, index, line, and message.
  • preFilter and postFilter have been replaced by declarative constraints. See Constraint Logic and Migration Guide.

Why replace preFilter with constraints?

preFilter was a simple, flexible callback — you could write any JavaScript/Python function to accept or reject a row. So why give up that freedom?

The problem is that preFilter is a black box. The engine can only call it and observe true or false. It cannot reason about why a row was rejected, which fields the filter depends on, or whether a partial row could ever satisfy it. This leads to:

  • Blind trial and error — The engine must try every candidate and discard failures. With many constraints and factors, this becomes extremely slow.
  • No early pruning — Even when a pair like (Region=US, Currency=EUR) is obviously impossible, the engine cannot detect this until the row is fully filled.
  • No constraint propagation — Chain constraints (e.g. Language=ja → Region∈{JP,APAC}Currency∈{JPY,USD}) cannot be followed ahead of time, leading to dead-end rows that waste computation.

Declarative constraints solve this by giving the engine visibility into the constraint structure:

  • Three-valued logic — When a referenced field is not yet set, the result is null (unknown) rather than false. The engine can defer judgment instead of rejecting prematurely.
  • extractKeys() — Each constraint declares its dependencies, so the engine only re-evaluates constraints when relevant fields change.
  • Forward checking — Before committing a pair, the engine prunes the domains of unfilled factors. If any domain becomes empty, the pair is rejected without filling the entire row.
  • Initial pruning — Infeasible pairs are detected and removed before generation even starts, reducing the search space.

The result: a 22-factor, 53-constraint model (heavy.pict) that would churn for minutes with preFilter now completes in seconds with 100% pairwise coverage.

preFilter / postFilter still work for backward compatibility, but constraints is the recommended approach for v3.

New Features

  • Declarative constraints — The new constraints option accepts Condition[] objects evaluated under Kleene three-valued logic. Replaces opaque preFilter functions with structured, analyzable constraint trees.
  • Forward checking — Constraint propagation prunes infeasible pairs before and during generation. Detects constraint chain conflicts (e.g. Language=deRegion=EUCurrency IN {EUR,GBP}).
  • Peer propagation — Extends forward checking to handle multi-value domain bottlenecks.
  • Controller.stats — Generation statistics including totalPairs, prunedPairs, coveredPairs, progress, rowCount, uncoveredPairs, and completions.
  • PictModel — Parses a complete PICT-format model (parameters, sub-models, constraints, invalid values, weights) and generates rows directly. Exported from covertable/pict.
  • Sub-models — The subModels option (and PICT { A, B } @ N syntax) lets you apply a different N-wise strength to a specific group of factors.
  • Weights — The weights option (and PICT value (N) syntax) biases value selection during the row-completion phase.
  • Presets — The presets option lets you specify rows that must appear in the output. Equivalent to PICT's /e:file seeding feature.
  • Invalid values — PICT's ~value syntax marks values as negative-test inputs. At most one invalid value per row. Output includes ~ prefix.
  • Aliases — PICT's value | alias syntax lets you refer to a value by multiple names in constraints.
  • Unconditional constraints — PICT-style invariants without an IF clause (e.g. [A] <> [B];).
  • caseInsensitive optionPictModel matches PICT's default case-insensitive behavior.
  • weightsByValue helper — Convert value-keyed weights into index-keyed form.
  • Custom comparer — Override comparison operators for constraint evaluation.
  • Rescue phase — Remaining uncovered pairs are retried individually after greedy + close.
  • NeverMatch with diagnostics — When make() can't cover all pairs, it throws NeverMatch with uncoveredPairs detailing which pairs failed and which constraints are involved.

Tooling

  • Build system migrated from webpack to Vite (library mode, dual ESM/CJS output).
  • Package manager switched to pnpm.
  • TypeScript upgraded to 5.x.
  • Interactive Compatible PICT online tool with Web Worker support.