From d3f5e613ec73abd96a0fd85a5a3ff45bf196e1d6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 04:23:16 +0000 Subject: [PATCH 1/7] feat: open Calendar commitments onto Event Lineage (v2.14.0) Calendar names authorized commitments as current. Opening one focuses Event Lineage. Home-list opens do not. --- AGENTS.md | 2 ++ ARCHITECTURE.md | 3 +- .../2.14.0-calendar-open-event-lineage.md | 5 +++ CHANGELOG.md | 10 ++++++ CLAUDE.md | 7 ++++ ...094-calendar-open-focuses-event-lineage.md | 33 +++++++++++++++++ frontend/package.json | 2 +- frontend/src/App.test.tsx | 27 ++++++++++++++ frontend/src/App.tsx | 36 ++++++++++++++----- frontend/src/i18n.test.ts | 1 + frontend/src/i18n.ts | 8 +++++ pyproject.toml | 2 +- 12 files changed, 125 insertions(+), 11 deletions(-) create mode 100644 CHANGELOG.d/2.14.0-calendar-open-event-lineage.md create mode 100644 docs/adr/0094-calendar-open-focuses-event-lineage.md diff --git a/AGENTS.md b/AGENTS.md index 5f4743a73..0562c531b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -169,6 +169,8 @@ never a fabricated theta or a local psychometric substitute. Buyer Board **Weekly VOC** is an ISO-8601 week list filter (ADR 0092). Opening that filtered post focuses Event Lineage (ADR 0093). Do not invent a week, a theta, or a cutoff body. +Opening a Calendar commitment uses the same focus path (ADR 0094). Do not +invent a week, a theta, a cutoff body, or a CalDAV event. ## Tests diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 23b864f16..f5ee1be41 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -282,7 +282,8 @@ Keycloak issued; `src/App.tsx` renders a git-branch SVG of `GET /api/lineage` (click a node to open that post; `post_admin` can rebuild), the post list with a named Weekly VOC ISO-8601 week filter (ADR 0092; opening that filtered post focuses Event Lineage, ADR 0093), -and a full detail popup: Korean +Calendar commitments use the same Event Lineage focus path (ADR 0094), and +the full detail popup includes Korean summary/key-events/R&R, VOC evidence excerpts, an Event Lineage panel (direct vs. indirect links; a link opens that post), the Keyman affiliate tree (resolved ancestors plus unresolved org roots), Keyman + diff --git a/CHANGELOG.d/2.14.0-calendar-open-event-lineage.md b/CHANGELOG.d/2.14.0-calendar-open-event-lineage.md new file mode 100644 index 000000000..c09605d5e --- /dev/null +++ b/CHANGELOG.d/2.14.0-calendar-open-event-lineage.md @@ -0,0 +1,5 @@ +# 2.14.0 Opening a Calendar commitment focuses Event Lineage + +Calendar names authorized commitments as current and to open one to read +Event Lineage. That open focuses the popup Event Lineage heading. Home-list +opens do not. No TEPP theta is invented. diff --git a/CHANGELOG.md b/CHANGELOG.md index f02f09408..e4432d7b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ 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). +## [2.14.0] - 2026-08-19 + +### Added + +- Opening a Calendar commitment now focuses Event Lineage and names Keyman + and evaluation as the next read. Calendar names authorized commitments + as current before that open. Home-list opens do not add that focus or + copy. No TEPP theta is invented. No cutoff body is invented + (ADR 0094 / ADR 0016). + ## [2.13.0] - 2026-08-19 ### Added diff --git a/CLAUDE.md b/CLAUDE.md index 81fefe402..40e173ead 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -78,3 +78,10 @@ ISO-8601 week stay; other VOC types and older weeks drop out. The Board names Event Lineage as the next read (ADR 0092). Open a remaining post: Event Lineage takes focus and names Keyman and evaluation next (ADR 0093). A home-list open does not. Do not invent a theta. + +## Calendar open (v2.14.0) + +Open Calendar. Authorized commitments are current. Open a commitment: +Event Lineage takes focus and names Keyman and evaluation next +(ADR 0094). A home-list open does not. Do not invent a theta or a +CalDAV event. diff --git a/docs/adr/0094-calendar-open-focuses-event-lineage.md b/docs/adr/0094-calendar-open-focuses-event-lineage.md new file mode 100644 index 000000000..0b7b1a8b9 --- /dev/null +++ b/docs/adr/0094-calendar-open-focuses-event-lineage.md @@ -0,0 +1,33 @@ +# ADR 0094: Opening a Calendar commitment focuses Event Lineage + +- Status: Accepted +- Date: 2026-08-19 + +## Context + +Board Weekly VOC opens already focus Event Lineage (ADR 0093). Calendar is +the other buyer destination that opens a source post from an authorized +commitment. That open was a home-list open: the popup body appeared and +Event Lineage did not take focus. + +## Decision + +Opening a commitment on Calendar is a `fromCalendar` open. That open reuses +the Event Lineage focus path used by report-member and Weekly VOC opens: + +- Calendar names the next action: authorized commitments are current; open + a commitment to read Event Lineage. +- The popup Event Lineage heading takes focus. +- The popup names the opened post as current in Event Lineage and tells + the buyer to read Keyman and evaluation next. + +A Board home-list open does not focus Event Lineage and does not add that +copy. A `?post=` deep link is still a home-list open. + +No TEPP theta is invented. No cutoff body is invented (ADR 0016). Calendar +does not invent a CalDAV event (ADR 0038). + +## Consequences + +- Calendar, Weekly VOC, and report-member opens share one focus contract. +- Closing the popup clears the Calendar open flag. diff --git a/frontend/package.json b/frontend/package.json index 20fda0d5f..c6a4389de 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "frontend", "private": true, - "version": "2.13.0", + "version": "2.14.0", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx index 61763f574..5b0f9c235 100644 --- a/frontend/src/App.test.tsx +++ b/frontend/src/App.test.tsx @@ -1844,6 +1844,33 @@ describe("App, authenticated", () => { expect(screen.queryByRole("status", { name: "Event Lineage next action" })).not.toBeInTheDocument(); }); + it("opening a Calendar commitment focuses Event Lineage; a home list open does not", async () => { + stubBackend(); + render(); + + await userEvent.click(await screen.findByRole("button", { name: "Calendar" })); + const calendar = await screen.findByRole("region", { name: "Calendar" }); + expect(within(calendar).getByLabelText("Next action")).toHaveTextContent( + "Authorized commitments are current. Open a commitment to read Event Lineage.", + ); + await userEvent.click( + within(calendar).getByRole("button", { name: "Open commitment for: Public post" }), + ); + + await waitFor(() => expect(screen.getByText("The full body text.")).toBeInTheDocument()); + expect(document.getElementById("post-event-lineage")).toHaveFocus(); + expect(screen.getByRole("status", { name: "Event Lineage next action" })).toHaveTextContent( + "Public post is current in Event Lineage. Read Keyman and evaluation next.", + ); + + await userEvent.click(screen.getByRole("button", { name: "Close" })); + const board = screen.getByRole("region", { name: "Board" }); + await userEvent.click(within(board).getByRole("button", { name: "View post: Public post" })); + await waitFor(() => expect(screen.getByText("The full body text.")).toBeInTheDocument()); + expect(document.getElementById("post-event-lineage")).not.toHaveFocus(); + expect(screen.queryByRole("status", { name: "Event Lineage next action" })).not.toBeInTheDocument(); + }); + it("renders the A-100 fork as a git-style DAG, not a flat edge list", async () => { stubBackend(); render(); diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index b824ad597..1c964c966 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -2466,6 +2466,7 @@ type SelectPostOptions = { knowledgeCutoff?: string; fromReportMember?: boolean; fromWeeklyVoc?: boolean; + fromCalendar?: boolean; }; /** @@ -3046,9 +3047,11 @@ function RankingsPanel({ function CalendarPanel({ accessToken, onSelectPost, + namedNextAction = false, }: { accessToken: string; - onSelectPost: (postId: string) => void; + onSelectPost: (postId: string, options?: SelectPostOptions) => void; + namedNextAction?: boolean; }) { const [calendar, setCalendar] = useState(null); const [error, setError] = useState(null); @@ -3068,8 +3071,13 @@ function CalendarPanel({ const caldavNextAction = calendar.calendar_sources?.caldav_next_action; return ( -
-

{t("Calendar")}

+
+

{t("Calendar")}

+ {namedNextAction && commitments.length > 0 ? ( +

+ {t("Authorized commitments are current. Open a commitment to read Event Lineage.")} +

+ ) : null}

{t("CalDAV events")}

{events.length === 0 ? ( @@ -3104,7 +3112,7 @@ function CalendarPanel({