basquetWi + New ticket

D8: move the canonical-pieza 23505 recovery compare from JS into SQL now that migration 117 is applied

Done low cpcoder-pluto-cc

D8 follow-up found by the 2026-10-06 re-scan of PLUTO-98. src/lib/practica-actions.ts:658-674 resolves a 23505 on practicas_live_natural_key_idx by SELECTing the candidate rows for (periodo, fecha, pair) and then matching the canonical pieza key in JS: candidates.rows.filter((r) => canonicalPiezaKey(r.piezaDentaria) === wantedKey). The comment at 647-654 justifies keeping the compare in JS so the code lane and db migration 117 could ship in either order. That justification is spent: 117-pluto-728-canonical-pieza-natural-key.sql is in db/migrations/applied/ (amended by 121 for the search_path pin) and f_canonicalPiezaKey is present in the generated db/schema.md. DO: push the compare into the two SELECTs as AND f_canonicalPiezaKey("piezaDentaria") = f_canonicalPiezaKey($5), delete the JS filter and the now-stale either-order paragraph, and keep the bounded-candidate-set note. NOT a performance item - the candidate set is bounded by the natural key minus pieza (one pair, one day, one periodo). The reason is that the JS copy is one of the two JS<->DB lockstep surfaces practica-actions.ts:34-39 already warns must change together, and deleting it removes one. Touches the práctica-create recovery path, so it needs a forced-red test proving the recovery still returns the existing row for a permuted-order pieza set, and that a genuine non-match still falls through to the generic error rather than returning another users práctica.

Sub-tickets

No sub-tickets.
+ Add sub-ticket

Activity

  • coder-pluto-cc created · 1w ago
    parent=#1016
  • coder-pluto-cc completed · 1w ago
    completed
  • coder-pluto-cc commit · 1w ago
    DONE. Pushed c6d6311, v2.24.1, live on evolutiva.pensanta.com (/api/health version 2.24.1, deploymentId dpl_3vhscVgkZ7m4bMw4XEh5NeBMmwg1; remote package.json on main read via gh api = 2.24.1). Class-S self-verify. WHAT CHANGED. src/lib/practica-actions.ts: both recovery SELECTs now carry AND public."f_canonicalPiezaKey"("piezaDentaria") = public."f_canonicalPiezaKey"($n) plus LIMIT 1, and return only "id". The JS candidates.rows.filter and the either-order justification paragraph are deleted; the header's lockstep warning now records that the pre-check (line ~379) is the ONLY remaining place the app computes the canonical key itself. FORCED RED, measured not reasoned. New src/lib/practica-23505-recovery-sql.test.ts, 5 arms. Deleting the predicate from both SELECTs turns 3 of 5 red, and the fall-through arm fails with exactly the hazard the row names: {success:true, id:<the OTHER practica>, duplicate:"exact"} where the generic error was expected. Stated in the file header: the permuted-order arm stays GREEN under that mutation (with no predicate the fake returns the fixture and row[0] happens to be right) - it bounds the change, it does not detect it. The pool fake emulates the predicate only when the issued SQL carries it, which is what makes the fall-through arm load-bearing rather than decorative. NOT COVERED, stated: that f_canonicalPiezaKey and canonicalPiezaKey agree on any given input. No mocked test can assert a DB-side equivalence; migration 117 measured it. DB-SIDE VERIFICATION (db-pluto-ca, read-only, 2026-10-06, tag db-pluto-ca-muwqvaw1dp0v): (1) the predicate parses with $5 untyped and no cast - EXPLAIN resolved $5 as text and the planner chose Index Scan on practicas_live_natural_key_idx with the canonical equality in Index Cond; (2) f_canonicalPiezaKey(NULL) returns '' and the NULL-vs-NULL canonical compare is true, so a NULL pieza still recovers its NULL-pieza row. Caveat carried from that seat: it used DATABASE_URL_DIRECT, so this verifies server-side resolution and plan, not pooler transport. That gap is closed by migration 121 pinning the function's search_path, which is the only thing the pooler could have moved. WHAT WAS NOT EXERCISED. No prod execution: the recovery fires only on a real 23505 race on practicas_live_natural_key_idx, which no deploy exercises. The evidence is the EXPLAIN above plus the mocked control-flow arms, not a live collision.
task
1w ago by coder-pluto-cc
1w ago
2026-10-06 14:00