Build Document · boondoggling.ai
20 sequenced steps separating what Claude builds from what only the human can build — with copy-pasteable prompts, supervisory capacity labels, and explicit handoff conditions at every step.
Two verification items must be completed before the score begins. Neither requires writing production code. Run both in parallel.
npx serve public/dev). Build a minimal test HTML page with an iframe pointing to it with sandbox="allow-same-origin allow-popups". Open it in a browser. Verify: (1) the doc renders correctly, (2) anchor links within the doc work, (3) external links open in a new tab, (4) no JavaScript executes. If any behavior is broken, identify which additional sandbox permission is required and update S1 Component 3 before proceeding.boondoggling-ai. Copy the full Musinique codebase as the starting point. Update package.json (name, description). Update NEXT_PUBLIC_SITE_URL in .env.local to http://localhost:3000. Confirm next dev runs without errors. Confirm /admin/login works with the Musinique admin password. You are not building yet — you are confirming the foundation is sound before Claude touches it.next dev runs. /admin/login authenticates. /blog, /tools, /videos render with Musinique content or empty state. No TypeScript errors on startup.
tools table schema. Migration run order from P3.You are a senior Next.js/PostgreSQL engineer working on a project called boondoggling.ai.
The existing Musinique codebase uses a `tools` table with these columns:
id, name, slug, description, tool_type, claude_url, chatgpt_url, artifact_id, artifact_embed_code, tags, created_at, updated_at
I need you to write the complete SQL migration script for boondoggling.ai. The script must run in this exact order:
1. CREATE the new `dev_docs` table:
CREATE TABLE IF NOT EXISTS dev_docs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
title TEXT NOT NULL,
slug TEXT UNIQUE NOT NULL,
description TEXT,
blob_url TEXT NOT NULL,
build_url TEXT,
quality_signal TEXT DEFAULT 'community',
category TEXT,
methodology TEXT,
submitted_by TEXT,
published BOOLEAN DEFAULT false,
published_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
2. ALTER the existing `tools` table to add four new columns (use IF NOT EXISTS):
- prompt_text TEXT
- quality_signal TEXT DEFAULT 'community'
- version TEXT DEFAULT '1.0'
- published BOOLEAN DEFAULT false
3. Add a CHECK constraint to the existing `videos` table:
ALTER TABLE videos ADD CONSTRAINT video_source_required
CHECK (youtube_id IS NOT NULL OR blob_url IS NOT NULL);
4. Backfill existing tools:
UPDATE tools SET published = true, quality_signal = 'curated' WHERE published IS NULL OR published = false;
5. Enable RLS on dev_docs and add policies:
- public_read_dev_docs: SELECT WHERE published = true
- service_role_dev_docs: ALL operations
6. Add a videos table RLS policy (if not already present):
- public_read_videos: SELECT WHERE published = true
- service_role_videos: ALL operations
Output: a single SQL script with each step clearly commented. Include a comment before each step stating what it does and why the order matters..sql file with six numbered, commented sections. Each ALTER TABLE uses IF NOT EXISTS. The backfill UPDATE runs after the ALTER (step 4 after step 2). RLS policies use the Musinique pattern.published — a column added in step 2? If yes, the order is correct. (2) Does the CHECK constraint reference youtube_id and blob_url — columns that already exist in the videos table? Verify against the Musinique schema. (3) Are the RLS policies consistent with the Musinique pattern (service_role FOR ALL USING (true))? Run the script in Neon SQL Editor. Confirm zero errors.dev_docs table exists. tools has prompt_text, quality_signal, version, published columns. videos has video_source_required CHECK constraint. Existing tools records have published=true, quality_signal='curated'.
lib/html-meta.ts, specifically scanHtmlSubdirs(). SDD S1 Component 3 (filesystem taxonomy: WWW/, Agents/, Games/, gru/ within each).You are working on boondoggling.ai, a Next.js 15 App Router project using TypeScript.
The project has an existing utility at lib/html-meta.ts with a scanHtmlSubdirs() function that scans subdirectories under a given public/ path and returns an array of objects with { title, description, slug, path } extracted from HTML meta tags.
I need you to extend this file — create a new function scanDevDocs() — that scans /public/dev/ with this taxonomy:
Top-level subdirectories: WWW, Agents, Games (and any future additions)
Within each: an optional gru/ subdirectory for Gru-produced docs
All other HTML files in the category root are non-Gru docs
The function must return an array of objects with this shape:
{
title: string,
description: string,
slug: string, // e.g. "www/gru/musinique-build" or "agents/my-agent"
path: string, // filesystem path for iframe src
category: string, // 'www' | 'agents' | 'games' (lowercased)
methodology: string | null, // 'gru' if in /gru/ subfolder, null otherwise
source: 'filesystem'
}
Requirements:
- slugs must be lowercase
- the function must handle missing subdirectories gracefully
- the function must be callable at build time in a Next.js server component
- do not modify any existing functions
Return: the complete updated lib/html-meta.ts file with scanDevDocs() added as a new export.lib/html-meta.ts with scanDevDocs() exported. Handles missing subdirectories without throwing. Returns all six fields. Category always lowercase. Methodology is 'gru' for files in /gru/ subfolder, null otherwise.scanDevDocs() compiles without TypeScript errors. Called against the filesystem scaffold from Step 5, it returns correct objects for each HTML file found.
mkdir -p public/dev/WWW/gru, mkdir -p public/dev/Agents/gru, mkdir -p public/dev/Games/gru. Place at least one real Gru SDD HTML file in each /gru/ subfolder. Confirm each HTML file has <title> and <meta name="description"> tags. Confirm scanDevDocs() returns the correct objects when called against this structure.scanDevDocs() returns at least three objects, one per category, all with methodology: 'gru'. Each object has a non-empty title and description.
/app/api/admin/login/route.ts and middleware.ts. SDD D2 auth routes. SDD P3 environment variables.You are working on boondoggling.ai, a Next.js 15 App Router project forked from Musinique.
The admin auth system (HMAC-SHA256 session cookie, middleware.ts, /api/admin/login, /api/admin/logout) is already present from Musinique.
Audit the existing auth implementation against these five requirements:
1. ADMIN_PASSWORD environment variable is used (not hardcoded)
2. admin_session cookie is httpOnly, sameSite: 'lax', secure: true in production
3. middleware.ts protects all /admin/* routes and all /api/* write routes (POST, PATCH, DELETE)
4. /api/admin/login uses constant-time password comparison
5. /api/admin/logout clears the cookie unconditionally
For each requirement: state PASS or FAIL with the relevant code snippet.
For each FAIL: provide the exact corrected code.
Do not rewrite files that pass. Only output corrected code for files that fail./admin/dashboard redirects to /admin/login when no session cookie is present.
/app/tools/page.tsx, /app/tools/[slug]/page.tsx. SDD S1 Component 2. SDD D1 Tool entity. The four new columns: prompt_text, quality_signal, version, published. Step 0A result (confirmed character limit).You are working on boondoggling.ai, a Next.js 15 App Router project.
Update the /tools page and /tools/[slug] page:
CARD GRID (/app/tools/page.tsx):
- Add quality_signal badge to each card: "Curated" (filled) or "Community" (outlined)
- Only show tools WHERE published = true from the DB query
- Card layout: name, description, quality_signal badge, version tag, tool_type label
TOOL DETAIL (/app/tools/[slug]/page.tsx):
For tool_type = 'prompt':
- Display full prompt_text in a block with monospace font
- Add a Copy button using navigator.clipboard.writeText(prompt_text)
- Clipboard fallback: if clipboard API fails, show "Select all" button
- Display character count: "X characters" below the copy button
- If character count > 180000: show warning "This prompt is long. Verify it fits in your Claude Project Instructions before use."
- Display version as a small label: "Version X.X"
- Guard: if prompt_text is null for a prompt-type tool, render a clear error state (do not show a blank page)
For tool_type = 'artifact' and 'link': no changes needed.
Use existing Musinique Tailwind classes and bb- color variables. No new dependencies.
Return: updated /app/tools/page.tsx and /app/tools/[slug]/page.tsx only.
prompt_text renders error state, not blank page.<pre> block with working copy button. Clipboard fallback renders when clipboard API is unavailable. Character count displays correctly. Null prompt_text renders an error state.
/admin/dashboard/tools. Create three tool entries: (1) Gru — tool_type: prompt, paste full Gru system prompt, quality_signal: curated, version: 1.0, write a one-paragraph description, published: true. (2) Ada — same pattern from the Ada HTML file. (3) Admin Doc Audit Prompt — a Gru /g2 variant prompt for auditing community SDD submissions. This last entry is content you write, not code.quality_signal='curated'. /tools/gru renders the full prompt with copy button and character count. The Step 0A character limit result determines whether the warning displays.
scanDevDocs() function). Musinique /app/dev/ pages. SDD S1 Component 3. SDD D1 DevDoc entity. dev_docs DB table schema. Confirmed sandbox attribute string from Step 0B.You are working on boondoggling.ai, a Next.js 15 App Router project.
Build the /dev section — two pages:
PAGE 1: /app/dev/page.tsx — Card grid
Data sources (merge both):
1. Filesystem docs: call scanDevDocs() → { title, description, slug, path, category, methodology, source: 'filesystem' }
2. DB docs: SELECT * FROM dev_docs WHERE published = true → { title, description, slug, blob_url, category, methodology, quality_signal, build_url, source: 'db' }
Merge and sort: Featured quality_signal first, then by published_at descending. Filesystem docs are treated as quality_signal='featured'.
Group by category: three sections — "WWW", "Agents", "Games". Within each group, methodology='gru' docs show a "Built with Gru" badge.
Card layout:
- Title, description (truncated to 2 lines), category label
- "Built with Gru" badge if methodology === 'gru'
- Quality signal badge: "Featured" (filled) or "Community" (outlined)
- "Live Build →" link if build_url is present (opens in new tab)
PAGE 2: /app/dev/[...slug]/page.tsx — Doc viewer
- Resolve slug: check filesystem docs first, then DB docs
- Filesystem docs: iframe src = /public/dev/[path]
- DB docs: iframe src = blob_url
- iframe: sandbox="[insert confirmed string from Step 0B here]", width="100%", height="100vh", style="border:none"
- 404 fallback: if blob_url HEAD request returns 404, render named error component: "This document could not be loaded. If you submitted this document, please contact us."
- Do not attempt to handle iframe onError for cross-origin 404s — use server-side HEAD check instead
Use existing Musinique Tailwind classes and bb- color variables. No new dependencies.
Return: /app/dev/page.tsx and /app/dev/[...slug]/page.tsx.methodology='gru'. Iframe uses the exact sandbox string from Step 0B. Broken blob_url renders the error component, not a blank frame./api/dev/upload full contract. SDD S4 edge cases D7, D8. Musinique /api/upload/route.ts. dev_docs table schema.You are working on boondoggling.ai, a Next.js 15 App Router project.
Build POST /api/dev/upload. This is the most critical write endpoint — it handles community HTML file uploads to Vercel Blob and creates a draft dev_doc record.
REQUEST: multipart/form-data with fields:
- file: HTML file (required)
- title, slug, description (required)
- submitted_by, build_url (optional)
- category: 'www' | 'agents' | 'games' (required)
SERVER-SIDE VALIDATION (run in this exact order, return immediately on failure):
1. Content-type check: file.type must be 'text/html' OR (file.type is 'application/octet-stream' AND filename ends with '.html')
→ 400 { "error": "File must be an HTML file" }
2. Size check: file.size must be ≤ 512000 bytes (500kb)
→ 400 { "error": "File too large (max 500kb)" }
3. HTML signature check: read first 100 bytes, check for 'dev_docs record with published=false. A simulated DB failure after Blob success logs the orphaned URL and returns 500 — the Blob file is not deleted. Auth check returns 401 before any file processing.
/api/dev/upload directly (with a valid admin session cookie). Run five tests: (1) upload a valid HTML file — expect 201. (2) upload a .txt file renamed to .html — expect 400 "Invalid HTML file". (3) upload a valid HTML file over 500kb — expect 400 "File too large". (4) upload with slug containing a slash — expect 400 "Slug must be lowercase…". (5) upload the same valid file twice (same slug) — expect 409 on second attempt. Document results.dev_docs table schema. /api/dev/upload contract from Step 11.You are working on boondoggling.ai, a Next.js 15 App Router project.
Build the Admin Dashboard Dev tab at /app/admin/dashboard/dev/page.tsx.
VIEW 1: Draft queue
- List all dev_docs WHERE published = false, ordered by created_at DESC
- Each row: title, submitted_by, category, created_at, "Publish" button, "Decline" button
- "Publish" button: opens inline form to set quality_signal, build_url, and audit_notes before confirming
- "Decline" button: confirms hard delete of DB record (Blob cleanup is manual — do not delete Blob here)
VIEW 2: Published docs
- List all dev_docs WHERE published = true, ordered by published_at DESC
- Each row: title, category, methodology, quality_signal badge, build_url if present, "Unpublish" and "Edit" buttons
- "Edit": opens inline form to update title, description, quality_signal, build_url, category
UPLOAD FORM (above both views):
- File input: accepts .html only
- Fields: title (required), slug (required, auto-slugify from title on input), description (required), category dropdown (WWW / Agents / Games), submitted_by (optional), build_url (optional)
- Submit button: "Upload Draft"
- POST to /api/dev/upload as multipart/form-data
- On success: show "Draft created", refresh draft queue
- On error: show error message from API inline (do not clear the form)
AUDIT NOTES FIELD (in publish inline form):
- Textarea labeled: "Audit report (paste from Claude)"
- Not a DB gate — a UI reminder only
- Label text: "Paste your Doc Audit Prompt report here before publishing."
Do not use HTML form tags — use React state and onClick handlers throughout.
Use existing Musinique admin patterns, Tailwind classes, and bb- color variables.
Return the complete /app/admin/dashboard/dev/page.tsx file./api/dev/upload. Error messages render inline. Publish flow requires quality_signal before confirm activates. Audit notes textarea visible and labeled. No <form> tags.published=true via PATCH. Unpublish sets published=false. Audit notes textarea visible in publish form. No <form> tags.
You are working on boondoggling.ai, a Next.js 15 App Router project using Tailwind CSS and the bb- color palette (--bb-1 through --bb-8 as CSS variables).
Build the homepage at /app/page.tsx as a five-beat scrolling narrative. Fully static — no JS-gated progression, no animations that block content, no account required.
BEAT 1 — What is boondoggling?
Headline: "You use Claude. You don't conduct it."
Body: 3–4 sentences. Claude solves faster than any human. That gap will not close. What will not change: Claude cannot verify its output against domain reality, cannot reframe a poorly formulated problem, cannot supply accountability. The human's job is not to type less — it is to decide more precisely.
CTA: none
BEAT 2 — Why does it matter?
Headline: "The five things only you can do."
Five labeled blocks (not bullet points):
[PA] Plausibility Auditing — one concrete sentence
[PF] Problem Formulation — one concrete sentence
[TO] Tool Orchestration — one concrete sentence
[IJ] Interpretive Judgment — one concrete sentence
[EI] Executive Integration — one concrete sentence
Keep descriptions concrete — not "think strategically" but "hear the wrong note before you recompute it."
BEAT 3 — How does it work?
Headline: "Meet Gru."
Body: 2–3 sentences. Gru is a system prompt for Claude that builds SDDs and generates a Boondoggle Score — a sequenced plan separating what Claude builds from what you build. You run it in your own Claude Project.
CTA: text link → /videos ("Watch how it works →")
BEAT 4 — Get the tool
Headline: "Copy Gru. Paste it into a Claude Project. Start building."
Three numbered steps:
1. Go to /tools and copy the Gru system prompt
2. Open claude.ai → create a new Project → paste into Project Instructions
3. Type /help to start
CTA: primary button → /tools ("Get Gru →"), bg-[var(--bb-2)], text-white, hover:bg-[var(--bb-1)]
BEAT 5 — See it in action
Headline: "Real builds. Real SDDs. Real Boondoggle Scores."
Body: 1–2 sentences. Every doc in /dev is a real build produced using Gru.
CTA: text link → /dev ("Browse worked examples →")
DESIGN:
- Each beat: full-width section, generous vertical padding (py-24 or equivalent)
- Beats 1, 3, 5: bg-[var(--bb-8)]; beats 2, 4: white background
- All text: bb-1 body, bb-2 headlines
- Beat 2 capacity blocks: monospace label in bb-5, name in bb-2 bold, description in bb-1
- Mobile responsive, single column at all breakpoints
- No JavaScript required for any beat to render
Return: the complete /app/page.tsx file.NEXT_PUBLIC_SITE_URL=https://www.boondoggling.ai).You are working on boondoggling.ai, a Next.js 15 App Router project forked from Musinique.
Audit these five files and confirm whether they need changes for boondoggling.ai:
1. /app/admin/login/page.tsx and /api/admin/login/route.ts — Should work unchanged. Confirm.
2. /app/sitemap.ts
- Update NEXT_PUBLIC_SITE_URL to boondoggling.ai
- Add /dev routes (filesystem slugs and DB slugs)
- Add /tools routes (filesystem slugs and DB slugs)
- Keep /blog routes
3. /app/robots.ts
- Disallow: /admin/, /api/
- Sitemap: https://www.boondoggling.ai/sitemap.xml
- Confirm or correct.
4. /components/Header/Header.tsx
- Update nav: About, Blog, Tools, Dev, Videos
- Logo: placeholder text "boondoggling.ai" (logo asset TBD)
- Keep dark/light mode toggle
- Remove any Musinique-specific links
5. /components/Footer/Footer.tsx
- Update site name and links for boondoggling.ai routes
- Keep legal links (Privacy, Terms)
- Remove Spotify embed section
For each file: return UNCHANGED with a one-line confirmation, or return the complete updated file. Minimal diff only./sitemap.xml. Header nav links resolve correctly. Footer has no broken links or Musinique-specific references. robots.txt disallows /admin/ and /api/.
You are working on boondoggling.ai. Audit five pre-launch Must-Fix cases. For each: state MITIGATED or UNMITIGATED with the relevant code snippet. Provide targeted fixes only for UNMITIGATED cases — do not rewrite entire files.
CASE 1 — D2: Community HTML sandbox
File: /app/dev/[...slug]/page.tsx
Check: does every iframe serving a DB-sourced dev_doc have the confirmed sandbox attribute? Does every iframe serving a filesystem dev_doc also have the same sandbox attribute?
CASE 2 — D8: Non-HTML file with .html extension
File: /app/api/dev/upload/route.ts
Check: does step 3 of validation (HTML signature check) read the first bytes of file content and check for DOCTYPE/html signature, independent of the filename extension?
CASE 3 — T4: NULL prompt_text for prompt-type tool
File: /app/tools/[slug]/page.tsx
Check: is there a guard that handles prompt_text being null for a prompt-type tool? What does it render?
CASE 4 — A6: Server-side validation for prompt_text
Files: /app/api/tools/route.ts (POST) and /app/api/tools/[id]/route.ts (PATCH)
Check: does the server validate that prompt_text is non-null and non-empty when tool_type='prompt'?
CASE 5 — A3: Blob delete failure blocking DB delete
File: /app/api/dev/[id]/route.ts (DELETE handler)
Check: does Blob delete failure (404 from Blob SDK) log the error and continue to the DB delete, rather than throwing and aborting?| Capacity | Steps | Notes |
|---|---|---|
| [PA] Plausibility Auditing | 4 steps (0B, 3, 10, 12) | Covers security-critical and data-critical verification points |
| [PF] Problem Formulation | 1 step (0A) | Pre-build spike — defines a constraint before Claude sees the build |
| [TO] Tool Orchestration | 2 steps (1, 19) | Environment setup and infrastructure execution |
| [IJ] Interpretive Judgment | 3 steps (8, 15, 20) | Content decisions and the launch decision — irreducibly human |
| [EI] Executive Integration | 2 steps (5, 18) | Filesystem scaffold and end-to-end integration test |
No capacity appears zero times. All five supervisory roles are exercised.
0A (prompt length) ─────────────────────────────────┐ 0B (sandbox string) ──────────────┐ │ 1 → 2 → 3 (migration) │ │ → 4 → 5 (filesystem scaffold) │ │ → 6 (auth audit) │ │ → 7 (tools UI) ◄────────────────│──────────────────┘ → 9 (dev pages) ◄───────────────┘ → 11 → 12 → 13 (upload pipeline) → 14 → 15 (homepage) → 16 (Musinique ports) → 17 → 18 → 19 → 20 (hardening → launch) Critical: 1 → 3 → 9 → 10 → 11 → 12 → 13 → 17 → 18 → 19 → 20
Any delay in the migration (Step 3) or upload route verification (Step 12) cascades to everything downstream.