# Changelog
This workspace ships two publishable artifacts across two registries:
- `csp-solver` — the CSP solver crate (crates.io)
- `@mkbabb/csp-solver-wasm` — wasm-pack bindings for `csp-solver` (npm)
**Excised (W11/W12):** `morph-core` (crates.io) and `@mkbabb/morph` (npm) moved
to [`github.com/mkbabb/morph`](https://github.com/mkbabb/morph) at commit
`4568dc7e` (tag `pre-morph-excision`). Their `0.1.0` entries below stay as
historical record — they really did ship from this repo — but everything past
`morph-core 0.1.0` / `@mkbabb/morph 0.1.1` lives in that repo's changelog. The
general-purpose `csp_solver::assignment()` / `AssignmentBuilder` surface morph
was built on stays here; `morph` now consumes it as an ordinary crates.io
dependency (`csp-solver = "0.2"`).
## 0.5.0 — 2026-07-13 (tranche-4, W6 — generation truth: futoshiki difficulty axis)
_Staged, unpublished — the team lead owns the crates.io/`file:`-link publish._
### crates.io
- **`csp-solver@0.4.0 → 0.5.0`** — additive public surface: futoshiki grows a
`Difficulty` axis it never had (GEN-2). New exports under
`puzzles::futoshiki`: the `Difficulty` enum (`Easy`/`Medium`/`Hard`) and
`generate_futoshiki_difficulty_seeded(n, difficulty, seed)`, a keep-density +
inequality-density ladder (Easy 0.6 / Medium 0.45 / Hard 0.3 keep; carets ≈
n / 1.5n / 2n) wired through the existing `generate_futoshiki_tuned_seeded`.
Givens fall strictly Easy→Hard (15/11/8 on a 5×5), each tier unique 30/30. The
single-tier `generate_futoshiki` / `generate_futoshiki_seeded` entries are
unchanged. Minor bump per the pre-1.0 discipline (new surface across the 0.x
minor slot).
- **Solver micro-rows (W6 lane L4, output-identical)** — the §7 perf-audit rows,
each gated on byte-identical dealt boards and solve traces:
- *GENREUSE* — generation's slow hole-dig reuses one finalized CSP skeleton
across all hole candidates instead of rebuilding it per candidate. New
`puzzles::sudoku` exports `sudoku_csp_skeleton(n)` + `sudoku_given(board)`
(`create_sudoku_csp` composes them, signature unchanged). Allocations/deal
fall 7–14× (9×9-Medium 39.1k → 5.3k, 16×16-Medium 362.8k → 26.1k).
- *VALUES* — the search kernel's per-node domain snapshot uses `SmallVec`
(inline for domains ≤ 16), removing ~1 alloc/node. New dependency
`smallvec = "1"`; **+2,869 B** lean wasm (89,995 B, clears the 93 KB budget).
- *MRV* — the `Ordering::Mrv` weighted degree is precomputed once
(`ordering::precompute_var_wdeg`) instead of re-summed per node.
**BREAKING (internal API):** `ordering::select_variable` takes a `var_wdeg`
slice in place of `(constraint_weights, var_constraint_ids)`. Size-neutral.
### npm
- **`@mkbabb/csp-solver-wasm@0.4.0 → 0.5.0`** — **BREAKING**. `generateFutoshiki`
gains a `difficulty` argument: `generateFutoshiki(boardSize, seed)` →
`generateFutoshiki(boardSize, difficulty, seed)`, mirroring
`generateSudoku(n, difficulty, seed)`. New `FutoshikiDifficulty`
(`Easy`/`Medium`/`Hard`) enum export. The frontend Worker call-site is updated
in the same tranche (W6 lane L3). Minor bump encodes the breaking signature
change across the 0.x minor slot.
### npm
- **`@mkbabb/csp-solver-wasm@0.2.0 → 0.4.0`** — **BREAKING**. The `full-mirror`
feature and its `isomorphic` module are excised; the published `.d.ts` drops
`Csp`, `SolveConfig`, `SolveStats`, `OptimizationMode`, `Ordering`,
`PropagationStrategy`, `Pruning` (the generic py-mirror surface — 7 exports).
The shipped surface is now the purpose-built `sudoku` + `futoshiki` +
`assignment` layers only. No in-repo consumer is affected — the frontend
Worker imports `solveSudoku` / `solveFutoshiki` / `generate*` /
`solveAssignmentCop` only, never the isomorphic classes; the lean deploy build
never compiled the isomorphic surface, and its bytes are byte-identical
pre/post. The `0.4.0` minor bump both encodes the breaking removal across the
0.x minor slot and aligns the package to the core crate's `0.4.0` surface.
## 0.3.0 — 2026-07-10 (tranche-2, W3 — substrate excision)
### crates.io
- **`csp-solver@0.3.0`** — dead-substrate excision. Breaking (pre-1.0 minor-bump
class — a `0.2.1` would stay inside the `^0.2` (`>=0.2.0,<0.3.0`) compat range
and let the crates.io `morph` consumer (`csp-solver = "0.2"`) silently pick up
the removals; the `0.3.0` minor bump keeps that contract honest):
- Removed the deferred-driver substrate: `solver/restart.rs` (Luby),
`solver/heuristic.rs` (conflict-history weighting), `solver/nogoods.rs`
(`NogoodStore`) and their `solver/mod.rs` `pub mod` lines. The restart /
nogood / CHS driver was never wired onto the unified kernel; the modules
were inert public API.
- `Ordering::Chs` deleted (its only distinction from `Mrv` was the dynamic
conflict-history weighting that lived in the now-removed `heuristic.rs`;
weights are frozen at 1.0, so `Chs ≡ Mrv` in behavior).
- `SolveConfig::restarts` field deleted (accepted-but-inert flag; breaks
exhaustive struct literals — spread `..Default::default()`).
- The `SoftConstraint` island removed: the `SoftConstraint` trait,
`SoftLambdaConstraint` (`constraint/soft.rs`), `ConstraintEnum::Soft`, and
`Csp::add_soft_constraint`. Branch-and-bound now scores domain costs only.
`OptimizationMode` / `CostDomain` / `DomainCostEval` are **kept** — bbnf's
live `MinimizeCost` path draws cost from `CostDomain::cost` via
`DomainCostEval`, not from soft penalties.
- `Variable::clear_log` / `Variable::reset_to` deleted (dead restart-driver
helpers with no remaining caller).
- Tests: `tests/optimize.rs` trimmed to the `CostDomain` cases;
`tests/nogoods.rs` and `tests/restart_nogood_soundness.rs` deleted.
## 0.2.0 — 2026-07-06 (grand-uplift tranche, W1–W12)
### crates.io
- **`csp-solver@0.2.0`** — the kernel wave. Breaking (pre-1.0 minor-bump class):
- `Ordering::DomWdeg` → `Ordering::Mrv` (the name was a proven misnomer:
weights frozen at 1.0; `Chs ≡ DomWdeg` bit-for-bit).
- `SolveConfig::backjumping` deleted (CBJ excised with the unified search
kernel; breaks exhaustive struct literals — spread `..Default::default()`).
- `SolveConfig::default()` → `Ac3` + `FailFirst` (was ForwardChecking +
Chronological); behavior-visible for default-config consumers.
- New: unified search kernel (`solver/search.rs`), GAC-in-AllDifferent
default-ON (Régin, warm-started incremental matching; corpus aggregate
13.36×, nodes 41,807→5,948), restart/nogood substrate (driver deferred),
typed `CspError` + `py/` module with 4 typed exceptions, `cancel` +
`node_budget` + `optimization_mode` on `SolveConfig`, zero-alloc hot path,
release-guarded `BitsetDomain` 0..128 invariant, Futoshiki generation +
`FutoshikiPuzzle::from_parts` validation, crate-owned embedded puzzle data
(`include_dir!`), `AssignmentError::BudgetExceeded`.
- Excised: `min_conflicts` local search, `CardinalityConstraint`,
`ConstraintEnum::Lambda` (zero construction sites), CLI-format fixtures.
- Soundness: the AC-3 `Unsatisfiable` trail-push fix (false-UNSAT 26/113→0/113);
lattice AC-3 termination fix (default revise honors `prune`'s bool);
parity matrix 170 rows, 0 completeness violations; solution-set invariance
+ 11 restart/nogood canaries standing guard.
### npm
- **`@mkbabb/csp-solver-wasm@0.2.0`** — split surface: lean sudoku+futoshiki
deploy artifact (87,853 B raw under `--profile wasm-release
--no-default-features`) vs the `full-mirror` feature (generic `Csp` mirror,
`solveAssignmentCop`). Flat-index `Uint32Array` wires with seeded RNG; coded
errors (`BUDGET_EXCEEDED` vs `UNSAT` distinguishable); `MRV` on the wire;
vestigial `backjumping` removed; hardened difficulty-parity contract tests.
## 0.1.0 — 2026-05-28 (G.W5 — first publish)
First registry publish of the workspace, landed as part of the muster tranche G
release-engineering wave (G.W5 sub-wave A, CSC411-fold pass).
### crates.io
- **`csp-solver@0.1.0`** — generic constraint satisfaction problem solver: chronological
backtracking + conflict-directed backjumping, AC-3 + AC-FC propagation, GAC
all-different (Regin 1994), min-conflicts local search, bitset + finite + lattice
domains. Cargo.toml carries the crates.io-mandatory `description` + `license = "MIT"`
+ `repository` fields.
- **`morph-core@0.1.0`** — form alignment + landmark matching primitives, built on
`csp-solver`. Depended on `csp-solver` via a registry version pin alongside the
workspace-internal path (`csp-solver = { version = "0.1.0", path = ".." }`); the
path resolved local builds, the version resolved the crates.io edge. _Excised to
[`mkbabb/morph`](https://github.com/mkbabb/morph); `0.2.0` onward ships from there._
### npm (@mkbabb scope)
- **`@mkbabb/csp-solver-wasm@0.1.0`** — wasm-pack-emitted bindings for `csp-solver`.
Published under the `@mkbabb/*` scope; emitted JS surface is `csp_solver_wasm.{js,d.ts,_bg.wasm}`.
- **`@mkbabb/morph@0.1.0`** — wasm-pack-emitted bindings for `morph-core`. The
underlying Rust crate is named `morph`; the emitted JS surface is
`morph.{js,d.ts,_bg.wasm}`. Consumed by bbnf-buddy through the npm registry.
_Excised to [`mkbabb/morph`](https://github.com/mkbabb/morph); `0.2.0` onward
ships from there. (`0.1.1` — the next patch — was the last release from this repo.)_