this string has no description
0
claude-roast.md
138 lines 7.0 kB view raw view rendered
1 🔥 The FARE Website Roast 2 3 You wrote a 648-line, 27KB AGENTS.md that lovingly documents the One True Way 4 to handle errors, validate inputs, and shape return types... and then your 5 actual code reads like nobody on the team has ever opened it. The doc is a 6 beautiful constitution for a country that's in open civil war. Let's tour the 7 battlefield. 8 9 1. You preach tryCatch, then console.error your way through 19 actions 10 11 Your CLAUDE.md says, in bold, "never a bare console.error()". There are 19 of 12 them in src/actions/ alone. Some are even better — signOutAction.tsx:23 does 13 console.log("Deconnection réussie"), leaving a French success log in 14 production like a sticky note on a server rack. You built a captureActionError 15 helper, wrote three paragraphs about why it's superior, and then ignored it 16 everywhere except the files you happened to write last. 17 18 The tell: editArticleAction.tsx does error handling flawlessly. Eight sibling 19 actions break the exact rule it follows. That's not a style — that's 20 copy-paste archaeology. You can date the strata. 21 22 2. The discriminated union you mandated... doesn't exist 23 24 CLAUDE.md, line 96, capital letters energy: return { success: true } | { 25 success: false; error }, "never optional success?/error? fields." 26 27 Reality (grep doesn't lie): 28 ): Promise<{ error?: string; success?: boolean }> // 29 approveAssociationAction.tsx:14 30 Both fields optional, so if (result.success) console.log(result.error) 31 type-checks fine and means nothing. You wrote a rule specifically forbidding 32 this, have a TODO.md item #3 admitting you haven't done it, and shipped it 33 anyway. The type system is right there, begging to help you, and you put it in 34 a headlock with as unknown as 14 times — including undefined as unknown as 35 File in the adhesion form, which is just any wearing a fake moustache. 36 37 3. schema.prisma: zero indexes. ZERO. 38 39 @@index count: 0 40 Every foreign key — authorId, creatorId, categoryId, userId — unindexed. Every 41 WHERE on a relation is a full table scan waiting to happen. It's fine now 42 because your tables have twelve rows, but the day this student federation gets 43 popular, the database is going to fall over and the post-mortem is going to 44 be one line long. 45 46 And 17 columns default to "". email String @default("") means "missing" and 47 "empty" are the same value, so where: { email: "" } cheerfully returns every 48 junk record. You turned NULL — a perfectly good word for "nothing" — into a 49 scavenger hunt. 50 51 4. assocation 52 53 assocation String // schema.prisma:149 54 A typo. In the schema. Migrated to the database. Now load-bearing across 8 55 files — ticket.assocation in pages, cards, actions, and the test that asserts 56 it's spelled wrong. You had every opportunity to fix it and instead you 57 enshrined it. It's not a bug anymore, it's heritage. Future you will type 58 association, get undefined, and lose forty minutes to a missing 's'. 59 60 5. A 1,629-line form component 61 62 src/app/(public)/a-propos/adhesion/form.tsx — 1,629 lines. That's not a 63 component, that's a novella. Right behind it: a 944-line form, an 819-line 64 form, and a 753-line sidebar. The same 40-line <form.Field> render-prop 65 boilerplate is pasted verbatim 15+ times per file because extracting a 66 <FieldInput> would have been too easy. You have react-hook-form AND 67 @tanstack/react-form in package.json simultaneously — TODO.md #2 admits you're 68 mid-migration between two form libraries you haven't finished picking. 69 70 Meanwhile eventCard.tsx does hover effects via raw DOM mutation in 71 onMouseEnter (e.currentTarget.style.backgroundColor = ...) in a codebase whose 72 rules say "❌ Don't add inline styles." You're hand-rolling :hover in 73 JavaScript. CSS has done this for free since 1996. 74 75 6. The config quietly disables the safety nets 76 77 - tsconfig.json:5 → skipLibCheck: true 78 - next.config.mjs:65 → typescript: { ignoreBuildErrors: true } 79 80 The build will not fail on type errors. You've delegated all type safety to 81 "CI runs it separately," which means your production build is one red CI 82 checkmark away from shipping undefined is not a function. You also 83 console.log(formDataToString(formData)) inside editMemberButton.tsx — logging 84 raw member form data, including whatever PII a student federation collects, 85 straight to the console. 86 87 7. The test suite is a Potemkin village 88 89 50/50 server actions tested — genuinely good, credit where due. But it's 5.8% 90 component coverage (7 of ~120) and the action tests mock every boundary: DB, 91 email, captcha, storage. They prove your if statements branch correctly. They 92 prove nothing about whether Prisma's actual types match, whether the email 93 payload is real, or whether the upload works. It's a test suite that turns 94 green when the schema changes underneath it. Confidence theatre with a 95 50-action cast. 96 97 And TODO.md #1, marked HIGH PRIORITY: an unfixed XSS gap in 98 dangerouslySetInnerHTML rich-text rendering. The thing every web security 99 checklist screams about, parked in a markdown file with a 🟠 emoji and a vibe 100 of "this quarter, probably." 101 102 --- 103 The actual diagnosis 104 105 Here's the thing that should sting the most: you clearly know what good looks 106 like. The 648-line guide proves it. editArticleAction.tsx proves it. tryCatch, 107 the custom oxlint rule, the Sentry wrapper — these are the instincts of 108 someone who cares. The problem isn't ignorance. 109 110 The problem is you wrote the documentation of discipline instead of the 111 enforcement of it. Your local/no-try-catch rule is set to "warn" — a warning 112 nobody reads. Your discriminated-union mandate has no lint rule at all. So the 113 codebase did what every codebase does without a gate: it drifted toward the 114 path of least resistance, one copy-paste at a time, while the AGENTS.md sat in 115 the corner being aspirational. 116 117 You don't have a knowledge problem. You have a "make the linter angry instead 118 of the markdown sad" problem. 119 120 If you fix five things, fix these (highest pain ÷ effort): 121 122 1. Turn local/no-try-catch and a new "no optional success/error" rule into 123 errors, then pnpm lint --fix your way through the carnage. Let the robot 124 enforce what the doc only requests. 125 2. Add @@index to every FK in schema.prisma. One migration. Cheap insurance 126 against the day this matters. 127 3. Delete skipLibCheck/ignoreBuildErrors OR accept they're off and stop 128 pretending the build is a safety net. 129 4. Extract one <FieldInput> and one <DialogForm> wrapper — that's ~3,000 lines 130 of duplication gone in two components. 131 5. Do TODO.md #1 (the XSS one). It's been "HIGH PRIORITY" long enough to 132 qualify for tenure. 133 134 You built something real and ambitious here — full-stack, RBAC, the works. 135 It's just held together with discipline you wrote down instead of discipline 136 you ran. Wire the rules into CI and 70% of this roast evaporates on its own. 137 138 Now go make the linter mad. 🫡