feat(scratchnode): import published event recap into NodeBench workspace (roadmap #3 slice 1)#494
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
PR size advisoryThis PR adds 1314 lines of substantive change. CONTRIBUTING.md defines a soft limit of ~400 LOC. If the PR is genuinely cohesive (e.g. an architecture map, a generated migration, a deletion of a dead module), no action is needed. Otherwise consider:
This is advisory — it does not block the merge. |
✅ Dogfood Visual QA Gate: PASSED
ArtifactsDownload the Generated by Dogfood QA Gate |
Roadmap #3, slice 1. Lets a visitor import a published ScratchNode event wiki as ONE editable NodeBench product document under a fresh NodeBench-origin anonymous product identity. On later sign-in the existing bootstrap merge path (claimAnonymousProductWorkspace) re-owns it from anon:<sessionId> to user:<id> — no bespoke merge code. Backend: - convex/events.ts: getPublishedWikiStructuredBySlug + shared reader loadStructuredPublishedWiki — the same published snapshot as getPublishedWikiBySlug, structured (answers + sources) for the importer. PUBLISHED-only; private notes excluded at publish time. BOUND ≤ 20/20. - convex/domains/product/scratchnodeImport.ts (new): importPublishedWiki + getScratchnodeImportStatus. Reuses the existing productDocuments/Blocks/Snapshots primitives (no parallel doc system), creates the canonical event entity (entityType "event", owner-private), and is idempotent via a stable entity slug + hash(eventId|version|owner) import key (same version = no-op, newer version = new revision). Honest no-op on draft/unpublished/unknown. No fuzzy entity extraction (deferred). Frontend: - ScratchnodeEventsSurface.tsx: per-row "Import this recap into NodeBench" action, gated on a published wiki, importing under the fresh product anon identity (getAnonymousProductSessionId, not the cross-domain sn_session_id), with real importing/done/error states and a link to /entity/<entitySlug>. Reliability: BOUND, HONEST_STATUS, BOUND_READ, DETERMINISTIC. PUBLIC-DATA-ONLY — never reads or writes userNotes / private-note content. Tests: 11 convex-test scenarios (happy import, idempotency, re-publish→new revision, unpublished/draft/unknown no-ops, privacy no-leak, status query, structured read). 11/11 green; tsc --noEmit clean; npm run build clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
0a783dd to
81acd8c
Compare
|
Demo: walkthrough of the surfaces this PR changed is available as a workflow artifact ( |
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.
What
Roadmap item #3, slice 1: import a published ScratchNode event wiki into NodeBench as one editable product document, under a fresh NodeBench-origin anonymous product identity (the founder decision: anon-preview → merge on sign-in). PUBLIC-DATA-ONLY — only the published wiki snapshot is read; private notes are never touched.
Founder identity decision, realized with zero new merge code
Import writes to the existing
productDocuments/productDocumentBlocks/productDocumentSnapshots/productEntitiestables underanon:<productSessionId>. Those tables are already inbootstrap.ts'sPRODUCT_OWNED_TABLES, so the existingclaimAnonymousProductWorkspacemerge path re-owns the imported recap fromanon:<sessionId>→user:<id>on later sign-in. No bespoke owner-key merge was needed.Changes
Backend
convex/events.ts—getPublishedWikiStructuredBySlug({ slug })+ shared readerloadStructuredPublishedWiki(ctx, slug). Returns the SAME published snapshot asgetPublishedWikiBySlug, but structured ({ eventId, slug, eventName, roomCode, wikiVersion, answers:[{question,body}], sources:[{title,uri,excerpt}] }) so the importer builds editable blocks with no HTML round-trip. PUBLISHED-only; BOUND ≤ 20 answers / 20 sources. Purely additive (120 insertions, 0 deletions).convex/domains/product/scratchnodeImport.ts(new) —importPublishedWiki({ slug, anonymousSessionId })andgetScratchnodeImportStatus({ slug, anonymousSessionId }). Creates oneentity_memorydocument ("<eventName> — recap", Q&A + sources blocks) + the canonical event entity (entityType: "event", owner-private). Returns{ ok, documentId, entitySlug, created, alreadyImported }. No fuzzy company/person extraction (deferred — avoids fabricated entities).Frontend
src/features/redesign/surfaces/ScratchnodeEventsSurface.tsx— per-row "Import this recap into NodeBench" action, only shown when a published wiki exists, importing under the fresh product anon identity (getAnonymousProductSessionId, NOT the cross-domainsn_session_id), with real importing/done/error states and a link to/entity/<entitySlug>. The existing "Open in ScratchNode" CTA is unchanged.Idempotency
scratchnode-event-<hash(eventId)>maps every re-import of the same event to the same document.hash(eventId|wikiVersion|ownerKey)recorded on the import-event ledger: re-importing the same published version is a no-op (alreadyImported:true); a newer published version writes a fresh revision/snapshot on the same document (no duplicate).Reliability (agentic_reliability 8-point)
BOUND (reads ≤ 25), HONEST_STATUS (
ok:false, reason:"no_published_wiki"on draft/unpublished/unknown; throws on entity-create failure), BOUND_READ (dangling ids skipped, text capped), DETERMINISTIC (FNV-1a hash, stable block ids / slug / import key). No external fetch → SSRF/TIMEOUT N/A.Privacy
PUBLIC-DATA-ONLY. The importer reads only the published wiki via
loadStructuredPublishedWiki; it never reads or writesuserNotes/liveEventNoteAnchors, and never writes under another user's owner key. A dedicated test asserts a private-note marker never reaches the imported document.Verification
convex/domains/product/scratchnodeImport.test.ts— 11/11 convex-test scenarios green (happy import, idempotency no-dup, re-publish → new revision, unpublished/draft/unknown honest no-ops, privacy no-leak, status query, structured read).scratchnode.publicWiki+publicWikiReadsuites still green (additive change).npx tsc --noEmit --pretty false— clean (0 errors).npm run build— clean.npx convex codegenwas NOT run (no deploy creds in this env); new function refs use(api as any)in tests/frontend, mirroringScratchnodeEventsSurface.tsx, so tsc passes before CI codegen.Notes / open questions
ScratchnodeWikiBridge.tsx+/events/:slug/wikiReact route as the receiving surface; those don't exist onorigin/main— the public wiki is served byapi/scratchnode-wiki.js(static SSR HTML, no React, can't host an interactive button). The honest in-app home for the import action isScratchnodeEventsSurface.tsx(/scratchnode-events), the real React surface with Convex hooks. Flagged for review.🤖 Generated with Claude Code