Files
LexAI/docs/LESSONS_LEARNED.md
john kevin asprec 2a2fecbfdd
Some checks failed
CI — Test & Build / Test & Build (push) Has been cancelled
docs: add changelog-accuracy lesson and pre-tag eval
2026-08-12 07:54:03 +08:00

77 lines
5.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Lessons learned — LexAI
> Evidence-backed invariants and guardrails for this codebase. Not a transcript or issue tracker. The one-line active rules live in `CLAUDE.md` → `## Lessons`; the detail lives here.
## Active guardrails
`CLAUDE.md``## Lessons` is the canonical active list loaded every session. The entries below are the established codebase invariants (from Phase 1 and the 2026-07-13 code read) that break things silently when violated.
### L-CORE-01 — `onMessage` listener must `return true`
- **Root cause / failure boundary:** async provider responses need the message channel held open; a listener that doesn't `return true` drops every response with no error.
- **Prevention:** never remove `return true` from the `chrome.runtime.onMessage` handler in `background.ts`.
- **Eval:** manual (candidate: a unit test asserting the listener returns `true`).
### L-CORE-02 — Snapshot selection before any `await`
- **Root cause / failure boundary:** focus shifts to the toolbar and the live selection is gone by the time an async response returns.
- **Prevention:** in `content.ts`, capture active element + offsets eagerly (mouseup + button mousedown) and snapshot before awaiting; Replace uses the snapshot. Handle textarea/input (`selectionStart/End`) **and** contenteditable/DOM (`Range` API).
- **Eval:** E-BASE-04 manual; DOM test tracked in TASKS #10.
### L-CORE-03 — Keep both `ANALYZE_TEXT` message shapes
- **Root cause / failure boundary:** callers send both `{ payload: {…} }` and flat `{ text, action, style }`; dropping either breaks a call path. `fix` normalizes to `grammar`.
- **Prevention:** if you touch the handler, keep both shapes and the action normalization.
### L-CORE-04 — Preserve the `data-lexai="true"` guard
- **Root cause / failure boundary:** without it, LexAI's own injected UI re-triggers selection/click handlers.
- **Prevention:** set `data-lexai="true"` on every injected node; handlers skip `target.closest('[data-lexai="true"]')`.
### L-CORE-05 — Never expose the API key; keep the plaintext fallback
- **Root cause / failure boundary:** the key is a user secret; and legacy installs still have plaintext `apiKey`.
- **Prevention:** prefer `apiKeyEnc` + `encKey`; never log the key; never send it anywhere except the user's selected provider endpoint; don't drop the plaintext `apiKey` fallback without a migration.
### L-CORE-06 — Provider code is duplicated (`callX` + `callXWithPrompt`)
- **Root cause / failure boundary:** each provider has two near-identical functions; a request-shape change to one silently diverges from the other.
- **Prevention:** update both until the layer is refactored (TASKS #4). Keep error handling uniform (network → friendly string; `!res.ok` → provider message; empty → explicit message).
### L-CORE-07 — Content script / popup must not call providers
- **Root cause / failure boundary:** CORS and key handling belong in the service worker; a direct provider `fetch` from content/popup leaks the key path and fails CORS.
- **Prevention:** route everything through `ANALYZE_TEXT` / `COPY_AS` to `background.ts`.
### L-CORE-08 — Version lives in two files
- **Root cause / failure boundary:** manifest version comes from `wxt.config.ts`; `package.json` has its own — they drift and have caused git churn.
- **Prevention:** bump `version` in **both** `package.json` and `wxt.config.ts` (until T-16 single-sources it). A `v*.*.*` tag triggers the CWS deploy.
### L-CORE-09 — UI is inline styles; Tailwind is inactive
- **Root cause / failure boundary:** Tailwind is installed but WXT PostCSS was never wired; Tailwind classes silently do nothing.
- **Prevention:** style with inline objects and the existing dark palette; don't add Tailwind classes unless the task is explicitly to wire PostCSS.
### L-RELEASE-01 — Verify CHANGELOG against code before tagging
- **Root cause / failure boundary:** release notes authored from commit subjects and handoff summaries are not facts; two false claims in v1.1.0 CHANGELOG were caught pre-tag: (1) off-by-one count of actions (`12 … plus Auto` when auto is one of 12), (2) feature listed for 1.0.1 that doesn't exist in code (commit subject claimed it but Options.tsx has no such tab).
- **Prevention:** before `git tag v*.*.*`, verify at least 23 user-visible changes claimed in CHANGELOG against the actual code diff or feature. Check version in CHANGELOG matches `package.json`.
- **Eval:** E-RELEASE-01.
## Recording policy
Add a lesson only after a material, evidenced learning signal (correction, unexpected failure, regression, rejected review, proven wrong assumption). Each needs a durable prevention; link a deterministic eval when possible. No secrets, credentials, personal data, or raw transcripts.
## Lesson template
```markdown
### L-YYYY-MM-DD-NN — [short imperative guardrail]
- **Status:** active | archived | superseded by [ID]
- **Trigger / Root cause / Prevention / Evidence / Eval / Owner-review**
```
## Archive
_Historical lessons move here with their original IDs and a one-line archival reason._