Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -540,3 +540,5 @@ published beside the ontology.
ADR 0294 keeps grouping-comparison incomplete-item coverage in the LineageWeave read-model boundary: consume only authorization-filtered persisted coverage for a full grouping, omit partial visibility, and never recompute psychometrics in the client.

ADR 0295 keeps grouping-comparison reconstruction in the LineageWeave read-model/UI boundary: format only persisted `R̂`, expose the value in the pair button accessible name, and never derive psychometric reconstruction from UI-visible proxies.

ADR 0296 adds persisted grouping-comparison explained-leftover share `e` in the LineageWeave read-model/UI boundary: expose it in the pair button accessible name, keep the duplicate visible badge presentation-only, and never derive or clamp psychometric values.
2 changes: 2 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1093,3 +1093,5 @@ the rare creation branch serializes.
ADR 0294 adds persisted incomplete-item coverage to grouping comparison presentation while the API remains the full-group visibility authority; the frontend formats only an admitted persisted count.

ADR 0295 adds persisted `R̂` to grouping-comparison pair buttons. The explicit button accessible name carries the reconstruction label/value; the visible duplicate badge is presentation-only. Psychometric computation remains owned by fast-mlsirm.

ADR 0296 adds persisted explained-leftover share `e = R̂²/R²` to grouping-comparison pair actions; the button accessible name is authoritative for assistive technology and fast-mlsirm remains psychometric owner.
5 changes: 5 additions & 0 deletions CHANGELOG.d/2.53.0-leftover-map-compare-explained-share.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## 2.53.0 — Grouping comparison explained-leftover share

- Show persisted `e = R̂²/R²` on grouping-comparison pair buttons (ADR 0296).
- Put the localized metric/value in the button accessible name; the duplicate visible badge is `aria-hidden`.
- Missing/non-finite values omit only this metric; zero and values above 1 are preserved and no proxy is derived.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ All notable changes to this project are documented here. Format follows

### Added

- Grouping comparison pair buttons now expose persisted explained-leftover share `e = R̂²/R²` (ADR 0296 / v2.53.0). The visible duplicate badge is accessibility-hidden while the actionable button name includes the localized label and formatted persisted value. Missing/non-finite `e` omits only the suffix; finite zero and values above 1 remain explicit without clamping or client-side derivation.

- Grouping comparison leftover-pair buttons now expose persisted leftover-map reconstruction `R̂` (ADR 0295 / v2.52.0). The visible badge is decorative to accessibility APIs; the button accessible name carries the reconstruction label and value, so the explicit button `aria-label` cannot suppress the metric. Missing/non-finite `R̂` omits only that value and no client-side proxy is invented.

- Grouping comparison rows now show persisted leftover-map incomplete-item count through `leftoverMapIncompleteItemCount` (ADR 0294 / v2.51.0) only for fully caller-visible persisted groupings. Partial visibility remains omitted by the API; valid zero stays visible, and the UI never derives dropped criteria from scored-minus-used.
Expand Down
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,5 @@ here.
ADR 0294 is the Proposed grouping-comparison incomplete-item presentation contract; preserve server-side full-group visibility gating and do not infer dropped criteria in the frontend.

ADR 0295 is the Proposed grouping-comparison reconstruction contract: persisted `R̂` only, no client inference, accessible-name exposure required despite the button's explicit aria-label.

ADR 0296 is the Proposed grouping-comparison explained-share contract: persisted `e` only, accessible-name exposure required, no client derivation or clamping.
11 changes: 11 additions & 0 deletions docs/adr/0296-leftover-map-compare-explained-share.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# ADR 0296 — Grouping comparison explained-leftover share

**Decision status:** Proposed

## Problem
The serialized comparison surface omits persisted explained-leftover share `e = R̂²/R²`. The historical implementation rendered a descendant badge inside a button with an explicit accessible name, so the visible metric was not reliably announced as part of the action.

## Decision
Format only persisted `leftover_map_explained_share` through `formatLeftoverMapExplainedShare`. Append the localized label and formatted value to the pair button accessible name, and mark the duplicate visible badge `aria-hidden`. Missing/non-finite values omit only this suffix; finite zero and values above 1 remain explicit. Never derive or clamp `e` from `R̂`, `R`, distance, coordinates, residuals, rank, coverage, or visible-subset proxies. fast-mlsirm remains psychometric owner.

This ADR remains Proposed while Draft. Acceptance requires current-head hosted tests, rendered keyboard/screen-reader/a11y evidence, canonical eight-locale ledger convergence, independent approval, and normal protected-branch merge.
4 changes: 4 additions & 0 deletions docs/product-technical-gap-baseline.md
Original file line number Diff line number Diff line change
Expand Up @@ -1397,3 +1397,7 @@ Exact parent `#824@499d653ed6e9206249e3f3a07518ad3fc01f14bd` lacks the grouping-
### 2026-09-07 — #826 exact-parent reconstruction candidate

Exact parent `#825@b73b10e3079e77f3e62235b2b709dc8a3f450292` omits persisted `R̂` from grouping-comparison pair rows. ADR 0295 / v2.52.0 restores that valid read-model delta and repairs the historical accessibility defect by carrying the label/value in the button accessible name. Candidate evidence is not protected-main/release evidence; current-head browser/screen-reader/a11y, canonical eight-locale ledger consumption, independent review, and normal protected merge remain outstanding.

### 2026-09-07 — #827 exact-parent reconstruction candidate

Exact parent `#826@66a7750a10ad0e9526716d382707f99528a75d90` omits persisted grouping-comparison explained-leftover share. ADR 0296 / v2.53.0 restores that valid delta and fixes its historical accessible-name defect. Candidate evidence is not protected-main/release evidence; current-head browser/screen-reader/a11y, canonical eight-locale ledger consumption, independent review, and normal protected merge remain outstanding.
2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "frontend",
"private": true,
"version": "2.52.0",
"version": "2.53.0",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -923,6 +923,7 @@ describe("App, authenticated", () => {
leftover_distance: 0.12,
leftover_residual: 0.4,
leftover_map_reconstruction: 0.248,
leftover_map_explained_share: 0.76,
},
],
leftover_map_coverage: {
Expand Down Expand Up @@ -4342,6 +4343,10 @@ describe("App, authenticated", () => {
name: /open leftover closest pair from comparison: public post.*leftover map comparison reconstruction R̂ \+0\.25/i,
});
expect(reconstructionPair).toHaveTextContent("R̂ +0.25");
const explainedSharePair = screen.getByRole("button", {
name: /open leftover closest pair from comparison: public post.*leftover map comparison reconstruction R̂ \+0\.25.*leftover map comparison explained leftover share R̂²\/R² 0\.76/i,
});
expect(explainedSharePair).toHaveTextContent("R̂²/R² 0.76");
await waitFor(() =>
expect(fetchMock).toHaveBeenCalledWith(
expect.stringContaining("/api/reports/thread_group/2026-W02"),
Expand Down
16 changes: 16 additions & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ import {
formatLeftoverMapReconstruction,
LEFTOVER_MAP_COMPARE_RECONSTRUCTION_LABEL,
} from "./leftoverMapReconstruction";
import {
formatLeftoverMapExplainedShare,
LEFTOVER_MAP_COMPARE_EXPLAINED_SHARE_LABEL,
} from "./leftoverMapExplainedShare";
import "./App.css";

const AdminPanel = lazy(() => import("./components/AdminPanel").then((module) => ({ default: module.AdminPanel })));
Expand Down Expand Up @@ -4073,10 +4077,17 @@ function ReportsPanel({
const reconstruction = formatLeftoverMapReconstruction(
pair.leftover_map_reconstruction,
);
const explainedShare = formatLeftoverMapExplainedShare(
pair.leftover_map_explained_share,
);
Comment on lines +4080 to +4082

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Explained-share badge never appears

Real comparison responses omit leftover_map_explained_share, so formatLeftoverMapExplainedShare always receives undefined. The new badge appears only with mocked test data.

Prompt for agents
The grouping comparison UI now consumes leftover_map_explained_share in frontend/src/App.tsx, but backend/app/report_ingestion.py fetch_period_comparison selects and serializes only leftover_map_reconstruction for each leftover pair. Add the persisted explained-share column to that comparison query and payload, then extend the comparison API test to verify finite values are returned and null values remain null. Keep the existing authorization filtering in backend/app/main.py unchanged.
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

const pairAccessibleName = `Open leftover ${pair.pair_kind} pair from comparison: ${pair.post_title} · ${criterion}${
reconstruction
? ` · ${t(LEFTOVER_MAP_COMPARE_RECONSTRUCTION_LABEL)} ${reconstruction}`
: ""
}${
explainedShare
? ` · ${t(LEFTOVER_MAP_COMPARE_EXPLAINED_SHARE_LABEL)} ${explainedShare}`
: ""
}`;
return (
<li
Expand Down Expand Up @@ -4109,6 +4120,11 @@ function ReportsPanel({
{reconstruction}
</span>
) : null}
{explainedShare ? (
<span className="post-badge" aria-hidden="true">
{explainedShare}
</span>
) : null}
</button>
</li>
);
Expand Down
1 change: 1 addition & 0 deletions frontend/src/i18n.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ describe("i18n", () => {
"Leftover map comparison incomplete posts",
"Leftover map comparison incomplete items",
"Leftover map comparison reconstruction",
"Leftover map comparison explained leftover share",
"Leftover-map graphic item coverage",
"Leftover map item coverage",
"Leftover map incomplete posts",
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ const TRANSLATIONS: Partial<Record<Locale, Record<string, string>>> = {
"Leftover map comparison incomplete posts": "잔여 지도 비교 불완전 글",
"Leftover map comparison incomplete items": "잔여 지도 비교 불완전 기준",
"Leftover map comparison reconstruction": "잔여 지도 비교 재구성",
"Leftover map comparison explained leftover share": "잔여 지도 비교 설명 잔여 비율",
"Leftover map item coverage": "잔여 지도 기준 포함 범위",
"Leftover map incomplete posts": "잔여 지도 불완전 글",
"Leftover map incomplete items": "잔여 지도 불완전 기준",
Expand Down Expand Up @@ -887,6 +888,7 @@ const TRANSLATIONS: Partial<Record<Locale, Record<string, string>>> = {
"Leftover map comparison incomplete posts": "残差地图比较不完整帖文",
"Leftover map comparison incomplete items": "残差地图比较不完整准则",
"Leftover map comparison reconstruction": "残差地图比较重建",
"Leftover map comparison explained leftover share": "残差地图比较已解释残差占比",
"Leftover map item coverage": "残差地图准则覆盖范围",
"Leftover map incomplete posts": "残差地图不完整帖文",
"Leftover map incomplete items": "残差地图不完整准则",
Expand Down Expand Up @@ -1522,6 +1524,7 @@ const TRANSLATIONS: Partial<Record<Locale, Record<string, string>>> = {
"Leftover map comparison incomplete posts": "残差マップの比較不完全投稿",
"Leftover map comparison incomplete items": "残差マップの比較不完全基準",
"Leftover map comparison reconstruction": "残差マップの比較再構成",
"Leftover map comparison explained leftover share": "残差マップの比較説明済み残差比率",
"Leftover map item coverage": "残差マップの基準対象範囲",
"Leftover map incomplete posts": "残差マップの不完全投稿",
"Leftover map incomplete items": "残差マップの不完全基準",
Expand Down Expand Up @@ -2136,6 +2139,7 @@ const TRANSLATIONS: Partial<Record<Locale, Record<string, string>>> = {
"Leftover map comparison incomplete posts": "Bài không đầy đủ so sánh trên bản đồ phần dư",
"Leftover map comparison incomplete items": "Tiêu chí không đầy đủ so sánh trên bản đồ phần dư",
"Leftover map comparison reconstruction": "Tái dựng so sánh bản đồ phần dư",
"Leftover map comparison explained leftover share": "Tỷ phần phần dư được giải thích khi 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ư",
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/leftoverMapExplainedShare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
export const LEFTOVER_MAP_EXPLAINED_SHARE_ACTION =
"Leftover map leaves explained leftover share {value} of raw residual after IRT main effects. Open this post to read {criterion}.";

export const LEFTOVER_MAP_COMPARE_EXPLAINED_SHARE_LABEL =
"Leftover map comparison explained leftover share";

export function formatLeftoverMapExplainedShare(
value: number | null | undefined,
): string | null {
Expand Down
2 changes: 1 addition & 1 deletion lineageweave/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,4 @@
"serialize_lineage_analysis_result",
]

__version__ = "2.52.0"
__version__ = "2.53.0"
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "lineageweave"
version = "2.52.0"
version = "2.53.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" }
Expand Down
8 changes: 8 additions & 0 deletions tests/test_adr_0296_governance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from pathlib import Path
ROOT=Path(__file__).resolve().parents[1]
def test_explained_share_comparison_uses_distinct_proposed_adr_0296() -> None:
assert (ROOT/'docs/adr/0295-leftover-map-compare-reconstruction.md').exists()
text=(ROOT/'docs/adr/0296-leftover-map-compare-explained-share.md').read_text(encoding='utf-8')
assert '**Decision status:** Proposed' in text
assert '**Decision status:** Accepted' not in text
assert 'accessible name' in text and 'fast-mlsirm' in text