Document Sign-Off MVP + roadmap MRP mapping - #356
Open
QSchlegel wants to merge 4 commits into
Open
Conversation
…m/hours line The month headings were renumbered on 2026-08-03 (984aa46) so that Month 1 = April, but MRP task cards created before that date still carry bullet text for the following month — the card headed "MRP Month 2" lists the June workstreams. That mismatch has now cost enough time to be worth writing down. - Add an MRP task mapping table at the top: MRP Month N = roadmap Month N = calendar month, for all twelve months, with the on-chain task hashes we have and a note explaining why the cards disagree. - Route each MRP month to the actual merged PRs behind it (April 10, May 3, June 51, July 16), each count linking to the exact GitHub search so any row can be reproduced rather than taken on trust. - Widen "Delivered to date" from May–July to April–July. April's output is infrastructure, so it folds into the existing sections: the preprod environment and real-chain smoke CI (#218, #217) under Testing & CI, and a new transaction-and-signing-integrity through-line under Platform (#217 VKey witness filtering -> #227 invalid-CBOR guard -> #257 Mesh pin + witness verification guard). - Recover two May items the 2026-07-26 audit had missed: the Import Wallet wizard (#259) and the #257 signing fix. - Fix the timeline left stale by the renumbering: April 2026 – March 2027, in ROADMAP.md, the /roadmap page and the SEO description. - Remove the "Quirin + Andre · ~25 h/wk" line from ROADMAP.md and the public roadmap page. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…gn-off Implements PRD-001: a wallet-native, off-chain approval layer where a team binds approval to an exact content hash, inherits the wallet's signer set and threshold, and exports a proof anyone can verify without an account. Two rules carry the feature, and both are enforced server-side rather than in the UI: 1. Version-hash binding. submitSignerAction rebuilds the canonical signed payload from the server's own records and requires a byte-identical match *before* the signature is checked, so a signature collected for one version can never be replayed onto another, and a tampered comment invalidates the submission. Inline content is re-hashed server-side and rejected on mismatch. 2. Threshold inheritance from a frozen snapshot. DocumentSignerSnapshot captures the wallet's signers, threshold and policy hash when the round starts; approval counting reads the snapshot, never the live wallet, so changing wallet membership cannot rewrite a decision already made. Data model (5 models + migration 20260805090000): Document, DocumentVersion, DocumentReview, DocumentSignerSnapshot, DocumentEvent. The migration enables RLS with deny-all PostgREST policies on all five tables, matching the contract in 20260706100000_enable_rls_followup_tables (#332). Router (src/server/api/routers/documents.ts): createDocument, uploadVersion, startReview, submitSignerAction, exportProof, verifyProof, plus the reads the pages need. CIP-8 verification uses Mesh's checkSignature against the signer's address. verifyProof is public on purpose — a counterparty holding the JSON and the file must be able to check it without an account, and it touches no DB. Four routes under /wallets/[wallet]/documents: list, create, detail, and the version review page. Files are hashed in the browser (SHA-256 via WebCrypto); only the digest is sent, so the bytes never leave the signer's machine. src/lib/documents/ is dependency-free apart from node crypto, with the signature check injected into the verifier — the same code can run in an offline verifier with no Mesh install. Uploading a new version supersedes the previous one and starts a fresh round at zero approvals: approval is bound to the hash, not the title. Tests: 28 covering canonicalization, version-hash binding, threshold evaluation and proof verification including tampering, duplicate signers, out-of-snapshot signers and non-canonical payloads. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Both branches appended new models to the end of prisma/schema.prisma — Document Sign-Off (five entities) here, the OAuth 2.1 authorization server on preprod — so the conflict was positional, not semantic. Kept both blocks. Everything else auto-merged: the `document` router sits alongside the new `mcp` router in root.ts, and preprod's `/oauth` noindex prefix landed next to the existing ones in seo.ts.
Every page shared the same og-image, so a /governance link and a /roadmap link were indistinguishable in a feed — the preview said "Mesh Multisig" and nothing about where it pointed. - generate-og-image.mjs now renders ten cards from one template. Each has its own eyebrow, headline, subhead, footer chips and accent tint, so the card identifies the destination at thumbnail size. Copy is authored as explicit lines (SVG has no wrapping) and the script warns when a line's estimated width would overflow the content column. - routeSeo entries carry `image` + `imageAlt`; getRouteSeo resolves them, _app falls back route-card-first, and Metatags emits real alt text instead of repeating the title. Blog posts without their own artwork now get the blog card rather than the home card. - og:image URLs carry ?v= (OG_IMAGE_VERSION). Scrapers cache by URL and would otherwise keep serving the old artwork indefinitely. Nothing at build time ties the PNGs to the paths in seo.ts, so ogCards asserts each referenced card exists, is 1200x630, and has alt text.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three independent changes, one per commit — review them separately.
1.
docs(roadmap)— MRP mapping and the April–July windowThe 2026-08-03 renumbering (
984aa46) moved Month 1 to April, but MRP task cards created before that still carry the following month's bullet text — the card headed "MRP Month 2" lists the June workstreams. This writes the mapping down so it stops costing time.ROADMAP.md: MRP Month N = roadmap Month N = calendar month, all twelve months, with the on-chain task hashes we have.April 2026 – March 2027) inROADMAP.md, the/roadmappage and the SEO description; removes theQuirin + Andre · ~25 h/wkline from both.2.
feat(documents)— Document Sign-Off MVP (PRD-001)A wallet-native, off-chain approval layer: bind approval to an exact content hash, inherit the wallet's signers and threshold, export a proof anyone can verify without an account. No chain dependency.
Two rules carry the feature, and both are enforced server-side, not in the UI:
submitSignerActionrebuilds the canonical signed payload from the server's own records and requires a byte-identical match before the signature is checked. A signature collected for one version cannot be replayed onto another, and a tampered comment invalidates the submission. Inline content is re-hashed server-side and rejected on mismatch.DocumentSignerSnapshotcaptures the wallet's signers, threshold and policy hash when the round starts. Approval counting reads the snapshot, never the live wallet — changing wallet membership cannot rewrite a decision already made.Data model —
Document,DocumentVersion,DocumentReview,DocumentSignerSnapshot,DocumentEvent, migration20260805090000_add_document_signoff. The migration enables RLS with deny-all PostgREST policies on all five tables, matching the contract in20260706100000_enable_rls_followup_tables(#332).Router —
createDocument,uploadVersion,startReview,submitSignerAction,exportProof,verifyProof. CIP-8 verification via Mesh'scheckSignatureagainst the signer's address.verifyProofis public on purpose: a counterparty holding the JSON and the file must be able to check it without an account, and it reads nothing from the database.Four routes under
/wallets/[wallet]/documents— list, create, detail, version review. Files are hashed in the browser via WebCrypto; only the digest is sent, so the bytes never leave the signer's machine.src/lib/documents/is dependency-free apart from nodecrypto, with the signature check injected into the verifier — the same code runs in an offline verifier with no Mesh install.3.
feat(seo)— a social card per routeEvery page shared one
og-image.png, so a/governancelink and a/roadmaplink produced identical previews: the card said "Mesh Multisig" and nothing about where it pointed.scripts/generate-og-image.mjsnow renders ten cards from one template — home, features, governance, DRep explorer, roadmap, feature graph, blog, API docs, dApps, import wallet. Each carries its own eyebrow, headline, subhead, footer chips and accent tint, so the destination is legible at feed-thumbnail size. Copy is authored as explicit lines (SVG has no wrapping) and the script warns when an estimated line width would overflow the content column.routeSeoentries carryimage+imageAlt;getRouteSeoresolves them,_appfalls back route-card-first, andMetatagsemits real alt text instead of repeating the title. Blog posts without their own artwork now fall back to the blog card rather than the home card.og:imageURLs carry?v=(OG_IMAGE_VERSION). Scrapers cache by image URL and would otherwise keep serving the old artwork indefinitely — the query string is what makes X, LinkedIn and Slack re-fetch.Nothing at build time ties the committed PNGs to the paths in
seo.ts, sosrc/__tests__/ogCards.test.tsasserts every referenced card exists, is exactly 1200×630, and has alt text.Heads-up for the deploy
This adds a migration. Railway's
prestartrunsprisma migrate deploy, so merging topreprodwill apply20260805090000_add_document_signoffto the preprod database on the next deploy. Five new tables, no changes to existing ones.Verification
Re-run after merging
origin/preprod(the only conflict was positional — both branches appended models to the end ofprisma/schema.prisma, Document Sign-Off here and the OAuth 2.1 server onpreprod; both blocks kept):npx tsc --noEmit— clean. ThetxScriptRecovery.test.tserrors noted earlier are gone;preprodfixed them.npx jest— 1084 passing, 2 skipped, 100 suites. Includes the 28 Document Sign-Off tests (canonicalization, version-hash binding, threshold evaluation, proof verification under tampering, duplicate signers, out-of-snapshot signers and non-canonical payloads) and the 29 new OG card tests.npx prisma validate— schema valid after the mergenpm run build— passes; all four document routes register as server-rendered/roadmapchecked in a local production build (next devcannot serve SSR in a worktree — Turbopack resolves the whisky WASM to/ROOT/node_modules/…)Not included
PRD-001 is still
status: Draft. Proof export is JSON only — the PDF summary and the public verify page are the M5 half of the MVP.🤖 Generated with Claude Code