Skip to content

graypaper: pin reader URLs to latest hash + version - #255

Merged
tomusdrw merged 3 commits into
mainfrom
td-graypaper-version-url
Apr 28, 2026
Merged

graypaper: pin reader URLs to latest hash + version#255
tomusdrw merged 3 commits into
mainfrom
td-graypaper-version-url

Conversation

@tomusdrw

@tomusdrw tomusdrw commented Apr 27, 2026

Copy link
Copy Markdown
Member

Summary

  • Result links now use https://graypaper.fluffylabs.dev/#/<shortHash>?v=<version>&search=...&section=..., skipping the reader's client-side redirect.
  • The latest hash + version is persisted in data/graypaper/versions.md frontmatter (captured by updateGraypaperVersions from metadata.json) and exposed via GET /graypaper/latest.
  • Single URL composer in shared/graypaper.ts is shared between backend (tools.ts, AI agent citations) and frontend (results pages, via a useGraypaperLatest hook).

Test plan

  • Search for a graypaper section and click "Open reader" — URL contains #/ab2cdbd?v=0.7.2&... and the reader opens directly with no redirect.
  • Same check on the "View all graypaper" page.
  • Ask the AI a graypaper question and confirm citation links use the new format.
  • GET /graypaper/latest returns {hash, version}.
  • On a stale data dir (no latest_hash in versions.md), composer falls back to the un-pinned URL — links still work.
  • npm run qa (lint + biome) and npm test pass on both backend and client.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Graypaper reader links now use a fetched "latest" reference so embedded readers and "Open reader" links point to the current version instead of a hardcoded URL.
    • New endpoint exposes the latest graypaper metadata for clients to consume.
  • Tests

    • Added tests validating latest-version behavior, caching, and preservation/clearing of pinned latest metadata.

tomusdrw and others added 2 commits April 27, 2026 22:29
Embed the latest graypaper hash and version in result links
(`/#/<shortHash>?v=<version>&...`) so the reader skips its
client-side redirect. The latest ref is persisted in
`versions.md` frontmatter and exposed via `GET /graypaper/latest`;
both backend and frontend share a single URL composer in
`shared/graypaper.ts`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@netlify

netlify Bot commented Apr 27, 2026

Copy link
Copy Markdown

Deploy Preview for jam-search2 ready!

Name Link
🔨 Latest commit 5e4e104
🔍 Latest deploy log https://app.netlify.com/projects/jam-search2/deploys/69efce29509d9a000820e61d
😎 Deploy Preview https://deploy-preview-255--jam-search2.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 64c59987-a242-4e6b-8612-53b83c5459ab

📥 Commits

Reviewing files that changed from the base of the PR and between 6fc0f87 and 5e4e104.

📒 Files selected for processing (4)
  • backend/src/__tests__/data/graypaperLatest.test.ts
  • backend/src/data/graypaperLatest.ts
  • backend/src/data/writer.ts
  • backend/src/scripts/updateGraypaperVersions.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/src/data/graypaperLatest.ts

📝 Walkthrough

Walkthrough

Adds dynamic graypaper latest metadata retrieval and caching (from versions.md), threads dataDir through backend tools and MCP handlers, exposes GET /graypaper/latest, updates writer/version scripts to persist pinned latest, and updates frontend to fetch/use latest when building graypaper reader URLs.

Changes

Cohort / File(s) Summary
Backend: Tools & Agent
backend/src/ask/tools.ts, backend/src/ask/agentLoop.ts, backend/src/mcp/server.ts, backend/src/__tests__/ask/tools.test.ts
Thread dataDir into executeGetFullDocument/tool calls and update call sites and tests to pass the extra parameter; buildGraypaperUrl usage updated to accept latest metadata.
Backend: Data & Writer
backend/src/data/graypaperLatest.ts, backend/src/data/writer.ts, backend/src/scripts/updateGraypaperVersions.ts
Add getGraypaperLatest(dataDir) with file-mtime keyed cache and resetGraypaperLatestCache(); make writeGraypaperVersions optionally persist/clear pinned latest metadata; update script to compute and write latest.
Backend: API
backend/src/api.ts
Add GET /graypaper/latest route that returns getGraypaperLatest(dataDir) result as JSON.
Client: Hooks, API, Tests
client/src/hooks/useGraypaperLatest.ts, client/src/lib/api.ts, client/src/lib/__tests__/graypaperUrl.test.ts
Add useGraypaperLatest() React Query hook; add fetchGraypaperLatest() client API; add tests covering buildGraypaperUrl behavior with pinned/unpinned states.
Client: Components & Pages
client/src/components/results/GraypaperResults.tsx, client/src/pages/viewall/graypaper.tsx
Replace hardcoded reader URLs with buildGraypaperUrl(..., latest) driven by useGraypaperLatest().
Shared
shared/graypaper.ts, shared/index.ts
Add GraypaperLatest type and buildGraypaperUrl helper; export graypaper namespace from shared barrel.
Backend Tests
backend/src/__tests__/data/graypaperLatest.test.ts
Add tests validating getGraypaperLatest caching, fallbacks, and writeGraypaperVersions pinning semantics.

Sequence Diagram

sequenceDiagram
    participant Client as React Client
    participant Hook as useGraypaperLatest Hook
    participant API as Backend API
    participant DataLayer as graypaperLatest Module
    participant FS as File System

    Client->>Hook: mount -> request latest
    activate Hook
    Hook->>API: GET /graypaper/latest
    activate API
    API->>DataLayer: getGraypaperLatest(dataDir)
    activate DataLayer
    DataLayer->>FS: read versions.md (if exists)
    FS-->>DataLayer: frontmatter ({latest_hash, latest_version})
    DataLayer->>DataLayer: cache by dataDir (invalidate on mtime change)
    DataLayer-->>API: {hash, version}
    deactivate DataLayer
    API-->>Hook: JSON {hash, version}
    deactivate API
    Hook-->>Client: latest (cached)
    deactivate Hook

    Client->>Client: buildGraypaperUrl(title, query, latest)
    Client->>Client: render reader link (pinned or unpinned)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I hopped through versions, small and fleet,

Cached a hash where frontmatter meets,
I threaded dataDir, precise and bright,
Built reader links that pin or flight,
A rabbit's cheer for metadata delight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.74% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: pinning graypaper reader URLs to latest hash and version, which is the central theme across all file modifications.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch td-graypaper-version-url

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/src/data/graypaperLatest.ts`:
- Around line 23-27: The cached value in getGraypaperLatest (stored in cache)
never updates when the on-disk graypaper/versions.md is rewritten, so calls keep
returning stale data; fix by invalidating or updating that cache when the source
changes—either: (A) modify getGraypaperLatest to detect on-disk changes (stat
versions.md or include a timestamp from readFromDisk and compare it to cache)
and reload if changed, or (B, simpler) clear the cache after writes by resetting
cache in updateGraypaperVersions immediately after successfully writing the file
(ensure you reference cache, getGraypaperLatest, readFromDisk, and
updateGraypaperVersions when making the change).

In `@backend/src/data/writer.ts`:
- Around line 261-282: writeGraypaperVersions currently drops previously
persisted latest_hash/latest_version when called without the optional latest;
change it to preserve existing frontmatter by reading the current file at
filePath (versions.md) if it exists, parsing its frontmatter (using the same
frontmatter parser used elsewhere), and merge any existing
latest_hash/latest_version into the frontmatter object only when the incoming
latest argument is undefined; ensure you still overwrite the versions array from
the versions parameter, and update references to writeGraypaperVersions and any
callers (e.g., exportToMarkdown.ts) only as needed so omissions no longer
destroy the pinned metadata.

In `@backend/src/scripts/updateGraypaperVersions.ts`:
- Around line 63-69: The code currently only sets latestChanged when
resolveLatest() returns a non-null latest, so if resolveLatest() returns null
stale existing.latestHash/existing.latestVersion remain persisted; update the
logic around resolveLatest()/latestChanged to treat the transition from a
non-null persisted latest to null as a change (e.g., set latestChanged = true
when latest === null but existing.latestHash or existing.latestVersion are
present), and ensure the persistence/write path for the metadata (the code that
writes existing.latestHash/existing.latestVersion when hasNewVersion or
latestChanged is true) clears those fields when latest is null so the on-disk
versions.md drops pinned values and falls back to unpinned form.

In `@client/src/pages/viewall/graypaper.tsx`:
- Line 93: The buildGraypaperUrl call is passing raw user content (query and
section.title) into URL parameters which can break links when values contain
characters like & or #; update the shared builder (function buildGraypaperUrl in
shared/graypaper.ts) to URL-encode the query and section/section.title values
using encodeURIComponent (or an equivalent encoder) before concatenating them
into the search string so the generated url prop in
client/src/pages/viewall/graypaper.ts is safe and preserves the original values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4c372c9a-8a81-4804-8297-1f7bd07b984f

📥 Commits

Reviewing files that changed from the base of the PR and between b697ce4 and 6fc0f87.

⛔ Files ignored due to path filters (1)
  • data/graypaper/versions.md is excluded by !data/**
📒 Files selected for processing (15)
  • backend/src/__tests__/ask/tools.test.ts
  • backend/src/api.ts
  • backend/src/ask/agentLoop.ts
  • backend/src/ask/tools.ts
  • backend/src/data/graypaperLatest.ts
  • backend/src/data/writer.ts
  • backend/src/mcp/server.ts
  • backend/src/scripts/updateGraypaperVersions.ts
  • client/src/components/results/GraypaperResults.tsx
  • client/src/hooks/useGraypaperLatest.ts
  • client/src/lib/__tests__/graypaperUrl.test.ts
  • client/src/lib/api.ts
  • client/src/pages/viewall/graypaper.tsx
  • shared/graypaper.ts
  • shared/index.ts

Comment thread backend/src/data/graypaperLatest.ts
Comment thread backend/src/data/writer.ts
Comment thread backend/src/scripts/updateGraypaperVersions.ts
Comment thread client/src/pages/viewall/graypaper.tsx
- getGraypaperLatest invalidates its cache on versions.md mtime change
  (graypaperJob runs out-of-process and rewrites the file).
- writeGraypaperVersions now treats `latest === undefined` as "preserve"
  so callers like exportToMarkdown.ts don't wipe the pinned ref, and
  `latest === null` as "clear" so the metadata-no-longer-has-latest
  case drops the stale pin.
- updateGraypaperVersions detects null↔value transitions both ways.

Declined the suggestion to URL-encode params: prior PR #250 explicitly
removed encoding to match the reader's hash-router contract — changing
that needs reader-side verification, not a drive-by edit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@tomusdrw
tomusdrw merged commit c909d29 into main Apr 28, 2026
11 checks passed
@tomusdrw
tomusdrw deleted the td-graypaper-version-url branch April 28, 2026 12:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant