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 @@ -536,3 +536,5 @@ columns). Do not silently rewrite either historical form. The SHACL
shapes graph (`docs/ontology/lineageweave-kg-shapes.ttl`) is the
closed-world data-validation boundary for DB-to-RDF projections and is
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.
2 changes: 2 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1089,3 +1089,5 @@ so it also covers the multi-entity opposite-order case a per-name lock
would still deadlock on. Every already-cataloged entity still resolves
through the unchanged, lock-free similarity-matching fast path; only
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.
5 changes: 5 additions & 0 deletions CHANGELOG.d/2.51.0-leftover-map-compare-incomplete-item.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## 2.51.0 — Grouping comparison incomplete-item coverage

- Show persisted leftover-map incomplete-item coverage on grouping comparison rows with a distinct accessible label (ADR 0294).
- Preserve API authorization: partial-visibility groupings have no full-population coverage object; the client does not reconstruct or recompute psychometrics.
- Persisted zero remains visible; invalid or contradictory counts are omitted.
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 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.

- Grouping comparison rows now show persisted leftover-map incomplete-post count through `leftoverMapIncompletePostCount` (ADR 0293 / v2.50.0) only for a fully caller-visible persisted grouping. Partial visibility omits the aggregate; valid persisted zero remains visible, and the UI never derives dropped posts from scored-minus-used.

- Period leftover pairs now caption leftover-axis report badges with persisted leftover-map singular values `σ_k` (ADR 0292 / v2.49.0). Missing, non-finite, or negative singular values omit only that `σ` caption; rank-0 zero remains `σ 0.00`, and axis share remains distinct.
Expand Down
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,5 @@ migration replay (ADR 0166), docstring coverage, and the measurement
boundary are all stated in [AGENTS.md](AGENTS.md) -- read it before
changing code, tests, or runtime policy rather than restating anything
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.
13 changes: 13 additions & 0 deletions docs/adr/0294-leftover-map-compare-incomplete-item.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# ADR 0294 — Grouping comparison incomplete-item coverage

**Decision status:** Proposed

## Problem
The grouping comparison strip omits the persisted incomplete-item count even though the full persisted grouping read model already carries authorization-filtered coverage.

## Decision
Render `leftoverMapIncompleteItemCount(row.leftover_map_coverage)` with the distinct accessible label `Leftover map comparison incomplete items`. Reuse the persisted-count validator. Do not derive the count from scored-minus-used, pair count, plotted criteria, or any other client-side proxy.

Only a full persisted grouping authorized by the API may carry the coverage object. Partial visibility must keep coverage absent; the frontend must not reconstruct a hidden full-population denominator or recompute psychometrics from the visible subset. Valid persisted zero remains visible. Missing, negative, non-integer, or contradictory counts fail closed.

This ADR remains Proposed while the PR is Draft. Accepted status requires current-head hosted tests, rendered keyboard/accessibility evidence, canonical eight-locale translation-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 @@ -1389,3 +1389,7 @@ The ONET rows stacked into base branches (#743/#745/#746/#740/#732) reached
`main` together through the #759 promotion; their per-base merge records are
historical evidence only. The job-architecture artifact ship originally via
#749 is now re-verified on `main` from the promotion.

### 2026-09-07 — #825 current-parent reconstruction candidate

Exact parent `#824@499d653ed6e9206249e3f3a07518ad3fc01f14bd` lacks the grouping-comparison incomplete-item note while carrying authorization-filtered persisted coverage. ADR 0294 / v2.51.0 adds that buyer-visible read-model presentation and regression. Candidate evidence is not protected-main/release evidence; current-head browser/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.50.0",
"version": "2.51.0",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
21 changes: 18 additions & 3 deletions frontend/src/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4308,15 +4308,30 @@ describe("App, authenticated", () => {
expect(
within(screen.getByLabelText("Grouping comparison")).queryByLabelText("Leftover map incomplete items"),
).not.toBeInTheDocument();
expect(
within(screen.getByLabelText("Grouping comparison")).getAllByLabelText(
"Leftover map comparison incomplete items",
),
).toHaveLength(2);
expect(
within(screen.getByLabelText("Grouping comparison")).getAllByLabelText(
"Leftover map comparison incomplete items",
)[0],
).toHaveTextContent("Leftover map dropped 0 incomplete criteria");
expect(
within(screen.getByLabelText("Grouping comparison")).queryByLabelText(
"Leftover-map graphic incomplete items",
),
).not.toBeInTheDocument();
expect(
screen.getByRole("button", { name: "Compare Business unit (PU): Demo Report High, mean θ 0.81" }),
).toHaveTextContent("mean θ 0.81");
await userEvent.click(
screen.getByRole("button", { name: "Compare Thread group: A-100, mean θ 0.81" }),
);
expect(screen.getByRole("status")).toHaveTextContent(
"A-100 is the opened grouping. Read its mean θ and member posts below, then open a post.",
);
expect(
screen.getByText("A-100 is the opened grouping. Read its mean θ and member posts below, then open a post."),
).toBeInTheDocument();
expect(
screen.getByRole("button", {
name: /open leftover closest pair from comparison: public post/i,
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ import {
LEFTOVER_MAP_COMPARE_COVERAGE_LABEL,
LEFTOVER_MAP_COMPARE_ITEM_COVERAGE_LABEL,
LEFTOVER_MAP_COMPARE_INCOMPLETE_POST_LABEL,
LEFTOVER_MAP_COMPARE_INCOMPLETE_ITEM_LABEL,
LEFTOVER_MAP_LIST_COVERAGE_LABEL,
LEFTOVER_MAP_LIST_INCOMPLETE_ITEM_LABEL,
LEFTOVER_MAP_LIST_INCOMPLETE_POST_LABEL,
Expand Down Expand Up @@ -4004,6 +4005,7 @@ function ReportsPanel({
const comparisonCoverageCounts = leftoverMapCoverageCounts(row.leftover_map_coverage);
const comparisonItemCoverageCounts = leftoverMapItemCoverageCounts(row.leftover_map_coverage);
const comparisonIncompletePostCount = leftoverMapIncompletePostCount(row.leftover_map_coverage);
const comparisonIncompleteItemCount = leftoverMapIncompleteItemCount(row.leftover_map_coverage);
return (
<li key={`${row.grouping_kind}:${row.grouping_key}`} className="ticket-list-item">
<button
Expand Down Expand Up @@ -4049,6 +4051,11 @@ function ReportsPanel({
{tf(LEFTOVER_MAP_PLOT_INCOMPLETE_POST, comparisonIncompletePostCount)}
</p>
) : null}
{comparisonIncompleteItemCount !== null ? (
<p className="post-meta" role="note" aria-label={t(LEFTOVER_MAP_COMPARE_INCOMPLETE_ITEM_LABEL)}>
{tf(LEFTOVER_MAP_PLOT_INCOMPLETE_ITEM, comparisonIncompleteItemCount)}
</p>
) : null}
{row.leftover_pairs && row.leftover_pairs.length > 0 && (
<ul className="ticket-list" aria-label={`Leftover pairs for ${row.grouping_label}`}>
{row.leftover_pairs.map((pair) => {
Expand Down
11 changes: 11 additions & 0 deletions frontend/src/i18n.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ describe("i18n", () => {
"Leftover map comparison coverage",
"Leftover map comparison item coverage",
"Leftover map comparison incomplete posts",
"Leftover map comparison incomplete items",
"Leftover-map graphic item coverage",
"Leftover map item coverage",
"Leftover map incomplete posts",
Expand Down Expand Up @@ -416,6 +417,16 @@ describe("i18n", () => {
expect(t("Leftover map comparison incomplete posts")).toBe(expected);
});

it.each([
["ko", "잔여 지도 비교 불완전 기준"],
["zh", "残差地图比较不完整准则"],
["ja", "残差マップの比較不完全基準"],
["vi", "Tiêu chí không đầy đủ so sánh trên bản đồ phần dư"],
] as const)("formats leftover map comparison incomplete items label in %s", (locale, expected) => {
setLocale(locale);
expect(t("Leftover map comparison incomplete items")).toBe(expected);
});

it.each([
["ko", "잔여 지도 그림 기준 포함 범위"],
["zh", "残差图图形准则覆盖范围"],
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ const TRANSLATIONS: Partial<Record<Locale, Record<string, string>>> = {
"Leftover map comparison coverage": "잔여 지도 비교 포함 범위",
"Leftover map comparison item coverage": "잔여 지도 비교 기준 포함 범위",
"Leftover map comparison incomplete posts": "잔여 지도 비교 불완전 글",
"Leftover map comparison incomplete items": "잔여 지도 비교 불완전 기준",
"Leftover map item coverage": "잔여 지도 기준 포함 범위",
"Leftover map incomplete posts": "잔여 지도 불완전 글",
"Leftover map incomplete items": "잔여 지도 불완전 기준",
Expand Down Expand Up @@ -883,6 +884,7 @@ const TRANSLATIONS: Partial<Record<Locale, Record<string, string>>> = {
"Leftover map comparison coverage": "残差地图比较覆盖范围",
"Leftover map comparison item coverage": "残差地图比较准则覆盖范围",
"Leftover map comparison incomplete posts": "残差地图比较不完整帖文",
"Leftover map comparison incomplete items": "残差地图比较不完整准则",
"Leftover map item coverage": "残差地图准则覆盖范围",
"Leftover map incomplete posts": "残差地图不完整帖文",
"Leftover map incomplete items": "残差地图不完整准则",
Expand Down Expand Up @@ -1516,6 +1518,7 @@ const TRANSLATIONS: Partial<Record<Locale, Record<string, string>>> = {
"Leftover map comparison coverage": "残差マップの比較対象範囲",
"Leftover map comparison item coverage": "残差マップの比較基準対象範囲",
"Leftover map comparison incomplete posts": "残差マップの比較不完全投稿",
"Leftover map comparison incomplete items": "残差マップの比較不完全基準",
"Leftover map item coverage": "残差マップの基準対象範囲",
"Leftover map incomplete posts": "残差マップの不完全投稿",
"Leftover map incomplete items": "残差マップの不完全基準",
Expand Down Expand Up @@ -2128,6 +2131,7 @@ const TRANSLATIONS: Partial<Record<Locale, Record<string, string>>> = {
"Leftover map comparison coverage": "Phạm vi so sánh bản đồ phần dư",
"Leftover map comparison item coverage": "Phạm vi so sánh tiêu chí bản đồ phần dư",
"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 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
4 changes: 3 additions & 1 deletion frontend/src/leftoverMapCoverage.ts
Original file line number Diff line number Diff line change
@@ -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 / ADR 0290 / ADR 0291). */
/** 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 0290 / ADR 0291 / ADR 0294). */

import type { LeftoverMapCoverage } from "./api";

Expand All @@ -12,6 +12,8 @@ export const LEFTOVER_MAP_COMPARE_ITEM_COVERAGE_LABEL = "Leftover map comparison

export const LEFTOVER_MAP_COMPARE_INCOMPLETE_POST_LABEL = "Leftover map comparison incomplete posts";

export const LEFTOVER_MAP_COMPARE_INCOMPLETE_ITEM_LABEL = "Leftover map comparison incomplete items";

export const LEFTOVER_MAP_PLOT_COVERAGE =
"Leftover map used {used} of {scored} scored posts (complete-case)";

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.50.0"
__version__ = "2.51.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.50.0"
version = "2.51.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
14 changes: 14 additions & 0 deletions tests/test_adr_0294_governance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]


def test_incomplete_item_comparison_uses_distinct_proposed_adr_0294() -> None:
assert (ROOT / "docs/adr/0293-leftover-map-compare-incomplete-post.md").exists()
path = ROOT / "docs/adr/0294-leftover-map-compare-incomplete-item.md"
assert path.exists()
text = path.read_text(encoding="utf-8")
assert text.startswith("# ADR 0294 —")
assert "**Decision status:** Proposed" in text
assert "**Decision status:** Accepted" not in text
assert "full persisted grouping" in text and "Partial visibility" in text