From 41d9d24bc7abd2f6ea7be465e1aafa65a17e711b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 17 Aug 2026 21:35:31 +0900 Subject: [PATCH] feat: name the first cited source after the landed Ask answer (v2.7.0) Opening Public post from Demo Corp members now names Linked post as the first cited source after the landed first Ask answer. Home list opens do not add that copy. --- ...2.7.0-name-first-cited-after-ask-answer.md | 5 +++++ CHANGELOG.md | 9 +++++++++ frontend/package.json | 2 +- frontend/src/App.test.tsx | 17 +++++++++++++++++ frontend/src/App.tsx | 19 +++++++++++++++++++ frontend/src/components/CitationChip.tsx | 3 +++ lineageweave/__init__.py | 2 +- pyproject.toml | 2 +- uv.lock | 2 +- 9 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 CHANGELOG.d/2.7.0-name-first-cited-after-ask-answer.md diff --git a/CHANGELOG.d/2.7.0-name-first-cited-after-ask-answer.md b/CHANGELOG.d/2.7.0-name-first-cited-after-ask-answer.md new file mode 100644 index 00000000..93d454ce --- /dev/null +++ b/CHANGELOG.d/2.7.0-name-first-cited-after-ask-answer.md @@ -0,0 +1,5 @@ +# 2.7.0 Name the first cited source after the landed Ask answer + +Open Public post from the landed Demo Corp members and the first +cited source on the landed Ask answer is named. Home list opens do +not. diff --git a/CHANGELOG.md b/CHANGELOG.md index e4a3c9ab..29fdb26e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,15 @@ 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.7.0] - 2026-08-17 + +### Added + +- Opening Public post from the landed Demo Corp members now names the + first cited source after the landed first Ask answer: Linked post, + then open that evidence. Home list opens do not add that copy. No + TEPP theta is invented. No cutoff body is invented (ADR 0016). + ## [2.6.0] - 2026-08-17 ### Added diff --git a/frontend/package.json b/frontend/package.json index 1ddeeb39..0f6b4069 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "frontend", "private": true, - "version": "2.6.0", + "version": "2.7.0", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx index 53a8fab1..dca4697e 100644 --- a/frontend/src/App.test.tsx +++ b/frontend/src/App.test.tsx @@ -1491,6 +1491,7 @@ describe("App, authenticated", () => { expect(screen.queryByRole("status", { name: "Related next action" })).not.toBeInTheDocument(); expect(screen.queryByRole("status", { name: "Ask next action" })).not.toBeInTheDocument(); expect(screen.queryByRole("status", { name: "Ask seed next action" })).not.toBeInTheDocument(); + expect(screen.queryByRole("status", { name: "Ask citation next action" })).not.toBeInTheDocument(); expect(screen.queryByText("Related to Ada West")).not.toBeInTheDocument(); expect(screen.queryByText("Related to Priya Nair")).not.toBeInTheDocument(); const popup = document.querySelector(".popup-panel"); @@ -2425,6 +2426,19 @@ describe("App, authenticated", () => { expect( within(popup as HTMLElement).getAllByText("The seeded follow-up after the site visit."), ).toHaveLength(1); + const citedNext = await screen.findByRole("status", { name: "Ask citation next action" }); + expect(citedNext).toHaveTextContent( + "Linked post is the first cited source. Open that evidence next.", + ); + expect( + firstAskAnswer.compareDocumentPosition(citedNext) & Node.DOCUMENT_POSITION_FOLLOWING, + ).not.toBe(0); + expect(citedNext.compareDocumentPosition(askInput) & Node.DOCUMENT_POSITION_FOLLOWING).not.toBe( + 0, + ); + expect( + within(popup as HTMLElement).getByRole("button", { name: "Open evidence: Linked post" }), + ).toHaveAttribute("aria-current", "true"); await waitFor(() => expect(document.getElementById("post-ask")).toHaveFocus()); } finally { HTMLElement.prototype.scrollIntoView = originalScrollIntoView; @@ -2723,6 +2737,9 @@ describe("App, authenticated", () => { expect(firstAskAnswer.compareDocumentPosition(affiliate) & Node.DOCUMENT_POSITION_FOLLOWING).not.toBe( 0, ); + expect(await screen.findByRole("status", { name: "Ask citation next action" })).toHaveTextContent( + "Linked post is the first cited source. Open that evidence next.", + ); await waitFor(() => expect(document.getElementById("post-ask")).toHaveFocus()); }); diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 4c53b73a..f8aa35db 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -133,10 +133,12 @@ function ChatCitations({ citedPosts, citedPostIds, onOpenEvidence, + currentPostId, }: { citedPosts?: { post_id: string; post_title: string }[]; citedPostIds: string[]; onOpenEvidence: (postId: string) => void; + currentPostId?: string; }) { if ((citedPosts?.length ?? citedPostIds.length) === 0) return null; const chips = @@ -150,6 +152,7 @@ function ChatCitations({ postId={cited.post_id} postTitle={cited.post_title} onOpenEvidence={onOpenEvidence} + current={cited.post_id === currentPostId} /> ))} @@ -209,6 +212,10 @@ function ChatPanel({ } } + const firstCitedTitle = + exchanges[0]?.cited_posts?.[0]?.post_title ?? + (exchanges[0]?.cited_post_ids[0] ? exchanges[0].cited_post_ids[0].slice(0, 8) : null); + return (

@@ -227,9 +234,17 @@ function ChatPanel({ citedPosts={exchanges[0].cited_posts} citedPostIds={exchanges[0].cited_post_ids} onOpenEvidence={setEvidencePostId} + currentPostId={ + exchanges[0].cited_posts?.[0]?.post_id ?? exchanges[0].cited_post_ids[0] + } /> ) : null} + {nameFirstAsk && firstCitedTitle ? ( +

+ {firstCitedNextAction(firstCitedTitle)} +

+ ) : null} {!seededOnly && (
void; + current?: boolean; }; /** @@ -13,12 +14,14 @@ export function CitationChip({ postId, postTitle, onOpenEvidence, + current, }: CitationChipProps) { return (