diff --git a/AGENTS.md b/AGENTS.md index da1b19520..347e41661 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -851,6 +851,11 @@ returns a leftover-map origin leftover-map item coordinate caption independently `leftover-map criterion {label} at leftover-map origin ζ {item}`. Rank-0 unused axes still name leftover-map origin `(0.00, 0.00)`. Do not invent leftover-map origin from leftover-map person coordinates `ξ`. leftoverMapComparePlotCriterionBadge stays leftover-map comparison graphic leftover-map criterion leftover-map item coordinate keys. leftoverMapPlotPostBadge stays leftover-map graphic leftover-map post leftover-map person coordinate keys this increment. +ADR 0348 captions leftover-map graphic leftover-map post leftover-map origin leftover-map person coordinates with leftover-map origin when leftoverMapPlotPostBadge +returns a leftover-map origin leftover-map person coordinate caption independently of leftover-map criterion leftover-map item coordinates. Distinct accessible name +`Open leftover-map post {title} at leftover-map origin ξ {person}`. Rank-0 unused axes still name leftover-map origin `(0.00, 0.00)`. +Do not invent leftover-map origin from leftover-map item coordinates `ζ`. leftoverMapComparePlotPostBadge stays leftover-map comparison graphic leftover-map +post leftover-map person coordinate keys. leftoverMapPlotCriterionBadge stays leftover-map graphic leftover-map criterion leftover-map origin leftover-map item coordinate keys this increment. 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 @@ -1034,6 +1039,8 @@ Leftover-map leftover-axis origin ticks independently of leftover-map axis share (ADR 0346) captions leftover-map leftover-axis origin ticks when leftoverMapAxisTickBadge returns a leftover-map origin tick caption. Leftover-map graphic leftover-map criterion leftover-map origin leftover-map item coordinates independently of leftover-map person coordinates (ADR 0347) captions leftover-map graphic leftover-map criterion leftover-map origin leftover-map item coordinates when leftoverMapPlotCriterionBadge returns a leftover-map origin leftover-map item coordinate caption. +Leftover-map graphic leftover-map post leftover-map origin leftover-map person coordinates independently of leftover-map criterion leftover-map item coordinates +(ADR 0348) captions leftover-map graphic leftover-map post leftover-map origin leftover-map person coordinates when leftoverMapPlotPostBadge returns a leftover-map origin leftover-map person coordinate caption. 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) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 40609cbe2..e6bf0fd85 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -819,6 +819,8 @@ ADR 0346 captions leftover-map leftover-axis origin ticks when leftoverMapAxisTi returns a leftover-map origin tick caption independently of leftover-map axis share and leftover-map singular values. ADR 0347 captions leftover-map graphic leftover-map criterion leftover-map origin leftover-map item coordinates when leftoverMapPlotCriterionBadge returns a leftover-map origin leftover-map item coordinate caption independently of leftover-map person coordinates. +ADR 0348 captions leftover-map graphic leftover-map post leftover-map origin leftover-map person coordinates when leftoverMapPlotPostBadge +returns a leftover-map origin leftover-map person coordinate caption independently of leftover-map criterion leftover-map item coordinates. 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 diff --git a/CHANGELOG.d/2.105.0-leftover-map-plot-post-origin-badge.md b/CHANGELOG.d/2.105.0-leftover-map-plot-post-origin-badge.md new file mode 100644 index 000000000..a77a4378a --- /dev/null +++ b/CHANGELOG.d/2.105.0-leftover-map-plot-post-origin-badge.md @@ -0,0 +1,12 @@ +## 2.105.0 — Leftover-map origin on leftover-map graphic leftover-map post leftover-map person coordinates independently of leftover-map criterion leftover-map item coordinates + +- Caption leftover-map graphic leftover-map post leftover-map origin leftover-map person coordinates through leftoverMapPlotPostBadge + independently of leftover-map criterion leftover-map item coordinates (ADR 0348). After `make seed`, leftover-map graphic leftover-map + post leftover-map origin leftover-map person coordinates name leftover-map origin when leftoverMapPlotCoordinatePairIsOrigin returns true. Distinct accessible name + `Open leftover-map post {title} at leftover-map origin ξ {person}`. Rank-0 unused axes still name leftover-map origin `(0.00, 0.00)`. + Non-origin leftover-map graphic leftover-map post leftover-map person coordinates stay + `Open leftover-map post {title} at ξ {person}`. leftoverMapComparePlotPostBadge stays leftover-map comparison graphic leftover-map + post leftover-map person coordinate keys. leftoverMapPlotCriterionBadge stays leftover-map graphic leftover-map criterion leftover-map + origin leftover-map item coordinate keys this increment. Do not invent leftover-map origin from leftover-map item coordinates `ζ`. + Do not mix #877. A finite negative leftover is shown, never clamped. Never invent a leftover score. + Never invent a theta. No new columns. diff --git a/CHANGELOG.md b/CHANGELOG.md index aa651f107..78e3264ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,16 @@ All notable changes to this project are documented here. Format follows ### Added +- Period leftover pairs now caption leftover-map graphic leftover-map post leftover-map origin leftover-map person coordinates + through leftoverMapPlotPostBadge independently of leftover-map criterion leftover-map item coordinates (ADR 0348 / v2.105.0). After `make seed`, leftover-map graphic leftover-map + post leftover-map origin leftover-map person coordinates name leftover-map origin when leftoverMapPlotCoordinatePairIsOrigin returns true. Distinct accessible name + `Open leftover-map post {title} at leftover-map origin ξ {person}`. Rank-0 unused axes still name leftover-map origin `(0.00, 0.00)`. + Non-origin leftover-map graphic leftover-map post leftover-map person coordinates stay + `Open leftover-map post {title} at ξ {person}`. leftoverMapComparePlotPostBadge stays leftover-map comparison graphic leftover-map + post leftover-map person coordinate keys. leftoverMapPlotCriterionBadge stays leftover-map graphic leftover-map criterion leftover-map + origin leftover-map item coordinate keys this increment. Do not invent leftover-map origin from leftover-map item coordinates `ζ`. + Do not mix #877. A finite negative leftover is shown, never clamped. Never invent a leftover score or a theta. No new columns. + - Period leftover pairs now caption leftover-map graphic leftover-map criterion leftover-map origin leftover-map item coordinates through leftoverMapPlotCriterionBadge independently of leftover-map person coordinates (ADR 0347 / v2.104.0). After `make seed`, leftover-map graphic leftover-map criterion leftover-map origin leftover-map item coordinates name leftover-map origin when leftoverMapPlotCoordinatePairIsOrigin returns true. Distinct accessible name diff --git a/docs/adr/0338-leftover-map-plot-post-coordinates.md b/docs/adr/0338-leftover-map-plot-post-coordinates.md index 46aa9630f..f3b4bebed 100644 --- a/docs/adr/0338-leftover-map-plot-post-coordinates.md +++ b/docs/adr/0338-leftover-map-plot-post-coordinates.md @@ -4,7 +4,9 @@ **Date:** 2026-08-31 **Amended by:** [ADR 0339](0339-leftover-map-list-post-coordinates.md) -(leftover-map person coordinates on leftover-map pair leftover-map post independently of leftover-map pair leftover-map criterion leftover-map item coordinates) +(leftover-map person coordinates on leftover-map pair leftover-map post independently of leftover-map pair leftover-map criterion leftover-map item coordinates); +[ADR 0348](0348-leftover-map-plot-post-origin-badge.md) +(leftover-map origin on leftover-map graphic leftover-map post leftover-map person coordinates independently of leftover-map criterion leftover-map item coordinates) Amends leftover-map graphic display of persisted coordinates ([ADR 0268](0268-leftover-map-graphic-display.md)), leftover-map diff --git a/docs/adr/0348-leftover-map-plot-post-origin-badge.md b/docs/adr/0348-leftover-map-plot-post-origin-badge.md new file mode 100644 index 000000000..3ceaad62c --- /dev/null +++ b/docs/adr/0348-leftover-map-plot-post-origin-badge.md @@ -0,0 +1,87 @@ +# ADR 0348 — Name leftover-map origin on leftover-map graphic leftover-map post leftover-map person coordinates independently of leftover-map criterion leftover-map item coordinates + +**Decision status:** Accepted +**Date:** 2026-08-31 + +Amends leftover-map person coordinates on leftover-map graphic leftover-map post markers independently of leftover-map comparison graphic leftover-map post markers +([ADR 0338](0338-leftover-map-plot-post-coordinates.md)). Independent of leftover-map graphic leftover-map criterion leftover-map origin leftover-map item coordinates independently of leftover-map person coordinates +([ADR 0347](0347-leftover-map-plot-criterion-origin-badge.md)). Independent of leftover-map leftover-axis origin ticks independently of leftover-map axis share and leftover-map singular values +([ADR 0346](0346-leftover-map-axis-tick-origin-badge.md)). Independent of leftover-map leftover-axis origin ticks on the competing origin-tick stack (do not mix #877). + +## Context + +ADR 0338 already names leftover-map graphic leftover-map post leftover-map person coordinates +`Open leftover-map post {title} at ξ {person}`. Rank-0 unused axes still persist leftover-map +origin `(0.00, 0.00)` (`formatSignedLeftoverValue(0)` emits no plus). Origin leftover-map +person coordinates still interpolate leftover-map origin only through leftover-map post leftover-map +person coordinate keys. A missing or non-finite leftover-map person coordinate pair omits independently +of leftover-map item coordinates `ζ`. A finite negative leftover is shown, never clamped. +Do not invent leftover-map origin from leftover-map item coordinates `ζ`. leftoverMapComparePlotPostBadge stays +`Open leftover map comparison graphic leftover-map post {title} at ξ {person}` +this increment. leftoverMapPlotCriterionBadge stays leftover-map graphic leftover-map criterion leftover-map +origin leftover-map item coordinate keys this increment. + +This increment names leftover-map graphic leftover-map post leftover-map origin leftover-map +person coordinates as leftoverMapPlotPostBadge. Leftover-map graphic leftover-map post leftover-map origin leftover-map +person coordinates stay +`Open leftover-map post {title} at leftover-map origin ξ {person}` +when leftoverMapPlotCoordinatePairIsOrigin returns true, so they stay distinct from leftover-map +graphic leftover-map post leftover-map person coordinates +`Open leftover-map post {title} at ξ {person}`, +leftover-map comparison graphic leftover-map post leftover-map person coordinates +`Open leftover map comparison graphic leftover-map post {title} at ξ {person}`, +and leftover-map graphic leftover-map criterion leftover-map origin leftover-map item coordinates +`leftover-map criterion {label} at leftover-map origin ζ {item}`. +It does not add columns. Do not invent a leftover score. Do not invent a theta. + +This protected increment uses **0348** so it does not collide with leftover-map graphic leftover-map +criterion leftover-map origin leftover-map item coordinates independently of leftover-map person coordinates +(0347) or leftover-map leftover-axis origin ticks (#877). + +## Decision + +On leftover-map graphic leftover-map post markers, caption leftover-map origin +when leftoverMapPlotCoordinatePairIsOrigin returns true. Rank-0 unused axes still name leftover-map +origin `(0.00, 0.00)`. Non-origin leftover-map person coordinates stay leftover-map post leftover-map +person coordinate keys. leftoverMapComparePlotPostBadge, leftoverMapPlotCriterionBadge, +leftoverMapListPostBadge, leftoverMapCompareListPostBadge, leftoverMapPlotTickAxisBadge, +leftoverMapComparePlotTickAxisBadge, leftoverMapCompareAxisTickBadge, and leftoverMapAxisTickBadge +do not change leftover-map origin naming this increment. + +Do not add SQL. Do not edit shipped migrations. Do not invent a leftover +score. Do not invent a theta. + +## Consequences + +After `make seed`, leftover-map graphic leftover-map post leftover-map origin leftover-map person +coordinates name leftover-map origin independently of leftover-map criterion leftover-map item coordinates. Rank-0 unused +axes still name leftover-map origin `(0.00, 0.00)`. leftoverMapComparePlotPostBadge stays leftover-map +comparison graphic leftover-map post leftover-map person coordinate keys. leftoverMapPlotCriterionBadge +stays leftover-map graphic leftover-map criterion leftover-map origin leftover-map item coordinate keys. leftover-map graphic leftover-map +post leftover-map person coordinates independently of leftover-map comparison graphic leftover-map post markers +(ADR 0338) remain. + +## Related + +Independent of leftover-map graphic leftover-map criterion leftover-map origin leftover-map item +coordinates independently of leftover-map person coordinates +([ADR 0347](0347-leftover-map-plot-criterion-origin-badge.md)). Independent of leftover-map +person coordinates on leftover-map graphic leftover-map post markers independently of leftover-map +comparison graphic leftover-map post markers ([ADR 0338](0338-leftover-map-plot-post-coordinates.md)). Independent of leftover-map +leftover-axis origin ticks independently of leftover-map axis share and leftover-map singular values +([ADR 0346](0346-leftover-map-axis-tick-origin-badge.md)). Independent of leftover-map +comparison graphic leftover-map post leftover-map person coordinates independently of leftover-map +criterion leftover-map item coordinates ([ADR 0337](0337-leftover-map-compare-plot-post-coordinates.md)). + +## 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.) diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index ec465adcf..48dad4fcc 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -1,5 +1,15 @@ # Product & Technical Gap Baseline +> Exact-head loop overlay: 2026-09-01 02:20 KST. Protected `main` is +> `cb187cadee5fb6c46d8a944815ccc154a1e028d1`. Writes work. Do not +> self-approve. #891 leftover-map graphic leftover-map criterion leftover-map origin leftover-map +> item coordinates independently of leftover-map person coordinates (v2.104.0 / ADR 0347) is +> `47dd988`, stacked on #889 @ `4bfb490`. Independent APPROVE is missing (Devin COMMENTED, +> not APPROVE). Next buyer increment on this cycle: leftover-map graphic leftover-map post leftover-map origin leftover-map +> person coordinates independently of leftover-map criterion leftover-map item coordinates as leftoverMapPlotPostBadge (ADR 0348 / v2.105.0) delivered locally on +> `feat/leftover-map-plot-post-origin-badge-v21050`. Distinct accessible name +> `Open leftover-map post {title} at leftover-map origin ξ {person}`. Rank-0 unused axes still name leftover-map origin `(0.00, 0.00)`. leftoverMapComparePlotPostBadge stays leftover-map comparison graphic leftover-map post leftover-map person coordinate keys. leftoverMapPlotCriterionBadge stays leftover-map graphic leftover-map criterion leftover-map origin leftover-map item coordinate keys. Do not invent leftover-map origin from leftover-map item coordinates `ζ`. Do not mix #877. Stack onto #891. Leave open for independent review. Do not squash-merge. Do not self-approve. Issues #79 and #87 stay open. Do not merge #808 without independent APPROVE. #96 closed as weaker duplicate of #91. + > Exact-head loop overlay: 2026-08-31 19:35 KST. Protected `main` is > `cb187cadee5fb6c46d8a944815ccc154a1e028d1`. Writes work. Do not > self-approve. #889 leftover-map leftover-axis origin ticks independently of leftover-map diff --git a/frontend/package.json b/frontend/package.json index 8401b2758..b94b545db 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "frontend", "private": true, - "version": "2.104.0", + "version": "2.105.0", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/components/LeftoverMapPlot.test.tsx b/frontend/src/components/LeftoverMapPlot.test.tsx index 01c021b5f..9ccf3aaa6 100644 --- a/frontend/src/components/LeftoverMapPlot.test.tsx +++ b/frontend/src/components/LeftoverMapPlot.test.tsx @@ -170,8 +170,13 @@ describe("LeftoverMapPlot", () => { />, ); expect( - screen.getByRole("button", { name: "Open leftover-map post Public post at ξ (0.00, 0.00)" }), + screen.getByRole("button", { + name: "Open leftover-map post Public post at leftover-map origin ξ (0.00, 0.00)", + }), ).toBeInTheDocument(); + expect( + screen.queryByRole("button", { name: "Open leftover-map post Public post at ξ (0.00, 0.00)" }), + ).not.toBeInTheDocument(); expect(screen.getByLabelText("leftover-map criterion sales-lead at leftover-map origin ζ (0.00, 0.00)")).toBeInTheDocument(); expect(screen.queryByLabelText("leftover-map criterion sales-lead at ζ (0.00, 0.00)")).not.toBeInTheDocument(); expect(screen.queryByLabelText("Criterion ζ sales-lead")).not.toBeInTheDocument(); @@ -1007,6 +1012,11 @@ describe("LeftoverMapPlot", () => { name: "Open leftover-map post Public post at ξ (0.00, 0.00)", }), ).not.toBeInTheDocument(); + expect( + screen.queryByRole("button", { + name: "Open leftover-map post Public post at leftover-map origin ξ (0.00, 0.00)", + }), + ).not.toBeInTheDocument(); expect(screen.queryByLabelText("Criterion ζ sales-lead")).not.toBeInTheDocument(); expect(screen.queryByLabelText("leftover-map criterion sales-lead at ζ (0.00, 0.00)")).not.toBeInTheDocument(); expect( diff --git a/frontend/src/components/LeftoverMapPlot.tsx b/frontend/src/components/LeftoverMapPlot.tsx index 96ae0e2a2..58c7547e1 100644 --- a/frontend/src/components/LeftoverMapPlot.tsx +++ b/frontend/src/components/LeftoverMapPlot.tsx @@ -167,6 +167,8 @@ function leftoverMapPlotPostText( * when leftoverMapPlotCriterionBadge returns a usable leftover-map criterion * leftover-map item coordinate caption. Caption leftover-map origin on leftover-map graphic leftover-map * criterion leftover-map item coordinates independently of leftover-map person coordinates when leftoverMapPlotCoordinatePairIsOrigin + * returns true. Caption leftover-map origin on leftover-map graphic leftover-map + * post leftover-map person coordinates independently of leftover-map criterion leftover-map item coordinates when leftoverMapPlotCoordinatePairIsOrigin * returns true. Caption leftover-map comparison graphic leftover-map * criterion markers with persisted leftover-map item coordinates ``ζ_{1:2}`` * when leftoverMapComparePlotCriterionBadge returns a usable leftover-map comparison @@ -298,6 +300,9 @@ function leftoverMapPlotPostText( * ADR 0347 fail-closes leftover-map graphic leftover-map criterion leftover-map origin leftover-map * item coordinates through leftoverMapPlotCriterionBadge independently of leftover-map * person coordinates. + * ADR 0348 fail-closes leftover-map graphic leftover-map post leftover-map origin leftover-map + * person coordinates through leftoverMapPlotPostBadge independently of leftover-map + * criterion leftover-map item coordinates. * Never invent a leftover score. */ export function LeftoverMapPlot({ diff --git a/frontend/src/i18n.test.ts b/frontend/src/i18n.test.ts index f60beb5f1..73f53946c 100644 --- a/frontend/src/i18n.test.ts +++ b/frontend/src/i18n.test.ts @@ -171,6 +171,7 @@ describe("i18n", () => { "Leftover map dropped {dropped} incomplete criteria", "Leftover map after IRT main effects. Axis ticks name persisted leftover-map coordinates. Pair segments name leftover-map distance d, leftover-map reconstruction R̂, leftover-map explained leftover share e, leftover-map unexplained leftover share s, leftover-map cross share x, leftover-map unexplained leftover U, leftover residual R, leftover observed Y, leftover expected E, and leftover-map rank. 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. Click a post marker to open that post. The plot does not invent a leftover score.", "Open leftover-map post {title} at ξ {person}", + "Open leftover-map post {title} at leftover-map origin ξ {person}", "Open leftover-map post {title}", "Open leftover map comparison graphic leftover-map post {title} at ξ {person}", "Read observed Y {observed} and expected E {expected} after IRT main effects, then open this post.", @@ -488,6 +489,24 @@ describe("i18n", () => { }, ); + it.each([ + ["ko", "잔여 지도 원점 글 Public post 열기 (ξ (0.00, 0.00))"], + ["zh", "打开残差图原点帖子 Public post(ξ (0.00, 0.00))"], + ["ja", "残差マップの原点投稿 Public post を開く(ξ (0.00, 0.00))"], + ["vi", "Mở bài viết gốc bản đồ phần dư Public post tại ξ (0.00, 0.00)"], + ] as const)( + "formats leftover-map graphic leftover-map post leftover-map origin ξ in %s", + (locale, expected) => { + setLocale(locale); + expect( + tf("Open leftover-map post {title} at leftover-map origin ξ {person}", { + title: "Public post", + person: "(0.00, 0.00)", + }), + ).toBe(expected); + }, + ); + it.each([ ["ko", "잔여 지도 비교 그림 기준 sales-lead (ζ (+0.50, −0.02))"], ["zh", "残差地图比较图形准则 sales-lead(ζ (+0.50, −0.02))"], diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts index bce2f3e63..d45eb28e4 100644 --- a/frontend/src/i18n.ts +++ b/frontend/src/i18n.ts @@ -736,6 +736,8 @@ const TRANSLATIONS: Partial>> = { "IRT 주효과 이후 잔여 지도입니다. 축 눈금은 저장된 잔여 지도 좌표입니다. 쌍 선분은 잔여 지도 거리 d, 잔여 지도 재구성 R̂, 잔여 지도 설명 잔여 점유율 e, 잔여 지도 미설명 잔여 점유율 s, 잔여 지도 교차 점유율 x, 잔여 지도 미설명 잔여 U, 잔여 R, 관측 Y, 기대 E, 잔여 지도 순위입니다. 저장된 완전사례 포함 범위와 기준 포함 범위와 불완전 글과 불완전 기준이 있으면 그림이 그 범위를 표시합니다. 글 표식을 눌러 그 글을 여세요. 이 그림은 잔여 점수를 만들어내지 않습니다.", "Open leftover-map post {title} at ξ {person}": "잔여 지도 글 {title} 열기 (ξ {person})", + "Open leftover-map post {title} at leftover-map origin ξ {person}": + "잔여 지도 원점 글 {title} 열기 (ξ {person})", "Read observed Y {observed} and expected E {expected} after IRT main effects, then open this post.": "IRT 주효과 이후 관측 Y {observed}와 기대 E {expected}를 읽은 다음, 이 글을 여세요.", "Leftover map has no leftover structure after IRT main effects. Open this post.": @@ -1456,6 +1458,8 @@ const TRANSLATIONS: Partial>> = { "IRT 主效应后的残差图。轴刻度标出已保存的残差图坐标。配对线段标出残差图距离 d、残差图重建 R̂、残差图已解释残差份额 e、残差图未解释残差份额 s、残差图交叉份额 x、残差图未解释残差 U、残差 R、观测 Y、期望 E 与残差图秩。图在已保存时标出完全案例覆盖范围、准则完全案例覆盖范围、不完整帖文与不完整准则。点击帖子标记打开该帖子。此图不会虚构残差分数。", "Open leftover-map post {title} at ξ {person}": "打开残差图帖子 {title}(ξ {person})", + "Open leftover-map post {title} at leftover-map origin ξ {person}": + "打开残差图原点帖子 {title}(ξ {person})", "Read observed Y {observed} and expected E {expected} after IRT main effects, then open this post.": "阅读 IRT 主效应后的观测 Y {observed} 与期望 E {expected},然后打开这篇帖子。", "Leftover map has no leftover structure after IRT main effects. Open this post.": @@ -2180,6 +2184,8 @@ const TRANSLATIONS: Partial>> = { "IRT主効果後の残差マップです。軸目盛は保存済みの残差マップ座標です。ペア線分は残差マップ距離 d、残差マップ再構成 R̂、残差マップ説明済み残差割合 e、残差マップ未説明残差割合 s、残差マップ交差割合 x、残差マップ未説明残差 U、残差 R、観測 Y、期待 E、残差マップ階数です。保存済みの完全ケース対象範囲と基準の完全ケース対象範囲と不完全投稿と不完全基準があるときはその範囲を示します。投稿マーカーをクリックしてその投稿を開いてください。この図は残差スコアを作りません。", "Open leftover-map post {title} at ξ {person}": "残差マップの投稿 {title} を開く(ξ {person})", + "Open leftover-map post {title} at leftover-map origin ξ {person}": + "残差マップの原点投稿 {title} を開く(ξ {person})", "Read observed Y {observed} and expected E {expected} after IRT main effects, then open this post.": "IRT主効果後の観測 Y {observed} と期待 E {expected} を読んでから、この投稿を開いてください。", "Leftover map has no leftover structure after IRT main effects. Open this post.": @@ -2909,6 +2915,8 @@ const TRANSLATIONS: Partial>> = { "Bản đồ phần dư sau hiệu ứng chính IRT. Vạch trục ghi tọa độ bản đồ phần dư đã lưu. Đoạn cặp ghi khoảng cách bản đồ phần dư d, tái dựng bản đồ phần dư R̂, phần dư giải thích e, phần dư chưa giải thích s, phần giao x, phần dư chưa giải thích U, phần dư R, Y quan sát, E kỳ vọng và hạng bản đồ phần dư. Hình ghi phạm vi trường hợp đầy đủ của bài viết và tiêu chí cùng bài không đầy đủ và tiêu chí không đầy đủ khi đã lưu. Nhấn dấu bài viết để mở bài đó. Hình này không tạo ra điểm phần dư.", "Open leftover-map post {title} at ξ {person}": "Mở bài viết bản đồ phần dư {title} tại ξ {person}", + "Open leftover-map post {title} at leftover-map origin ξ {person}": + "Mở bài viết gốc bản đồ phần dư {title} tại ξ {person}", "Read observed Y {observed} and expected E {expected} after IRT main effects, then open this post.": "Đọc Y quan sát {observed} và E kỳ vọng {expected} sau hiệu ứng chính IRT, rồi mở bài viết này.", "Leftover map has no leftover structure after IRT main effects. Open this post.": diff --git a/frontend/src/leftoverMapAxisBadge.ts b/frontend/src/leftoverMapAxisBadge.ts index a54f5fe04..eb81991fd 100644 --- a/frontend/src/leftoverMapAxisBadge.ts +++ b/frontend/src/leftoverMapAxisBadge.ts @@ -32,6 +32,8 @@ * independently of leftover-map axis share and leftover-map singular values. * ADR 0347 names leftover-map origin on leftover-map graphic leftover-map criterion leftover-map * item coordinates as leftoverMapPlotCriterionBadge, not this helper. + * ADR 0348 names leftover-map origin on leftover-map graphic leftover-map post leftover-map + * person coordinates as leftoverMapPlotPostBadge, not this helper. */ import type { LeftoverMapAxis } from "./api"; diff --git a/frontend/src/leftoverMapCoordinates.test.ts b/frontend/src/leftoverMapCoordinates.test.ts index 88e10f5d8..e6be8c09e 100644 --- a/frontend/src/leftoverMapCoordinates.test.ts +++ b/frontend/src/leftoverMapCoordinates.test.ts @@ -9,6 +9,7 @@ import { LEFTOVER_MAP_PLOT_CRITERION, LEFTOVER_MAP_PLOT_CRITERION_ORIGIN, LEFTOVER_MAP_PLOT_POST_ACTION, + LEFTOVER_MAP_PLOT_POST_ACTION_ORIGIN, } from "./leftoverMapPlotLayout"; import { formatLeftoverMapCoordinatePair, @@ -65,7 +66,7 @@ describe("formatLeftoverMapCoordinatePair", () => { }); describe("leftoverMapPlotCoordinatePairIsOrigin", () => { - it("names leftover-map origin independently of leftover-map person coordinates", () => { + it("names leftover-map origin independently of leftover-map item coordinates and leftover-map person coordinates", () => { expect(leftoverMapPlotCoordinatePairIsOrigin("(0.00, 0.00)")).toBe(true); expect(leftoverMapPlotCoordinatePairIsOrigin("(+0.50, \u22120.02)")).toBe(false); expect(leftoverMapPlotCoordinatePairIsOrigin("(+0.00, 0.00)")).toBe(false); @@ -76,10 +77,16 @@ describe("leftoverMapPlotCoordinatePairIsOrigin", () => { expect(leftoverMapComparePlotCriterionBadge("sales-lead", 0, 0)?.key).not.toBe( LEFTOVER_MAP_PLOT_CRITERION_ORIGIN, ); + expect(leftoverMapPlotPostBadge("Public post", 0, 0)?.key).toBe(LEFTOVER_MAP_PLOT_POST_ACTION_ORIGIN); expect(leftoverMapPlotPostBadge("Public post", 0, 0)?.key).not.toBe(LEFTOVER_MAP_PLOT_CRITERION_ORIGIN); + expect(leftoverMapPlotPostBadge("Public post", 0, 0)?.key).not.toBe(LEFTOVER_MAP_PLOT_POST_ACTION); + expect(leftoverMapComparePlotPostBadge("Public post", 0, 0)?.key).not.toBe( + LEFTOVER_MAP_PLOT_POST_ACTION_ORIGIN, + ); expect(leftoverMapListCriterionBadge("sales-lead", 0, 0)?.key).not.toBe( LEFTOVER_MAP_PLOT_CRITERION_ORIGIN, ); + expect(leftoverMapListPostBadge("Public post", 0, 0)?.key).not.toBe(LEFTOVER_MAP_PLOT_POST_ACTION_ORIGIN); }); }); @@ -100,6 +107,7 @@ describe("leftoverMapListPostBadge", () => { key: LEFTOVER_MAP_LIST_POST_ACTION, values: { title: "Public post", person: "(0.00, 0.00)" }, }); + expect(leftoverMapListPostBadge("Public post", 0, 0)?.key).not.toBe(LEFTOVER_MAP_PLOT_POST_ACTION_ORIGIN); }); it("omits leftover-map person coordinates when ξ is missing or non-finite", () => { @@ -120,6 +128,7 @@ describe("leftoverMapListPostBadge", () => { "leftover pair leftover-map post {title} at ξ {person}", ); expect(LEFTOVER_MAP_LIST_POST_ACTION).not.toBe(LEFTOVER_MAP_PLOT_POST_ACTION); + expect(LEFTOVER_MAP_LIST_POST_ACTION).not.toBe(LEFTOVER_MAP_PLOT_POST_ACTION_ORIGIN); expect(LEFTOVER_MAP_LIST_POST_ACTION).not.toBe(LEFTOVER_MAP_COMPARE_PLOT_POST_ACTION); expect(LEFTOVER_MAP_LIST_POST_ACTION).not.toBe(LEFTOVER_MAP_COMPARE_LIST_POST_ACTION); expect(leftoverMapListPostBadge("Public post", 0.5, 0.1)?.key).not.toBe( diff --git a/frontend/src/leftoverMapCoordinates.ts b/frontend/src/leftoverMapCoordinates.ts index 99de1f500..a0b1e7574 100644 --- a/frontend/src/leftoverMapCoordinates.ts +++ b/frontend/src/leftoverMapCoordinates.ts @@ -3,6 +3,10 @@ * criterion leftover-map item coordinates as leftoverMapPlotCriterionBadge * independently of leftover-map person coordinates. leftoverMapListCriterionBadge * stays leftover-map pair leftover-map criterion leftover-map item coordinate keys. + * ADR 0348 names leftover-map origin on leftover-map graphic leftover-map + * post leftover-map person coordinates as leftoverMapPlotPostBadge independently of leftover-map + * criterion leftover-map item coordinates. leftoverMapListPostBadge stays leftover-map pair leftover-map + * post leftover-map person coordinate keys. */ import { formatSignedLeftoverValue } from "./leftoverMapUnexplained"; @@ -56,7 +60,7 @@ export function formatLeftoverMapCoordinatePair( /** True when the formatted leftover-map coordinate pair is leftover-map origin * `(formatSignedLeftoverValue(0), formatSignedLeftoverValue(0))`. Rank-0 unused * axes still name leftover-map origin `(0.00, 0.00)`. Do not invent leftover-map - * origin from leftover-map person coordinates ξ. + * origin from leftover-map axis share or leftover-map singular values. */ export function leftoverMapPlotCoordinatePairIsOrigin(pairLabel: string): boolean { const origin = formatLeftoverMapCoordinatePair(0, 0); @@ -77,7 +81,10 @@ export function formatLeftoverMapCoordinates( return `${PERSON_BADGE} ${person} ${ITEM_BADGE} ${item}`; } -/** ADR 0339 leftover-map pair leftover-map post leftover-map person coordinates ξ. */ +/** ADR 0339 leftover-map pair leftover-map post leftover-map person coordinates ξ. + * ADR 0348 names leftover-map origin on leftover-map graphic leftover-map post leftover-map + * person coordinates as leftoverMapPlotPostBadge, not this helper. + */ export function leftoverMapListPostBadge( title: string, axis1: number | null | undefined, diff --git a/frontend/src/leftoverMapPlotAxisSingular.ts b/frontend/src/leftoverMapPlotAxisSingular.ts index f5ff73070..9ae05e2f8 100644 --- a/frontend/src/leftoverMapPlotAxisSingular.ts +++ b/frontend/src/leftoverMapPlotAxisSingular.ts @@ -43,6 +43,9 @@ * ADR 0347 names leftover-map origin on leftover-map graphic leftover-map criterion leftover-map * item coordinates as leftoverMapPlotCriterionBadge independently of leftover-map person * coordinates, not leftoverMapPlotTickAxisBadge. + * ADR 0348 names leftover-map origin on leftover-map graphic leftover-map post leftover-map + * person coordinates as leftoverMapPlotPostBadge independently of leftover-map criterion leftover-map + * item coordinates, not leftoverMapPlotTickAxisBadge. */ import type { LeftoverMapAxis } from "./api"; diff --git a/frontend/src/leftoverMapPlotLayout.test.ts b/frontend/src/leftoverMapPlotLayout.test.ts index a3b761cb6..ac8c7a976 100644 --- a/frontend/src/leftoverMapPlotLayout.test.ts +++ b/frontend/src/leftoverMapPlotLayout.test.ts @@ -27,6 +27,7 @@ import { LEFTOVER_MAP_PLOT_CRITERION_ORIGIN, LEFTOVER_MAP_COMPARE_PLOT_CRITERION, LEFTOVER_MAP_PLOT_POST_ACTION, + LEFTOVER_MAP_PLOT_POST_ACTION_ORIGIN, LEFTOVER_MAP_PLOT_POST_ACTION_OMITTED, LEFTOVER_MAP_COMPARE_PLOT_POST_ACTION, LEFTOVER_MAP_PLOT_TICK, @@ -1233,6 +1234,8 @@ describe("leftoverMapPlotCriterionBadge", () => { expect(leftoverMapPlotCriterionBadge("sales-lead", 0, 0)?.key).not.toBe(LEFTOVER_MAP_PLOT_CRITERION); expect(leftoverMapPlotCriterionBadge("sales-lead", 0, 0)?.key).not.toBe(LEFTOVER_MAP_COMPARE_PLOT_CRITERION); expect(leftoverMapPlotPostBadge("Public post", 0, 0)?.key).not.toBe(LEFTOVER_MAP_PLOT_CRITERION_ORIGIN); + expect(leftoverMapPlotPostBadge("Public post", 0, 0)?.key).toBe(LEFTOVER_MAP_PLOT_POST_ACTION_ORIGIN); + expect(leftoverMapPlotCriterionBadge("sales-lead", 0, 0)?.key).not.toBe(LEFTOVER_MAP_PLOT_POST_ACTION_ORIGIN); }); it("omits leftover-map item coordinates when ζ is missing or non-finite", () => { @@ -1330,11 +1333,14 @@ describe("leftoverMapPlotPostBadge", () => { }); }); - it("names rank-0 origin leftover-map person coordinates as ξ (0.00, 0.00)", () => { + it("names rank-0 leftover-map origin leftover-map person coordinates independently of leftover-map item coordinates", () => { expect(leftoverMapPlotPostBadge("Public post", 0, 0)).toEqual({ - key: LEFTOVER_MAP_PLOT_POST_ACTION, + key: LEFTOVER_MAP_PLOT_POST_ACTION_ORIGIN, values: { title: "Public post", person: "(0.00, 0.00)" }, }); + expect(leftoverMapPlotPostBadge("Public post", 0, 0)?.key).not.toBe(LEFTOVER_MAP_PLOT_POST_ACTION); + expect(leftoverMapPlotPostBadge("Public post", 0, 0)?.key).not.toBe(LEFTOVER_MAP_COMPARE_PLOT_POST_ACTION); + expect(leftoverMapPlotCriterionBadge("sales-lead", 0, 0)?.key).not.toBe(LEFTOVER_MAP_PLOT_POST_ACTION_ORIGIN); }); it("omits leftover-map person coordinates when ξ is missing or non-finite", () => { @@ -1346,6 +1352,12 @@ describe("leftoverMapPlotPostBadge", () => { it("stays distinct from leftover-map comparison graphic leftover-map post markers and leftover-map graphic leftover-map criterion markers", () => { expect(LEFTOVER_MAP_PLOT_POST_ACTION).toBe("Open leftover-map post {title} at ξ {person}"); + expect(LEFTOVER_MAP_PLOT_POST_ACTION_ORIGIN).toBe( + "Open leftover-map post {title} at leftover-map origin ξ {person}", + ); + expect(LEFTOVER_MAP_PLOT_POST_ACTION_ORIGIN).not.toBe(LEFTOVER_MAP_PLOT_POST_ACTION); + expect(LEFTOVER_MAP_PLOT_POST_ACTION_ORIGIN).not.toBe(LEFTOVER_MAP_COMPARE_PLOT_POST_ACTION); + expect(LEFTOVER_MAP_PLOT_POST_ACTION_ORIGIN).not.toBe(LEFTOVER_MAP_PLOT_CRITERION_ORIGIN); expect(LEFTOVER_MAP_PLOT_POST_ACTION).not.toBe(LEFTOVER_MAP_COMPARE_PLOT_POST_ACTION); expect(LEFTOVER_MAP_PLOT_POST_ACTION).not.toBe(LEFTOVER_MAP_PLOT_POST_ACTION_OMITTED); expect(LEFTOVER_MAP_PLOT_POST_ACTION).not.toBe(LEFTOVER_MAP_PLOT_CRITERION); @@ -1353,6 +1365,9 @@ describe("leftoverMapPlotPostBadge", () => { expect(leftoverMapPlotPostBadge("Public post", 0.5, 0.1)?.key).not.toBe( LEFTOVER_MAP_COMPARE_PLOT_POST_ACTION, ); + expect(leftoverMapPlotPostBadge("Public post", 0.5, 0.1)?.key).not.toBe( + LEFTOVER_MAP_PLOT_POST_ACTION_ORIGIN, + ); expect(leftoverMapPlotPostBadge("Public post", 0.5, 0.1)?.key).not.toBe( LEFTOVER_MAP_PLOT_CRITERION, ); @@ -1376,6 +1391,9 @@ describe("leftoverMapComparePlotPostBadge", () => { key: LEFTOVER_MAP_COMPARE_PLOT_POST_ACTION, values: { title: "Public post", person: "(0.00, 0.00)" }, }); + expect(leftoverMapComparePlotPostBadge("Public post", 0, 0)?.key).not.toBe( + LEFTOVER_MAP_PLOT_POST_ACTION_ORIGIN, + ); }); it("omits leftover-map person coordinates when ξ is missing or non-finite", () => { @@ -1390,6 +1408,7 @@ describe("leftoverMapComparePlotPostBadge", () => { "Open leftover map comparison graphic leftover-map post {title} at ξ {person}", ); expect(LEFTOVER_MAP_COMPARE_PLOT_POST_ACTION).not.toBe(LEFTOVER_MAP_PLOT_POST_ACTION); + expect(LEFTOVER_MAP_COMPARE_PLOT_POST_ACTION).not.toBe(LEFTOVER_MAP_PLOT_POST_ACTION_ORIGIN); expect(LEFTOVER_MAP_COMPARE_PLOT_POST_ACTION).not.toBe(LEFTOVER_MAP_PLOT_POST_ACTION_OMITTED); expect(LEFTOVER_MAP_COMPARE_PLOT_POST_ACTION).not.toBe(LEFTOVER_MAP_COMPARE_PLOT_CRITERION); expect(LEFTOVER_MAP_COMPARE_PLOT_POST_ACTION).not.toBe(LEFTOVER_MAP_PLOT_CRITERION); diff --git a/frontend/src/leftoverMapPlotLayout.ts b/frontend/src/leftoverMapPlotLayout.ts index 3cb819dff..0f34a8d66 100644 --- a/frontend/src/leftoverMapPlotLayout.ts +++ b/frontend/src/leftoverMapPlotLayout.ts @@ -163,6 +163,10 @@ * leftover-map item coordinates through leftoverMapPlotCriterionBadge independently of leftover-map * person coordinates ξ, not leftover-map comparison graphic leftover-map criterion leftover-map * item coordinates. + * ADR 0348 fail-closes leftover-map graphic leftover-map post leftover-map origin leftover-map + * person coordinates through leftoverMapPlotPostBadge independently of leftover-map + * criterion leftover-map item coordinates ζ, not leftover-map comparison graphic leftover-map post leftover-map + * person coordinates. */ import { formatLeftoverMapCoordinatePair, leftoverMapPlotCoordinatePairIsOrigin } from "./leftoverMapCoordinates"; @@ -192,6 +196,9 @@ export const LEFTOVER_MAP_COMPARE_PLOT_SVG = "Leftover map comparison"; export const LEFTOVER_MAP_PLOT_POST_ACTION = "Open leftover-map post {title} at ξ {person}"; +export const LEFTOVER_MAP_PLOT_POST_ACTION_ORIGIN = + "Open leftover-map post {title} at leftover-map origin ξ {person}"; + export const LEFTOVER_MAP_PLOT_POST_ACTION_OMITTED = "Open leftover-map post {title}"; @@ -254,7 +261,11 @@ export function leftoverMapPlotPostBadge( if (person === null) { return null; } - return { key: LEFTOVER_MAP_PLOT_POST_ACTION, values: { title, person } }; + const origin = leftoverMapPlotCoordinatePairIsOrigin(person); + return { + key: origin ? LEFTOVER_MAP_PLOT_POST_ACTION_ORIGIN : LEFTOVER_MAP_PLOT_POST_ACTION, + values: { title, person }, + }; } export function leftoverMapComparePlotPostBadge( diff --git a/pyproject.toml b/pyproject.toml index f82235b76..e4f8a7808 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "lineageweave" -version = "2.104.0" +version = "2.105.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" }