Lesson #1462

← Back to Knowledge Board
Structural fixes vs band-aid (Pflasterarbeit) β€” find root rules first
ID
1462
Author
ai
Agent
agent-claude
Reviewed
✓ Yes
Source authority
75 / 100
Source
User explicit: "alles nachhaltig beheben, keine pflasterarbeite" β€” patch base rules, not symptom-only
Source issue
β€”
Created at
2026-05-12T10:00:23.509695+00:00
Valid until
β€”
Deprecated at
β€”
Supersedes
β€”
Obsidian path
/root/.claude/projects/-nvmetank1-projects/memory/feedback_structural_vs_bandaid.md
Obsidian hash
428a9240a6058b43284780ea6ba1bb1e
Tags
claude-memory,feedback

Content

**Rule:** When fixing CSS/JS/architectural bugs, locate the **base/root definition** of the buggy class/rule, not just the responsive variant or specific instance. User said 2026-05-05: "genau, alles nachhaltig beheben, keine pflasterarbeite. baue handler, baue css fixes usw."

**Why:** Cost in churn. yoga#92 (dropdown clipping) had THREE PRs before the actual fix:
- PR #93 (or-loop drifted off-target β€” unrelated href bugs)
- PR #94 (only fixed `@media` mobile variant of `.card-box{overflow:hidden}`)
- PR #96 (FINALLY fixed the BASE rule at line 414 of `_ui_css.py` that applied at all screen sizes)

Same with the calendar popup (yoga#90 β†’ PR #90 superficial; PR #96 added bulletproof handlers in capture phase + global window exposure). 

**How to apply:**

1. **CSS bug** β€” `grep -n "\.classname\s*{" file.css`. Find ALL definitions, not just the first match. Check base + every `@media` variant.
2. **JS handler bug** β€” verify the function is actually IN SCOPE at click-time. Add `window.X = X` for globally-bound `onclick=` attrs that may run in different scope. Use **delegated handler in capture phase** at document level for bulletproof binding.
3. **Race conditions** β€” if a 3rd-party lib (FullCalendar, Bootstrap modal) intercepts events, your `addEventListener('click', …, true)` (capture=true) runs FIRST.
4. **Belt-and-suspenders** β€” apply both class-toggle AND inline-style for show/hide, in case CSS selector specificity loses to !important elsewhere.
5. **Test the deployed code** β€” after each fix, `docker exec <container> grep -c "<expected-marker>" /app/<file>` to confirm the change is live.

**Anti-patterns to recognize:**

- Patching only the `@media` variant when base rule has same bug
- Adding type="button" without verifying that's actually the click-failure cause
- Trusting `onclick="closeX()"` when X may not be in scope
- Spawning agents on already-failing tasks instead of orchestrator-direct edit when the bug-location is clear

**Heuristic when to go orchestrator-direct (vs delegating to agents):**

- Bug location is precisely known (file:line from user-report or grep)
- Fix is < 30 LOC
- Agent has failed 2+ times on same prompt (or-loop drift signal)
- User says "still not fixed" β€” second-iteration on same bug

**Reference PRs that demonstrate sustainable fixes:**
- yoga#66 (buttons F26+F31+F36 root cause: 110-line cascade-war β†’ 8-line canonical)
- yoga#94β†’#96 (.card-box overflow root rule, not @media variant)
- yoga#96 (delegated capture-phase click handler bypassing FullCalendar event-grab)
- glug#841 (port of yoga#96 β€” same root cause both repos)
- yoga#97 (belt-and-suspenders inline-style needs symmetric clear β€” open() must `style.display=''` before classList.add since inline > class specificity)

**Anti-pattern: asymmetric open/close**
If close() sets inline `style.display='none'` as fallback, open() MUST clear inline first. Otherwise inline-style wins forever after first close. Pattern:
```js
function open(){ el.style.display=''; el.classList.add('open'); }
function close(){ el.classList.remove('open'); el.style.display='none'; }
```