This repository is public and permanently world-readable, including history and the docs/ and .claude/ trees. An audit of the recent merge window plus the standing always-public surface found several classes of content written with an internal-audience mindset. This removes or genericizes them and adds a standing rule to CLAUDE.md so the class does not recur. Removed — production data about real people: - Two spent admin/temp backfill scripts with real user identifiers and account attributes inlined as literal arrays (435 and 189 entries). Both were one-shot migrations that already ran, with zero external references. - An internal triage doc reproducing named creators' private financial detail and message contents, and two moderation shortlists containing named creators with internal content classifications. - A tracked sample payload under local/ (a path that is otherwise gitignored) carrying real workflow telemetry and a staging webhook token. Removed — security reviews listing unfixed findings: - Five auth review and cutover documents that enumerated OPEN findings with file:line pointers. A findings list on a public repo is a to-do list for an attacker. The records themselves are valuable and belong in the private repo. - Dangling references to them were rewritten rather than left broken; several of those reference lines restated the findings inline, so the link text was part of the disclosure. Removed — operational runbooks and vendored foreign manifests: - A production pod-exec/profiling runbook including container reconnaissance. - Three deploy workflow files copied from other repositories, carrying an internal deploy-webhook host and serving no function here. Genericized rather than removed, because the docs are otherwise useful: - The postgres-query skill no longer documents the bastion path to production or which private-repo files hold the SSH keys and database password; it now points at an infra owner. - The flipt skill no longer publishes the endpoint hostname or the authentication posture of the service behind it. - The deploy-status skill no longer publishes the canary rollback thresholds, which described the conditions required to force a production rollback. - Blanked a filled-in bucket name and endpoint in one .env.example that its sibling app correctly left empty; genericized a real analytics org id. - Replaced private infra-repo directory paths in two .env.example headers, a README, a service comment, and a one-off script. - Replaced developer home directory paths across seven docs. - Dropped a token-hash construction and a "known gap" admission from the post-deploy checklist. CLAUDE.md gains a Security section stating plainly that the repo is public and listing the eight content classes that belong in the private infra repo, with a before-committing test and the note that removal is not remediation. Source changes in this commit are comment-only. The two deleted route files had no importers. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
12 KiB
3D Models Implementation — Live Tracker
Purpose: machine-death recovery surface + parallel-agent status board for Phase 1 implementation of docs/3d-models-plan.md (rev 9).
How to use: check this file to see what's been done, what's in flight, and what's next. Each agent commits its work to its own git worktree branch — branch names + paths are recorded below. If the machine dies, the worktree branches survive and the next session can pick up from the last recorded state.
Task list (live): use the TaskList tool to see runtime status. This doc is the durable record.
Phase 1 plan summary
Source of truth: docs/3d-models-plan.md rev 9. Migration already applied to user's DB. Prisma client regenerated (49 Model3D references in prisma/schema.prisma).
Workstreams (most are independent):
| ID | Workstream | Status | Worktree branch | Owner |
|---|---|---|---|---|
| A | Backend foundation: model3d.service.ts, model3d.router.ts, model3d-review.service.ts |
pending | — | — |
| B | Orchestrator PolyGen: polygen.schema.ts (Zod), polyGen.handler.ts, generation.config.ts registration |
pending | — | — |
| C | Existing-file touch points: ReportEntity / SearchIndexEntityTypes / commentv2.schema.ts / buzz.schema.ts / image-scan-result.ts enum + allow-list edits |
pending | — | — |
| D | UI scaffold: install three.js, build Model3DViewer component, scaffold /3d-models/[id] page stub |
pending | — | — |
| E | Jobs + notifications: updateModel3DNsfwLevels, updateModel3DMetrics, comment notification SQL |
pending (depends on A) | — | — |
| F | Generation form integration: add 3D Model tab to GenerationForm.tsx, build text-to-3D + image-to-3D sub-tabs |
pending (depends on B) | — | — |
| G | "Post from Generation" + detail page + reviews modal | pending (depends on A, D) | — | — |
| H | Feature flags + mod tooling | pending | — | — |
Recovery protocol
If the main session dies:
- Check this doc for the last recorded status of each workstream.
git branch | grep model3d-to find live agent worktree branches.git worktree listto see active worktrees and their paths.git log --all --oneline | grep -i 'Model3D\|model3d'for recent commits.- For each in-flight workstream, the worktree branch contains the agent's progress. Either:
- Resume by spawning a new agent with the same brief + worktree path
- Or merge the worktree's branch into
main/feature branch and continue manually
- Run
pnpm run typecheckin the main worktree to spot anything broken by merged work.
Active agents — Wave 2 status
Wave 2 ran, several agents died mid-flight, recovered + merged what landed:
| Workstream | Status | Notes |
|---|---|---|
| E | DONE + merged | 3 commits — NSFW propagation + Model3DMetric rollup + comment notifications |
| F | DONE + merged | Model3DGenerationForm, GenerationForm tab, QueueItem 3D branch, generate3D mutation, whatif preview |
| G (partial) | partially merged | Detail page rebuild + 4 components + review backend endpoints all done. Remaining: reviews page replacement + Post-from-Generation wiring (G2 spawned) |
| H (partial) | partially merged | 2 flags + surface gating done. Remaining: thumbnail-driven mod affordance (H2 spawned) |
Phase 2 — Wave 3 — COMPLETE
All seven workstreams from docs/3d-models-followups.md shipped:
| Workstream | Commit | Notes |
|---|---|---|
| N: report queue surface (M2-P1) | d0b64cd67 |
getReportsHandler selects model3d/model3dReview joins; getReportLink covers both. Existing /moderator/reports tabs now produce data. |
| O: mod content endpoints + ModBar (M2-P3) | 57b53e242 |
model3d.moderation.{setNsfwLevel,toggleFlag,restore}; Model3DModBar on detail page (mod-only); updateModel3DNsfwLevels honors lockedProperties. |
| P: count plumbing (M1-P1) | 4a7e7a51d |
userModel3DCount{,Sfw,Public}Cache + UserContentOverview.model3dCount; refresh on publish/unpublish/delete. |
| Q: centralized ReportModal (M2-P2) | 5751b9dc3 |
ReportModal accepts Model3D + Model3DReview across NSFW/TOS/AdminAttention/Spam; detail-page report button wired; NSFW side-effect flips Model3D.nsfw. |
| R: profile tab + nav (M1-P2..4) | 6e89a721f |
New /user/[username]/3d-models page + ProfileNavigation entry behind model3dFeed. ProfileLayout2 regex tightened for hyphenated subpages. |
| S: appeals switch + strikes verify (M2-P4) | 505f4c5a0 |
createEntityAppealHandler accepts EntityType.Model3D; detail page surfaces appeal CTA when isOwner sees Unpublished/Deleted. Strikes confirmed working as-is. |
| T: mod-actions skill (M2-P5) | 5da10170e |
.claude/skills/mod-actions/model3ds.mjs + SKILL.md updates. 12 commands; strikes deferred to existing strikes.mjs. |
Agent reliability note: Wave 2 agents (Q/R/S) and Wave 3 (T) were all blocked by Edit/Write denials in their isolated worktrees. Their research was solid and complete; landed inline based on their plans. Wave 1 agents (N/O/P) shipped cleanly via worktrees as intended.
Plan source of truth: docs/3d-models-followups.md (rev 1; profile feed + moderation phases).
Phase 2 — Wave 4 (post-launch polish) — COMPLETE
User-driven follow-ups from card/mod review + generator surfacing:
| Workstream | Commit | Notes |
|---|---|---|
| U: mod actions → single dropdown menu | 7ad54c7ca |
Model3DModBar (button-row + inline Menu + Popover) replaced with Model3DModMenu — canonical Mantine pattern (LegacyActionIcon + IconDotsVertical + single Menu.Dropdown). Destructive confirms via openConfirmModal; NSFW level edits via a Mantine Modal. |
| V: card redesign + inline preview | 071e06b6a |
Model3DCard rebuilt with ModelCard-shape footer (UserAvatarSimple + stat/rating chips). NSFW handling via ImageGuard2 (added 'model3d' to ConnectType union). Header IconEye Preview button lazily loads primary file via trpc.model3d.getFiles and renders three.js viewer inline as an absolute overlay. |
| W: V2 generator integration | 07377c88b |
Registers PolyGen ecosystem (ECO.PolyGen=71, BM.PolyGen=90) and '3D Models' category in GenerationFormV2. Extends WorkflowCategory/OutputType/MediaType unions to include 'model3d'. Adds txt2model3d + img2model3d workflow configs (feature-flagged on model3dGenerator, noSubmit: true). Model3DGenerationForm is rendered as the workflow body inside GenerationForm.tsx — bypasses the unified generateFromGraph path and uses the existing generate3D + generate3DWhatIf mutations. Empty polygen-graph.ts placeholder is registered in ecosystem-graph.ts. |
Reverted along the way: aaa9b7541 — first W attempt edited the legacy GenerationForm.tsx (no end-user importers; GenerationFormLegacy is dead code). Reverted via 96f48ade1; replaced with the V2 integration above.
Open follow-ups from W (intentional, not blockers):
BaseModelRecord.typefor PolyGen is'image'because PrismaMediaTypeenum has no'model3d'variant. The record ishidden: trueso it never surfaces in pickers. Future migration could extend the enum.WORKFLOW_TAGShas no'model3d'tag. Orchestration would mis-tag PolyGen submissions as'vid', but the dispatcher increateEcosystemStepthrows before reaching that code (PolyGen submits viagenerate3D, notgenerateFromGraph). Add the tag if the unified path is ever wired.- The empty
polyGenGraphmeans PolyGen contributes zero form nodes through the unified graph (intentional — the standalone form IS the workflow body). If a future workstream wants graph-driven PolyGen inputs, build out the nodes + a handler entry increateEcosystemStep.
Active agents — Wave 2.5 (continuation) — DONE
| Workstream | Status | Notes |
|---|---|---|
| G2: reviews page + post-from-gen | DONE + merged | 3 commits — reviews page (6cc97db7a), Post-from-Gen wiring + getByWorkflowId (53a826201), publish-hook flips Model3D Draft→Published (8a9b1cf04). Merged with import conflict resolved (H2's getByThumbnailImageId + G2's getByWorkflowId coexist). |
| H2: mod tooling | DONE + merged | 3 commits — getByThumbnailImageId procedure + Model3DModAction component + wired into src/pages/moderator/images.tsx:655. |
✅ Phase 1 complete
All 8 workstreams (A–H) merged on main. pnpm run typecheck clean across all Model3D code.
Phase 1 surface summary:
- Schema + migration applied
- Services: model3d / model3d-review / model3d-report
- Router: model3d (with
reviewsandreportssub-routers) + 12 procedures - Orchestrator: PolyGen handler + Zod schema + generation-config registration
- UI: 3D Model generation form, queue card branch, detail page, reviews page, reviews modal
- Viewer: three.js + GLTFLoader (dynamic-imported)
- Jobs: NSFW propagation + Model3DMetric rollup + comment notifications
- Mod affordance: thumbnail-driven "Also unpublish parent Model3D"
- Feature flags:
model3d-feed+model3d-generator(Flipt, mod-only at launch) - Post-publish hook: linked Model3D auto-flips Draft → Published
Open follow-ups (intentionally deferred per plan):
- ClickHouse download event emission (Model3DMetric.downloadCount currently stays 0)
- Post-edit page surfacing Model3D-specific fields (currently passes
?model3dId=through but doesn't render the form) - Reviews pagination switched from page-based to cursor-based if scale demands it
- Dedicated
model3dMeilisearch index implementation (search-parser is registered but routes nowhere yet) - User uploads (Phase 3, schema is upload-ready)
Active agents — Wave 1 (done, merged)
See "Commit log" below.
Integration plan (once C lands)
Merge order: D → B → C → A (least conflict risk; A touches the most, lands last).
cd <local-path>/Work/civitaigit merge --no-ff worktree-agent-a7e6b0ab4d05237b9(D)pnpm install(picks up three.js)git merge --no-ff worktree-agent-a240a7c10f27a2a52(B)git merge --no-ff worktree-agent-ad3d34f1b06bd20c6(C — once committed)git merge --no-ff worktree-agent-a032ad75027b491bc(A)- Reconcile B's
upsertModel3DDraftTODO → A's actualupsertModel3Dshape (one-line edit inpolyGen.handler.ts) rm src/pages/3d-models/[id]/.gitkeeppnpm run typecheck(expect 0 new errors; pre-existing main errors unchanged)pnpm run lintgit commit -am "feat(model3d): integrate Phase 1 wave 1 (workstreams A, B, C, D)"git worktree removeeach worktree (cleanup)
Monitoring commands
# Live task status (runtime, in-conversation)
# Use the TaskList tool in the assistant session.
# Worktrees + branches survive across machine restarts:
git worktree list
git branch | grep -i model3d
# Recent commits across all branches:
git log --all --oneline --since="1 hour ago" | head -30
Commit log (post-merge)
| Date | Workstream | Branch merged | Notes |
|---|---|---|---|
| 2026-05-27 | groundwork | (main) 2f7b86a7a |
schema + migration + docs + civitai-client bump |
| 2026-05-27 | D | worktree-agent-a7e6b0ab4d05237b9 @ 9efe10370 |
three.js + viewer + 3 page stubs |
| 2026-05-27 | B | worktree-agent-a240a7c10f27a2a52 @ 8ff736a52 |
PolyGen schema + handler + generation.config registration |
| 2026-05-27 | C | worktree-agent-ad3d34f1b06bd20c6 @ dc27eb04d |
10 touch-point files (enums, allow-lists, collection.utils, job-queue, user.service) |
| 2026-05-27 | A | worktree-agent-a032ad75027b491bc @ 5c60f5645 |
services + router + Zod + router registration |
| 2026-05-27 | reconcile A↔B | (main) 4e8570350 |
added upsertModel3DFromWorkflow; wired polyGen.handler.ts; currencies: [] for WorkflowTemplate |
Phase 1 Wave 1 complete. pnpm run typecheck passes.
Next steps after Phase 1 lands
- Apply the migration to the next environment (staging/prod) per user request.
- Phase 2: feed broadening, profile tab, community "Makes/Uses" Post linkage.
- Phase 3: user uploads (deferred; schema is upload-ready).