Add Open Graph Tags for Link Previews

I've read the whole codebase and run the test suite and eval. Here is the picture.
What OnlyJevs is. A demo repo for a conference talk, built to be worked on by coding agents live. It is a fake dating app for twelve fictional developers. The twist is that the app does not score couples itself. It generates the couple's "first change together" from their habits, pushes it on a match branch, links an Entire Trail, and lets Jev, the judge, score that diff. The verdict on the match card is derived from Jev's numbers.
Core flow, in file order:
src/profiles/holds the type and twelve fixtures. Profiles carry timezone, languages, indentation, review tone, chronotype, PR size and whether they pair.src/match/score.tsis the older v2 scorer. It still supplies the "reasons" and "deal-breaker" text on the card, but not the score.src/match/pairing.tsis the heart. It deterministically renders a small dinner-slot finder whose code smells map to the couple's habit clashes: mixed tabs and spaces, a Python helper glued in with a shell string when they share no language, swallowed errors and a TODO handoff when they are over eight hours apart, a duplicated helper for opposite chronotypes, padding helpers for big-PR people, and test depth by whether they pair.src/trail/entire.tsshells out to the Entire CLI for three cell API calls: list Trails, get one Trail with monitors, create a Trail.src/match/verdict.tsturns Jev's monitor points into one score and a band. The formula is compatibility minus half risk minus half security minus a flat slop penalty, banded at 60 and 35.src/trail/verdicts.tswrites and reads the committed record ateval/verdicts.json, so the server, the eval and the static site all work offline.src/server.tsis a tiny http server with a few JSON routes plus staticweb/.scripts/build-site.tsproduces the same thing as a static Netlify site with precomputed data files.scripts/match.tsdoes the git work in a temp worktree: opening commit with just README and match.json, create the Trail, then push the real change so Jev judges it on push.eval/has twenty human-labelled couples and a runner that compares Jev-derived bands against the labels. Exit code is always zero by design.
Current state I verified:
| Check | Result |
|---|---|
npm test | 31 pass, 0 fail |
npm run eval | 9 of 20 in band, 45%, "matches feel random" |
| Record | 21 Trails, all judged, as of 24 Sep |
| Branch | pivot/onlyjevs, clean |
Two things worth knowing before making changes:
- Only two runners exist. The README describes four Jev runners, but
.entire/runners/holds only compatibility and slop. Risk and security were never added in the pivot commit. The verdict code tolerates this and prints "not configured", so every current score is just compatibility minus the slop penalty. - The eval is deliberately failing. The 45% is the story of the talk, not a bug to fix without being asked. Calibration via
--calibrateis the sanctioned lever for thresholds.
The stack is plain Node 24 with no dependencies, TypeScript run directly by Node, node:test, and node:sqlite for match history. Entire session tracking is on with git-refs checkpoints, so this session is being captured.
now I want to write a linkedIn post to launch the site :) What should it be? obviously we want people to test out trails and if they want access to reach out to me :)
Here is a draft. It leads with the joke, lands the real point in the middle, and ends with the ask you want.
I built a dating app for developers. Every match is a code review.
Meet OnlyJevs: twelve fictional developers, one match card, and a judge that never learns it is judging a couple.
Pick two people. The app writes their first change together from their habits. Tabs meets spaces, so the file has both. No shared language, so there is a Python helper glued in with a shell string. Nine hours apart, so errors get swallowed and a TODO says "you pick this up tomorrow."
Then Jev scores that diff the way it scores any diff: compatibility, risk, security, slop. The verdict on the card comes from the judge, not from the app.
Why build something this silly? Because every match is a Trail.
A Trail is the unit of work Entire tracks when agents write code: the branch, the sessions, the checkpoints, the judgments on every push. OnlyJevs was the demo repo for my talk at WeAreDevelopers World Congress last week, and every session, checkpoint and verdict in it is real. You can read them all.
Current score: 9 of 20 labelled couples land where a human put them. The app itself calls that "matches feel random." I left the number in on purpose. The eval is the product.
Try it: onlyjevs.com What a Trail is, on the real Trails: onlyjevs.com/learn Source is MIT. Fork it, break a couple up, see what Jev says.
If you want to try Trails on your own repository, message me. I would love to see what your diffs say about your team.
Two notes before you post:
- The talk date. Today is 29 September and the talk was 25 September, so "last week" is right. Change it if you post later.
- Add a screenshot. A match card with the score ring and the four Jev numbers will carry the post better than the text. The priya + tomas card at 35, "worth a coffee", is a good one because it shows the formula line.
A shorter version if you want something punchier:
Every match is a code review.
OnlyJevs pairs two fictional developers, writes their first change together from their habits, and lets Jev judge the diff. Tabs and spaces in one file. A Python helper nobody asked for. Errors swallowed across nine time zones.
The verdict comes from the judge, not the app. And every match is a real Trail on Entire you can open.
onlyjevs.com
Want Trails on your own repo? Message me.
The site has no Open Graph or Twitter Card tags, so LinkedIn has nothing to build a preview from.
What LinkedIn looks for is a handful of meta tags in the page head. The home page has only a title, a charset and a viewport tag. There is no description, no og:title, no og:image, and no favicon. Without an image tag LinkedIn usually shows nothing at all rather than a text-only card. The learn page has a description but also no Open Graph tags, so the same happens there.
Two smaller contributors:
- No social image exists. The only asset in the repository is the Trail screenshot used on the learn page. LinkedIn wants an image of at least 1200 by 627 pixels, and it must be an absolute URL.
- LinkedIn caches unfurls. Once you add the tags, the first share of onlyjevs.com may still show the old empty result until you run the URL through the LinkedIn Post Inspector at linkedin.com/post-inspector.
The fix is a block in each page's head, roughly:
The learn page should get its own title and url in the same block. The tracker mock can share the home page's tags.
The one thing that needs a design decision is the image. Options are a screenshot of a judged match card, or a generated card with the heart, the name and the tagline. A match card screenshot is more honest to the product and needs no design work. If you want, I can add the tags to all three pages, capture a match card at the right size for the image, and rebuild the site so you can check the Post Inspector result before posting.
Done. The site now has everything LinkedIn needs for a preview, and the card is built from real record data.
What changed, all uncommitted on pivot/onlyjevs:
- Social card at
web/img/og.png, 1200 by 630. It shows the Priya and Tomás match from Trail #2: the 35 score ring, the "worth a coffee" band, Jev's four tiles and the formula line. It uses the site's own palette so it reads as the product, not a poster. - Meta tags on all three pages: description, Open Graph title, description, url, image with dimensions and alt text, site name, Twitter large-image card, and a favicon link. The learn page kept its existing description text. Each page carries its own canonical url.
- Favicon at
web/favicon.svg, the rose tile with the heart from the site header.
Verified:
| Check | Result |
|---|---|
npm test | 31 pass, 0 fail |
npm run build:site | tags present in site/index.html, image and favicon copied |
Two honest notes on the card. Risk and security read "not configured" because only two Jev runners exist in the repository. I left that visible rather than hiding it, since the site itself shows it. If you would rather the launch image not advertise that, adding the two runners and re-judging is the real fix. The card is also a snapshot: if the record changes, it will not update itself.
To go live: commit and push these five files, wait for the Netlify build, then paste the url into linkedin.com/post-inspector. LinkedIn caches unfurls, so the inspector forces a fresh fetch and shows you exactly the card it will render. I have not committed anything. Say the word and I will commit and push.