diff --git a/README.md b/README.md index 9258fc03..a6cdd5fb 100644 --- a/README.md +++ b/README.md @@ -17,12 +17,11 @@ Markover is an **Early macOS preview** for reviewing Markdown as a document tree and returning block-level feedback to an agent. -

- Markover showing a document inbox, structured Markdown, and an annotation with labeled screenshots. -

+> Markover is free and MIT-licensed. It requires no account, and ordinary +> review data stays in your macOS account.

- Markover showing every rendered annotation in the active document. + Markover showing a document inbox, structured Markdown, and an annotation with labeled screenshots.

## Features @@ -59,6 +58,9 @@ Discussions](https://github.com/lastobelus/markover/discussions). ## For agents: open a review without installing +> **Use the complete GitHub package URL below.** The public npm package named +> `markover` is an unrelated Markdown-cleanup tool. + Open a document with: ```sh diff --git a/doc/launch/issue-16/README.md b/doc/launch/issue-16/README.md new file mode 100644 index 00000000..0963676b --- /dev/null +++ b/doc/launch/issue-16/README.md @@ -0,0 +1,51 @@ +# Issue 16 launch-production bundle + +This directory contains editable sources for Markover's focused-preview launch +assets. Public copy must stay simple, clear, descriptive, calm, and free of +glazing or large unsupported claims. + +## Preparation assets + +| Source | Purpose | Published output | +| --- | --- | --- | +| `launch-manifest.json` | Desired repository metadata, profile pins, release gate, and asset hashes | Live GitHub settings after finalization | +| `github-social-preview.svg` | Editable 1280×640 GitHub card | `docs/user/assets/markover-github-social-preview.png` | +| `pages-social-card.svg` | Editable 1200×630 Pages card | `docs/user/assets/markover-pages-social-card.png` | +| `launch-brief.md` | Purpose-built document reviewed in the demo | Demo only | +| `handoff-summary.jq` | Readable projection of real handoff fields | Demo only | +| `demo-storyboard.md` | Recording steps, captions, and transcript source | Final MP4 and Pages transcript | +| `reddit-draft.md` | Ready-to-finalize r/codex copy | Posted by issue 17 | + +Build the raster cards on macOS with: + +```sh +npm run build:social-cards +``` + +The build reads the canonical `design/brand/markover-lockup.svg`, embeds it in a +temporary rendering source, and verifies the PNG dimensions and GitHub file-size +limit. `test/launch-assets.test.ts` verifies the committed source and output +hashes against the manifest. + +## Finalization gate + +Do not publish `reddit-draft.md` or copy its placeholders to a public surface. +`v0.1.3` is the current public Apple Silicon candidate, not the selected final +focused-preview release. Issue 93 must finish the clean-profile batch, issue 11 +must accept that evidence, and issue 10 must select the exact release. Then: + +1. Set `release.focusedPreviewTag` in the manifest and update every public + launcher URL to that exact tag. +2. Capture the current public landing-page hero as + `current-landing-page.png` for the demo fixture. +3. Run the generic CLI lifecycle and the Codex/T3 Code `AGENTS.md` workflow end + to end, then record the evidence. +4. Record and export the final MP4, poster, and transcript from the verified + selected release. +5. Resolve `[PREVIEW_TAG]` and `[DEMO_URL]`, deploy Pages, and verify public link + previews. +6. Apply the repository settings and profile pins from the manifest only after + the matching default-branch assets are live. + +Issue 16 remains open until those steps have final-state evidence. Issue 17 owns +posting to r/codex and operating the feedback round. diff --git a/doc/launch/issue-16/demo-storyboard.md b/doc/launch/issue-16/demo-storyboard.md new file mode 100644 index 00000000..5eb1a727 --- /dev/null +++ b/doc/launch/issue-16/demo-storyboard.md @@ -0,0 +1,74 @@ +# Markover focused-preview demo storyboard + +## Production status + +This is the preparation storyboard. `v0.1.3` is the current Apple Silicon +candidate, not the selected final release. Replace `` and +`` only after issues 93, 11, and 10 complete the clean-profile and +release-selection gate. Record the final video from that build. + +## Recording setup + +- 1920×1080 at 30 fps, exported as silent H.264 MP4 with fast-start metadata. +- Clean Codex or T3 Code thread, clean Markover review history, and notifications + disabled. +- Only `launch-brief.md`, the public landing-page screenshot, and the filtered + handoff appear. +- Public copy and captions stay simple, clear, descriptive, calm, and free of + glazing or large claims. + +## Timeline + +### 0:00–0:07 — Open from the agent thread + +Prompt the agent: + +> Open `doc/launch/issue-16/launch-brief.md` in Markover for review. + +Show the agent run the exact command for ``, retain ``, +and stop. Caption: **Open an agent-written Markdown document.** + +### 0:07–0:20 — Ask a screenshot-backed question + +In Markover, select the paragraph under **Landing page**. Type “Could we lead +with the block-level review workflow shown in ”, paste +`current-landing-page.png`, and relabel it `current landing page`. Keep the +rewritten `[!current landing page]` reference visible. Caption: **Attach a +question and label its visual context.** + +### 0:20–0:31 — Propose an exact source change + +Select “Markover works with every coding agent.” and propose: + +> Markover is provider-neutral for agents with macOS shell access and verified +> with Codex and T3 Code. + +Show the real word-level diff. Caption: **Propose an exact edit without changing +the review target.** + +### 0:31–0:43 — Check Markover in the same thread + +Return to the agent thread and type: + +> Check Markover. + +Show the agent invoke `get` with the retained review ID. Briefly show Markover +become read-only/with-agent, then show the real response projected through +`handoff-summary.jq`: review status, question, attachment ID and edited label, +and original/current source proposal. Caption: **Return one structured handoff +to the agent thread.** + +### 0:43–0:45 — End card + +Show the Markover lockup, **Free, MIT-licensed early preview for Apple Silicon +Macs**, and `github.com/lastobelus/markover`. + +## Pages transcript + +An agent opens a Markdown launch brief in Markover and waits. The reviewer asks +a question on one exact block, pastes a screenshot, and changes its label to +“current landing page.” On another block, the reviewer proposes an exact source +change, which Markover shows as a word-level diff while preserving the original +document. Back in the same agent thread, the reviewer says “Check Markover.” The +agent retrieves one structured handoff containing the question, labeled +attachment, and source proposal, and Markover marks the review read-only. diff --git a/doc/launch/issue-16/github-social-preview.svg b/doc/launch/issue-16/github-social-preview.svg new file mode 100644 index 00000000..02c435a9 --- /dev/null +++ b/doc/launch/issue-16/github-social-preview.svg @@ -0,0 +1,8 @@ + + Markover + Markover — Structured review for Markdown. + + + + Structured review for Markdown. + diff --git a/doc/launch/issue-16/handoff-summary.jq b/doc/launch/issue-16/handoff-summary.jq new file mode 100644 index 00000000..b9200472 --- /dev/null +++ b/doc/launch/issue-16/handoff-summary.jq @@ -0,0 +1,32 @@ +def reviewed_blocks: + [ + .. + | objects + | select((.id? // "") | startswith("block-")) + | select( + ((.feedback? // "") | length) > 0 + or (.sourceEdit? != null) + ) + | { id } + + (if ((.feedback? // "") | length) > 0 + then { feedback } + else {} + end) + + (if ((.attachments? // []) | length) > 0 + then { + attachments: [ + .attachments[] | { id, label } + ] + } + else {} + end) + + (if .sourceEdit? != null + then { sourceEdit } + else {} + end) + ]; + +{ + review: { status: .review.status }, + blocks: reviewed_blocks +} diff --git a/doc/launch/issue-16/launch-brief.md b/doc/launch/issue-16/launch-brief.md new file mode 100644 index 00000000..4e5b3954 --- /dev/null +++ b/doc/launch/issue-16/launch-brief.md @@ -0,0 +1,14 @@ +# Focused preview launch brief + +## Landing page + +Open with a general description of the product before showing the workflow. + +## Agent compatibility + +Markover works with every coding agent. + +## Preview status + +The first public post should describe the current build as an early macOS +preview and ask readers to report the first confusing or broken step. diff --git a/doc/launch/issue-16/launch-manifest.json b/doc/launch/issue-16/launch-manifest.json new file mode 100644 index 00000000..bfabe004 --- /dev/null +++ b/doc/launch/issue-16/launch-manifest.json @@ -0,0 +1,70 @@ +{ + "issue": 16, + "phase": "focused-preview", + "canonicalDescriptor": "Structured review for Markdown.", + "repository": { + "description": "Local-first macOS app for reviewing agent-produced Markdown and returning structured, block-level feedback to the agent thread.", + "homepage": "https://lastobelus.github.io/markover/", + "topics": [ + "markdown", + "document-review", + "annotations", + "ai-agents", + "coding-agents", + "codex", + "local-first", + "macos", + "electron", + "developer-tools" + ], + "socialPreview": "docs/user/assets/markover-github-social-preview.png" + }, + "profilePins": [ + "lastobelus/markover", + "lastobelus/lastCode" + ], + "pages": { + "url": "https://lastobelus.github.io/markover/", + "socialCard": "docs/user/assets/markover-pages-social-card.png" + }, + "canonicalProductImage": "docs/user/assets/markover-review-editor@2x.png", + "release": { + "currentPublicTag": "v0.1.3", + "focusedPreviewCandidateTag": "v0.1.3", + "focusedPreviewTag": null, + "platform": "macOS 14 Sonoma or newer on Apple Silicon", + "blockedByIssues": [93, 11, 10] + }, + "announcement": { + "community": "r/codex", + "title": "I built a little Electron app for reviewing agent-written Markdown block by block", + "postingIssue": 17 + }, + "assets": { + "canonicalLockup": { + "path": "design/brand/markover-lockup.svg", + "sha256": "47914a9d87e0d0348f2c12a5957db9e07bc15e890db306096496bdffc1b385da" + }, + "githubSocialPreview": { + "source": "doc/launch/issue-16/github-social-preview.svg", + "sourceSha256": "650deb7001393089f387832ee874546fee58e908e293226cbc7a42bcdb813431", + "output": "docs/user/assets/markover-github-social-preview.png", + "outputSha256": "9598063f0cdefe022a5da72c1a03d6efbd388c13c4d820298570744e71e031b4", + "width": 1280, + "height": 640, + "maximumBytes": 1000000 + }, + "pagesSocialCard": { + "source": "doc/launch/issue-16/pages-social-card.svg", + "sourceSha256": "f74fcec7be607fb9b6146a0bb2facecf0b9e349839249181cf1df1cffd7b5654", + "output": "docs/user/assets/markover-pages-social-card.png", + "outputSha256": "f2497ad43da09a8750bdef3bff482431c80e3790968612f2b28a2ac411d0716a", + "width": 1200, + "height": 630 + }, + "canonicalProductImage": { + "path": "docs/user/assets/markover-review-editor@2x.png", + "sha256": "b42e781cb60923a169ed763a736487fced21d352c893a57da05b9c54a9046839" + } + } +} diff --git a/doc/launch/issue-16/pages-social-card.svg b/doc/launch/issue-16/pages-social-card.svg new file mode 100644 index 00000000..c17eb743 --- /dev/null +++ b/doc/launch/issue-16/pages-social-card.svg @@ -0,0 +1,8 @@ + + Markover + Markover — Structured review for Markdown. + + + + Structured review for Markdown. + diff --git a/doc/launch/issue-16/reddit-draft.md b/doc/launch/issue-16/reddit-draft.md new file mode 100644 index 00000000..6c27841e --- /dev/null +++ b/doc/launch/issue-16/reddit-draft.md @@ -0,0 +1,55 @@ +# r/codex focused-preview draft + +## Title + +I built a little Electron app for reviewing agent-written Markdown block by block + +## Body + +I kept running into the same problem when an agent produced a long plan or +specification: chat made it hard to keep each note attached to the exact +paragraph, list item, or code block it referred to. + +Screenshots made that worse. I wanted to paste several visual references, give +them useful labels, and cite them from a question. I also wanted to propose an +exact edit beside ordinary feedback without losing the original document. When +I came back later, I wanted every note to retain its source context. + +I made Markover for that workflow. An agent opens a Markdown file, then stops +while you review it as a document tree. You can add Markdown feedback and +labeled screenshots to individual blocks, or edit a source block to create a +proposal shown as a word-level diff. When you say “Check Markover” in the agent +thread, the agent retrieves one structured handoff containing the original +source, annotations, attachments, proposals, and review context. + +The short demo shows that complete loop: **[DEMO_URL]** + +Markover is a free, MIT-licensed early preview for Apple Silicon Macs running +macOS 14 Sonoma or newer. It requires no account, and ordinary review data stays +in your macOS account. The generic workflow works with agents that have macOS +shell access; the focused preview is verified with Codex and T3 Code. + +The preview currently requires Node.js 22.13.0 or newer. The app is ad-hoc +signed rather than Developer ID signed or notarized, so macOS may ask you to +confirm opening it. The README has the exact command pinned to **[PREVIEW_TAG]** +and explains the full handoff lifecycle: + +**https://github.com/lastobelus/markover** + +The product page and guide are here: + +**https://lastobelus.github.io/markover/** + +If this matches your workflow, try it on one real Markdown document and tell me +the first step that is confusing or broken. A reply here is fine for a quick +note. For a reproducible failure, the repository has a structured bug form that +captures the Mac and launch details needed to investigate it. + +## Posting checklist + +- Replace `[DEMO_URL]` and `[PREVIEW_TAG]` with verified final values. +- Recheck r/codex project-promotion rules on posting day. +- Upload the MP4 natively and select the canonical demo poster if Reddit allows + a custom thumbnail. +- Do not post until issue 16 has final-state evidence and issue 17 authorizes the + announcement window. diff --git a/doc/plans/2026-08-03__issue-16-preview-launch-assets.md b/doc/plans/2026-08-03__issue-16-preview-launch-assets.md new file mode 100644 index 00000000..397ba853 --- /dev/null +++ b/doc/plans/2026-08-03__issue-16-preview-launch-assets.md @@ -0,0 +1,220 @@ +# Issue 16: focused-preview repository metadata and launch assets + +## Outcome + +Prepare one candid, coherent launch kit for Markover's focused preview. Every +public link used by the preview must identify Markover clearly, explain the +human-to-agent review workflow accurately, and lead an Apple Silicon macOS +Codex user to one tested release without unsupported claims. + +Issue 16 prepares and verifies the kit. Issue 17 owns recruiting the initial +testers, publishing the r/codex post, responding during the announcement +window, and triaging resulting feedback. Broad-announcement venue copy remains +out of scope until issue 18. + +## Audience and positioning + +- Optimize for users on Apple Silicon Macs running macOS 14 Sonoma or newer who + already use coding agents to produce substantial Markdown, with Codex and T3 + Code as the verified initial fit. Native Intel support remains deferred to + issue 80. +- Lead with the problem: chat flattens structured review, making it difficult to + attach notes to exact blocks, organize and reference labeled screenshots, + combine proposed edits with questions, and revisit notes in the context of + the original document. +- Describe the workflow as reviewing agent-written Markdown block by block and + returning a structured handoff. Do not position Markover as a general + Markdown editor, collaboration service, or live discussion system. +- Keep **“Structured review for Markdown.”** as the sole canonical descriptor. + Use “local-first” judiciously as a factual attribute, always clarified as no + account and review data staying on the user's Mac. + +## Repository and profile metadata + +Record the desired settings in a versioned launch manifest, then apply them only +after the matching public assets are live on the default branch. + +- Description: `Local-first macOS app for reviewing agent-produced Markdown and returning structured, block-level feedback to the agent thread.` +- Homepage: `https://lastobelus.github.io/markover/` +- Topics: `markdown`, `document-review`, `annotations`, `ai-agents`, + `coding-agents`, `codex`, `local-first`, `macos`, `electron`, and + `developer-tools`. +- Profile pins: Markover first and `lastCode` second. +- GitHub social preview: one warm-palette 1280×640 card with the Markover lockup + and canonical descriptor; no UI screenshot. +- Pages social preview: a matching 1200×630 Open Graph card with complete Open + Graph and large-card metadata. + +Commit editable SVG sources beside deterministic PNG exports. Extend the +existing macOS-native asset-build approach rather than adding an image-processing +dependency. Local tests must verify sources, output dimensions, file sizes, +metadata references, and manifest consistency; live GitHub state is verified at +issue closure rather than mutated by a permanent repository script. + +## Canonical product image + +Use `docs/user/assets/markover-review-editor@2x.png` as the canonical product +screenshot. Show it once inline in the README, use it for the linked demo +thumbnail and full-frame 16:9 demo poster, and retain it as the first image in +the Pages gallery. Keep the other three screenshots in the Pages gallery rather +than stacking them in the README. + +Retain the current screenshot unless the selected release materially changes a +visible workflow or interface. If it does, recapture once and update every use +together. + +## Demo + +Publish an approximately 45-second, silent 1920×1080, 30 fps H.264 MP4 with +burned-in captions, deliberate cursor movement, clean cuts, gentle zooms, and +web fast-start metadata. The Pages embed must use a warm 16:9 poster based on the +complete canonical screenshot, visible controls, inline playback, +`preload="metadata"`, no autoplay, and a text transcript. Reddit receives a +native upload and uses the same thumbnail when the venue permits it. + +The demo begins and ends in one clean agent thread, following one real review ID +and one purpose-built `launch-brief.md`: + +1. Ask the agent to open `launch-brief.md` for review. Show it run the exact + pinned release command, retain the returned review ID, and stop while the + human reviews. +2. Select a block, paste a fresh screenshot of the public Markover landing-page + hero, and relabel it `current landing page`. Keep the automatically rewritten + `[!current landing page]` reference visible inside a genuine question about + leading with the block-level workflow. +3. On a different block, replace an overbroad every-agent compatibility claim + with the honest boundary: provider-neutral for shell-capable agents and + verified with Codex and T3 Code. Show the real word-level source diff. +4. Return to the same agent thread and type “Check Markover.” Show the agent use + the retained ID to invoke `markover get `, briefly show the app + become read-only/with-agent, then display a `handoff-summary.jq` projection + containing only real handoff fields: `review.status`, feedback, attachment + `id` and `label`, and `sourceEdit.original`/`current`. +5. End for two seconds on the Markover lockup, “Free, MIT-licensed early preview + for macOS,” and the repository URL. + +Record from the exact release selected by issue 10 after issue 93's clean-profile +batch satisfies issue 11, in a clean purpose-built workspace with notifications +disabled and no private paths, documents, review history, or thread metadata. +Commit the optimized MP4, not a large recording master or editor project. + +Store the storyboard, commands, captions, transcript, `launch-brief.md`, public +landing-page attachment, and `handoff-summary.jq` under +`doc/launch/issue-16/`. Store only browser-consumed outputs under +`docs/user/assets/`. + +## README and guide + +Reorder the README as purpose, preview disclosure, demo/screenshot, copyable +quick start, then detailed features and community information. Place the Pages +video directly below the existing hero and above the feature grid. The README +uses the canonical screenshot as a linked “Watch the 45-second demo” thumbnail +instead of attempting an inline video. + +All public language must be simple, clear, descriptive, and calm. No glazing, +superlatives, breathless launch language, or large unsupported claims. Describe +the problem concretely, state only behavior verified in the corresponding +release, and let the workflow demonstrate the product's value. + +Use this baseline disclosure on long-form launch surfaces: + +> Markover is a free, MIT-licensed early preview for Apple Silicon Macs running +> macOS 14 Sonoma or newer. It requires no account, and ordinary review data +> stays in your macOS account. + +Keep the ad-hoc-signing/notarization warning beside installation instructions. +State macOS and Node.js 22.13.0 or newer before the first command. Rename “Try +without installing” to “Try the preview” and explain that the launcher downloads, +verifies, and caches the matching app. Warn that the public npm package named +`markover` is unrelated and must not be installed or invoked; every copyable +block uses the full package URL pinned to the exact selected tag. No unresolved +release placeholder may appear on a public page. + +Provide two self-contained setup blocks inline: + +- A provider-neutral template for agents with Apple Silicon macOS shell access, + without claiming named compatibility. +- A verified repository-root `AGENTS.md` snippet for Codex and T3 Code. It uses + an explicit `--thread-id` when available or a fresh high-entropy + `mko_handoff_…` key otherwise. + +Both teach the full lifecycle: open once, retain the review ID, stop, wait for +the reviewer to say “Check Markover” or an obvious equivalent, call `get` once, +and use `edit` if the reviewer needs to amend feedback. After retrieval, agents +implement clear revision requests, answer questions without silently editing, +respond to discussion points, ask about ambiguity or conflict, treat exact +source proposals as requested revisions unless qualified, and inspect every +attachment using edited labels and inline references. The guide expands these +rules with troubleshooting and links to issue 9's canonical privacy, storage, +deletion, support, limitations, and compatibility documentation. + +## Focused-preview announcement copy + +Commit a ready-to-post r/codex draft under `doc/launch/issue-16/` with this title: + +> I built a little Electron app for reviewing agent-written Markdown block by block + +Use a candid first-person maintainer voice in approximately 300–450 words and +follow the same simple, clear, descriptive, calm, no-glazing tone rule as the +README and guide. Open with the structured-review problem, let the native demo +explain the mechanics, link primarily to the GitHub repository and secondarily +to Pages, and include the free/MIT/macOS/early-preview/local-data disclosure +plus the ad-hoc-signing warning. Ask readers to run one real Markdown review and +report the first confusing or broken step; do not request stars or upvotes. +Keep lightweight feedback in the Reddit thread and direct reproducible failures +to the structured GitHub bug form. + +Recheck r/codex's current project-promotion rules immediately before issue 17 +posts. Resolve the exact selected release tag and demo URL before issue 16 +closes. + +## Delivery checkpoints + +### 1. Preparation + +- Add the launch manifest, card sources and exports, demo fixtures, storyboard, + transcript source, filtered-output fixture, r/codex draft, and local checks. +- Restructure README/guide and Pages code only where the result is complete and + accurate with the currently published release. Keep unresolved preview values + confined to non-public production material. +- Run the full repository check and test suites, inspect the built Pages site, + and commit the completed slice after review findings are addressed. + +### 2. Preview finalization + +- Treat `v0.1.3` only as the current candidate. After issue 93 completes the + clean-profile batch and issue 11 accepts it, substitute the exact immutable + release tag selected by issue 10 and verify its Apple Silicon artifacts. +- Run the generic CLI lifecycle and a fresh Codex/T3 Code `AGENTS.md` workflow + end to end. Record surface/version, tag, date, and results. +- Record and export the final demo, publish the Pages embed/transcript, resolve + all launch-copy links, capture the fresh public landing-page attachment, and + rerun local and deployed checks. +- After the default branch and Pages deployment are live, set and verify GitHub + description, homepage, topics, social preview, and profile-pin order. + +Issue 16 remains open between checkpoints. It closes only when every public +placeholder is resolved and every acceptance criterion has final-state evidence. + +## Verification and closure evidence + +- Run `npm run check`, `npm test`, targeted asset/metadata tests, and + `git diff --check` for each implementation checkpoint. +- Verify Pages in Safari and Chromium at desktop and narrow widths, including + video controls, poster, transcript, screenshot gallery, and social metadata. + Repeat against the deployed Pages URL after merge. +- Verify GitHub repository metadata, social preview, profile pin order, README + links, Pages preview, pinned launcher URL, release assets, and bug-form route. +- Add an issue-16 closure comment mapping concrete links, commands, test results, + and screenshots to every original acceptance criterion. Keep the issue body + unchanged; this plan owns the detailed requirements. + +## Explicitly out of scope + +- Publishing the r/codex post or operating the feedback round (issue 17). +- Broad-launch copy or additional announcement venues (issue 18). +- Replacing issue 9's canonical policy and limitation documentation. +- Claiming verification for Claude Code, Gemini CLI, Cursor, or other named + agents. +- Adding a hosted service, account system, telemetry, live discussion, or + automatic intent-classification claim. diff --git a/docs/user/agents/index.html b/docs/user/agents/index.html index 11c56870..420dd179 100644 --- a/docs/user/agents/index.html +++ b/docs/user/agents/index.html @@ -5,6 +5,21 @@ + + + + + + + + + + + + + + + For agents — Markover documentation @@ -49,6 +64,7 @@

Review a Markdown document with Markover

Open one review

Give Markover one Markdown path and a short summary of why the document exists and what feedback would help:

+
Use the complete GitHub package URL. The public npm package named markover is an unrelated Markdown-cleanup tool.
npx --yes \
   --package=https://github.com/lastobelus/markover/releases/latest/download/markover-cli.tgz \
   markover open ./DOCUMENT.md \
diff --git a/docs/user/assets/markover-github-social-preview.png b/docs/user/assets/markover-github-social-preview.png
new file mode 100644
index 00000000..c25f87d1
Binary files /dev/null and b/docs/user/assets/markover-github-social-preview.png differ
diff --git a/docs/user/assets/markover-pages-social-card.png b/docs/user/assets/markover-pages-social-card.png
new file mode 100644
index 00000000..d4a8bb43
Binary files /dev/null and b/docs/user/assets/markover-pages-social-card.png differ
diff --git a/docs/user/guide/index.html b/docs/user/guide/index.html
index 3aa93eea..8c689dd0 100644
--- a/docs/user/guide/index.html
+++ b/docs/user/guide/index.html
@@ -5,6 +5,21 @@
     
     
     
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
     Getting started — Markover documentation
     
     
diff --git a/docs/user/index.html b/docs/user/index.html
index 5190a9d2..1ab09c28 100644
--- a/docs/user/index.html
+++ b/docs/user/index.html
@@ -5,6 +5,21 @@
     
     
     
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
     Markover — Early macOS preview
     
     
diff --git a/docs/user/limitations/index.html b/docs/user/limitations/index.html
index 45c33391..356a7cec 100644
--- a/docs/user/limitations/index.html
+++ b/docs/user/limitations/index.html
@@ -5,6 +5,21 @@
     
     
     
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
     Markdown support and preview limitations — Markover documentation
     
     
diff --git a/docs/user/privacy/index.html b/docs/user/privacy/index.html
index 0fd95bdc..e5fa9389 100644
--- a/docs/user/privacy/index.html
+++ b/docs/user/privacy/index.html
@@ -5,6 +5,21 @@
     
     
     
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
     Privacy, storage, and recovery — Markover documentation
     
     
diff --git a/package.json b/package.json
index 1c88e8a9..b5cc0f25 100644
--- a/package.json
+++ b/package.json
@@ -10,6 +10,7 @@
   "scripts": {
     "build": "npm run clean && tsc -p tsconfig.build.json && node build/scripts/copy-build-assets.js && node build/scripts/build-app.js",
     "build:icon:mac": "npm run build --silent && node build/scripts/build-macos-icon.js",
+    "build:social-cards": "npm run build --silent && node build/scripts/build-social-cards.js",
     "build:cli": "npm run build --silent && node build/scripts/build-cli.js",
     "clean": "node -e \"require('node:fs').rmSync('build', { recursive: true, force: true })\"",
     "package:mac": "install-electron --no && npm run build:icon:mac && npm run notices:check:built && node build/scripts/package-macos.js --trust-mode=ad-hoc",
diff --git a/scripts/build-social-cards.ts b/scripts/build-social-cards.ts
new file mode 100644
index 00000000..7cb391aa
--- /dev/null
+++ b/scripts/build-social-cards.ts
@@ -0,0 +1,119 @@
+#!/usr/bin/env node
+
+import { spawnSync } from 'node:child_process'
+import fs from 'node:fs'
+import os from 'node:os'
+import path from 'node:path'
+
+const projectDirectory = path.resolve(__dirname, '../..')
+
+interface SocialCard {
+  source: string
+  output: string
+  width: number
+  height: number
+  maximumBytes?: number
+}
+
+export const cards: readonly SocialCard[] = [
+  {
+    source: 'doc/launch/issue-16/github-social-preview.svg',
+    output: 'docs/user/assets/markover-github-social-preview.png',
+    width: 1280,
+    height: 640,
+    maximumBytes: 1_000_000
+  },
+  {
+    source: 'doc/launch/issue-16/pages-social-card.svg',
+    output: 'docs/user/assets/markover-pages-social-card.png',
+    width: 1200,
+    height: 630
+  }
+]
+
+export interface ImageDimensions {
+  width: number
+  height: number
+}
+
+export function dimensions(bytes: Buffer): ImageDimensions {
+  if (bytes.subarray(1, 4).toString() !== 'PNG') {
+    throw new Error('Expected a PNG export')
+  }
+  return {
+    width: bytes.readUInt32BE(16),
+    height: bytes.readUInt32BE(20)
+  }
+}
+
+export function inlineCanonicalAssets(source: string): string {
+  const lockup = fs.readFileSync(
+    path.join(projectDirectory, 'design/brand/markover-lockup.svg')
+  )
+  return source.replace(
+    '../../../design/brand/markover-lockup.svg',
+    `data:image/svg+xml;base64,${lockup.toString('base64')}`
+  )
+}
+
+function render(card: SocialCard, temporaryDirectory: string): void {
+  const sourcePath = path.join(projectDirectory, card.source)
+  const outputPath = path.join(projectDirectory, card.output)
+  const temporarySource = path.join(
+    temporaryDirectory,
+    path.basename(card.source)
+  )
+  fs.writeFileSync(
+    temporarySource,
+    inlineCanonicalAssets(fs.readFileSync(sourcePath, 'utf8'))
+  )
+  const result = spawnSync('/usr/bin/sips', [
+    '-s',
+    'format',
+    'png',
+    temporarySource,
+    '--out',
+    outputPath
+  ], { encoding: 'utf8' })
+  if (result.error) throw result.error
+  if (result.status !== 0) {
+    throw new Error(
+      result.stderr.trim() || `sips exited ${String(result.status)}`
+    )
+  }
+
+  const bytes = fs.readFileSync(outputPath)
+  const size = dimensions(bytes)
+  if (size.width !== card.width || size.height !== card.height) {
+    throw new Error(
+      `${card.output} is ${size.width}×${size.height}; expected ${card.width}×${card.height}`
+    )
+  }
+  if (card.maximumBytes !== undefined && bytes.length >= card.maximumBytes) {
+    throw new Error(`${card.output} must remain under ${card.maximumBytes} bytes`)
+  }
+}
+
+export function main(): void {
+  if (process.platform !== 'darwin') {
+    throw new Error('Building Markover social cards requires macOS.')
+  }
+  const temporaryDirectory = fs.mkdtempSync(
+    path.join(os.tmpdir(), 'markover-social-cards-')
+  )
+  try {
+    for (const card of cards) render(card, temporaryDirectory)
+  } finally {
+    fs.rmSync(temporaryDirectory, { recursive: true, force: true })
+  }
+}
+
+if (require.main === module) {
+  try {
+    main()
+  } catch (error) {
+    const message = error instanceof Error ? error.message : String(error)
+    process.stderr.write(`markover social cards: ${message}\n`)
+    process.exit(1)
+  }
+}
diff --git a/test/launch-assets.test.ts b/test/launch-assets.test.ts
new file mode 100644
index 00000000..e41731d9
--- /dev/null
+++ b/test/launch-assets.test.ts
@@ -0,0 +1,228 @@
+import assert from 'node:assert/strict'
+import crypto from 'node:crypto'
+import fs from 'node:fs'
+import path from 'node:path'
+import test from 'node:test'
+
+const root = path.resolve(__dirname, '../..')
+const read = (relativePath: string): string =>
+  fs.readFileSync(path.join(root, relativePath), 'utf8')
+const bytes = (relativePath: string): Buffer =>
+  fs.readFileSync(path.join(root, relativePath))
+const sha256 = (contents: Buffer): string =>
+  crypto.createHash('sha256').update(contents).digest('hex')
+
+interface LaunchAsset {
+  source: string
+  sourceSha256: string
+  output: string
+  outputSha256: string
+  width: number
+  height: number
+  maximumBytes?: number
+}
+
+interface LaunchManifest {
+  issue: number
+  phase: string
+  canonicalDescriptor: string
+  repository: {
+    description: string
+    homepage: string
+    topics: string[]
+    socialPreview: string
+  }
+  profilePins: string[]
+  release: {
+    currentPublicTag: string
+    focusedPreviewCandidateTag: string
+    focusedPreviewTag: string | null
+    platform: string
+    blockedByIssues: number[]
+  }
+  announcement: {
+    community: string
+    title: string
+  }
+  assets: {
+    canonicalLockup: { path: string; sha256: string }
+    githubSocialPreview: LaunchAsset
+    pagesSocialCard: LaunchAsset
+    canonicalProductImage: { path: string; sha256: string }
+  }
+}
+
+interface PackageManifest {
+  scripts: Record
+}
+
+const manifest = JSON.parse(
+  read('doc/launch/issue-16/launch-manifest.json')
+) as LaunchManifest
+
+function pngDimensions(contents: Buffer): { width: number; height: number } {
+  assert.equal(contents.subarray(1, 4).toString(), 'PNG')
+  return {
+    width: contents.readUInt32BE(16),
+    height: contents.readUInt32BE(20)
+  }
+}
+
+test('launch manifest preserves the agreed repository metadata', () => {
+  assert.equal(manifest.issue, 16)
+  assert.equal(manifest.phase, 'focused-preview')
+  assert.equal(manifest.canonicalDescriptor, 'Structured review for Markdown.')
+  assert.deepEqual(manifest.repository, {
+    description: 'Local-first macOS app for reviewing agent-produced Markdown and returning structured, block-level feedback to the agent thread.',
+    homepage: 'https://lastobelus.github.io/markover/',
+    topics: [
+      'markdown',
+      'document-review',
+      'annotations',
+      'ai-agents',
+      'coding-agents',
+      'codex',
+      'local-first',
+      'macos',
+      'electron',
+      'developer-tools'
+    ],
+    socialPreview: 'docs/user/assets/markover-github-social-preview.png'
+  })
+  assert.deepEqual(manifest.profilePins, [
+    'lastobelus/markover',
+    'lastobelus/lastCode'
+  ])
+  assert.equal(manifest.release.currentPublicTag, 'v0.1.3')
+  assert.equal(manifest.release.focusedPreviewCandidateTag, 'v0.1.3')
+  assert.equal(manifest.release.focusedPreviewTag, null)
+  assert.equal(
+    manifest.release.platform,
+    'macOS 14 Sonoma or newer on Apple Silicon'
+  )
+  assert.deepEqual(manifest.release.blockedByIssues, [93, 11, 10])
+  assert.equal(manifest.announcement.community, 'r/codex')
+  assert.equal(
+    manifest.announcement.title,
+    'I built a little Electron app for reviewing agent-written Markdown block by block'
+  )
+})
+
+test('social-card sources and outputs match the launch manifest', () => {
+  const canonicalLockup = manifest.assets.canonicalLockup
+  assert.equal(sha256(bytes(canonicalLockup.path)), canonicalLockup.sha256)
+
+  const cards = [
+    manifest.assets.githubSocialPreview,
+    manifest.assets.pagesSocialCard
+  ]
+  for (const asset of cards) {
+    const source = bytes(asset.source)
+    const output = bytes(asset.output)
+    assert.equal(sha256(source), asset.sourceSha256)
+    assert.equal(sha256(output), asset.outputSha256)
+    assert.deepEqual(pngDimensions(output), {
+      width: asset.width,
+      height: asset.height
+    })
+    if (asset.maximumBytes !== undefined) {
+      assert.ok(output.length < asset.maximumBytes)
+    }
+
+    const svg = source.toString('utf8')
+    assert.match(svg, new RegExp(`width="${asset.width}" height="${asset.height}"`))
+    assert.match(svg, /design\/brand\/markover-lockup\.svg/)
+    assert.match(svg, /Structured review for Markdown\./)
+    assert.doesNotMatch(svg, /gradient/i)
+  }
+
+  const productImage = manifest.assets.canonicalProductImage
+  assert.equal(sha256(bytes(productImage.path)), productImage.sha256)
+})
+
+test('every Pages surface publishes complete large-card metadata', () => {
+  const pages = [
+    'docs/user/index.html',
+    'docs/user/guide/index.html',
+    'docs/user/agents/index.html',
+    'docs/user/privacy/index.html',
+    'docs/user/limitations/index.html'
+  ]
+  for (const page of pages) {
+    const html = read(page)
+    assert.match(html, /rel="canonical"/)
+    assert.match(html, /property="og:type" content="website"/)
+    assert.match(html, /property="og:site_name" content="Markover"/)
+    assert.match(html, /property="og:title"/)
+    assert.match(html, /property="og:description"/)
+    assert.match(html, /property="og:url"/)
+    assert.match(
+      html,
+      /property="og:image" content="https:\/\/lastobelus\.github\.io\/markover\/assets\/markover-pages-social-card\.png"/
+    )
+    assert.match(html, /property="og:image:width" content="1200"/)
+    assert.match(html, /property="og:image:height" content="630"/)
+    assert.match(html, /name="twitter:card" content="summary_large_image"/)
+    assert.match(html, /name="twitter:image:alt"/)
+  }
+})
+
+test('public setup surfaces preserve the repository launcher and preview boundary', () => {
+  const readme = read('README.md')
+  const agentGuide = read('docs/user/agents/index.html')
+  const userGuide = read('docs/user/guide/index.html')
+  const launcherUrl = /https:\/\/github\.com\/lastobelus\/markover\/releases\/latest\/download\/markover-cli\.tgz/
+
+  for (const source of [readme, agentGuide]) {
+    assert.match(source, launcherUrl)
+    assert.match(
+      source,
+      /public npm package named(?:\s|>)*(?:)?`?markover/
+    )
+  }
+  for (const source of [readme, userGuide]) {
+    assert.match(source, /Node(?:\.js)? 22\.13\.0 or newer/)
+  }
+
+  assert.equal((readme.match(/markover-review-editor@2x\.png/g) ?? []).length, 1)
+  assert.doesNotMatch(readme, /markover-annotation-browser@2x\.png/)
+  assert.match(readme, /free and MIT-licensed/)
+  assert.match(
+    readme,
+    /ordinary(?:\s|>)+review data stays in your macOS account/
+  )
+  assert.match(readme, /macOS 14 Sonoma or newer on Apple Silicon/)
+  assert.match(agentGuide, /Check Markover/)
+})
+
+test('prepared announcement and demo retain their finalization gates', () => {
+  const reddit = read('doc/launch/issue-16/reddit-draft.md')
+  const storyboard = read('doc/launch/issue-16/demo-storyboard.md')
+  const filter = read('doc/launch/issue-16/handoff-summary.jq')
+
+  assert.match(reddit, /\[DEMO_URL\]/)
+  assert.match(reddit, /\[PREVIEW_TAG\]/)
+  assert.match(reddit, /first step that is confusing or broken/)
+  assert.match(reddit, /Apple Silicon Macs running\s+macOS 14 Sonoma or newer/)
+  assert.doesNotMatch(reddit, /stars|upvotes/i)
+  assert.match(storyboard, /Check Markover/)
+  assert.match(storyboard, /current landing page/)
+  assert.match(storyboard, /Codex and T3 Code/)
+  assert.match(storyboard, /issues 93, 11, and 10/)
+  assert.match(filter, /review: \{ status: \.review\.status \}/)
+  assert.match(filter, /attachments/)
+  assert.match(filter, /sourceEdit/)
+})
+
+test('social-card build follows the macOS-native TypeScript pipeline', () => {
+  const script = read('scripts/build-social-cards.ts')
+  const packageJson = JSON.parse(read('package.json')) as PackageManifest
+  assert.equal(
+    packageJson.scripts['build:social-cards'],
+    'npm run build --silent && node build/scripts/build-social-cards.js'
+  )
+  assert.match(script, /\/usr\/bin\/sips/)
+  assert.match(script, /markover-lockup\.svg/)
+  assert.match(script, /docs\/user\/assets/)
+  assert.doesNotMatch(script, /sharp|canvas|playwright/i)
+})