diff --git a/AGENTS.md b/AGENTS.md index 75791e501..f5f687a98 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) 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) 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 @@ -379,6 +379,12 @@ every scored criterion entering the map; a missing, non-integer, or negative dropped count, or a dropped count that contradicts usable item complete-case integers, omits that leftover-map incomplete item note and does not invent dropped criteria from scored minus used or from plotted criterion marker count. +ADR 0288 fail-closes leftover-map post complete-case coverage on the pair list +through leftoverMapCoverageCounts so a used-greater-than-scored, negative, or +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. 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 @@ -391,7 +397,9 @@ residual SVD axes 1 and 2 and persists to `report_leftover_map_axis`. Rank-0 residuals emit two zero-share axes; the shares are report-level 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. Item complete-case coverage +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. +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) captions the pair list with how many scored criteria entered the map. Incomplete post coverage (ADR 0283) captions the leftover-map diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index aab40280e..d741206c1 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) 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) 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 @@ -666,7 +666,8 @@ captions the leftover-map graphic display with persisted leftover-map incomplete item coverage. ADR 0285 captions the leftover pair list with persisted leftover-map 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. Click a post marker or a pair button +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 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 diff --git a/CHANGELOG.d/2.45.0-leftover-map-list-post-coverage-helper.md b/CHANGELOG.d/2.45.0-leftover-map-list-post-coverage-helper.md new file mode 100644 index 000000000..c0436456d --- /dev/null +++ b/CHANGELOG.d/2.45.0-leftover-map-list-post-coverage-helper.md @@ -0,0 +1,31 @@ +## 2.45.0 — Leftover-map post complete-case coverage fail-closed on the pair list + +- Fail-close leftover-map post complete-case coverage on the leftover pair + list through leftoverMapCoverageCounts (ADR 0288). 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, and a pair-list + note that matches leftover-map incomplete item 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 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 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. Never invent a leftover score. + Never invent a theta. No new columns. diff --git a/CHANGELOG.md b/CHANGELOG.md index 6079ba373..71bf70876 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,37 @@ All notable changes to this project are documented here. Format follows ### Added +- 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 + 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, and a pair-list note + that matches leftover-map incomplete item 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 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 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. Never invent a leftover score or + a theta. No new columns. + - Period leftover pairs now caption the leftover pair list with persisted leftover-map incomplete item coverage (ADR 0287 / v2.44.0). After `make seed`, closest and farthest leftover pairs sit above the member diff --git a/CLAUDE.md b/CLAUDE.md index 347626b38..889f61d82 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), 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), 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/docs/adr/0288-leftover-map-list-post-coverage-helper.md b/docs/adr/0288-leftover-map-list-post-coverage-helper.md new file mode 100644 index 000000000..425a973a0 --- /dev/null +++ b/docs/adr/0288-leftover-map-list-post-coverage-helper.md @@ -0,0 +1,197 @@ +# ADR 0288 — Fail-close leftover-map post complete-case coverage on the pair list + +**Decision status:** Proposed +**Date:** 2026-08-30 + +Amends [ADR 0049](0049-leftover-pair-report-ui.md) and leftover-map complete-case +coverage ([ADR 0168](0168-leftover-map-complete-case-coverage.md)). Independent of +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` and captions the pair list with post +complete-case coverage. ADR 0281 already fail-closes that same coverage on +the leftover-map graphic display through leftoverMapCoverageCounts. The +pair-list note still interpolates raw `map_post_count` / `scored_post_count` +whenever `scored_post_count > 0`, so a used-greater-than-scored, negative, +or non-integer payload still captions a contradictory `used N of M scored +posts (complete-case)` note. Incomplete rows are dropped from Gabriel +factorization; a contradictory pair-list census lets a buyer treat those N +posts as the scored census even when the integers cannot be a complete-case +count. + +This increment fail-closes leftover-map post complete-case coverage on the +pair-list note through leftoverMapCoverageCounts. It does not add columns. +It does not recompute post coverage from 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 **0288** so it does not collide with +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 leftover pair list, caption persisted 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`. 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 coverage note +and keeps the pair-list item coverage note, the pair-list incomplete post +note, the pair-list incomplete item note, 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 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 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. Click a pair button to open that +post. The grouping comparison strip (ADR 0149) stays on its reduced leftover +payload and does not gain this leftover-map coverage caption. + +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`, closest and farthest leftover pairs 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 pair list 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, and leftover-map incomplete +item coverage 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; pair-list post coverage names how many +scored posts entered that factorization 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 6eaf49e5b..6159c7240 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -1,5 +1,11 @@ # Product & Technical Gap Baseline +> #819 current-parent reconstruction: v2.45 pair-list post coverage fail-close +> is being rebuilt from exact #818 `bc971a402ef9faecb2f0b4ffd743092f4779d76b`. +> Preserve only feature delta `a2c965511d26923bd878ec56d655fb8a0183c4d7`; +> historical convergence is evidence only. ADR 0288 remains Proposed while Draft, +> and package/frontend/runtime release identity must converge at 2.45.0 before promotion. + > Exact-head loop overlay: 2026-08-30 22:18 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 12ea76430..f0aa61428 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, 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. | `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 4850829ac..a1c5ca160 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "frontend", "private": true, - "version": "2.44.0", + "version": "2.45.0", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx index 60859037b..70a4ad8c6 100644 --- a/frontend/src/App.test.tsx +++ b/frontend/src/App.test.tsx @@ -4240,6 +4240,9 @@ describe("App, authenticated", () => { render(); expect(await screen.findByLabelText("Grouping comparison")).toBeInTheDocument(); + expect( + within(screen.getByLabelText("Grouping comparison")).queryByLabelText("Leftover map coverage"), + ).not.toBeInTheDocument(); 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 1fbfafabe..b17ee11ac 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -120,12 +120,15 @@ import { useLocale, } from "./i18n"; import { + leftoverMapCoverageCounts, leftoverMapIncompleteItemCount, leftoverMapIncompletePostCount, leftoverMapItemCoverageCounts, + LEFTOVER_MAP_LIST_COVERAGE_LABEL, LEFTOVER_MAP_LIST_INCOMPLETE_ITEM_LABEL, LEFTOVER_MAP_LIST_INCOMPLETE_POST_LABEL, LEFTOVER_MAP_LIST_ITEM_COVERAGE_LABEL, + LEFTOVER_MAP_PLOT_COVERAGE, LEFTOVER_MAP_PLOT_INCOMPLETE_ITEM, LEFTOVER_MAP_PLOT_INCOMPLETE_POST, LEFTOVER_MAP_PLOT_ITEM_COVERAGE, @@ -3823,6 +3826,7 @@ function ReportsPanel({ aria-label={openedGroupingLabel ? "Opened grouping report" : "Period report groups"} > {orderedReports.map((report) => { + const coverageCounts = leftoverMapCoverageCounts(report.leftover_map_coverage); const itemCoverageCounts = leftoverMapItemCoverageCounts(report.leftover_map_coverage); const incompletePostCount = leftoverMapIncompletePostCount(report.leftover_map_coverage); const incompleteItemCount = leftoverMapIncompleteItemCount(report.leftover_map_coverage); @@ -3856,14 +3860,11 @@ function ReportsPanel({ {report.selected_items[0].information.toFixed(2)} )} - {report.leftover_map_coverage && report.leftover_map_coverage.scored_post_count > 0 && ( -

- {tf("Leftover map used {used} of {scored} scored posts (complete-case)", { - used: report.leftover_map_coverage.map_post_count, - scored: report.leftover_map_coverage.scored_post_count, - })} + {coverageCounts !== null ? ( +

+ {tf(LEFTOVER_MAP_PLOT_COVERAGE, coverageCounts)}

- )} + ) : null} {itemCoverageCounts !== null ? (

{tf(LEFTOVER_MAP_PLOT_ITEM_COVERAGE, itemCoverageCounts)} diff --git a/frontend/src/components/LeftoverPairList.tsx b/frontend/src/components/LeftoverPairList.tsx index 94e489e40..cdee52429 100644 --- a/frontend/src/components/LeftoverPairList.tsx +++ b/frontend/src/components/LeftoverPairList.tsx @@ -79,7 +79,9 @@ export type LeftoverPairListProps = { * incomplete item coverage on the graphic. ADR 0285 names persisted leftover-map * item complete-case coverage on the pair-list note. ADR 0286 names persisted leftover-map * incomplete post coverage on the pair-list note. ADR 0287 names persisted leftover-map - * incomplete item coverage on the pair-list note. Every badge still + * 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 * renders together before opening the named post. */ export function LeftoverPairList({ diff --git a/frontend/src/leftoverMapCoverage.test.ts b/frontend/src/leftoverMapCoverage.test.ts index 1f05b14f0..1d5540955 100644 --- a/frontend/src/leftoverMapCoverage.test.ts +++ b/frontend/src/leftoverMapCoverage.test.ts @@ -5,9 +5,11 @@ import { leftoverMapIncompleteItemCount, leftoverMapIncompletePostCount, leftoverMapItemCoverageCounts, + LEFTOVER_MAP_LIST_COVERAGE_LABEL, LEFTOVER_MAP_LIST_INCOMPLETE_ITEM_LABEL, LEFTOVER_MAP_LIST_INCOMPLETE_POST_LABEL, LEFTOVER_MAP_LIST_ITEM_COVERAGE_LABEL, + LEFTOVER_MAP_PLOT_COVERAGE_LABEL, LEFTOVER_MAP_PLOT_INCOMPLETE_ITEM_LABEL, LEFTOVER_MAP_PLOT_INCOMPLETE_POST_LABEL, LEFTOVER_MAP_PLOT_ITEM_COVERAGE_LABEL, @@ -62,6 +64,12 @@ describe("leftoverMapCoverageCounts", () => { scored: 2, }); }); + + it("keeps the pair-list coverage label distinct from the graphic coverage label", () => { + expect(LEFTOVER_MAP_LIST_COVERAGE_LABEL).toBe("Leftover map coverage"); + expect(LEFTOVER_MAP_PLOT_COVERAGE_LABEL).toBe("Leftover-map graphic coverage"); + expect(LEFTOVER_MAP_LIST_COVERAGE_LABEL).not.toBe(LEFTOVER_MAP_PLOT_COVERAGE_LABEL); + }); }); describe("leftoverMapItemCoverageCounts", () => { diff --git a/frontend/src/leftoverMapCoverage.ts b/frontend/src/leftoverMapCoverage.ts index eaf57e8ad..0c4554441 100644 --- a/frontend/src/leftoverMapCoverage.ts +++ b/frontend/src/leftoverMapCoverage.ts @@ -1,9 +1,11 @@ -/** 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). */ +/** 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). */ import type { LeftoverMapCoverage } from "./api"; 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_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 8414cf2d1..05360efc9 100644 --- a/frontend/src/leftoverMapPlotLayout.ts +++ b/frontend/src/leftoverMapPlotLayout.ts @@ -12,10 +12,11 @@ * rank (ADR 0280). The plot names persisted leftover-map complete-case * coverage (ADR 0281), leftover-map item complete-case coverage * (ADR 0282), leftover-map incomplete post coverage (ADR 0283), and - * leftover-map incomplete item coverage (ADR 0284). 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. + * 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. */ import { formatLeftoverMapCoordinatePair } from "./leftoverMapCoordinates"; diff --git a/lineageweave/__init__.py b/lineageweave/__init__.py index 93a7733f1..1fca0e0a5 100644 --- a/lineageweave/__init__.py +++ b/lineageweave/__init__.py @@ -130,4 +130,4 @@ "serialize_lineage_analysis_result", ] -__version__ = "2.44.0" +__version__ = "2.45.0" diff --git a/pyproject.toml b/pyproject.toml index c0182fb90..df990f86f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "lineageweave" -version = "2.44.0" +version = "2.45.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" } diff --git a/tests/test_adr_0288_governance.py b/tests/test_adr_0288_governance.py new file mode 100644 index 000000000..fd2aad542 --- /dev/null +++ b/tests/test_adr_0288_governance.py @@ -0,0 +1,13 @@ +"""Governance regression for the draft ADR 0288 decision state.""" + +from pathlib import Path + + +ADR_PATH = Path(__file__).parents[1] / "docs/adr/0288-leftover-map-list-post-coverage-helper.md" + + +def test_draft_adr_0288_remains_proposed() -> None: + """Keep the unmerged decision proposed until independent acceptance exists.""" + text = ADR_PATH.read_text(encoding="utf-8") + assert "**Decision status:** Proposed" in text + assert "**Decision status:** Accepted" not in text