← back to Site Factory
critic/SELF-CRITIQUE.md
83 lines
# sf-critic — Self-Critique
Written 2026-04-30 by the worker about itself. Brutal-honest list of what's
missing for production and where the heuristics will lie.
## Missing for production
1. **No LLM-backed semantic review.** (high)
The whole point of the Manager critic is "code-reviewer + architect-reviewer
+ style-lint + a11y-check subagents." This worker only ships the cheap static
layer. Real reviewer-quality findings (does the prop drilling make sense?
does this component belong in `app/` or `components/`? does the copy match
the brand voice?) require a model. Add a second mode `mode: 'llm'` that fans
out to the four subagents in parallel and merges with these heuristic
findings.
2. **No rate limit on `/critique`.** (high)
The orchestrator could fan out 11 stages × 3 sites × N retries and hammer
this worker. There's no in-flight cap, no per-site queue, and no debounce.
Also no auth — anything on the box can post arbitrary findings to the
orchestrator via `/critique/stub`. Add (a) `express-rate-limit`, (b) a
shared-secret header, (c) a Bottleneck queue keyed by `site_id`.
3. **No screenshot a11y / visual diff.** (medium)
`a11y.js` accepts `screenshots[]` and explicitly logs that it's deferring.
Visual regression, contrast on rendered pixels, focus-ring presence, and
tap-target size all need image analysis (axe-core via Playwright, or an LLM
vision pass). Until then, "passes a11y" is misleading — it only means
"passes static a11y."
4. **No dedupe / suppression.** (medium)
Re-running `/critique` on the same paths re-posts identical findings every
time. The orchestrator's `manager_findings` table will grow without bound.
Need a `(site_id, stage, source, title)` unique-or-update key, or a fingerprint
column, with `resolved=false` reused across runs.
5. **No Prometheus / health metrics beyond `{ok:true}`.** (low)
Should expose check-duration, findings-by-severity counters, post failure
rate. Currently a silent failure (orchestrator down, FK error, network
timeout) reduces `findings_posted` and that's the only signal.
6. **`paths[]` trust.** (medium)
The worker reads anything the caller hands it. A malicious or accidental
`paths: ['/']` would walk the whole filesystem under glob. Need a base-dir
allowlist (e.g. only under `~/Projects/site-factory/sites/`).
## False-positive classes the heuristics will produce
- **`code.js` "unhandled async"** will fire on async functions that *do* handle
errors via `.catch()` at the call site, or via an outer `try` in the parent.
The body-only scan can't see those. Expected FP rate ~15–25 % on real
codebases. Severity overstated as `medium`.
- **`a11y.js` "icon-only button"** will flag buttons whose text comes from a
child component (e.g. `<button><Tooltip>Save</Tooltip></button>`) or from a
translated string in a JSX expression `{t('save')}`. Both are stripped by the
scanner.
- **`security.js` password regex** will match Tailwind class strings
(`password: "input input-bordered"`) and example/test fixtures
(`password: "password123"` in a `__tests__` dir). The current skip-list only
excludes `.env.example`, `*.md`, and `node_modules`. A `__tests__` /
`*.test.*` skip would help.
- **`architect.js` site-root detection** assumes the convention that a site is
any directory containing an `app/` child. A monorepo with `apps/web/app`
won't be discovered unless the caller passes the `apps/web` path explicitly.
## Severity per item
| Item | Severity |
|-----------------------------------------------|----------|
| No LLM-backed semantic review | high |
| No rate limit / no auth on /critique | high |
| No screenshot a11y | medium |
| No dedupe / suppression | medium |
| No metrics | low |
| paths[] trust / no base-dir allowlist | medium |
| async no-try false positives | medium |
| icon-button false positives | low |
| password-regex false positives | medium |
| site-root detection misses monorepos | low |