Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
cf7bb55
feat(reports): leftover-map comparison graphic leftover-map axis left…
seonghobae Aug 31, 2026
ab24e79
test(red): reconstruct comparison-graphic axis badge contract
seonghobae Sep 10, 2026
d9f3a11
chore(stack): converge comparison-graphic axis-badge lane
seonghobae Sep 11, 2026
862a6e5
chore(stack): converge comparison-graphic axis-badge lane on document…
seonghobae Sep 11, 2026
c888b28
chore(stack): converge #867 onto repaired #866 head
seonghobae Sep 12, 2026
86d2471
chore(stack): converge #867 onto repaired #866
seonghobae Sep 12, 2026
3e338bc
chore(stack): converge #867 onto repaired ancestry
seonghobae Sep 13, 2026
f8ff09f
chore(stack): converge #867 onto repaired #866
seonghobae Sep 15, 2026
cf5056e
chore(stack): converge #867 onto repaired #866
seonghobae Sep 15, 2026
bfc4005
merge: converge v2.83.0 onto current v2.82.0 parent
seonghobae Sep 15, 2026
6d55624
fix(reports): compose comparison plot axis evidence
seonghobae Sep 15, 2026
e467095
test(reports): pin comparison graphic axis badge wiring
seonghobae Sep 15, 2026
9e9e38d
fix(reports): render comparison graphic axis badges with graphic copy
seonghobae Sep 15, 2026
a812976
test(reports): execute comparison graphic axis badge state matrix
seonghobae Sep 15, 2026
b70c2a3
merge(reports): converge report-axis missingness repair into comparis…
seonghobae Sep 15, 2026
8ba4063
merge(reports): converge #867 onto #866 RED contract head
seonghobae Sep 15, 2026
8e76dc8
chore(stack): adopt current comparison tick contract
seonghobae Sep 15, 2026
a05cf72
chore(stack): adopt current comparison tick composition
github-actions[bot] Sep 15, 2026
1d5e0d7
chore(stack): converge current #866 into #867
github-actions[bot] Sep 15, 2026
42fee51
chore(stack): converge semantic RED harness onto #867
seonghobae Sep 16, 2026
09ee432
chore(stack): converge #866 product repair into #867
seonghobae Sep 16, 2026
bbbb7f4
merge: converge #867 on report contract repairs
seonghobae Sep 19, 2026
fc19017
chore(stack): converge #867 onto #866 owner stack
seonghobae Sep 20, 2026
6b2611c
chore(stack): converge #867 onto repaired #865 head
seonghobae Sep 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions frontend/src/components/LeftoverMapPlot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
} from "../leftoverMapPlotAxisShare";
import {
formatLeftoverMapPlotAxisSingular,
leftoverMapCompareAxisBadge,
leftoverMapComparePlotAxisBadge,
leftoverSingularForAxis,
LEFTOVER_MAP_COMPARE_PLOT_AXIS_SINGULAR,
LEFTOVER_MAP_COMPARE_PLOT_AXIS_SINGULAR_SHARE,
Expand Down Expand Up @@ -77,10 +77,12 @@ export type LeftoverMapPlotProps = {
variant?: LeftoverMapPlotVariant;
};

/** Return the four vertices for a diamond marker centered at the persisted plot point. */
function diamondPoints(x: number, y: number, radius: number): string {
return `${x},${y - radius} ${x + radius},${y} ${x},${y + radius} ${x - radius},${y}`;
}

/** Compose one axis caption from persisted share and singular evidence for the requested surface. */
function leftoverMapPlotAxisText(
axisIndex: 1 | 2,
leftoverMapAxes: LeftoverMapAxis[] | undefined,
Expand Down Expand Up @@ -236,7 +238,7 @@ export function LeftoverMapPlot({
const comparisonAxisBadges =
variant === "comparison"
? (leftoverMapAxes ?? [])
.map((axis) => leftoverMapCompareAxisBadge(axis))
.map((axis) => leftoverMapComparePlotAxisBadge(axis))
.filter((badge): badge is NonNullable<typeof badge> => badge !== null)
: [];

Expand Down
53 changes: 53 additions & 0 deletions frontend/src/leftoverMapAxisBadge.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import {
LEFTOVER_MAP_AXIS_BADGE_SINGULAR,
LEFTOVER_MAP_AXIS_BADGE_SINGULAR_ONLY,
} from "./leftoverMapAxisBadge";
import { LEFTOVER_MAP_COMPARE_PLOT_AXIS_SHARE } from "./leftoverMapPlotAxisShare";
import {
leftoverMapCompareAxisBadge,
leftoverMapComparePlotAxisBadge,
LEFTOVER_MAP_COMPARE_AXIS_SHARE,
LEFTOVER_MAP_COMPARE_AXIS_SINGULAR,
LEFTOVER_MAP_COMPARE_AXIS_SINGULAR_SHARE,
Expand Down Expand Up @@ -115,6 +117,57 @@ describe("leftoverMapAxisBadge", () => {
});
});

describe("leftoverMapComparePlotAxisBadge", () => {
it("returns no graphic badge when neither persisted measure is usable", () => {
expect(
leftoverMapComparePlotAxisBadge({
axis_index: 1,
leftover_singular_value: Number.NaN,
leftover_share: Number.POSITIVE_INFINITY,
}),
).toBeNull();
});

it("keeps graphic share-only evidence independent", () => {
expect(
leftoverMapComparePlotAxisBadge({
axis_index: 2,
leftover_singular_value: null,
leftover_share: 0.18,
}),
).toEqual({
template: LEFTOVER_MAP_COMPARE_PLOT_AXIS_SHARE,
values: { axis: 2, share: "18" },
});
});

it("keeps graphic singular-only evidence independent and preserves finite zero", () => {
expect(
leftoverMapComparePlotAxisBadge({
axis_index: 1,
leftover_singular_value: 0,
leftover_share: null,
}),
).toEqual({
template: LEFTOVER_MAP_COMPARE_PLOT_AXIS_SINGULAR,
values: { axis: 1, value: "0.00" },
});
});

it("combines persisted graphic singular and share evidence without deriving either", () => {
expect(
leftoverMapComparePlotAxisBadge({
axis_index: 1,
leftover_singular_value: 1.24,
leftover_share: 0.42,
}),
).toEqual({
template: LEFTOVER_MAP_COMPARE_PLOT_AXIS_SINGULAR_SHARE,
values: { axis: 1, value: "1.24", share: "42" },
});
});
});

describe("leftoverMapCompareAxisBadge", () => {
it("keeps persisted singular and share evidence independent", () => {
expect(
Expand Down
40 changes: 39 additions & 1 deletion frontend/src/leftoverMapPlotAxisSingular.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
/** Project persisted leftover-map singular-value evidence into buyer-visible axis captions. */

import type { LeftoverMapAxis } from "./api";
import { formatLeftoverMapPlotAxisShare } from "./leftoverMapPlotAxisShare";
import {
formatLeftoverMapPlotAxisShare,
LEFTOVER_MAP_COMPARE_PLOT_AXIS_SHARE,
} from "./leftoverMapPlotAxisShare";

export const LEFTOVER_MAP_PLOT_AXIS_SINGULAR =
"leftover-map axis {axis} σ {value}";
Expand Down Expand Up @@ -59,6 +62,41 @@ export function formatLeftoverMapPlotAxisSingular(
return leftoverSingular.toFixed(2);
}

/**
* Compose comparison-graphic axis evidence without deriving one persisted
* measurement from the other. Missing or invalid evidence is omitted
* independently; when both are absent, no badge is rendered.
*/
export function leftoverMapComparePlotAxisBadge(
Comment thread
seonghobae marked this conversation as resolved.
axis: Pick<LeftoverMapAxis, "axis_index"> & {
leftover_share?: LeftoverMapAxis["leftover_share"] | null;
leftover_singular_value?: LeftoverMapAxis["leftover_singular_value"] | null;
},
): LeftoverMapCompareAxisBadge | null {
const singular = formatLeftoverMapPlotAxisSingular(axis.leftover_singular_value);
const share = formatLeftoverMapPlotAxisShare(axis.leftover_share);

if (singular === null && share === null) {
return null;
}
if (singular === null && share !== null) {
return {
template: LEFTOVER_MAP_COMPARE_PLOT_AXIS_SHARE,
values: { axis: axis.axis_index, share },
};
}
if (singular !== null && share === null) {
return {
template: LEFTOVER_MAP_COMPARE_PLOT_AXIS_SINGULAR,
values: { axis: axis.axis_index, value: singular },
};
}
return {
template: LEFTOVER_MAP_COMPARE_PLOT_AXIS_SINGULAR_SHARE,
values: { axis: axis.axis_index, value: singular as string, share: share as string },
};
}

/**
* Compose comparison-strip axis evidence without deriving one persisted
* measurement from the other. Missing or invalid evidence is omitted
Expand Down
44 changes: 44 additions & 0 deletions tests/test_grouping_comparison_graphic_axis_badge_contract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""Executable contract for comparison-graphic axis singular/share composition."""

from pathlib import Path


ROOT = Path(__file__).resolve().parents[1]
SINGULAR_SOURCE = ROOT / "frontend" / "src" / "leftoverMapPlotAxisSingular.ts"
PLOT_SOURCE = ROOT / "frontend" / "src" / "components" / "LeftoverMapPlot.tsx"


def test_comparison_graphic_axis_badge_preserves_singular_when_share_is_missing() -> None:
"""Valid persisted σ remains visible on the comparison graphic without share."""
assert SINGULAR_SOURCE.exists(), "comparison singular-value helper is missing"
source = SINGULAR_SOURCE.read_text(encoding="utf-8")

assert "leftoverMapComparePlotAxisBadge" in source
assert (
'LEFTOVER_MAP_COMPARE_PLOT_AXIS_SINGULAR =\n'
' "leftover map comparison graphic leftover-map axis {axis} σ {value}"'
in source
)
assert "LEFTOVER_MAP_COMPARE_PLOT_AXIS_SINGULAR_SHARE" in source
assert "LEFTOVER_MAP_COMPARE_PLOT_AXIS_SHARE" in source


def test_comparison_graphic_axis_badge_keeps_sigma_and_share_independent() -> None:
"""σ-only, share-only, combined, and empty states remain persisted-data decisions."""
assert SINGULAR_SOURCE.exists(), "comparison singular-value helper is missing"
source = SINGULAR_SOURCE.read_text(encoding="utf-8")

assert "formatLeftoverMapPlotAxisSingular" in source
assert "Number.isFinite" in source
assert "Math.sqrt" not in source
assert "Math.max" not in source


def test_comparison_graphic_renders_graphic_specific_axis_badges() -> None:
"""Comparison plot badges must use graphic naming, not comparison-strip naming."""
assert PLOT_SOURCE.exists(), "comparison plot component is missing"
source = PLOT_SOURCE.read_text(encoding="utf-8")

assert "leftoverMapComparePlotAxisBadge" in source
assert ".map((axis) => leftoverMapComparePlotAxisBadge(axis))" in source
assert ".map((axis) => leftoverMapCompareAxisBadge(axis))" not in source
Loading