diff --git a/.claude/DECISIONS.md b/.claude/DECISIONS.md new file mode 100644 index 00000000..fb6e0278 --- /dev/null +++ b/.claude/DECISIONS.md @@ -0,0 +1,172 @@ +# Decisions + +Critical, non-obvious decisions made while working in this repo. Newest first. + +## 2026-07-10 — Comment threads nested under models: `/models/:id/comments/:commentId` + +**Context:** The standalone `/comments/:id` thread page had no model context. Comments belong to a model on the future backend, so the thread route moved under models. + +**Decisions:** +1. **Route contract:** `/models/:id/comments/:commentId` (`app/pages/models/[id]/comments/[commentId].vue`) plus the slug alias `model-slug-comment-thread` → `/models/:slug/:id/comments/:commentId` pushed in `nuxt.config.ts` `pages:extend`, matching the other slug aliases. This **supersedes** the `/comments/:id` contract recorded below — `app/pages/comments/[id].vue` is deleted. The page's back affordance links to `/models/:id` (works for both route shapes since `route.params.id` is the model id in each). +2. **Link construction depends on `Comment.modelId`** (new optional field, matching the future backend shape): CommentView's at-limit "Continue this thread" link is `/models/${comment.modelId}/comments/${comment.id}`. When `modelId` is missing the affordance hides entirely (no link, no see-more — same rendering as having no replies). Fixtures are stamped recursively with `DEMO_MODEL_ID = "model-demo"` via a `stampModelId` helper in `fixtures.ts`. + +**Context:** The comments feature has no backend yet. A dedicated bug-hunt pass found 16 defects (`app/components/comment/BUGS.md`). + +**Decisions:** +1. **Known bugs are pinned, not patched.** Each testable bug has an `it.fails` test in `app/components/comment/bugs.test.ts` asserting the CORRECT behavior — the suite stays green while the bug exists, and fixing a bug flips its pin loudly, forcing the fixer to promote it to a regular test. Refactoring done alongside was bug-for-bug behavior-preserving; `comment-tree.ts` deliberately reproduces two quirks (BUG-1, BUG-8 residue) with pointer comments. +2. **`/comments/[id]` (`app/pages/comments/[id].vue`) is a thin stub** over `CommentsSection :comment-id` — it is the target of the "Continue this thread" link CommentView renders at the nesting limit. Route shape `/comments/` is now a contract between CommentView and this page; change both together. + +## 2026-07-10 — Comment continue-thread link + collapsing: component-level branching, dumb metadata bar + +**Context:** Two additions to the recursive `CommentView`: a "Continue this thread" affordance at the nesting limit (supersedes the BUG-8 dead-end see-more) and Reddit-style per-comment collapsing. HARD constraints: `comment-tree.ts` untouched (its at-limit full-count quirk is pinned by tests), all 10 `it.fails` pins in `bugs.test.ts` keep failing (notably BUG-9's `@click`-fallthrough see-more wiring stays verbatim where `CommentSeeMore` still renders). + +**Decisions:** +1. **At-limit branching lives in `CommentView`, not `comment-tree.ts`:** `atNestingLimit = maximumNested <= 0` picks between a link-styled `UButton :to="/comments/"` ("Continue this thread (N replies)", N = `remainingReplyCount`, i.e. the full `replyPagination.count` at the limit) and the unchanged `CommentSeeMore` + `@click` → `load`. `load` is never emitted at the limit anymore. `/comments/[id]` is a future thread page — link-only for now. Both affordances share the existing spine-eraser/elbow wrapper so tree drawing is unchanged. +2. **Collapse state is a local `ref(false)` per `CommentView`** (no persistence, no provide/inject); `CommentMetadataBar` stays dumb with additive optional props `collapsible`/`collapsed`/`hiddenReplyCount` + a `toggle-collapse` emit, rendering the chevron toggle (`aria-expanded`, dynamic aria-label) and the muted collapsed summary ("· N replies hidden" / "· collapsed"). Collapsing is a read action: it never emits `write` and ignores `readOnly`. +3. **Collapsed = leaf:** everything below the metadata bar (body/actions/reply-input/replies/see-more) sits in one `