Skip to content
Open
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
5 changes: 3 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,13 @@ in the same spirit) -- never against real data, per the hard rule above.
against a live local stack (`make up`) and self-skip without one -- see
[README.md](README.md#local-product-stack-docker-compose).

Period leftover pairs (ADR 0017 / 0018) are computed in
Period leftover pairs (ADR 0017 / 0018 / 0028) are computed in
`lineageweave/leftover_pairs.py` from the residual after a real
GRM/GPCM score, never invented. Missing cells stay out of the
Gabriel factorization. Closest and farthest post–criterion pairs
persist to `report_leftover_pair` and sit above the member list so
a click opens that post.
a click opens that post. A leftover member also names the leftover
criterion next to θ.

`frontend/` has its own toolchain (Node pinned via `frontend/mise.toml`,
pnpm via Corepack -- do not add a second Node package manager or a
Expand Down
3 changes: 2 additions & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,8 @@ bank as the dummy high/low band rows, so comparison-strip click
through opens those DAG posts. Report members include the earliest
open ticket title, status lookup label, and due date when one exists. The home page renders
the actual mean θ, the FIPC delta, the CAT-selected item, leftover
closest/farthest pairs above the member list, and the
closest/farthest pairs above the member list, leftover criterion
badges on matching members, and the
PU / corp / thread comparison -- never a placeholder. TEPP is unchanged.

## Phase 6b: Knowledge Graph as a real Ontology + Semantic Layer
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.d/0.87.0-leftover-member-badge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# 0.87.0 — Leftover badge on the matching report member

## Added

- Report members that are leftover pairs name the leftover criterion
(ADR 0028). After `make seed`, the leftover member reads
**Closest leftover · sales-lead** next to θ; click opens that post.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ All notable changes to this project are documented here. Format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versioning follows
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.87.0] - 2026-08-17

### Added

- Report members that are leftover pairs name the leftover criterion
(ADR 0028). After `make seed`, the leftover member reads
**Closest leftover · sales-lead** next to θ; click opens that post.

## [0.75.0] - 2026-08-17

### Added
Expand Down
43 changes: 43 additions & 0 deletions docs/adr/0028-leftover-pair-on-report-member.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# ADR 0028 — Name leftover on the matching report member

**Decision status:** Accepted
**Date:** 2026-08-17

## Context

ADR 0018 puts leftover pairs above the member list. A buyer who scans
members first still sees only θ and the ticket. The leftover post is
already in that list; it just does not name the leftover criterion.

Do not invent a second leftover store. Do not invent a theta.

## Decision

When an authorized leftover pair names a member post, that member
button shows `Closest leftover · {criterion}` or
`Farthest leftover · {criterion}` and includes the same caption in
its accessible name.

A member that is not a leftover pair stays unmarked. A leftover pair
for a hidden post never reaches the member list (ADR 0017 ABAC).

After `make seed`, the leftover member reads **Closest leftover ·
sales-lead** next to θ; click still opens that post.

## Consequences

Leftover buttons above the list stay. This slice only labels the
already-visible member. Comparison-strip leftover is ADR 0025 / #233.
Opened-post leftover copy is #224.

## Related

Depends on [ADR 0017](0017-persist-lsirm-leftover-pairs.md) and
[ADR 0018](0018-leftover-pair-report-ui.md).

## References

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
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": "0.75.0",
"version": "0.87.0",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1365,7 +1365,14 @@ describe("App, authenticated", () => {
);
expect(farthestPair).toHaveTextContent("d 1.84");
const memberButton = screen.getByRole("button", { name: /open report post: public post/i });
expect(memberButton).toHaveTextContent("Closest leftover · sales-lead");
expect(memberButton).toHaveAccessibleName(
"Open report post: Public post (Closest leftover · sales-lead)",
);
expect(closestPair.compareDocumentPosition(memberButton) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
expect(
screen.getByRole("button", { name: /open report post: specification revision requested/i }),
).toHaveTextContent("Farthest leftover · negative");
expect(
screen.getByRole("button", { name: /open report post: specification revision requested/i }),
).toHaveTextContent("Send Westfield Power the revised specification");
Expand Down
34 changes: 31 additions & 3 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1620,15 +1620,42 @@ function ReportsPanel({
)}
{report.members.length > 0 && (
<ul className="ticket-list">
{report.members.map((member) => (
{report.members.map((member) => {
const leftoverForMember = (report.leftover_pairs ?? []).filter(
(pair) => pair.post_id === member.post_id,
);
const leftoverCaption = leftoverForMember
.map((pair) => {
const kindLabel =
pair.pair_kind === "farthest" ? "Farthest leftover" : "Closest leftover";
return `${kindLabel} · ${criterionShortLabel(pair.criterion_code)}`;
})
.join("; ");
return (
<li key={member.post_id} className="ticket-list-item">
<button
className="post-list-item"
aria-label={`Open report post: ${member.post_title}`}
aria-label={
leftoverCaption
? `Open report post: ${member.post_title} (${leftoverCaption})`
: `Open report post: ${member.post_title}`
}
onClick={() => onSelectPost(member.post_id)}
>
<span className="ticket-title">{member.post_title}</span>
<span className="post-badge">θ {member.theta_eap.toFixed(2)}</span>
{leftoverForMember.map((pair) => {
const kindLabel =
pair.pair_kind === "farthest" ? "Farthest leftover" : "Closest leftover";
return (
<span
key={`${pair.pair_kind}:${pair.criterion_code}`}
className="post-badge"
>
{kindLabel} · {criterionShortLabel(pair.criterion_code)}
</span>
);
})}
{member.ticket_title && (
<span className="post-badge">{member.ticket_title}</span>
)}
Expand All @@ -1642,7 +1669,8 @@ function ReportsPanel({
)}
</button>
</li>
))}
);
})}
</ul>
)}
</li>
Expand Down
2 changes: 1 addition & 1 deletion lineageweave/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,4 @@
"sentence_excerpts",
]

__version__ = "0.75.0"
__version__ = "0.87.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 = "0.75.0"
version = "0.87.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
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading