Public: anyone can read this; the crew can change it.

GET /api/v1/feed's first page carries the "Since you were here" digest, as the MCP feed tool's does #9107

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

`GET /api/v1/feed` now carries the "Since you were here" digest on its first page, built by the same call the MCP `feed` tool makes.

shipped production is serving it deploy e209d31

opened this

GET /api/v1/feed now carries the "Since you were here" digest on its first page, built by the same call the MCP feed tool makes.

I ran tsc, scripts/gate.ts, test/mcp-size.test.ts and about 480 neighbouring feed, ETag, digest and OpenAPI tests, and they pass. I did not run the whole suite; the forge's check does that on push.

What changed

  • Shared call: feedDigest(db, viewer, now) in src/web/studio/since.ts reads the last visit with lastVisit and returns sinceDigest(...) ?? null. The HTTP route and the MCP tool both call it.
  • Response shape: with no cursor the route answers digest: { heading, lines: [{ repo, says, href }] } | null beside the page. A page with a cursor has no digest key.
  • ETag (folded in, not skipped): feedTag takes an optional visit and hashes the last visit on the first page only, so a 304 never hides a changed digest. Cursor pages keep their old tags, and existing feedTag callers are unchanged.
  • Timing: the digest is its own digest Server-Timing phase.
  • Read-only: lastVisit is read-only and the route never calls noteVisit, so reading the feed over HTTP does not count as a visit.
  • OpenAPI: the summary for getApiV1Feed now names digest, says a cursor page has none, and says the ETag moves with it. I found no contract snapshot that pins this route, so none needed updating.

Test: test/feed-digest-http.test.ts seeds a serving event and a merged pull. It checks three things:

  • The HTTP first page's digest equals the MCP one.
  • A page with a cursor has no digest key, and the read leaves lastVisit unset.
  • Moving the last visit turns a matching If-None-Match from 304 into 200 with a new tag.

I confirmed the test fails with the source changes stashed.

I did not measure the 50 ms budget for the common request. The digest is one extra eventsSince read on first-page, uncached requests.

🤖 Generated with Claude Code

Closes #9104

commented

GET /api/v1/feed now carries the "Since you were here" digest on its first page, built by the same call the MCP feed tool makes.

I ran tsc, scripts/gate.ts, test/mcp-size.test.ts and about 480 neighbouring feed, ETag, digest and OpenAPI tests, and they pass. I did not run the whole suite; the forge's check does that on push.

What changed

  • Shared call: feedDigest(db, viewer, now) in src/web/studio/since.ts reads the last visit with lastVisit and returns sinceDigest(...) ?? null. The HTTP route and the MCP tool both call it.
  • Response shape: with no cursor the route answers digest: { heading, lines: [{ repo, says, href }] } | null beside the page. A page with a cursor has no digest key.
  • ETag (folded in, not skipped): feedTag takes an optional visit and hashes the last visit on the first page only, so a 304 never hides a changed digest. Cursor pages keep their old tags, and existing feedTag callers are unchanged.
  • Timing: the digest is its own digest Server-Timing phase.
  • Read-only: lastVisit is read-only and the route never calls noteVisit, so reading the feed over HTTP does not count as a visit.
  • OpenAPI: the summary for getApiV1Feed now names digest, says a cursor page has none, and says the ETag moves with it. I found no contract snapshot that pins this route, so none needed updating.

Test: test/feed-digest-http.test.ts seeds a serving event and a merged pull. It checks three things:

  • The HTTP first page's digest equals the MCP one.
  • A page with a cursor has no digest key, and the read leaves lastVisit unset.
  • Moving the last visit turns a matching If-None-Match from 304 into 200 with a new tag.

I confirmed the test fails with the source changes stashed.

I did not measure the 50 ms budget for the common request. The digest is one extra eventsSince read on first-page, uncached requests.

🤖 Generated with Claude Code

commented

The preview deploy didn't fail, so I made no change and pushed nothing.

The log you pasted ends in done: https://lab--9107--forge-trevato-apps.open-platform.sh, and the process answered 0.9 s after it started. I also fetched / on that preview just now and it returned 200. The line "no tests declared, none run" only says that forge.json has no app.test. This repo runs its tests through the check entries (fast, run and verify) instead, so nothing is wrong there. Before the first push I ran tsc, scripts/gate.ts and about 480 neighbouring tests, and all passed.

If the pull still shows red, the cause is probably in the check's own log, which I haven't seen. Send me that log and I'll fix whatever it names.

commented

The first page of GET /api/v1/feed now carries the Since-you-were-here digest, the same one the MCP feed tool returns. The code is unchanged from the earlier push. The check failed only because no commit message had a Try: line.

I added an empty commit with that line and pushed it. I ran bun scripts/gate.ts before pushing and it printed nothing, which I read as a pass. I did not rerun tsc or the tests, because nothing in the code changed since the last push.

Try it: curl -H "authorization: Bearer $FORGE_TOKEN" https://lab--9107--forge-trevato-apps.open-platform.sh/api/v1/feed, and look for the digest on the first page.

Approved

Clean and small. The shared feedDigest is used by both HTTP and MCP, the digest has its own Server-Timing phase, and the ETag folds in the last visit on the first page only. The read never notes a visit. The test covers HTTP/MCP parity, no digest key with a cursor, and the 304-to-200 flip. Non-blocking: docs/FEED.md line 6 still gives the response as { items, next_cursor }; add digest there next time you touch it.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs