diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md
index aa557bd7a..ccd2da98b 100644
--- a/ARCHITECTURE.md
+++ b/ARCHITECTURE.md
@@ -481,7 +481,7 @@ list, then open the Pending row to confirm the cutoff corpus.
unavailable, so that run is Failed rather than a fabricated score.
The home list is clickable: `GET /api/analysis-runs/{id}` fills a
labeled detail (cutoff, requested date, 12-character digest prefixes
-with full digests on hover, counts, status history)
+that disclose the full digest on activation, counts, status history)
without exposing a DSN or raw record. Opening a cutoff title warns
that the live body may have changed after the run. Status history is detail-only
and uses lookup labels plus occurrence times; a failure event keeps
diff --git a/CHANGELOG.d/0.85.1-analysis-run-digest-disclosure.md b/CHANGELOG.d/0.85.1-analysis-run-digest-disclosure.md
new file mode 100644
index 000000000..b91a0f40d
--- /dev/null
+++ b/CHANGELOG.d/0.85.1-analysis-run-digest-disclosure.md
@@ -0,0 +1,5 @@
+# 0.85.1 Analysis-run digest disclosure
+
+Activate a prefix on the run detail to read the full digest. The
+closed panel stays in the document with `hidden`. Hover is not the
+only verification path. The list stays aggregates-only.
diff --git a/CHANGELOG.d/0.86.1-analysis-run-digest-disclosure.md b/CHANGELOG.d/0.86.1-analysis-run-digest-disclosure.md
new file mode 100644
index 000000000..561523020
--- /dev/null
+++ b/CHANGELOG.d/0.86.1-analysis-run-digest-disclosure.md
@@ -0,0 +1,5 @@
+# 0.86.1 Analysis-run digest disclosure
+
+Activate a prefix on the run detail to read the full digest. The
+closed panel stays in the document with `hidden`. Hover is not the
+only verification path. The list stays aggregates-only.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 434c4d63b..b6a01bc6f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,18 @@ 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.86.1] - 2026-08-16
+
+### Fixed
+
+- Analysis-run digest prefixes are disclosure buttons. Open the Demo
+ Corp lineage run, activate `Code` or `Config`, and match the revealed
+ digest to the API payload. The closed panel stays in the document
+ with `hidden` so `aria-controls` has a target, and each prefix meets
+ the 24px pointer target (WCAG 2.2 SC 1.4.13 and 2.5.8; WAI-ARIA APG
+ Disclosure). A hover `title` is no longer the only path. The home
+ list still hides digests even when the list JSON includes them.
+
## [0.86.0] - 2026-08-16
### Added
diff --git a/CLAUDE.md b/CLAUDE.md
index 2f89bada7..3f0ab6d9c 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -3,7 +3,7 @@
Tool-specific pointer. Policy lives in [AGENTS.md](AGENTS.md) and the
ADRs under `docs/adr/`. Do not fork those rules here.
-## Analysis-run seed (v0.85.0)
+## Analysis-run seed (v0.86.1)
`make seed` writes a Demo Corp lineage run and a TEPP run on the same
snapshot (ADR 0013). The TEPP path goes through `tepp_client`. A missing
@@ -14,7 +14,7 @@ theta or a local psychometric substitute. The home list caption stays
(ADR 0014). Open a Failed TEPP row, then connect a live TEPP
transport. A failed lineage row retries reconstruction -- it does not
mention TEPP.
-Digest prefixes stay audible; hover a prefix to read the full digest.
+Digest prefixes stay audible; activate a prefix to read the full digest.
Opening a cutoff title shows the live post -- compare it with the
cutoff before treating the body as reconstructed evidence (ADR 0016).
`POST /api/analysis-runs` records Pending on an authorized
diff --git a/docs/adr/0016-analysis-run-knowledge-cutoff-posts.md b/docs/adr/0016-analysis-run-knowledge-cutoff-posts.md
index 089443374..60b4d838b 100644
--- a/docs/adr/0016-analysis-run-knowledge-cutoff-posts.md
+++ b/docs/adr/0016-analysis-run-knowledge-cutoff-posts.md
@@ -31,9 +31,13 @@ as reconstructed evidence.
Reproducibility digests on the same detail use a labeled group whose
accessible name does not replace the visible prefixes (W3C Accessible
-Name and Description Computation 1.1). Full digests stay on `title`
-for hover verification and on the API payload; the home list stays
-aggregates-only.
+Name and Description Computation 1.1). Each prefix is a disclosure
+button (WAI-ARIA APG Disclosure; WCAG 2.2 Success Criterion 1.4.13).
+The full digest stays in the document with `hidden` until activation
+so `aria-controls` has a target and keyboard and assistive technology
+can verify it the same way a pointer can. Each prefix button meets the
+WCAG 2.2 SC 2.5.8 24px minimum target. The home list stays
+aggregates-only; the API payload still carries the full values.
Seed and API fixtures backdate in-cutoff posts. A late own-corp private
post remains on the live post list and stays out of the January 2026
@@ -46,8 +50,8 @@ run.
post (2026-02-10) does not appear.
- Open the run, read the live-body warning, then open a listed post
and compare it with the cutoff date.
-- Hover a digest prefix to read the full code or configuration digest
- when you need to match the API payload.
+- Activate a digest prefix (Enter, Space, or click) to read the full
+ code or configuration digest when you need to match the API payload.
- Post-body versioning at the cutoff remains future work.
- Thread-group *run list* visibility now uses the same cutoff
(ADR 0018). A later public post cannot surface a previously hidden
@@ -65,3 +69,10 @@ Recommendation). https://www.w3.org/TR/owl-time/
World Wide Web Consortium. (2018). *Accessible name and description
computation 1.1* (W3C Recommendation).
https://www.w3.org/TR/accname-1.1/
+
+World Wide Web Consortium. (2024). *Web content accessibility guidelines
+(WCAG) 2.2* (W3C Recommendation). https://www.w3.org/TR/WCAG22/
+
+World Wide Web Consortium. (n.d.). *Disclosure (show/hide) pattern*.
+ARIA Authoring Practices Guide.
+https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/
diff --git a/docs/doctoring/ANALYSIS_RUN_REGISTRY_REFERENCES.md b/docs/doctoring/ANALYSIS_RUN_REGISTRY_REFERENCES.md
index b41b31c17..383cade0b 100644
--- a/docs/doctoring/ANALYSIS_RUN_REGISTRY_REFERENCES.md
+++ b/docs/doctoring/ANALYSIS_RUN_REGISTRY_REFERENCES.md
@@ -9,7 +9,9 @@
|---|---|---|
| W3C PROV-DM and PROV-O | Preserve identifiable entities, activities, agents, generation/use, and derivation without flattening provenance into display-only edges. | `analysis_source_snapshot`, `analysis_run`, authenticated requester, append-only status events, immutable digests; later product bindings continue to use the separate `provenance_*` layer from ADR 0011. |
| W3C Time Ontology in OWL | Keep temporal concepts explicit and avoid collapsing distinct clocks. | Evidence availability and snapshot capture remain on `analysis_source_snapshot`; analysis knowledge cutoff and request time remain on `analysis_run`; status occurrence and database record time remain distinct. `GET /api/analysis-runs/{id}` visible posts apply `created_at <= knowledge_cutoff` (ADR 0016). Opening a listed title warns that the live body may have changed after that cutoff. |
-| W3C Accessible Name and Description Computation 1.1 | Do not let `aria-label` replace visible text the operator must hear. | Analysis-run digest prefixes live in a labeled group; the prefixes remain the accessible contents and the full digest is on `title` for hover verification. |
+| W3C Accessible Name and Description Computation 1.1 | Do not let `aria-label` replace visible text the operator must hear. | Analysis-run digest prefixes live in a labeled group; the prefixes remain the accessible contents of disclosure buttons. |
+| WCAG 2.2 Success Criterion 1.4.13 | Additional content that appears only on hover or focus must not be the only way to complete a task. | Full digests are hidden until the operator activates a prefix button (Enter, Space, or click). Native `title` tooltips are not the verification path. |
+| WAI-ARIA APG Disclosure | Use a button with `aria-expanded` to show and hide the controlled digest. | `AnalysisRunReproducibilityDigests` toggles a `` panel per prefix. |
| ISO 8601-1:2019 | Use unambiguous timestamp representation and timezone-aware persistence. | PostgreSQL `timestamptz` for availability, capture, cutoff, request, occurrence, and record clocks; tests use explicit `Z` offsets. |
| PostgreSQL 18 constraints and trigger contracts | Put integrity close to durable truth and use constraints for row shape while triggers enforce cross-row state and serialization. | Digest/check constraints, category allowlists, account-scoped uniqueness, shape constraints, immutable-row triggers, shared snapshot-row locking, and serialized status transitions. |
| NIST SP 800-92 | Treat audit records as bounded, protected operational evidence rather than unstructured application logging. | Append-only status events, machine failure codes, actor identity, occurrence/record clocks, fail-closed rollback, and exclusion of raw source/provider payloads. |
@@ -104,3 +106,10 @@ computation 1.1* (W3C Recommendation). https://www.w3.org/TR/accname-1.1/
World Wide Web Consortium. (2022). *Time ontology in OWL* (W3C Recommendation).
https://www.w3.org/TR/owl-time/
+
+World Wide Web Consortium. (2024). *Web content accessibility guidelines
+(WCAG) 2.2* (W3C Recommendation). https://www.w3.org/TR/WCAG22/
+
+World Wide Web Consortium. (n.d.). *Disclosure (show/hide) pattern*.
+ARIA Authoring Practices Guide.
+https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/
diff --git a/docs/goals/analysis-run-operator-loop.md b/docs/goals/analysis-run-operator-loop.md
new file mode 100644
index 000000000..9832edb87
--- /dev/null
+++ b/docs/goals/analysis-run-operator-loop.md
@@ -0,0 +1,31 @@
+# Goal — analysis-run operator loop
+
+**Status:** Active
+**Date:** 2026-08-16
+
+## Goal
+
+A buyer can open a seeded analysis run, hear the digest prefixes, reveal
+the full digests without a pointer, and compare any opened live post
+with that run's cutoff before treating the body as reconstructed
+evidence.
+
+## Current loop
+
+1. #127 is on `feat/role-responsibility-agent-ontology` (`44912a6`).
+ Prefixes are audible; the live-body warning is present.
+2. Land #139 (v0.86.1) so keyboard and AT operators can match a digest
+ to the API payload. Closed panels stay in the document with `hidden`;
+ each prefix is a 24px target. Prefer #139 over #135. Do not
+ self-approve or merge from this automation.
+3. Keep #131 as the write-clock comparison slice. Do not open a second
+ write-clock PR.
+4. #125 (`POST /api/analysis-runs`) is on the same base. Do not open a
+ second create PR.
+5. Post-body versioning at the cutoff remains later work (ADR 0016).
+
+## Out of this loop
+
+Retention purge and the Storybook runner belong to the approved
+frontend-toolchain PR. Failed-run next-action copy already landed with
+#124.
diff --git a/docs/storybook-inventory.md b/docs/storybook-inventory.md
new file mode 100644
index 000000000..4a6960099
--- /dev/null
+++ b/docs/storybook-inventory.md
@@ -0,0 +1,21 @@
+# Storybook inventory
+
+Repeating web objects that must stay tokenized and independently
+composable. The Storybook runner itself lands with the approved
+frontend toolchain PR; this inventory is the product list those
+stories must cover.
+
+| Object | Tokens | Next action the story must teach |
+|---|---|---|
+| Analysis-run digest disclosure | `--lw-opacity-meta`, `--lw-font-size-meta`, `--lw-space-digest-gap`, `--lw-font-family-mono`, `--lw-focus-ring`, `--lw-focus-offset`, `--lw-target-min` | Activate a prefix, then match the revealed digest to the API payload. |
+| Analysis-run live-post warning | `--lw-opacity-meta`, `--lw-font-size-meta` | Compare the opened body with the run cutoff before treating it as reconstructed evidence. |
+| Meta caption (`.post-meta`) | `--lw-opacity-meta`, `--lw-font-size-meta` | Read the clock or count, then take the control beside it. |
+
+## References
+
+World Wide Web Consortium. (2024). *Web content accessibility guidelines
+(WCAG) 2.2* (W3C Recommendation). https://www.w3.org/TR/WCAG22/
+
+World Wide Web Consortium. (n.d.). *Disclosure (show/hide) pattern*.
+ARIA Authoring Practices Guide.
+https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/
diff --git a/frontend/package.json b/frontend/package.json
index dac241738..803acd91b 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,7 +1,7 @@
{
"name": "frontend",
"private": true,
- "version": "0.86.0",
+ "version": "0.86.1",
"type": "module",
"scripts": {
"dev": "vite",
diff --git a/frontend/src/AnalysisRunReproducibilityDigests.test.tsx b/frontend/src/AnalysisRunReproducibilityDigests.test.tsx
new file mode 100644
index 000000000..04e8ff355
--- /dev/null
+++ b/frontend/src/AnalysisRunReproducibilityDigests.test.tsx
@@ -0,0 +1,105 @@
+import { render, screen } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+import { describe, expect, it } from "vitest";
+import { ANALYSIS_RUN_DIGEST_TARGET_MIN_PX } from "./analysisRunDigests";
+import { AnalysisRunReproducibilityDigests } from "./AnalysisRunReproducibilityDigests";
+
+const CODE_REVISION_SHA = "abcdef0123456789deadbeefcafebabe";
+const CONFIGURATION_SHA256 =
+ "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
+
+describe("AnalysisRunReproducibilityDigests", () => {
+ it("renders nothing when the run has no digests", () => {
+ const { container } = render();
+ expect(container).toBeEmptyDOMElement();
+ });
+
+ it("keeps prefixes audible and hides full digests until activation", () => {
+ render(
+ ,
+ );
+ const group = screen.getByLabelText("Analysis run reproducibility digests");
+ expect(group).toHaveTextContent("Activate a prefix to read the full digest and match the API payload.");
+ expect(group).not.toHaveTextContent("Hover");
+ const codeButton = screen.getByRole("button", { name: "Code abcdef012345" });
+ const configButton = screen.getByRole("button", { name: "Config 0123456789ab" });
+ expect(codeButton).toHaveAttribute("aria-expanded", "false");
+ expect(configButton).toHaveAttribute("aria-expanded", "false");
+ expect(screen.getByText(CODE_REVISION_SHA)).not.toBeVisible();
+ expect(screen.getByText(CONFIGURATION_SHA256)).not.toBeVisible();
+ const codePanelId = codeButton.getAttribute("aria-controls");
+ const configPanelId = configButton.getAttribute("aria-controls");
+ expect(codePanelId).toBeTruthy();
+ expect(configPanelId).toBeTruthy();
+ expect(document.getElementById(codePanelId ?? "")).toHaveAttribute("hidden");
+ expect(document.getElementById(configPanelId ?? "")).toHaveAttribute("hidden");
+ expect(codeButton).toHaveStyle({
+ minHeight: `${ANALYSIS_RUN_DIGEST_TARGET_MIN_PX}px`,
+ minWidth: `${ANALYSIS_RUN_DIGEST_TARGET_MIN_PX}px`,
+ });
+ });
+
+ it("reveals the full code digest with Enter and hides it on the next activation", async () => {
+ const user = userEvent.setup();
+ render(
+ ,
+ );
+ await user.tab();
+ expect(screen.getByRole("button", { name: "Code abcdef012345" })).toHaveFocus();
+ await user.keyboard("{Enter}");
+ expect(screen.getByRole("button", { name: "Code abcdef012345" })).toHaveAttribute(
+ "aria-expanded",
+ "true",
+ );
+ expect(screen.getByText(CODE_REVISION_SHA)).toBeVisible();
+ expect(screen.getByText(CONFIGURATION_SHA256)).not.toBeVisible();
+ await user.keyboard("{Enter}");
+ expect(screen.getByRole("button", { name: "Code abcdef012345" })).toHaveAttribute(
+ "aria-expanded",
+ "false",
+ );
+ expect(screen.getByText(CODE_REVISION_SHA)).not.toBeVisible();
+ });
+
+ it("tells the operator to match the revealed digest after activation", async () => {
+ const user = userEvent.setup();
+ render(
+ ,
+ );
+ expect(
+ screen.getByText("Activate a prefix to read the full digest and match the API payload."),
+ ).toBeInTheDocument();
+ await user.click(screen.getByRole("button", { name: "Code abcdef012345" }));
+ expect(
+ screen.getByText("Match the revealed digest to the API payload."),
+ ).toBeInTheDocument();
+ expect(
+ screen.queryByText("Activate a prefix to read the full digest and match the API payload."),
+ ).not.toBeInTheDocument();
+ });
+
+ it("reveals the full configuration digest with Space", async () => {
+ const user = userEvent.setup();
+ render(
+ ,
+ );
+ await user.tab();
+ await user.tab();
+ expect(screen.getByRole("button", { name: "Config 0123456789ab" })).toHaveFocus();
+ await user.keyboard(" ");
+ expect(screen.getByText(CONFIGURATION_SHA256)).toBeVisible();
+ expect(screen.getByText(CODE_REVISION_SHA)).not.toBeVisible();
+ });
+});
diff --git a/frontend/src/AnalysisRunReproducibilityDigests.tsx b/frontend/src/AnalysisRunReproducibilityDigests.tsx
new file mode 100644
index 000000000..2ac77d3c0
--- /dev/null
+++ b/frontend/src/AnalysisRunReproducibilityDigests.tsx
@@ -0,0 +1,104 @@
+import { useId, useState } from "react";
+import {
+ ANALYSIS_RUN_DIGEST_TARGET_MIN_PX,
+ analysisRunDigestButtonLabel,
+ analysisRunDigestNextAction,
+ analysisRunDigestRevealedNextAction,
+ type AnalysisRunDigestKind,
+} from "./analysisRunDigests";
+
+/**
+ * One digest disclosure. The button name stays the audible prefix; the
+ * full value stays in the document with `hidden` until Enter, Space, or
+ * click so `aria-controls` always has a target (APG Disclosure).
+ */
+function AnalysisRunDigestDisclosure({
+ kind,
+ digest,
+ panelId,
+ open,
+ onOpenChange,
+}: {
+ kind: AnalysisRunDigestKind;
+ digest: string;
+ panelId: string;
+ open: boolean;
+ onOpenChange: (open: boolean) => void;
+}) {
+ const label = analysisRunDigestButtonLabel(kind, digest);
+ return (
+
+
+
+ {digest}
+
+
+ );
+}
+
+/**
+ * Labeled group of analysis-run reproducibility digests.
+ *
+ * Prefixes remain the accessible contents of the group. Full digests
+ * stay off the home list and stay `hidden` on the detail until the
+ * operator activates a prefix.
+ */
+export function AnalysisRunReproducibilityDigests({
+ codeRevisionSha,
+ configurationSha256,
+}: {
+ codeRevisionSha?: string;
+ configurationSha256?: string;
+}) {
+ const id = useId();
+ const [openCode, setOpenCode] = useState(false);
+ const [openConfig, setOpenConfig] = useState(false);
+ if (!codeRevisionSha && !configurationSha256) {
+ return null;
+ }
+ const anyOpen =
+ (Boolean(codeRevisionSha) && openCode) ||
+ (Boolean(configurationSha256) && openConfig);
+ return (
+
+ )}
{
+ it("keeps a 12-character prefix so the operator can match git-style short SHAs", () => {
+ expect(ANALYSIS_RUN_DIGEST_PREFIX_LENGTH).toBe(12);
+ expect(analysisRunDigestPrefix(CODE_REVISION_SHA)).toBe("abcdef012345");
+ expect(analysisRunDigestPrefix(CONFIGURATION_SHA256)).toBe("0123456789ab");
+ });
+
+ it("returns the whole digest when it is shorter than the prefix length", () => {
+ expect(analysisRunDigestPrefix("abc")).toBe("abc");
+ });
+});
+
+describe("analysisRunDigestKindLabel", () => {
+ it("names the two registered digest kinds without inventing a third", () => {
+ expect(analysisRunDigestKindLabel("code")).toBe("Code");
+ expect(analysisRunDigestKindLabel("config")).toBe("Config");
+ });
+});
+
+describe("analysisRunDigestButtonLabel", () => {
+ it("puts the kind and prefix in the accessible name, not the full digest", () => {
+ expect(analysisRunDigestButtonLabel("code", CODE_REVISION_SHA)).toBe(
+ "Code abcdef012345",
+ );
+ expect(analysisRunDigestButtonLabel("config", CONFIGURATION_SHA256)).toBe(
+ "Config 0123456789ab",
+ );
+ expect(analysisRunDigestButtonLabel("code", CODE_REVISION_SHA)).not.toContain(
+ CODE_REVISION_SHA,
+ );
+ });
+});
+
+describe("analysisRunDigestNextAction", () => {
+ it("tells every operator to activate a prefix, not to hover", () => {
+ const nextAction = analysisRunDigestNextAction();
+ expect(nextAction).toMatch(/Activate a prefix/);
+ expect(nextAction).not.toMatch(/Hover/i);
+ });
+
+ it("tells the operator to match the revealed digest to the API payload", () => {
+ const nextAction = analysisRunDigestRevealedNextAction();
+ expect(nextAction).toMatch(/Match the revealed digest/);
+ expect(nextAction).toMatch(/API payload/);
+ expect(nextAction).not.toMatch(/Hover/i);
+ expect(nextAction).not.toMatch(/Activate a prefix/);
+ });
+});
+
+describe("ANALYSIS_RUN_DIGEST_TARGET_MIN_PX", () => {
+ it("keeps the WCAG 2.5.8 24px minimum pointer target", () => {
+ expect(ANALYSIS_RUN_DIGEST_TARGET_MIN_PX).toBe(24);
+ });
+});
diff --git a/frontend/src/analysisRunDigests.ts b/frontend/src/analysisRunDigests.ts
new file mode 100644
index 000000000..7fc150478
--- /dev/null
+++ b/frontend/src/analysisRunDigests.ts
@@ -0,0 +1,79 @@
+/**
+ * Reproducibility-digest helpers for an analysis-run detail.
+ *
+ * The home list stays aggregates-only. Detail shows a 12-character
+ * prefix so assistive technology hears `Code` / `Config` values, then
+ * a disclosure button reveals the full digest (WCAG 2.2 SC 1.4.13;
+ * WAI-ARIA APG Disclosure). Native `title` tooltips are pointer-only
+ * and must not be the only way to verify a digest against the API.
+ */
+
+/** Git-style prefix length shown before the operator opens the full digest. */
+export const ANALYSIS_RUN_DIGEST_PREFIX_LENGTH = 12;
+
+/** WCAG 2.2 SC 2.5.8 minimum pointer target for each prefix button. */
+export const ANALYSIS_RUN_DIGEST_TARGET_MIN_PX = 24;
+
+/** Which digest the disclosure button reveals. */
+export type AnalysisRunDigestKind = "code" | "config";
+
+/**
+ * Visible prefix used on the disclosure button.
+ *
+ * @param digest - Full code revision SHA or configuration SHA-256.
+ * @returns The first 12 characters, or the whole string when shorter.
+ */
+export function analysisRunDigestPrefix(digest: string): string {
+ return digest.slice(0, ANALYSIS_RUN_DIGEST_PREFIX_LENGTH);
+}
+
+/**
+ * Visible label for a digest kind. Keep this as the button contents so
+ * `aria-label` does not replace the prefix (AccName 1.1).
+ *
+ * @param kind - Code revision or configuration digest.
+ * @returns `Code` or `Config`.
+ */
+export function analysisRunDigestKindLabel(kind: AnalysisRunDigestKind): string {
+ switch (kind) {
+ case "code":
+ return "Code";
+ case "config":
+ return "Config";
+ default: {
+ const _exhaustive: never = kind;
+ return _exhaustive;
+ }
+ }
+}
+
+/**
+ * Button text the operator hears and sees. The full digest is not part
+ * of the name; it appears only after activation.
+ *
+ * @param kind - Code revision or configuration digest.
+ * @param digest - Full digest string from the run payload.
+ * @returns For example `Code abcdef012345`.
+ */
+export function analysisRunDigestButtonLabel(
+ kind: AnalysisRunDigestKind,
+ digest: string,
+): string {
+ return `${analysisRunDigestKindLabel(kind)} ${analysisRunDigestPrefix(digest)}`;
+}
+
+/**
+ * Next action shown above the prefixes. True for keyboard, pointer, and
+ * assistive technology — unlike “Hover a prefix”.
+ */
+export function analysisRunDigestNextAction(): string {
+ return "Activate a prefix to read the full digest and match the API payload.";
+}
+
+/**
+ * Next action after a prefix is open. The operator already revealed the
+ * digest; the remaining step is to match it to the API payload.
+ */
+export function analysisRunDigestRevealedNextAction(): string {
+ return "Match the revealed digest to the API payload.";
+}
diff --git a/lineageweave/__init__.py b/lineageweave/__init__.py
index 5f70c6064..48bf6e481 100644
--- a/lineageweave/__init__.py
+++ b/lineageweave/__init__.py
@@ -55,4 +55,4 @@
"sentence_excerpts",
]
-__version__ = "0.86.0"
+__version__ = "0.86.1"
diff --git a/pyproject.toml b/pyproject.toml
index 0393b774a..eb2e26318 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "lineageweave"
-version = "0.86.0"
+version = "0.86.1"
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" }
diff --git a/uv.lock b/uv.lock
index b302b1250..c759df267 100644
--- a/uv.lock
+++ b/uv.lock
@@ -454,7 +454,7 @@ wheels = [
[[package]]
name = "lineageweave"
-version = "0.86.0"
+version = "0.86.1"
source = { virtual = "." }
dependencies = [
{ name = "certifi" },