diff --git a/AGENTS.md b/AGENTS.md index f5f687a98..ed9c57ea8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -273,7 +273,7 @@ stops startup instead of leaving a healthy-looking partial schema, and application code must not compensate for a missing table. Period leftover pairs (ADR 0017 / 0018 / 0048 / 0049 / 0119 / 0158 / 0162 / -0163 / 0164 / 0182 / 0185 / 0201 / 0233 / 0266 / 0267 / 0268 / 0269 / 0270 / 0271 / 0272 / 0273 / 0274 / 0275 / 0276 / 0277 / 0278 / 0279 / 0280 / 0281 / 0282 / 0283 / 0284 / 0285 / 0286 / 0287 / 0288) are computed in `lineageweave/leftover_pairs.py` from the +0163 / 0164 / 0182 / 0185 / 0201 / 0233 / 0266 / 0267 / 0268 / 0269 / 0270 / 0271 / 0272 / 0273 / 0274 / 0275 / 0276 / 0277 / 0278 / 0279 / 0280 / 0281 / 0282 / 0283 / 0284 / 0285 / 0286 / 0287 / 0288 / 0289) are computed in `lineageweave/leftover_pairs.py` from the residual after a real GRM/GPCM score, never invented. Distances are Euclidean on the two-dimensional Gabriel leftover map; missing cells stay out of the factorization. Closest and farthest post–criterion pairs @@ -385,6 +385,15 @@ non-integer payload cannot caption a contradictory `used N of M scored posts` note; a missing, non-integer, negative-used, non-positive-scored, or used-greater-than-scored coverage omits that leftover-map coverage note and does not invent post coverage from plotted marker count. +ADR 0289 captions the grouping comparison strip with persisted leftover-map +post complete-case coverage through leftoverMapCoverageCounts so a buyer who +compares leftover pairs can read how many scored posts entered each grouping's +Gabriel factorization; a missing, non-integer, negative-used, non-positive-scored, +or used-greater-than-scored coverage omits that leftover-map comparison coverage +note and does not invent post coverage from leftover pair count or plotted +marker count. The strip does not gain leftover-map item coverage, leftover-map +incomplete post coverage, leftover-map incomplete item coverage, or the +leftover-map graphic. When `R`, `R̂`, `U`, `x`, `s`, and `e` are finite, `e + s + x = 1`. When `Y`, `E`, and `R` are finite, `Y − E = R`. When `R`, `R̂`, and `U` are @@ -399,6 +408,8 @@ and are not a leftover score. Complete-case coverage (ADR 0168) persists to `report_leftover_map_coverage` and captions the pair list and the leftover-map graphic with how many scored posts entered the map. Pair-list post complete-case coverage (ADR 0288) fail-closes that pair-list note through leftoverMapCoverageCounts. +Grouping comparison complete-case coverage (ADR 0289) captions the grouping +comparison strip with how many scored posts entered each grouping's map. Item complete-case coverage (ADR 0282) captions the leftover-map graphic with how many scored criteria entered the map. Item complete-case coverage on the pair list (ADR 0285) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index d741206c1..4d885b575 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -637,7 +637,7 @@ information at the group's mean θ (Lord, 1980 max-info CAT). Rankings persist to `report_item_information`. After those IRT main effects, residual SVD leftover pairs on two Gabriel axes (Jeon et al., 2021; ADR 0017 / 0048 / 0049 / 0119 / 0148 / 0158 / 0162 / 0163 / 0164 / 0168 / -0182 / 0185 / 0201 / 0233 / 0266 / 0267 / 0268 / 0269 / 0270 / 0271 / 0272 / 0273 / 0274 / 0275 / 0276 / 0277 / 0278 / 0279 / 0280 / 0281 / 0282 / 0283 / 0284 / 0285 / 0286 / 0287 / 0288) persist to `report_leftover_pair` with signed residual `R`, +0182 / 0185 / 0201 / 0233 / 0266 / 0267 / 0268 / 0269 / 0270 / 0271 / 0272 / 0273 / 0274 / 0275 / 0276 / 0277 / 0278 / 0279 / 0280 / 0281 / 0282 / 0283 / 0284 / 0285 / 0286 / 0287 / 0288 / 0289) persist to `report_leftover_pair` with signed residual `R`, observed `Y`, expected `E[Y|θ, item]`, full leftover-map rank, unexplained leftover, ADR 0201 reconstruction evidence, ADR 0185 cross-share evidence, ADR 0233 unexplained leftover share `s`, ADR 0266 explained leftover @@ -667,7 +667,9 @@ item coverage. ADR 0285 captions the leftover pair list with persisted leftover- item complete-case coverage. ADR 0286 captions the leftover pair list with persisted leftover-map incomplete post coverage. ADR 0287 captions the leftover pair list with persisted leftover-map incomplete item coverage. ADR 0288 fail-closes leftover-map post complete-case coverage -on the leftover pair list through leftoverMapCoverageCounts. Click a post marker or a pair button +on the leftover pair list through leftoverMapCoverageCounts. ADR 0289 captions the +grouping comparison strip with persisted leftover-map post complete-case coverage +through leftoverMapCoverageCounts. Click a post marker or a pair button opens that post. Those ADRs are the normative mathematical, storage, and display contracts. Leftover-map axis share (Gabriel inertia of residual SVD axes 1 and 2; ADR 0148) persists to @@ -684,7 +686,8 @@ including on the pair list. Results persist to `GET /api/reports/{grouping}` lists the trend; `GET /api/reports/{grouping}/{period}` is ABAC-filtered; `GET /api/reports/compare/{period}` is the home-page grouping strip -and carries the same ABAC-filtered leftover pairs (ADR 0149); +and carries the same ABAC-filtered leftover pairs (ADR 0149) plus persisted +leftover-map complete-case coverage (ADR 0289); `POST .../rebuild` scores every grouping kind (post_admin). `make seed` folds A-100/B-200 Event Lineage fixtures (and the Riverbend calendar post) that already have constructed IRT cells into the same shared diff --git a/CHANGELOG.d/2.46.0-leftover-map-compare-coverage.md b/CHANGELOG.d/2.46.0-leftover-map-compare-coverage.md new file mode 100644 index 000000000..d6bdf6ff8 --- /dev/null +++ b/CHANGELOG.d/2.46.0-leftover-map-compare-coverage.md @@ -0,0 +1,35 @@ +## 2.46.0 — Leftover-map complete-case coverage on the grouping comparison strip + +- Caption leftover-map post complete-case coverage on the grouping comparison + strip through leftoverMapCoverageCounts (ADR 0289). After `make seed`, + closest and farthest leftover pairs sit above the member list with the + Gabriel biplot of already-named coordinates, leftover-map axis share when + finite, axis ticks that match `ξ (x, y) ζ (x, y)` on the pair row, pair + segments that match `d` on the pair row, pair segments that match `R̂` on + the pair row, pair segments that match `R̂²/R²` on the pair row, pair + segments that match `U²/R²` on the pair row, pair segments that match + `2R̂U/R²` on the pair row, pair segments that match `U` on the pair row, + pair segments that match `R` on the pair row, pair segments that match + `Y` on the pair row, pair segments that match `E` on the pair row, pair + segments that match leftover-map rank on the pair row, a plot caption + that matches leftover-map complete-case coverage above the pair list, a + plot caption that matches leftover-map item complete-case coverage, a + plot caption that matches leftover-map incomplete post coverage, a plot + caption that matches leftover-map incomplete item coverage, a pair-list + note that matches leftover-map post complete-case coverage, a pair-list + note that matches leftover-map item complete-case coverage, a pair-list + note that matches leftover-map incomplete post coverage, a pair-list + note that matches leftover-map incomplete item coverage, and a grouping + comparison note that matches leftover-map post complete-case coverage; + click a post marker or a pair button opens that post. A missing, + non-integer, negative used, non-positive scored, or used-greater-than-scored + coverage omits that leftover-map comparison coverage note. Coverage `0 of + M` is shown when that persisted used count is a non-negative integer and + scored is a positive integer. Do not invent post coverage from leftover + pair count, plotted marker count, leftover-map distance, leftover-map + rank, leftover-map item coverage, leftover-map incomplete post coverage, + leftover-map incomplete item coverage, or the count of unused axes. The + strip does not gain leftover-map item coverage, leftover-map incomplete + post coverage, leftover-map incomplete item coverage, or the leftover-map + graphic. Never invent a leftover score. Never invent a theta. No new + columns. diff --git a/CHANGELOG.md b/CHANGELOG.md index 71bf70876..1412f0a68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,40 @@ All notable changes to this project are documented here. Format follows ### Added +- Period leftover pairs now caption the grouping comparison strip with + persisted leftover-map post complete-case coverage through + leftoverMapCoverageCounts (ADR 0289 / v2.46.0). After `make seed`, + closest and farthest leftover pairs sit above the member list with the + Gabriel biplot of already-named coordinates, leftover-map axis share when + finite, axis ticks that match `ξ (x, y) ζ (x, y)` on the pair row, pair + segments that match `d` on the pair row, pair segments that match `R̂` on + the pair row, pair segments that match `R̂²/R²` on the pair row, pair + segments that match `U²/R²` on the pair row, pair segments that match + `2R̂U/R²` on the pair row, pair segments that match `U` on the pair row, + pair segments that match `R` on the pair row, pair segments that match + `Y` on the pair row, pair segments that match `E` on the pair row, pair + segments that match leftover-map rank on the pair row, a plot caption + that matches leftover-map complete-case coverage above the pair list, a + plot caption that matches leftover-map item complete-case coverage, a + plot caption that matches leftover-map incomplete post coverage, a plot + caption that matches leftover-map incomplete item coverage, a pair-list + note that matches leftover-map post complete-case coverage, a pair-list + note that matches leftover-map item complete-case coverage, a pair-list + note that matches leftover-map incomplete post coverage, a pair-list + note that matches leftover-map incomplete item coverage, and a grouping + comparison note that matches leftover-map post complete-case coverage; + click a post marker or a pair button opens that post. A missing, + non-integer, negative used, non-positive scored, or used-greater-than-scored + coverage omits that leftover-map comparison coverage note. Coverage `0 of + M` is shown when that persisted used count is a non-negative integer and + scored is a positive integer. Do not invent post coverage from leftover + pair count, plotted marker count, leftover-map distance, leftover-map + rank, leftover-map item coverage, leftover-map incomplete post coverage, + leftover-map incomplete item coverage, or the count of unused axes. The + strip does not gain leftover-map item coverage, leftover-map incomplete + post coverage, leftover-map incomplete item coverage, or the leftover-map + graphic. Never invent a leftover score or a theta. No new columns. + - Period leftover pairs now fail-close leftover-map post complete-case coverage on the leftover pair list through leftoverMapCoverageCounts (ADR 0288 / v2.45.0). After `make seed`, closest and farthest leftover diff --git a/CLAUDE.md b/CLAUDE.md index 889f61d82..4ce0f23a3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -49,7 +49,7 @@ cutoff. Global Ask optional `knowledge_cutoff` uses the same cover Create/start endpoint rules (ADR 0017 / 0021), tie-vs-miss similarity (ADR 0026), R&R catalog ids (ADR 0019 / 0027), leftover pairs -(ADR 0048–0164 / 0182 / 0185 / 0201 / 0233 / 0266 / 0267 / 0268 / 0269 / 0270 / 0271 / 0272 / 0273 / 0274 / 0275 / 0276 / 0277 / 0278 / 0279 / 0280 / 0281 / 0282 / 0283 / 0284 / 0285 / 0286 / 0287 / 0288), occupational construct catalog search +(ADR 0048–0164 / 0182 / 0185 / 0201 / 0233 / 0266 / 0267 / 0268 / 0269 / 0270 / 0271 / 0272 / 0273 / 0274 / 0275 / 0276 / 0277 / 0278 / 0279 / 0280 / 0281 / 0282 / 0283 / 0284 / 0285 / 0286 / 0287 / 0288 / 0289), occupational construct catalog search (ADR 0257), the text-channel embedding swap and cosine clamp (ADR 0190), per-edge channel-score persistence (ADR 0195), token-backed status notices (ADR 0220), diff --git a/backend/app/report_ingestion.py b/backend/app/report_ingestion.py index 8a8d37bc2..d675fe3ab 100644 --- a/backend/app/report_ingestion.py +++ b/backend/app/report_ingestion.py @@ -1070,6 +1070,22 @@ async def fetch_period_comparison( leftover_by_key: dict[tuple[str, str], list[asyncpg.Record]] = defaultdict(list) for row in leftover: leftover_by_key[(row["grouping_kind"], row["grouping_key"])].append(row) + leftover_coverage = await conn.fetch( + """ + select grouping_kind, grouping_key, map_post_count, scored_post_count, + map_item_count, scored_item_count, + incomplete_post_count, incomplete_item_count + from report_leftover_map_coverage + where period_code = $1 and rubric_version = $2 + and grouping_kind = any($3::text[]) + """, + period_code, + RUBRIC_VERSION, + list(GROUPING_KINDS), + ) + leftover_coverage_by_key = { + (row["grouping_kind"], row["grouping_key"]): row for row in leftover_coverage + } payload: list[dict[str, Any]] = [] for row in rows: label = await resolve_grouping_label(conn, row["grouping_kind"], row["grouping_key"]) @@ -1113,6 +1129,9 @@ async def fetch_period_comparison( } for pair in leftover_by_key.get((row["grouping_kind"], row["grouping_key"]), []) ], + "leftover_map_coverage": _leftover_map_coverage_payload( + leftover_coverage_by_key.get((row["grouping_kind"], row["grouping_key"])) + ), } ) return payload diff --git a/backend/tests/test_api.py b/backend/tests/test_api.py index 520277031..273a7ff94 100644 --- a/backend/tests/test_api.py +++ b/backend/tests/test_api.py @@ -5829,6 +5829,19 @@ def test_seed_period_report_surfaces_on_get_reports(client, demo_analyst_token, assert leftover_kinds <= {"closest", "farthest"} assert all(pair["post_title"] for pair in leftover_thread.get("leftover_pairs", [])) assert all(pair["leftover_distance"] >= 0 for pair in leftover_thread.get("leftover_pairs", [])) + leftover_compare_coverage = leftover_thread.get("leftover_map_coverage") + assert leftover_compare_coverage is not None + assert leftover_compare_coverage["map_post_count"] <= leftover_compare_coverage["scored_post_count"] + assert leftover_compare_coverage["scored_post_count"] > 0 + assert leftover_compare_coverage["incomplete_post_count"] == ( + leftover_compare_coverage["scored_post_count"] - leftover_compare_coverage["map_post_count"] + ) + assert leftover_compare_coverage["map_item_count"] <= leftover_compare_coverage["scored_item_count"] + assert leftover_compare_coverage["map_post_count"] != len( + leftover_thread.get("leftover_pairs", []) + ) or leftover_compare_coverage["scored_post_count"] > len( + leftover_thread.get("leftover_pairs", []) + ) assert all( {"visibility_code", "corporate_entity_id", "process_unit_id"}.isdisjoint(pair) for pair in leftover_thread.get("leftover_pairs", []) diff --git a/docs/adr/0289-leftover-map-compare-coverage.md b/docs/adr/0289-leftover-map-compare-coverage.md new file mode 100644 index 000000000..7a01f7024 --- /dev/null +++ b/docs/adr/0289-leftover-map-compare-coverage.md @@ -0,0 +1,210 @@ +# ADR 0289 — Name leftover-map complete-case coverage on the grouping comparison strip + +**Decision status:** Accepted +**Date:** 2026-08-30 + +Amends leftover pairs on the grouping comparison strip +([ADR 0149](0149-leftover-pairs-on-comparison-strip.md)) and leftover-map +complete-case coverage ([ADR 0168](0168-leftover-map-complete-case-coverage.md)). +Independent of leftover-map post complete-case coverage fail-closed on the pair +list ([ADR 0288](0288-leftover-map-list-post-coverage-helper.md)), leftover-map +incomplete item coverage on the pair list +([ADR 0287](0287-leftover-map-list-incomplete-item.md)), leftover-map incomplete +post coverage on the pair list +([ADR 0286](0286-leftover-map-list-incomplete-post.md)), leftover-map item +complete-case coverage on the pair list +([ADR 0285](0285-leftover-map-list-item-coverage.md)), leftover-map incomplete +item coverage on the graphic display +([ADR 0284](0284-leftover-map-plot-incomplete-item.md)), leftover-map incomplete +post coverage on the graphic display +([ADR 0283](0283-leftover-map-plot-incomplete.md)), leftover-map item +complete-case coverage on the graphic display +([ADR 0282](0282-leftover-map-plot-item-coverage.md)), leftover-map complete-case +coverage on the graphic display +([ADR 0281](0281-leftover-map-plot-coverage.md)), leftover-map rank on pair +segments ([ADR 0280](0280-leftover-map-segment-rank.md)), leftover expected on pair +segments ([ADR 0279](0279-leftover-map-segment-expected.md)), leftover observed on +pair segments ([ADR 0278](0278-leftover-map-segment-observed.md)), leftover +residual on pair segments ([ADR 0277](0277-leftover-map-segment-residual.md)), +leftover-map unexplained leftover on pair segments +([ADR 0276](0276-leftover-map-segment-unexplained-leftover.md)), leftover-map +cross share on pair segments ([ADR 0275](0275-leftover-map-segment-cross-share.md)), +leftover-map unexplained leftover share on pair segments +([ADR 0274](0274-leftover-map-segment-unexplained-share.md)), leftover-map +explained leftover share on pair segments +([ADR 0273](0273-leftover-map-segment-explained-share.md)), leftover-map +reconstruction on pair segments +([ADR 0272](0272-leftover-map-segment-reconstruction.md)), leftover-map +distance on pair segments ([ADR 0271](0271-leftover-map-segment-distance.md)), +leftover-map coordinate ticks ([ADR 0270](0270-leftover-map-coordinate-ticks.md)), +leftover-map axis share on the graphic display +([ADR 0269](0269-leftover-map-axis-share-plot.md)), leftover residual +disclosure ([ADR 0162](0162-leftover-residual-disclosure.md)), leftover +observed `Y` / expected `E` ([ADR 0163](0163-leftover-observed-expected.md)), +leftover-map explained leftover share persistence +([ADR 0266](0266-leftover-map-explained-share.md)), leftover-map +unexplained leftover share persistence +([ADR 0233](0233-leftover-map-unexplained-share.md)), leftover-map +reconstruction persistence ([ADR 0201](0201-leftover-map-reconstruction.md)), +leftover-map cross share persistence +([ADR 0185](0185-leftover-map-cross-share.md)), leftover-map unexplained leftover +persistence ([ADR 0182](0182-leftover-map-unexplained.md)), leftover-map rank +persistence ([ADR 0164](0164-leftover-map-rank.md)), and leftover-map axis +share persistence ([ADR 0148](0148-leftover-map-axis-share.md)). + +## Context + +ADR 0168 already persists `map_post_count` / `scored_post_count` on +`report_leftover_map_coverage`. ADR 0149 already carries ABAC-filtered leftover +pairs on `GET /api/reports/compare/{period}`. ADR 0281 already fail-closes that +same coverage on the leftover-map graphic. ADR 0288 already fail-closes that +same coverage on the leftover pair list. The grouping comparison strip still +names leftover pairs and `d` without naming how many scored posts entered the +Gabriel factorization for that grouping, so a buyer who compares PU / corp / +thread leftover pairs can treat a 2-of-8 complete-case map as if it used the +same census as an 8-of-8 map. + +This increment includes persisted leftover-map complete-case coverage on the +comparison payload and captions each grouping row through leftoverMapCoverageCounts. +It does not add columns. It does not recompute post coverage from leftover pair +count, plotted marker count, leftover-map distance, or leftover-map rank. It +does not persist leftover-map inner product, cosine, or length as separate +columns. It does not land Post quality on the leftover criterion. Leftover-map +distance stays two-axis Euclidean. Do not invent a leftover score. Do not invent +a theta. + +The dashboard stack already used neighbouring leftover facts under other +numbers. This protected increment uses **0289** so it does not collide with +leftover-map post complete-case coverage fail-closed on the pair list (0288), +leftover-map incomplete item coverage on the pair list (0287), leftover-map +incomplete post coverage on the pair list (0286), leftover-map item +complete-case coverage on the pair list (0285), leftover-map incomplete +item coverage on the graphic display (0284), leftover-map incomplete post +coverage on the graphic display (0283), leftover-map item complete-case +coverage on the graphic display (0282), leftover-map complete-case coverage +on the graphic display (0281), leftover-map rank on pair segments (0280), +leftover expected on pair segments (0279), leftover observed on pair +segments (0278), leftover residual on pair segments (0277), leftover-map +unexplained leftover on pair segments (0276), leftover-map cross share on +pair segments (0275), leftover-map unexplained leftover share on pair +segments (0274), leftover-map explained leftover share on pair segments +(0273), leftover-map reconstruction on pair segments (0272), leftover-map +distance on pair segments (0271), leftover-map coordinate ticks (0270), +leftover-map axis share on the graphic display (0269), leftover-map graphic +display (0268), leftover-map coordinates (0267 / migration 0245), leftover-map +explained leftover share persistence (0266 / migration 0244), leftover-map +unexplained leftover share persistence (0233 / migration 0233), leftover-map +reconstruction persistence (0201 / migration 0206), leftover-map cross share +persistence (0185), leftover-map unexplained leftover persistence (0182), +leftover residual disclosure (0162), leftover observed `Y` / expected `E` +persistence (0163), leftover-map rank persistence (0164), leftover coverage +persistence (0168), two-axis leftover-map distance persistence, leftover-map +axis share persistence (0148), leftover interaction-map persistence, +occupational construct catalog search (0265), or the dashboard stacks. + +## Decision + +On the grouping comparison strip, include persisted `leftover_map_coverage` on +each comparison row and caption leftover-map post complete-case coverage as +`Leftover map used {used} of {scored} scored posts (complete-case)` only when +leftoverMapCoverageCounts returns usable complete-case integers from persisted +`map_post_count` / `scored_post_count`. Use the distinct accessible name +`Leftover map comparison coverage` so the strip caption is not the pair-list +note (`Leftover map coverage`) and is not the graphic caption +(`Leftover-map graphic coverage`). A missing coverage row, a non-integer +count, a negative used count, a non-positive scored count, or used greater +than scored omits that leftover-map comparison coverage note and keeps the +strip leftover pairs, leftover-map distance `d`, and any leftover-map +distance, reconstruction, explained leftover share, unexplained leftover +share, leftover-map cross share, unexplained leftover, leftover residual, +leftover observed, leftover expected, leftover-map rank, leftover-map post +coverage, leftover-map item coverage, leftover-map incomplete post, or +leftover-map incomplete item caption on the pair list and graphic. Coverage +`0 of M` is shown when that persisted used count is a non-negative integer +and scored is a positive integer. Do not invent post coverage from leftover +pair count, plotted marker count, leftover-map distance, leftover-map rank, +leftover-map item coverage, leftover-map incomplete post coverage, +leftover-map incomplete item coverage, or the count of unused axes. Do not +caption leftover-map item coverage, leftover-map incomplete post coverage, +or leftover-map incomplete item coverage on the strip in this increment. +Do not add the leftover-map graphic to the strip. Coverage is aggregate and +non-identifying: ABAC that hides leftover pairs does not hide persisted +coverage counts. Click a leftover pair on the strip to open that post. + +Do not add SQL. Do not edit shipped migrations. Do not persist inner +product, cosine, or length as separate columns. Do not invent a leftover +score. Do not invent a theta. + +## Consequences + +After `make seed`, the grouping comparison strip names persisted leftover-map +post complete-case coverage on each grouping row when leftoverMapCoverageCounts +returns usable integers, then names leftover pairs with leftover-map distance +`d`. Closest and farthest leftover pairs still sit above the member list with +the leftover-map graphic display of persisted `ξ` and `ζ`, leftover-map axes +name persisted Gabriel inertia share when finite, leftover-map axis ticks name +the same coordinates shown on the pair row, pair segments name persisted +leftover-map distance `d`, persisted leftover-map reconstruction `R̂`, +persisted leftover-map explained leftover share `e`, persisted leftover-map +unexplained leftover share `s`, persisted leftover-map cross share `x`, +persisted leftover-map unexplained leftover `U`, persisted leftover residual +`R`, persisted leftover observed `Y`, persisted leftover expected `E`, +persisted leftover-map rank, the plot names persisted leftover-map +complete-case coverage, the plot names persisted leftover-map item +complete-case coverage, the plot names persisted leftover-map incomplete +post coverage, the plot names persisted leftover-map incomplete item +coverage, the pair list names persisted leftover-map post complete-case +coverage only when leftoverMapCoverageCounts returns usable integers, the +pair list names persisted leftover-map item complete-case coverage, the pair +list names persisted leftover-map incomplete post coverage, and the pair list +names persisted leftover-map incomplete item coverage; click a post marker +or a pair button opens that post. +Hidden posts stay hidden. Rank-0 unused axes still plot at the origin and still +name post coverage on the comparison strip when that coverage is persisted as +usable complete-case integers. + +## Related + +Independent of leftover interaction-map persistence, leftover-criterion +evaluation landing, leftover residual disclosure, leftover-map +complete-case coverage persistence, leftover-map axis share persistence, +leftover pairs on the grouping comparison strip, two-axis leftover-map +distance persistence, leftover-map rank persistence, leftover-map inner +product, leftover-map cosine, leftover-map length, leftover-map +reconstruction persistence, leftover-map unexplained leftover persistence, +leftover-map cross share persistence, leftover-map unexplained leftover +share persistence, leftover-map explained leftover share persistence, +leftover-map coordinate persistence, leftover-map graphic display, +leftover-map axis share on the graphic display, leftover-map coordinate +ticks, leftover-map distance on pair segments, leftover-map reconstruction +on pair segments, leftover-map explained leftover share on pair segments, +leftover-map unexplained leftover share on pair segments, leftover-map +cross share on pair segments, leftover-map unexplained leftover on pair +segments, leftover residual on pair segments, leftover observed on pair +segments, leftover expected on pair segments, leftover-map rank on pair +segments, leftover-map complete-case coverage on the graphic display, +leftover-map item complete-case coverage on the graphic display, +leftover-map incomplete post coverage on the graphic display, +leftover-map incomplete item coverage on the graphic display, +leftover-map item complete-case coverage on the pair list, leftover-map +incomplete post coverage on the pair list, leftover-map incomplete +item coverage on the pair list, and leftover-map post complete-case +coverage fail-closed on the pair list. + +## References + +Gabriel, K. R. (1971). The biplot graphic display of matrices with +application to principal component analysis. *Biometrika, 58*(3), +453–467. https://doi.org/10.1093/biomet/58.3.453 + +Jeon, M., Jin, I. H., Schweinberger, M., & Baugh, S. (2021). Mapping +unobserved item–respondent interactions: A latent space item response +model with interaction map. *Psychometrika, 86*(2), 378–403. +https://doi.org/10.1007/s11336-021-09762-5 +(LSIRM interaction `−γ‖ξ_j − ζ_i‖` after main effects +`α_j − β_i`; typically `p = 2` for the interaction map. Gabriel +reconstruction of the leftover cell is the two-axis inner product +`R̂ = ξ_{1:2} · ζ_{1:2}`. Incomplete rows are dropped from the +complete-case residual rectangle; grouping comparison coverage names how +many scored posts entered that factorization for that grouping only when +leftoverMapCoverageCounts returns usable complete-case integers.) diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index c30dfd145..bacb23230 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -1,5 +1,68 @@ # Product & Technical Gap Baseline +> Exact-head loop overlay: 2026-08-30 22:52 KST. Protected `main` is +> `cb187cadee5fb6c46d8a944815ccc154a1e028d1` after unauthorized squash +> of leftover-map coordinates (v2.24.0 / #782). Revert #808 +> (`revert-pr782-unauthorized` @ `1af3e53e`) restores `main` toward +> `fc13acaa` (v2.23.0) and still needs independent APPROVE. GitHub +> writes work (comment/close/create-PR/push). Token still has empty +> `X-OAuth-Scopes`; repo permission is ADMIN. Open leftover stack +> still lacks independent APPROVE. Do not self-approve. +> #782 leftover-map coordinates through pair-segment distance +> (v2.24.0–v2.28.0 / ADR 0267–0271) is `9bdd3e4d`. #802 explained +> leftover share on pair segments (v2.30.0 / ADR 0273, includes v2.29 +> reconstruction) is `79ec22f0`. #803 unexplained leftover share on pair +> segments (v2.31.0 / ADR 0274) is `089a1571`. #804 leftover-map cross +> share on pair segments (v2.32.0 / ADR 0275) is `c513002b`. #805 leftover-map +> unexplained leftover `U` on pair segments (v2.33.0 / ADR 0276) is +> `e3fdfd74`. #806 leftover residual `R` on pair segments (v2.34.0 / +> ADR 0277) is `33f6c3dd`. #809 leftover observed `Y` on pair segments +> (v2.35.0 / ADR 0278) is `b334b00e`. #810 leftover expected `E` on pair +> segments (v2.36.0 / ADR 0279) is `6e37757a`. #811 leftover-map rank on +> pair segments (v2.37.0 / ADR 0280) is `e626a1d0`. #812 leftover-map +> complete-case coverage on the graphic (v2.38.0 / ADR 0281) is `64964cb6`. +> #813 leftover-map item complete-case coverage on the graphic (v2.39.0 / +> ADR 0282) is `de2a8a8b`. #814 leftover-map incomplete post coverage on +> the graphic (v2.40.0 / ADR 0283) is `77292872`. #815 leftover-map +> incomplete item coverage on the graphic (v2.41.0 / ADR 0284) is +> `63092de`. #816 leftover-map item complete-case coverage on the pair +> list (v2.42.0 / ADR 0285) is `1e3d13e`. #817 leftover-map incomplete +> post coverage on the pair list (v2.43.0 / ADR 0286) is `ef30930`. +> #818 leftover-map incomplete item coverage on the pair list +> (v2.44.0 / ADR 0287) is `aa32089`. #819 leftover-map post complete-case +> coverage fail-closed on the pair list (v2.45.0 / ADR 0288) is `a2c96551`. +> Leave +> #782/#802/#803/#804/#805/#806/#808/#809/#810/#811/#812/#813/#814/#815/#816/#817/#818/#819 open for +> independent review. Do not squash-merge stacked leftover PRs onto an +> unprotected leftover base. Do not merge #808 without independent APPROVE. +> Strix on #782 failed closed at `Run Strix (quick)`; do not weaken +> fail-closed on `Vulnerabilities [1-9]`. Org Strix `ModelBehaviorError` +> classifier is already on ContextualWisdomLab/.github. Copilot review is +> not independent APPROVE. Issues #79 and #87 stay open. #96 is already +> closed. Only collaborator is `seonghobae`; no independent reviewer can +> be requested from this token. +> +> Next buyer increment on this cycle: leftover-map complete-case coverage +> on the grouping comparison strip (ADR 0289 / v2.46.0) delivered +> locally on `feat/leftover-map-compare-coverage-v2460`. Include persisted +> leftover_map_coverage on GET /api/reports/compare/{period} and caption +> each grouping row through leftoverMapCoverageCounts with distinct +> aria-label `Leftover map comparison coverage`. UI + compare payload; +> no new columns. Missing, non-integer, negative used, non-positive scored, +> or used-greater-than-scored coverage omits that leftover-map comparison +> coverage note. Coverage `0 of M` is shown when persisted. Do not invent +> post coverage from leftover pair count, plotted marker count, leftover-map +> distance, leftover-map rank, leftover-map item coverage, leftover-map +> incomplete post coverage, leftover-map incomplete item coverage, or the +> count of unused axes. Do not caption leftover-map item coverage, +> leftover-map incomplete post, leftover-map incomplete item, or the +> leftover-map graphic on the strip. Do not invent leftover scores. Stack +> onto leftover branch `feat/leftover-map-list-post-coverage-helper-v2450` +> / #819; leave the PR open for independent review. Do not squash-merge +> onto the leftover base. Do not persist leftover-map inner product, cosine, +> or length as separate columns. + + > Exact-head loop overlay: 2026-08-30 22:30 KST. Protected `main` is > `cb187cadee5fb6c46d8a944815ccc154a1e028d1` after unauthorized squash > of leftover-map coordinates (v2.24.0 / #782). Revert #808 diff --git a/docs/storybook-inventory.md b/docs/storybook-inventory.md index f0aa61428..654678c84 100644 --- a/docs/storybook-inventory.md +++ b/docs/storybook-inventory.md @@ -6,7 +6,7 @@ operator-facing control you can click before changing product CSS. | Story | Operator next action | Token / module | |---|---|---| | `Reports/LeftoverMapPlot` | Read the leftover-map graphic display of persisted `ξ` (posts) and `ζ` (criteria), match axis ticks to those coordinates, pair-segment `d` to leftover-map distance, pair-segment `R̂` to leftover-map reconstruction, pair-segment `R̂²/R²` to leftover-map explained leftover share, pair-segment `U²/R²` to leftover-map unexplained leftover share, pair-segment `2R̂U/R²` to leftover-map cross share, pair-segment `U` to leftover-map unexplained leftover, pair-segment `R` to leftover residual, pair-segment `Y` to leftover observed, pair-segment `E` to leftover expected, and pair-segment leftover-map rank, then click a post marker to open that post. Leftover-map axes name persisted leftover-map axis share when finite. The plot names leftover-map complete-case coverage, leftover-map item complete-case coverage, leftover-map incomplete post coverage, and leftover-map incomplete item coverage when persisted. `ClosestAndFarthest`, `RankZeroOrigin`, `MissingCoordinates`, and `MissingAxisShare` cover two-pair maps, rank-0 origin with 0% share, a `0` tick, `d 0.00`, `R̂ 0.00`, `R̂²/R² 0.00`, `U²/R² 0.00`, `2R̂U/R² 0.00`, `U 0.00`, `R 0.00`, `Y 0.00`, `E 0.00`, and `rank 0`, omitted plots, and missing share that keeps existing leftover-map axis text. The plot does not invent a leftover score. | `LeftoverMapPlot`, `leftoverMapPlotLayout`, `leftoverMapPlotAxisShare`, `--color-primary`, `--color-palette-blue-mid` | -| `Reports/LeftoverPairList` | Read closest/farthest leftover pairs with named `R`, `Y`/`E`, rank, `U`, `s`, `e`, `x`, `R̂`, `ξ`/`ζ`, and `d`, then open that post. The leftover-map graphic display sits above the pair buttons when coordinates are finite, leftover-map axes name persisted leftover-map axis share, leftover-map axis ticks name persisted coordinates, pair segments name persisted leftover-map distance, pair segments name persisted leftover-map reconstruction, pair segments name persisted leftover-map explained leftover share, pair segments name persisted leftover-map unexplained leftover share, pair segments name persisted leftover-map cross share, pair segments name persisted leftover-map unexplained leftover, pair segments name persisted leftover residual, pair segments name persisted leftover observed, pair segments name persisted leftover expected, and pair segments name persisted leftover-map rank. The pair-list note names leftover-map complete-case coverage only when leftoverMapCoverageCounts returns usable complete-case integers, leftover-map item complete-case coverage, leftover-map incomplete post coverage, and leftover-map incomplete item coverage when persisted. | `LeftoverPairList`, `LeftoverMapPlot`, `ticket-list`, `post-badge` | +| `Reports/LeftoverPairList` | Read closest/farthest leftover pairs with named `R`, `Y`/`E`, rank, `U`, `s`, `e`, `x`, `R̂`, `ξ`/`ζ`, and `d`, then open that post. The leftover-map graphic display sits above the pair buttons when coordinates are finite, leftover-map axes name persisted leftover-map axis share, leftover-map axis ticks name persisted coordinates, pair segments name persisted leftover-map distance, pair segments name persisted leftover-map reconstruction, pair segments name persisted leftover-map explained leftover share, pair segments name persisted leftover-map unexplained leftover share, pair segments name persisted leftover-map cross share, pair segments name persisted leftover-map unexplained leftover, pair segments name persisted leftover residual, pair segments name persisted leftover observed, pair segments name persisted leftover expected, and pair segments name persisted leftover-map rank. The pair-list note names leftover-map complete-case coverage only when leftoverMapCoverageCounts returns usable complete-case integers, leftover-map item complete-case coverage, leftover-map incomplete post coverage, and leftover-map incomplete item coverage when persisted. The grouping comparison strip names leftover-map complete-case coverage with a distinct comparison label when leftoverMapCoverageCounts returns usable integers. | `LeftoverPairList`, `LeftoverMapPlot`, `ticket-list`, `post-badge` | | `Workspace/OperationsDashboard` | Compare Event and post counts, inspect external-information coverage, then open the cited source behind a claim, handover, or repeat-issue fact. `EvidenceReady`, `NarrowViewport`, `AnalysisPendingAndMissingEvidence`, `AnalysisFailed`, and `LoadError` cover populated, mobile, unavailable-evidence, analysis-pending, retryable failure, and transport-error states. | `--color-dashboard-*`, `OperationsDashboard` | | `Post/SimilarVocPanel` | Compare ontology/semantic similar VOC and prior action evidence, then open the source; unavailable states show no fabricated TEPP theta or weight. | `SimilarVocPanel.css`, `SimilarVocPanel` | | `Post/Recorded perspectives` | Read the imported primary and every evidence-connected additional Voice with its recorded truth state instead of flattening them into one compound category. `CombinedEvidence`, `RejectedEvidence`, and `NarrowViewport` cover desktop, rejected-evidence, and narrow layouts. | `VoicePerspectiveList`, `ticket-list`, `post-meta` | diff --git a/frontend/package.json b/frontend/package.json index a1c5ca160..32a794de7 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "frontend", "private": true, - "version": "2.45.0", + "version": "2.46.0", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx index 70a4ad8c6..3cf4108be 100644 --- a/frontend/src/App.test.tsx +++ b/frontend/src/App.test.tsx @@ -890,6 +890,14 @@ describe("App, authenticated", () => { mean_theta: 0.81, post_count: 4, link_method: "fipc", + leftover_map_coverage: { + map_post_count: 5, + scored_post_count: 3, + map_item_count: 2, + scored_item_count: 2, + incomplete_post_count: 0, + incomplete_item_count: 0, + }, }, { grouping_kind: "corporate_entity", @@ -916,6 +924,14 @@ describe("App, authenticated", () => { leftover_residual: 0.4, }, ], + leftover_map_coverage: { + map_post_count: 2, + scored_post_count: 3, + map_item_count: 2, + scored_item_count: 2, + incomplete_post_count: 1, + incomplete_item_count: 0, + }, }, ], }), @@ -4243,6 +4259,16 @@ describe("App, authenticated", () => { expect( within(screen.getByLabelText("Grouping comparison")).queryByLabelText("Leftover map coverage"), ).not.toBeInTheDocument(); + expect( + within(screen.getByLabelText("Grouping comparison")).getByLabelText( + "Leftover map comparison coverage", + ), + ).toHaveTextContent("Leftover map used 2 of 3 scored posts (complete-case)"); + expect( + within(screen.getByLabelText("Grouping comparison")).getAllByLabelText( + "Leftover map comparison coverage", + ), + ).toHaveLength(1); expect( within(screen.getByLabelText("Grouping comparison")).queryByLabelText("Leftover map item coverage"), ).not.toBeInTheDocument(); diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index b17ee11ac..1d44607ab 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -124,6 +124,7 @@ import { leftoverMapIncompleteItemCount, leftoverMapIncompletePostCount, leftoverMapItemCoverageCounts, + LEFTOVER_MAP_COMPARE_COVERAGE_LABEL, LEFTOVER_MAP_LIST_COVERAGE_LABEL, LEFTOVER_MAP_LIST_INCOMPLETE_ITEM_LABEL, LEFTOVER_MAP_LIST_INCOMPLETE_POST_LABEL, @@ -3984,7 +3985,9 @@ function ReportsPanel({ {comparison && comparison.groupings.length > 0 && ( )} {openedGroupingLabel && ( diff --git a/frontend/src/api.ts b/frontend/src/api.ts index 5db021a05..e4781c206 100644 --- a/frontend/src/api.ts +++ b/frontend/src/api.ts @@ -1280,6 +1280,7 @@ export interface GroupingComparisonRow { post_count: number; link_method: string; leftover_pairs?: LeftoverPair[]; + leftover_map_coverage?: LeftoverMapCoverage | null; } export interface PeriodComparison { diff --git a/frontend/src/components/LeftoverPairList.tsx b/frontend/src/components/LeftoverPairList.tsx index cdee52429..bb6adeffb 100644 --- a/frontend/src/components/LeftoverPairList.tsx +++ b/frontend/src/components/LeftoverPairList.tsx @@ -81,7 +81,9 @@ export type LeftoverPairListProps = { * incomplete post coverage on the pair-list note. ADR 0287 names persisted leftover-map * incomplete item coverage on the pair-list note. ADR 0288 fail-closes pair-list * leftover-map post complete-case coverage through leftoverMapCoverageCounts so - * used-greater-than-scored, negative, or non-integer counts omit that note. Every badge still + * used-greater-than-scored, negative, or non-integer counts omit that note. ADR 0289 + * names persisted leftover-map post complete-case coverage on the grouping + * comparison strip, not this pair list. Every badge still * renders together before opening the named post. */ export function LeftoverPairList({ diff --git a/frontend/src/i18n.test.ts b/frontend/src/i18n.test.ts index 0d43fbdfd..f640f21b6 100644 --- a/frontend/src/i18n.test.ts +++ b/frontend/src/i18n.test.ts @@ -77,6 +77,7 @@ describe("i18n", () => { "leftover expected {label}", "leftover-map rank {label}", "Leftover-map graphic coverage", + "Leftover map comparison coverage", "Leftover-map graphic item coverage", "Leftover map item coverage", "Leftover map incomplete posts", @@ -380,6 +381,16 @@ describe("i18n", () => { expect(t("Leftover-map graphic coverage")).toBe(expected); }); + it.each([ + ["ko", "잔여 지도 비교 포함 범위"], + ["zh", "残差地图比较覆盖范围"], + ["ja", "残差マップの比較対象範囲"], + ["vi", "Phạm vi so sánh bản đồ phần dư"], + ] as const)("formats leftover map comparison coverage label in %s", (locale, expected) => { + setLocale(locale); + expect(t("Leftover map comparison coverage")).toBe(expected); + }); + it.each([ ["ko", "잔여 지도 그림 기준 포함 범위"], ["zh", "残差图图形准则覆盖范围"], diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts index 700f0ae50..a59a4ba0e 100644 --- a/frontend/src/i18n.ts +++ b/frontend/src/i18n.ts @@ -268,6 +268,7 @@ const TRANSLATIONS: Partial>> = { "Analysis runs": "분석 실행", "Period reports": "기간 리포트", "Leftover map coverage": "잔여 지도 포함 범위", + "Leftover map comparison coverage": "잔여 지도 비교 포함 범위", "Leftover map item coverage": "잔여 지도 기준 포함 범위", "Leftover map incomplete posts": "잔여 지도 불완전 글", "Leftover map incomplete items": "잔여 지도 불완전 기준", @@ -874,6 +875,7 @@ const TRANSLATIONS: Partial>> = { "Analysis runs": "分析运行", "Period reports": "周期报告", "Leftover map coverage": "残差地图覆盖范围", + "Leftover map comparison coverage": "残差地图比较覆盖范围", "Leftover map item coverage": "残差地图准则覆盖范围", "Leftover map incomplete posts": "残差地图不完整帖文", "Leftover map incomplete items": "残差地图不完整准则", @@ -1501,6 +1503,7 @@ const TRANSLATIONS: Partial>> = { "Analysis runs": "分析実行", "Period reports": "期間レポート", "Leftover map coverage": "残差マップの対象範囲", + "Leftover map comparison coverage": "残差マップの比較対象範囲", "Leftover map item coverage": "残差マップの基準対象範囲", "Leftover map incomplete posts": "残差マップの不完全投稿", "Leftover map incomplete items": "残差マップの不完全基準", @@ -2107,6 +2110,7 @@ const TRANSLATIONS: Partial>> = { "Analysis runs": "Lần chạy phân tích", "Period reports": "Báo cáo theo kỳ", "Leftover map coverage": "Phạm vi bản đồ phần dư", + "Leftover map comparison coverage": "Phạm vi so sánh bản đồ phần dư", "Leftover map item coverage": "Phạm vi tiêu chí bản đồ phần dư", "Leftover map incomplete posts": "Bài không đầy đủ trên bản đồ phần dư", "Leftover map incomplete items": "Tiêu chí không đầy đủ trên bản đồ phần dư", diff --git a/frontend/src/leftoverMapCoverage.test.ts b/frontend/src/leftoverMapCoverage.test.ts index 1d5540955..53145e80b 100644 --- a/frontend/src/leftoverMapCoverage.test.ts +++ b/frontend/src/leftoverMapCoverage.test.ts @@ -5,6 +5,7 @@ import { leftoverMapIncompleteItemCount, leftoverMapIncompletePostCount, leftoverMapItemCoverageCounts, + LEFTOVER_MAP_COMPARE_COVERAGE_LABEL, LEFTOVER_MAP_LIST_COVERAGE_LABEL, LEFTOVER_MAP_LIST_INCOMPLETE_ITEM_LABEL, LEFTOVER_MAP_LIST_INCOMPLETE_POST_LABEL, @@ -70,6 +71,12 @@ describe("leftoverMapCoverageCounts", () => { expect(LEFTOVER_MAP_PLOT_COVERAGE_LABEL).toBe("Leftover-map graphic coverage"); expect(LEFTOVER_MAP_LIST_COVERAGE_LABEL).not.toBe(LEFTOVER_MAP_PLOT_COVERAGE_LABEL); }); + + it("keeps the grouping comparison coverage label distinct from the pair-list and graphic labels", () => { + expect(LEFTOVER_MAP_COMPARE_COVERAGE_LABEL).toBe("Leftover map comparison coverage"); + expect(LEFTOVER_MAP_COMPARE_COVERAGE_LABEL).not.toBe(LEFTOVER_MAP_LIST_COVERAGE_LABEL); + expect(LEFTOVER_MAP_COMPARE_COVERAGE_LABEL).not.toBe(LEFTOVER_MAP_PLOT_COVERAGE_LABEL); + }); }); describe("leftoverMapItemCoverageCounts", () => { diff --git a/frontend/src/leftoverMapCoverage.ts b/frontend/src/leftoverMapCoverage.ts index 0c4554441..7290eca96 100644 --- a/frontend/src/leftoverMapCoverage.ts +++ b/frontend/src/leftoverMapCoverage.ts @@ -1,4 +1,4 @@ -/** Leftover-map complete-case coverage after IRT main effects (ADR 0168 / ADR 0281 / ADR 0282 / ADR 0283 / ADR 0284 / ADR 0285 / ADR 0286 / ADR 0287 / ADR 0288). */ +/** Leftover-map complete-case coverage after IRT main effects (ADR 0168 / ADR 0281 / ADR 0282 / ADR 0283 / ADR 0284 / ADR 0285 / ADR 0286 / ADR 0287 / ADR 0288 / ADR 0289). */ import type { LeftoverMapCoverage } from "./api"; @@ -6,6 +6,8 @@ export const LEFTOVER_MAP_PLOT_COVERAGE_LABEL = "Leftover-map graphic coverage"; export const LEFTOVER_MAP_LIST_COVERAGE_LABEL = "Leftover map coverage"; +export const LEFTOVER_MAP_COMPARE_COVERAGE_LABEL = "Leftover map comparison coverage"; + export const LEFTOVER_MAP_PLOT_COVERAGE = "Leftover map used {used} of {scored} scored posts (complete-case)"; diff --git a/frontend/src/leftoverMapPlotLayout.ts b/frontend/src/leftoverMapPlotLayout.ts index bb17d4e02..4528ee6f0 100644 --- a/frontend/src/leftoverMapPlotLayout.ts +++ b/frontend/src/leftoverMapPlotLayout.ts @@ -15,8 +15,10 @@ * leftover-map incomplete item coverage (ADR 0284). Pair-list post * complete-case coverage fail-closed through leftoverMapCoverageCounts * (ADR 0288), pair-list item complete-case coverage (ADR 0285), pair-list - * incomplete post coverage (ADR 0286), and pair-list incomplete item - * coverage (ADR 0287) caption the pair list, not this graphic layout. + * incomplete post coverage (ADR 0286), pair-list incomplete item + * coverage (ADR 0287), and grouping comparison complete-case coverage + * (ADR 0289) caption the pair list or the grouping comparison strip, + * not this graphic layout. */ import { formatLeftoverMapCoordinatePair } from "./leftoverMapCoordinates"; diff --git a/pyproject.toml b/pyproject.toml index df990f86f..1ae5db2cb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "lineageweave" -version = "2.45.0" +version = "2.46.0" description = "Reconstructs git-branch-style lineage DAGs from scattered short records using multi-channel score fusion and LLM adjudication." readme = "README.md" license = { text = "MIT" }