diff --git a/CHANGELOG.d/2.23.2-project-history-lineage-counts.md b/CHANGELOG.d/2.23.2-project-history-lineage-counts.md new file mode 100644 index 000000000..4935c269e --- /dev/null +++ b/CHANGELOG.d/2.23.2-project-history-lineage-counts.md @@ -0,0 +1,6 @@ +### Added + +- Project history now shows how many displayed authorized project posts + participate in persisted forward lineage edges and how many distinct + evidence-lineage groups those posts form. The same bounded counts appear from + post and Ask entry points. diff --git a/docs/adr/0128-authorized-project-history-buyer-surface.md b/docs/adr/0128-authorized-project-history-buyer-surface.md index c0c3b4c10..3581668f2 100644 --- a/docs/adr/0128-authorized-project-history-buyer-surface.md +++ b/docs/adr/0128-authorized-project-history-buyer-surface.md @@ -35,6 +35,19 @@ preserving Project History as the Event Lineage focus. Counts, display names, responsibility transitions, and related paths are bounded projections, not an HR ledger or a causal graph. +The projection reports three explicit evidence-fusion statistics over the same +bounded authorized event set: `event_count` counts displayed project posts, +`connected_post_count` counts posts incident to at least one forward +`post_lineage_edge`, and `lineage_count` counts weakly connected components +containing those posts. Isolated project posts remain in `event_count` but do +not become a lineage. These are LineageWeave evidence-DAG statistics, not TEPP +topic identities; TEPP topic birth, split, merge, dormancy, and reactivation +stay on the separate fail-closed TEPP contract. + +The reader-facing label always says that the post count is the shown set. When +`truncated` is true, none of these statistics claims to cover project evidence +outside the bounded response. + The UI uses the existing design-token and Storybook component boundary. The Figma file above is the design source for the desktop, mobile, and evidence boundary states; no second component-specific token system is introduced. @@ -45,6 +58,8 @@ boundary states; no second component-specific token system is introduced. source evidence in one workflow. - Hidden or post-cutoff records cannot affect the index, counts, transitions, or related paths. +- A reader can distinguish all project posts from posts already connected into + one or more evidence lineages without treating an isolated post as a topic. - Semantic project mentions remain visibly inferred and do not overwrite a source project identity. - The current document-time fallback remains explicit until a durable event diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index 1702da9dc..6cbc26312 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -331,6 +331,23 @@ runtime note into a shipped/live claim. - The next stacked slice attaches this same canonical timeline and TEPP metadata to Global Ask and post-scoped Ask without re-retrieving hidden evidence. +## Project-history lineage counts (2026-08-23) + +- The shared timeline now distinguishes displayed authorized project posts from + posts incident to a persisted forward `post_lineage_edge`, and reports the + number of weakly connected evidence-lineage components in that bounded set. +- Isolated project posts remain visible but do not inflate the lineage count. + These counts describe LineageWeave's evidence-fusion DAG; they are not TEPP + topic identities and do not close the TRSL-TM topic-lineage dependency. +- The same projection supplies the dedicated Project history destination, each + post's project-evidence action, post-scoped Ask, and Global Ask, so those + surfaces cannot drift into competing count definitions. +- Stacked delivery evidence is PR #487 on PR #258. Local evidence is the full + 993-test backend suite, focused Project History UI tests, the complete App test + file, lint, production build, Storybook build, and rendered-story inspection. + Protected merge, authenticated runtime, and the TEPP topic-identity result + contract remain open and must not be represented as protected-main behavior. + ## Ask-to-project-history integration (2026-08-21) - Protected-stack checkpoint: PR #342 is based on PR #339 head diff --git a/docs/storybook-inventory.md b/docs/storybook-inventory.md index ddb09aea8..caa724ded 100644 --- a/docs/storybook-inventory.md +++ b/docs/storybook-inventory.md @@ -9,6 +9,7 @@ reader-facing control you can click before changing product CSS. | `Evidence/CitationChip` | Click a cited title to open that source post. | `--color-chip-border`, `--radius-chip`, `CitationChip` | | `AnalysisRun/CutoffKnownBody` | Read the cutoff-known sentence, then compare it with the live body below. | `--color-accent-border`, `--space-panel-block`, `--radius-panel`, `CutoffKnownBody` | | `Analysis/LineageEntityPicker` | Choose which corp to reconstruct, then click Request a lineage reconstruction. | `--space-control-gap`, `--size-control-min`, `--radius-control`, `LineageEntityPicker` | +| `Evidence/ProjectHistoryTimeline` | Compare all project posts with connected-post and evidence-lineage counts, then open the selected source. | shared spacing, status, focus, and timeline tokens; `ProjectHistoryTimeline` | | `Chrome/PopupCloseButton` | Close the evidence panel or post popup. | `--space-close-inset`, `--font-size-close`, `PopupCloseButton` | | `Evidence/LineageDag` | Inspect a branching Event Lineage, then open a record or read its evidence trail. | `--color-primary`, `--color-accent-orange`, `LineageDag` | diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx index f54e5edc5..bb6a17d10 100644 --- a/frontend/src/App.test.tsx +++ b/frontend/src/App.test.tsx @@ -1914,6 +1914,8 @@ describe("App, authenticated", () => { focus_event_id: "post-1", time_basis_code: "source_post_created_at_fallback", event_count: 1, + connected_post_count: 0, + lineage_count: 0, distinct_actor_count: 0, distinct_observed_actor_count: 0, evidence_boundary_code: "authorized_visible_source_posts", diff --git a/frontend/src/components/AskProjectHistoryLinks.test.tsx b/frontend/src/components/AskProjectHistoryLinks.test.tsx index 16da5ce7c..c5973a316 100644 --- a/frontend/src/components/AskProjectHistoryLinks.test.tsx +++ b/frontend/src/components/AskProjectHistoryLinks.test.tsx @@ -23,6 +23,8 @@ const projection: ProjectHistoryProjection = { knowledge_cutoff: "2026-08-20T12:00:00Z", evidence_boundary_code: "authorized_visible_source_posts", event_count: 1, + connected_post_count: 0, + lineage_count: 0, distinct_actor_count: 0, distinct_observed_actor_count: 0, truncated: false, diff --git a/frontend/src/components/ProjectHistoryTimeline.stories.tsx b/frontend/src/components/ProjectHistoryTimeline.stories.tsx index 49d4e8675..8f33fc14a 100644 --- a/frontend/src/components/ProjectHistoryTimeline.stories.tsx +++ b/frontend/src/components/ProjectHistoryTimeline.stories.tsx @@ -60,6 +60,8 @@ const projection: ProjectHistoryProjection = { knowledge_cutoff: "2026-08-20T00:00:00Z", evidence_boundary_code: "authorized_visible_source_posts", event_count: 5, + connected_post_count: 5, + lineage_count: 1, distinct_actor_count: 3, distinct_observed_actor_count: 2, truncated: false, diff --git a/frontend/src/components/ProjectHistoryTimeline.tepp.test.tsx b/frontend/src/components/ProjectHistoryTimeline.tepp.test.tsx index e33ad1ff8..0a9fdaef3 100644 --- a/frontend/src/components/ProjectHistoryTimeline.tepp.test.tsx +++ b/frontend/src/components/ProjectHistoryTimeline.tepp.test.tsx @@ -14,6 +14,8 @@ const projection = { knowledge_cutoff: "2026-08-20T12:00:00Z", evidence_boundary_code: "authorized_visible_source_posts", event_count: 1, + connected_post_count: 0, + lineage_count: 0, distinct_actor_count: 0, distinct_observed_actor_count: 0, truncated: false, diff --git a/frontend/src/components/ProjectHistoryTimeline.test.tsx b/frontend/src/components/ProjectHistoryTimeline.test.tsx index 8b53f654b..3ec742bb5 100644 --- a/frontend/src/components/ProjectHistoryTimeline.test.tsx +++ b/frontend/src/components/ProjectHistoryTimeline.test.tsx @@ -14,6 +14,8 @@ const projection: ProjectHistoryProjection = { knowledge_cutoff: "2026-08-20T00:00:00+00:00", evidence_boundary_code: "authorized_visible_source_posts", event_count: 3, + connected_post_count: 3, + lineage_count: 1, distinct_actor_count: 2, distinct_observed_actor_count: 1, truncated: false, @@ -142,6 +144,7 @@ describe("ProjectHistoryTimeline", () => { expect(screen.getByText(/evidence gap/i, { selector: "dd" })).toBeInTheDocument(); expect(screen.getByText(/related history, not causality/i)).toBeInTheDocument(); expect(screen.getByText(/permission, visibility, publication, and cutoff gates/i)).toBeInTheDocument(); + expect(screen.getByText(/3 shown project posts · 3 connected · lineage count 1/i)).toBeInTheDocument(); fireEvent.click(screen.getByRole("button", { name: /open source record: VOC received/i })); expect(onOpenPost).toHaveBeenCalledWith("post-voc"); diff --git a/frontend/src/components/ProjectHistoryTimeline.tsx b/frontend/src/components/ProjectHistoryTimeline.tsx index 12c7a7854..940028103 100644 --- a/frontend/src/components/ProjectHistoryTimeline.tsx +++ b/frontend/src/components/ProjectHistoryTimeline.tsx @@ -103,6 +103,8 @@ export function ProjectHistoryTimeline({
{projectHistoryText(locale, "summaryCounts", { events: projection.event_count, + connected: projection.connected_post_count, + lineages: projection.lineage_count, actors: actorCount, })}
diff --git a/frontend/src/projectHistory.ts b/frontend/src/projectHistory.ts index c976baeb3..1219baf4b 100644 --- a/frontend/src/projectHistory.ts +++ b/frontend/src/projectHistory.ts @@ -109,6 +109,8 @@ export interface ProjectHistoryProjection { knowledge_cutoff?: string; evidence_boundary_code?: "authorized_visible_source_posts" | string; event_count: number; + connected_post_count: number; + lineage_count: number; distinct_actor_count?: number; distinct_observed_actor_count: number; truncated: boolean; @@ -253,7 +255,7 @@ const EN: Record