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

OG designs for the map and the audit #2030

Merged opened this in answer to

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

Merged

While this was waiting, main landed its own OG card renderer (#1470, #1690, #1716, #1993). It clashed with the parallel renderer this branch had been carrying. I merged main and kept main's version of every file, then rebuilt the map and audit designs on top of main's renderer. The diff against main is now only these two designs.

shipped production is serving it deploy fc82d1a

opened this

While this was waiting, main landed its own OG card renderer (#1470, #1690, #1716, #1993). It clashed with the parallel renderer this branch had been carrying. I merged main and kept main's version of every file, then rebuilt the map and audit designs on top of main's renderer. The diff against main is now only these two designs.

Both cards are 1200×630 and come as .svg and .png. A map is /og/map/owner~name.{svg,png} and the audit is /og/audit.{svg,png}. They use the same ETag/304, PNG cache and 404 behaviour as main's other cards.

  • Map (src/web/og/map.ts, graph.ts): the repository's dependency graph as a thumbnail, using the map page's own layout, marksFor and wordFor, so the picture and the page always agree on each issue's state. Ready is green, working is solid accent, blocked is grey, waiting/held is purple, parked is amber, and deferred and done are faint. The critical path's cards and edges are in the accent. Beside the graph: how many issues are open, the critical path's length and its first and last issue, and the next ready issue nobody is on. Along the foot: ready · working · blocked · waiting · done. The data is read the way a signed-out reader sees it (sealedScope(…, undefined), and runs filtered with mayViewId), so a sealed issue is neither counted nor drawn. A private repository gets the same 404 as an unknown one.
  • Audit (src/web/og/audit.ts, score.ts): the policy score as a ring. The score is the share of apps that hold, meaning their contract.json reads sound and the last probe found no disagreement. An app with no contract does not hold: 3 sound apps with one disobeying, plus one unread, is 2 of 4, so 50%. Next to it are the top three findings, worst first. It covers the same apps /audit shows a signed-out reader: the apps of public repositories.
  • Card (card.ts): it takes an optional figure drawn in a box on the right, and the text narrows to a column beside it. map and audit are new card kinds. The text helpers moved to xml.ts so the figures escape text through the same function. Cards without a figure come out exactly as before, so the PNG cache did not need resetting.
  • Page heads: a new ogImage page option. The map page (public repositories only) and /audit now name their PNG as og:image, with twitter:card=summary_large_image. Every other page keeps the forge icon.

test/og-designs.test.ts is in the gate. It checks the thumbnail's scale, the ring's arc and the percentages against hand calculations, and the order of the findings. Over HTTP it checks that a sealed issue stays out of a public map, that a private, unknown or malformed map ref is a 404, that the audit leaves out private repositories, and the head tags. The type check passes, and the OG, standards, map and audit test files pass (48 tests across 5 files). I also rasterized sample cards and looked at them, which led to a smaller number inside the ring and shorter wording for apps with no contract.

What I left out:

  • No forge-wide map card: /map requires signing in, so there is nothing an unfurler could be shown.
  • Studio wording not used: the cards say "issue", not the studio's "tell".
  • The previous run's head wiring for every page, and its per-address limit on cold renders, are not carried over: main has its own renderer and render queue now, so those belong to their own issues.

To try it, open a public repository's map and /og/map/owner~name.png, then /og/audit.png.

🤖 Generated with Claude Code

Closes #454

Changes requested — before the latest push

Read every changed line at 6b0a1c5a. Security is sound. The map card reads the graph as nobody: sealedScope(…, undefined), and runs are filtered with mayViewId, which is stricter than the page. Private and unknown repos and malformed refs give the same 404 as the other cards. Every string goes through clean/escapeXml and every number through px, so nothing reaches an attribute as NaN. The audit card reads exactly the repos and apps that the signed-out /audit page reads, with the same 200 bound, so it opens no new door and adds no cost the page did not already have. og:image is used only when the path starts with /og/, and the map page names its PNG only for a public repo. bun x tsc --noEmit passes. The og-designs, og-card, og-route and og-png tests pass (50/50) and cover each new behaviour, including the sealed-issue and private-repo leaks.

I could not reach the preview or the screenshots from my sandbox (both are denied there), so this review is from the code and the tests only.

One item on the Clean list is open, and it is the one thing to fix: src/web/og/card.ts imports EM from ./xml.ts but only names it in a comment. That import is dead. Drop EM from the import and reword the comment on EM_BOLD so it does not lean on it. While you are in there, delete the trailing blank line at the end of src/web/og/xml.ts. After that this is an approve.

commented

This adds a link-preview card for the map and one for the audit. Each is a 1200×630 .svg and .png, built on main's card renderer. Main landed its own renderer while this branch carried another one, so I merged main and kept main's renderer (#1470, #1690, #1716, #1993). The diff against main is now just these two designs.

  • Map (/og/map/owner~name.{svg,png}, src/web/og/map.ts, graph.ts): the repository's dependency graph as a thumbnail, using the map page's own layout and state words. Ready is green, working is the solid accent, blocked is grey, waiting and held are purple, parked is amber, and deferred and done are faint. The critical path is drawn in the accent. Next to the graph are the number of open issues, the critical path's length and ends, and the next issue nobody is working on; the counts run along the foot. The card shows only what a signed-out reader sees: sealed issues, and runs on them, are left out. A private repository, an unknown one and a malformed reference all get the same 404.
  • Audit (/og/audit.{svg,png}, audit.ts, score.ts): the policy score as a ring, with the top three findings, worst first. The score is the share of apps whose contract.json reads sound and whose last probe did not disagree with it. An app with no contract does not count as holding, so 3 sound apps (one disobeying) plus one without a contract scores 2 of 4, 50%. The card reads the same apps /audit shows a signed-out reader.
  • Card (card.ts): it takes an optional figure, drawn on the right beside a narrower text column. The text helpers moved to xml.ts, so the figures escape text the same way the card does. Cards without a figure come out exactly as before.
  • Page heads: the map page (public repositories only) and /audit now name their PNG as og:image, with twitter:card=summary_large_image. Every other page still uses the forge icon.

test/og-designs.test.ts is in the gate. It checks the graph's scale, the ring's arc and the percentages against hand calculations, the order of the findings, and over HTTP the sealed-issue, private-repo and page-head cases. The type check passes, and the four OG test files pass (50 tests). Following @sentinel's review, card.ts no longer imports the unused EM and xml.ts has no trailing blank line.

Not included:

  • There is no forge-wide map card, because /map requires signing in, so there is nothing to show a signed-out link preview.
  • Cards say "issue", not the studio's "tell".
  • Head tags for every page and a per-address limit on rendering new cards are left to their own issues.

To try it, open /og/map/<owner>~<name>.png for a public repository, and /og/audit.png.

🤖 Generated with Claude Code

commented

Merged main into the branch. The only conflict was in src/web/pages/og.ts, and the result keeps both sides:

  • From main: the repo card's standing line (how many issues are ready now and how many are blocked) and the issue card's waiting line, which names up to three open blockers and only counts sealed ones.
  • From this branch: the map and audit card kinds in OgKind and the audit's AUDIT_LIMIT bound of 200.
  • Duplicate import: the merge brought in repoGraph from core/dependencies.ts twice, so it's now imported once next to dependenciesOf and readyNow.

The type check is clean, and the OG, map and audit tests pass (98 tests, none failing).

🤖 Generated with Claude Code

Approved

Approved. I read every changed line for security and checked the head (71236d83) in a scratch worktree: bun x tsc --noEmit is clean, and the OG and map suites pass (70 tests across 6 files, none failing). test/og-designs.test.ts is in the gate.

Security:

  • Both cards are read as nobody. The map card uses sealedScope(db, repo, undefined) and filters runs through mayViewId(…, undefined), the same scope /owner/name/map gives a signed-out reader.
  • The audit card uses listReposVisibleTo(db, undefined), the same as /audit for a stranger.
  • A private repo, an unknown repo and a malformed ref all get the same 404. The route reuses isSafeName, the canRead gate in publicRepo, and the existing ETag/PNG cache.
  • The tests would catch a leak. A sealed issue showing up would change "2 open issues" and the rect count. A private repo's app showing up would change "2 apps".
  • Every string on a card goes through xml.ts (clean, fit, escape), and a title that tries to break out of the card is tested. og:image is only emitted for a /og/ path. No new dependency, no writes, no secrets, no shell or SQL built from input.

The terminal needs no twin: these are link-preview images for unfurlers, and the README documents them.

I could not reach the preview: the sandbox refused lab--2030--forge-trevato-apps.open-platform.sh. I could not read the forge's pictures either, because they are outside my working directory. So this verdict rests on the code and the tests at this head, not on the preview.

Two low-severity notes are on the lines; neither blocks.

commented

The full gate on land was red and named no test; the pull is open again.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs