Summary and Byproducts
Project summary, decisions made during implementation, and lessons learned for the Agentation → Hypothesis bridge integration.
Timeline
Section titled “Timeline”- 2026-03-25: Feasibility analysis, specification, project plan
- 2026-03-25: Implementation (Run 1: hypothesis-mcp, Run 2: ux-prototype)
- 2026-03-25: Interactive testing and iterative fixes
- 2026-03-25/26: CI fixes, PR creation, merge
Decisions Made During Implementation
Section titled “Decisions Made During Implementation”These decisions were made during implementation based on testing feedback, diverging from the original specification where noted.
D-01: Vite Plugin Instead of Express Middleware
Section titled “D-01: Vite Plugin Instead of Express Middleware”Spec said: Use .storybook/middleware.ts (Express middleware).
Actual: Storybook 10 uses Vite, not Express. The middleware file is ignored.
Implemented as a Vite configureServer plugin inline in .storybook/main.ts.
D-02: TextQuoteSelector Required for Sidebar Display
Section titled “D-02: TextQuoteSelector Required for Sidebar Display”Spec said: CssSelector alone would work for anchoring.
Actual: The Hypothesis sidebar hides annotations it cannot anchor to page
text (“orphaned”). A TextQuoteSelector matching visible text on the page is
required for annotations to appear in the Annotations tab. Without it, the
annotation is invisible (not even in Page Notes).
Resolution: The bridge enriches each annotation with live DOM text from the
annotated element or its ancestors (within #storybook-root only). When no
anchorable text exists, the annotation is posted as a page note (target with
source but no selectors), visible in the Page Notes tab.
D-03: URL Normalization for Annotation Matching
Section titled “D-03: URL Normalization for Annotation Matching”Spec said: Normalize to iframe.html?id=<storyId> (strip all params).
Actual: The Hypothesis sidebar resolves the document URI from <link rel="canonical">
and the document.uri config option. Both the canonical link and the config are
set dynamically via preview-head.html and preview-body.html scripts. The
normalized URL retains id and viewMode but strips globals and args.
D-04: onCopy Instead of onSubmit for Bridge Trigger
Section titled “D-04: onCopy Instead of onSubmit for Bridge Trigger”Spec said: Wire the bridge to onSubmit.
Actual: Agentation’s onSubmit is the per-annotation comment dialog submit,
not the toolbar export action. The toolbar Copy button triggers onCopy, which
receives markdown output (not JSON). The bridge reads raw annotations from
Agentation’s localStorage via loadAnnotations() instead of parsing the
callback argument.
D-05: Agentation Overlay Cycle for Badge Cleanup
Section titled “D-05: Agentation Overlay Cycle for Badge Cleanup”Spec said: Clear localStorage to remove badges.
Actual: saveAnnotations(pathname, []) clears localStorage but the
Agentation React component’s useState still holds annotations in memory.
Badges persist until component remount. The bridge cycles the Storybook global
off/on (300ms gap) via __STORYBOOK_ADDONS_CHANNEL__ to force unmount/remount,
clearing all badges.
D-06: Group-Read Permissions Required
Section titled “D-06: Group-Read Permissions Required”Spec said: Set group: "e4e5jGAx" in the payload.
Actual: Setting the group is not enough. The permissions.read field must
explicitly include ["group:e4e5jGAx"]. Without it, annotations default to
user-only read permission and are invisible to other group members in the
sidebar.
D-07: Express Middleware File Removed
Section titled “D-07: Express Middleware File Removed”The .storybook/middleware.ts file was initially created per the spec but
removed after discovering Storybook 10 does not load it. The proxy logic lives
entirely in the Vite plugin in .storybook/main.ts.
D-08: next-themes Missing Dependency
Section titled “D-08: next-themes Missing Dependency”During CI validation, the Storybook build failed because next-themes (required
by the vendored sonner.tsx component) was listed in vendored-deps.json but
not in package.json. Added as an explicit dependency.
What Worked Well
Section titled “What Worked Well”- Hypothesis MCP server as a reference implementation made the API integration
straightforward. The
createAnnotationfunction pattern was directly reusable. - Agentation’s exported utilities (
loadAnnotations,saveAnnotations,getStorageKey) enabled programmatic access to annotation state without hacking into the component internals. - Playwright MCP tools for browser automation during debugging helped identify the URL mismatch and orphaned annotation issues.
- Storybook’s
__STORYBOOK_ADDONS_CHANNEL__global provided a clean way to toggle the Agentation overlay from the preview iframe without needing cross-frame postMessage hacks.
What Was Harder Than Expected
Section titled “What Was Harder Than Expected”- Hypothesis URL matching: The sidebar uses a complex URI resolution chain
(canonical link,
document.uriconfig, rawwindow.location.href) and the exact behavior wasn’t documented. Required multiple iterations to get the annotation URI, canonical link, and config to all agree. - Hypothesis orphan filtering: Annotations without a
TextQuoteSelectorare silently hidden. The sidebar shows “no annotations” with no indication that orphaned annotations exist. This was the hardest bug to diagnose — annotations were being created correctly but invisible. - Storybook 10 middleware: The
.storybook/middleware.tsconvention from Storybook 7 documentation is silently ignored in Storybook 10 (Vite-based). No error, no warning — the routes simply don’t exist. - Agentation’s
onCopyvsonSubmit: The API naming doesn’t match the toolbar UI.onCopyis the toolbar Copy button;onSubmitis the per-annotation comment dialog. Required reading the minified source to understand.
Suggestions for Future Work
Section titled “Suggestions for Future Work”- Agentation
onCopycallback should receive annotations array — currently receives markdown text only, requiring the bridge to read localStorage directly. - Hypothesis
document.uriconfig should be better documented for SPA/iframe environments where the page URL contains volatile parameters. - Badge click → scroll to sidebar annotation — currently badges show a tooltip but don’t navigate to the corresponding sidebar entry.
- Severity/intent from Agentation — the Agentation component’s comment
dialog doesn’t expose intent/severity fields to the
onCopycallback, so these tags are only present when the annotation data includes them (not always). - Deployed Storybook integration — the Hypothesis sidebar works on
localhostbut the URI matching needs testing on the deployed Vercel/GitHub Pages URLs.
Copyright: © Arda Systems 2025-2026, All rights reserved