📦 deps(thirdparty): update snapshots

This commit is contained in:
ci[bot]
2026-06-18 16:04:36 +00:00
parent 71b421806e
commit dd4c084042
416 changed files with 35467 additions and 3065 deletions
@@ -1,7 +1,7 @@
{
"name": "antigravity-awesome-skills",
"version": "12.7.0",
"description": "Plugin-safe Claude Code distribution of Antigravity Awesome Skills with 1,536 supported skills.",
"version": "12.8.0",
"description": "Plugin-safe Claude Code distribution of Antigravity Awesome Skills with 1,561 supported skills.",
"author": {
"name": "sickn33 and contributors",
"url": "https://github.com/sickn33/antigravity-awesome-skills"
@@ -127,6 +127,20 @@ Estimate:
---
### Tracking Verification (Required before Gate 8)
Before entering the Execution Readiness Gate below, run through this checklist to make "Tracking is verified" mean something concrete:
1. **Event firing:** Trigger each event the primary and secondary metrics depend on (sign-up, add-to-cart, custom event) on staging or a debug page, and confirm it lands in your analytics destination within 30 seconds.
2. **Variant attribution:** Verify that the variant assignment ID is attached to every fired event — not just the entry event. Use your analytics' raw event view to compare a sample of 5+ events per variant.
3. **De-duplication:** Confirm that a user reloading the page does not cause double-counted events. If your stack uses client-side de-duping, the variant ID must be part of the dedup key.
4. **Sample randomization:** Pull the first 100 assignment records from your assignment table; the variant split should be within ±5% of the configured allocation.
5. **Guardrail metric pipeline:** Each guardrail metric defined in §6️⃣ must have a working dashboard or alert by the time the test launches.
If any of the above fails, stop and resolve it before Gate 8.
---
## 8️⃣ Execution Readiness Gate (Hard Stop)
You may proceed to implementation **only if all are true**:
@@ -0,0 +1,9 @@
# astropy.coordinates — Reference
This file is a placeholder for the extended `astropy.coordinates` reference promised in the skill workflow.
For detailed coordinate frame descriptions, transformations, observer-dependent frames (AltAz), catalog matching, and performance tips, see the upstream astropy docs:
- https://docs.astropy.org/en/stable/coordinates/
- https://docs.astropy.org/en/stable/coordinates/frames.html
- https://docs.astropy.org/en/stable/coordinates/matchsep.html
@@ -0,0 +1,8 @@
# astropy.cosmology — Reference
This file is a placeholder for the extended `astropy.cosmology` reference promised in the skill workflow.
For available models, distance calculations, time calculations, density parameters, and neutrino effects, see the upstream astropy docs:
- https://docs.astropy.org/en/stable/cosmology/
- https://docs.astropy.org/en/stable/cosmology/realizations.html
@@ -0,0 +1,8 @@
# astropy.io.fits — Reference
This file is a placeholder for the extended `astropy.io.fits` reference promised in the skill workflow.
For file operations, header manipulation, image and table handling, multi-extension files, and performance considerations, see the upstream astropy docs:
- https://docs.astropy.org/en/stable/io/fits/
- https://docs.astropy.org/en/stable/io/fits/usage/headers.html
@@ -0,0 +1,8 @@
# astropy.table — Reference
This file is a placeholder for the extended `astropy.table` reference promised in the skill workflow.
For table creation, I/O operations, data manipulation, sorting, filtering, joins, grouping, and performance tips, see the upstream astropy docs:
- https://docs.astropy.org/en/stable/table/
- https://docs.astropy.org/en/stable/table/operations.html
@@ -0,0 +1,7 @@
# astropy.time — Reference
This file is a placeholder for the extended `astropy.time` reference promised in the skill workflow.
For time formats, time scales, conversions, arithmetic, observing features, and precision handling, see the upstream astropy docs:
- https://docs.astropy.org/en/stable/time/
@@ -0,0 +1,9 @@
# astropy.units — Reference
This file is a placeholder for the extended `astropy.units` reference promised in the skill workflow.
For comprehensive documentation, unit systems, equivalencies, performance optimization, and unit arithmetic, see the upstream astropy docs:
- https://docs.astropy.org/en/stable/units/
- https://docs.astropy.org/en/stable/units/equivalencies.html
- https://docs.astropy.org/en/stable/units/standards.html
@@ -0,0 +1,9 @@
# astropy.wcs and other modules — Reference
This file is a placeholder for the extended `astropy.wcs` (and related modules) reference promised in the skill workflow.
For WCS operations and transformations, plus related modules (`astropy.modeling`, `astropy.constants`, `astropy.convolution`, etc.), see the upstream astropy docs:
- https://docs.astropy.org/en/stable/wcs/
- https://docs.astropy.org/en/stable/modeling/
- https://docs.astropy.org/en/stable/constants/
@@ -44,7 +44,7 @@ use daily. You know the difference between a toy and a tool.
## Patterns
### Extension Architecture
### Architecture Patterns
Structure for modern browser extensions
@@ -0,0 +1,129 @@
---
name: ckw-design
description: "Frontend design entry point: direction, design system, visual philosophy. Use whenever building or touching the look of any web UI (components, pages, dashboards, React/Vue/HTML-CSS) or when the user says \"make this look better\", \"fix the spacing/layout\", or mentions styling, color, type, or polish."
risk: safe
source: community
source_type: community
source_repo: connerkward/ckw-design-skill
date_added: "2026-06-16"
author: Conner K Ward
license: MIT
tags:
- design
- frontend
- ui
- css
- typography
- responsive
tools:
- claude-code
- antigravity
- cursor
- gemini-cli
- codex-cli
---
## When to Use
Use whenever building or styling web UIs — components, pages, dashboards, landing pages, React/Vue/HTML-CSS layouts — or whenever the user asks to make something "look better/nicer", fix spacing/layout, or mentions styling, color, typography, fonts, responsive design, polish, or aesthetics, even without the word "design".
_Source: [connerkward/ckw-design-skill](https://github.com/connerkward/ckw-design-skill) (MIT)._
# Design (entry)
Use this skill when the user asks to build or style web UIs: components, pages, dashboards, landing pages, React/Vue/HTML-CSS layouts, or any frontend interface. Goal: distinctive, production-grade output that avoids generic AI aesthetics.
**Before reporting any design "done": render it and have a *separate* judge critique the image** (not the code, not self-grading) — see design-spatial §1. Blind generation can't see its own collisions; this applies to all design output, not just spatial work.
> **MANDATORY HORIZONTAL-OVERFLOW GATE — runs before ANY web UI is "done".**
> Measure `document.documentElement.scrollWidth - document.documentElement.clientWidth`
> at a **narrow width (~390px and ~1024px), this turn**, and confirm it's `0`. This
> bug is invisible at desktop width and re-appears every time a row (header, nav,
> toolbar) gains an item, so it ships repeatedly. Default to `flex-wrap:wrap` on
> header/toolbar rows + `body{overflow-x:clip}`, and **re-measure after adding any
> element to a horizontal row.** Full procedure + recurrence cases: design-spatial §4.
> If you haven't measured narrow, you are not done — don't claim it.
## Sub-skills (load when relevant)
- **design-thinking** — Load for every design task. Defines purpose, tone, domain, color world, review bar, and cross-domain lens (cinema, architecture, marketing, UX, automotive, industrial design). See [design-thinking/SKILL.md](https://github.com/connerkward/ckw-design-skill/blob/main/design-thinking/SKILL.md).
- **design-system** — Load when implementing: tokens, typography, motion, color semantics, backgrounds. Use when building components, pages, or design systems. See [design-system/SKILL.md](https://github.com/connerkward/ckw-design-skill/blob/main/design-system/SKILL.md).
- **design-spatial** — Load when composing layout: explicit grid + 8-point spacing constraints, visual-weight/balance/alignment, and a render-then-critique vision loop. The fix for "spatial understanding is off" — generated layout that's centered mush, misaligned, or breaks at some widths. See [design-spatial/SKILL.md](https://github.com/connerkward/ckw-design-skill/blob/main/deterministic-design/design-spatial/SKILL.md).
- **design-ux** — Load when auditing USABILITY (not just looks): a UI that "feels off"/"sucks to use", is hard to learn, needs an instruction wall, or any interactive tool/editor/app before shipping. Scores the rendered UI against Nielsen's 10 + interaction heuristics via a SEPARATE fresh-eyes judge → prioritized fix list. Usability ≠ aesthetics. See [design-ux/SKILL.md](https://github.com/connerkward/ckw-design-skill/blob/main/deterministic-design/design-ux/SKILL.md).
- **design-philosophy** — Load for high-concept work, campaigns, or when the user asks for a visual philosophy, manifesto, or unmistakable art-like aesthetic. See [design-philosophy/SKILL.md](https://github.com/connerkward/ckw-design-skill/blob/main/design-philosophy/SKILL.md).
## Visual assets — generate or source
When design-thinking identifies a need for visual assets (logos, icons, hero images, textures, backgrounds):
1. **Generate** → use an image-generation model or API for synthetic/branded assets.
2. **Source a real/archival one** → free stock or archival image search, often cheaper and more authentic than generating.
3. Use design-thinking output (tone, domain, color world) to craft prompts / queries.
4. Evaluate against the design philosophy, refine, integrate into the build.
## LLM-assisted work — always annotate model + cost
When design work involves running an LLM (generative assets, VLM analysis, layout critique, prompt generation, etc.):
- **Before running:** state which model will be used and the estimated cost (e.g., "gpt-4o-mini · ~$0.005/image" or "FLUX v1 · ~$0.006 per gen").
- **After results:** annotate the output with the model used, actual cost if different from estimate, and any key params (seed, prompt, settings). Cost goes *visible to the user* (in the message, contact sheet header, or asset caption), not buried in logs.
- **Why:** the user is deciding whether the cost-to-quality trade-off is worth it. Unlabeled or hidden costs hide the most important lever. This rule mirrors `media-attribution-rule` for generative assets and extends it to any LLM operation in the design workflow.
**Examples:**
- "Running gpt-4o-mini layout critique on 8 designs · est. ~$0.04 total" (before).
- Contact sheet header: "FLUX v1 · $0.48 total (6 gen × $0.08)" (after).
- Asset caption: "hero_banner_flux-dev_seed3891.jpg" (seeds enable reproducibility).
- Uncertainty slider result: "VLM triage on 46,978 images · gpt-4o-mini · ~$9.40" (before); "✓ Completed: 12,447 images classified · gpt-4o-mini · $7.62" (after).
## Algorithm / model explainers — show the equation, annotate the terms
Whenever a UI surfaces an algorithm or model to the user (an "ⓘ how this works"
panel, a model breakdown, a methods note), **include the actual equation, typeset,
with its key terms annotated** — don't settle for prose. A scorer described only in
words ("ranks by how much of the picked color is present") is unfalsifiable hand-
waving; the formula `score = Σ fracᵢ · max(0, 1 ΔEᵢ/τ)` with each term labelled
tells the user *exactly* what the knob does and builds trust that there's real math
under the hood.
**How to apply:**
- Render one clean, central equation per algorithm — the "sexy" core, not every
detail. Use proper notation: σ for sigmoid, Σ for sums, ‖·‖ for norms,
superscripts, ΔE, ∇²; a monospace/serif-math block set off from the prose.
- **Annotate every symbol** immediately below: what `e_x`, `w`, `τ`, `Q` each are,
in one line each. An unlabelled equation is decoration; a labelled one is a spec.
- Keep it dependency-light — styled HTML/Unicode math is fine and works offline; only
reach for KaTeX/MathJax if the expressions genuinely need it.
- State the **decision rule** alongside the score (e.g. "personal if P ≥ 0.55").
- This composes with the model+cost annotation above: the equation says *what* it
computes, the model/cost line says *what ran it and for how much*.
**Example (a logistic head):**
> P(personal │ x) = σ(**w**·**e**ₓ + b), σ(z) = 1 / (1 + e⁻ᶻ)
> • **e**ₓ — the image's 768-d embedding · **w**, b — weights learned from your labels
> · decision: personal if P ≥ 0.55, reference if P ≤ 0.40.
## Select-all always has a deselect — no dead-end selections
Any **"Select all"** affordance MUST be paired with a way to **clear the selection**
preferably the *same button*, label-flipped when everything is already selected
("Select all" ⇄ "Deselect all"). A select-all with no inverse is a trap: the user
over-selects (or hits it by reflex), then has to un-click items one by one, or reload
the page, to get back. The cost is silent — it only bites *after* they've committed to
the wrong set.
**How to apply:**
- **Toggle the same button** (simplest, fewest controls): when all visible items are
selected, the button reads "Deselect all" and clears; otherwise "Select all". One
control, no dead end.
- Or a **separate Clear/Deselect** shown whenever the selection is non-empty.
- The deselect must reach the **same scope** the select-all did (all *shown*, all
*filtered*, all *on this page*) — don't let "Select all" grab 500 but "Clear" only
drop the 50 on screen.
- This generalizes: any reversible bulk toggle (select, expand-all, mute-all,
check-all) needs its inverse one tap away. Symmetry of action — see
restraint-rule (don't strand the user mid-task).
## Limitations
- This skill improves visual direction and review discipline, but it does not replace rendering the actual UI and checking it in target browsers or devices.
- Some recommendations assume access to screenshots, browser automation, or vision review; when those are unavailable, treat the guidance as a design checklist rather than proof.
- Brand, legal, accessibility, and localization constraints from the product owner override the taste rules here.
@@ -0,0 +1,56 @@
---
name: deterministic-design
description: "Render the UI and prove it's balanced + usable: a deterministic layout audit (centroid / optical-center / pixel-oracle balance via explicit math + annotated screenshot) plus a vision-judged Nielsen usability audit by a separate fresh-eyes judge. The measurement layer taste-only design skills lack."
risk: safe
source: community
source_type: community
source_repo: connerkward/deterministic-design-skill
date_added: "2026-06-16"
author: Conner K Ward
license: MIT
tags:
- design
- layout
- usability
- audit
- verification
- vision
tools:
- claude-code
- antigravity
- cursor
- gemini-cli
- codex-cli
---
## When to Use
Use to catch AI-generated UI that "looks off", is misaligned or centered-mush, or fails usability — when you need to PROVE a layout is balanced and usable instead of trusting the model's eye. Compose it with any taste/token design skill before reporting design "done".
_Source: [connerkward/deterministic-design-skill](https://github.com/connerkward/deterministic-design-skill) (MIT)._
# deterministic-design
Thesis: **determinism beats AI randomness.** A model can't trust its own eye on layout — so
don't. Render the UI and *measure* it.
Two sub-skills (load as needed):
- **[design-spatial](https://github.com/connerkward/deterministic-design-skill/blob/main/design-spatial/SKILL.md)** — deterministic layout audit: explicit grid
+ 8-pt spacing, and `layout-audit.js` computes centroid / optical-center / pixel-oracle
balance and draws an annotated screenshot. **Numbers, not vibes.** Plus a render-then-
critique vision loop.
- **[design-ux](https://github.com/connerkward/deterministic-design-skill/blob/main/design-ux/SKILL.md)** — usability audit: scores the rendered UI against
Nielsen's 10 + interaction heuristics via a SEPARATE fresh-eyes judge → prioritized fix list.
This **improves** existing design skills (including the default Anthropic one) by adding the
layer they lack — it doesn't just advise on taste, it renders, measures, and judges the
output. Composable with any design skill.
In central this lives as a subdir of ckw-design; it **publishes separately** as
`deterministic-design-skill` (its own distribution) via publish-skill. One of the two
flagship narratives — the *determinism* one; its sibling is human-in-the-loop (lookdev).
## Limitations
- Layout metrics and vision-judged audits catch many spatial and usability failures, but they are not a substitute for product judgment or user testing.
- The workflow requires a rendered UI or screenshot; it cannot validate components that have not been built or captured.
- Automated scoring can miss brand nuance, copy tone, accessibility needs, and domain-specific user expectations.
@@ -24,7 +24,9 @@ date_added: "2026-02-27"
## The Quality Loop
1. **Write/Edit Code**
2. **Run Audit:** `npm run lint && npx tsc --noEmit`
2. **Run Audit** for the project's ecosystem:
- **Node.js / TypeScript:** `npm run lint && npx tsc --noEmit`
- **Python:** `ruff check . --fix && mypy . && bandit -r . -ll`
3. **Analyze Report:** Check the "FINAL AUDIT REPORT" section.
4. **Fix & Repeat:** Submitting code with "FINAL AUDIT" failures is NOT allowed.
@@ -0,0 +1,102 @@
---
name: lookdev-auto
description: "Automated visual tuning: a vision or video model rates rendered variants in a loop. Render several labeled variants into one artifact, ask the model to rate them and suggest better values, render the suggestions, ask it to pick the best, repeat until good — the model is the eye, you run the loop."
risk: safe
source: community
source_type: community
source_repo: connerkward/lookdev-auto-skill
date_added: "2026-06-16"
author: Conner K Ward
license: MIT
tags:
- visual-eval
- vision-model
- tuning
- automation
- render-loop
tools:
- claude-code
- antigravity
- cursor
- gemini-cli
- codex-cli
---
## When to Use
Use whenever "looks/feels right" is the success criterion and there's no cheap numeric metric — animation easing/timing, zoom/camera feel, color grade, layout/spacing, design params, render/encoder settings, prompt params. Use the automated counterpart to lookdev when there's no human to sit the loop.
_Source: [connerkward/lookdev-auto-skill](https://github.com/connerkward/lookdev-auto-skill) (MIT)._
# Visual eval loop — let a vision/video model tune what only an eye can judge
When the target is "does this LOOK/FEEL right" (not a number you can minimize), a
vision model (image) or video-understanding model (motion/timing) can be the judge in
a tight optimize loop. Worked reference: the `screenstudio-alternative` skill (`iteration.py`)
(tuned zoom-animation feel via `fal-ai/video-understanding`).
## The loop
1. **Render N labeled variants into ONE artifact.** Vary the parameter(s) across a
small spread. **Annotate each variant's params ON the artifact** (burn the label in:
"A · 2.2Hz · ζ0.5"). Images → a labeled grid/contact sheet. Video/motion → a
labeled *sequence* (label card or burned-in overlay before/over each clip) so the
model can compare temporally.
2. **One model call, structured output.** Send the single artifact with an explicit
rubric (define what "good" means — and what "too much"/"too little" look like).
Ask for **per-variant ratings + concrete suggested new values as JSON**:
`{"ratings":{"A":n,...},"best_so_far":"X","suggest":[[p1,p2],...]}`.
3. **Coarse → fine.** Round 1 = wide spread to locate the region. Round 2 = render the
model's suggestions (+ carry the current best) into one artifact; ask it to **pick
the single best**. Usually converges in **2 rounds**.
4. **Stop when sufficient** — best rates high and suggestions cluster. Apply the winner.
## Token / quality / step reductions (do these)
- **One artifact per round, not one call per variant.** The biggest saver — a 6-variant
round is 1 upload + 1 inference, not 6. Montage/grid beats a loop of single calls.
- **Burn params onto the artifact.** The model sees label+result together → no separate
"variant A used X" context to carry → fewer tokens, fewer mistakes.
- **Structured JSON out + parse.** No re-asking, no free-text wrangling. Prompt "return
ONLY JSON"; regex the first `{...}`.
- **Short representative sample.** Tune on a 3-5s clip / one frame / one component, not
the whole asset. Cheaper render, smaller upload, faster inference. Apply the found
params to the full render once.
- **Cap variants at ~5-6.** More doesn't improve the model's discrimination and multiplies
render + token cost. Wide-but-sparse round 1, narrow round 2.
- **Calibration anchors.** Include one deliberately-bad and one safe-default variant as
fixed anchors each round — gives the model a reference scale and exposes when its
"best" is worse than the safe default (catch a bad recommendation early).
- **Independent rubric, stated up front.** Define "good" concretely in the prompt
(smooth, subtle settle, not bouncy, not sluggish). Don't ask "which do you like" —
that lets it echo your framing. A held-out criterion keeps the judge honest
(see verify-outputs-rule: the check must be independent of what you tuned).
- **Reuse renders across rounds.** Carry the round-1 winner's clip into round 2 instead
of re-rendering it.
- **Early-exit.** If round-1 top ≥9/10 and the three suggestions are within a small delta,
skip round 2.
- **Cheapest judge that can see the failure.** Frames-through an image VLM can judge
spatial things (layout, color, crop); only reach for a true *video* model when the
thing being judged is **temporal** (easing, timing, motion smoothness) — those are
invisible in stills.
## When NOT to use it
- A real numeric metric exists and correlates with quality → optimize that directly;
don't pay a model per step.
- The judgment is subjective-to-the-user (their taste, brand) → show them the variants
and let them pick; a model's "best" isn't their best. (This is why the screen-studio
spring auto-tune was dropped — the model's pick didn't match the owner's eye.)
- One or two variants → just look yourself.
## Caveats (learned)
- The model's pick is an *opinion*, not ground truth — anchor it, and sanity-check the
winner against the safe default yourself before committing.
- Vision/video models perceive gross differences well, fine ones poorly — keep variant
spacing perceptible; near-identical variants get noise-rated.
## Limitations
- Model ratings are probabilistic aesthetic judgments, not objective truth; keep a human review step for brand-critical or subjective work.
- Automated rounds can become expensive or slow when renders are heavy or many variants are explored.
- This skill needs screenshots, frames, or clips that expose the quality difference; it is weak for subtle motion, audio, copy nuance, or user-preference calls.
@@ -0,0 +1,229 @@
---
name: lookdev
description: "Human-in-the-loop web studio to tune AI-generated output by eye. Stand up a local interactive studio (sliders, pickers, drag handles) or an inline edit/highlight/comment annotation studio for prose & media, instead of guessing values or shipping a static comparison grid."
risk: safe
source: community
source_type: community
source_repo: connerkward/lookdev-studio-skill
date_added: "2026-06-16"
author: Conner K Ward
license: MIT
tags:
- lookdev
- design
- ui
- tuning
- studio
- visual-eval
- annotation
tools:
- claude-code
- antigravity
- cursor
- gemini-cli
- codex-cli
---
## When to Use
Use when the user says "lookdev", or asks to tune / dial in / iterate on the look of something, compare variations by feel, or review / edit / annotate a blog post, doc, copy, or media set. Use whenever "show me, I'll pick" beats asking the user to specify a number, and whenever you'd otherwise hand back a static grid or a wall of prose for review.
_Source: [connerkward/lookdev-studio-skill](https://github.com/connerkward/lookdev-studio-skill) (MIT)._
# Lookdev
When the user says **"lookdev"** — or any of: *tune*, *dial in*, *iterate on the look of*, *compare variations of*, *let me adjust*, *let me edit/annotate/mark up*, *review this post/doc/copy* — they mean **build an interactive in-browser tool the user directly manipulates**. Not a static grid of N variations. Not a Q&A where they specify numbers. Not a wall of prose they're asked to read and reply to in chat. A real-time studio where they act on the artifact and the change is captured.
**Two studio shapes — pick by what's being tuned:**
- **Visual-parameter lookdev** — the artifact's *look* is set by numbers/choices (color, type, layout, image treatment, animation, 3D). Controls = sliders, pickers, drag handles. This is the bulk of this skill (below).
- **Text & media lookdev** — the artifact is a *document, blog post, copy, or media set* and the user is editing/curating it: rewriting sentences, cutting boring paragraphs, highlighting, leaving margin comments, flagging "diagram goes here" / "wrong image, replace." Controls = **direct inline editing + selection highlight + anchored comments + media annotation**. See the dedicated section below. **A blog post / doc / script review IS this mode — never hand back a long markdown file and ask the user to react in chat. Stand up the annotation studio.**
## What it covers
Any visual decision the user picks by feel, not by spec. Expand this list as needed:
- **Image processing** — dither, halftone, posterize, ASCII, blur, edge, quantize, mosaic, color-grade
- **Color** — palette extraction (show coverage %), per-band pickers, saturation / contrast / gamma curves, harmony presets, theme tokens
- **Typography** — font selector, size / weight / leading / tracking / measure, live sample text, fallback stack
- **Layout, positioning, framing, spacing** — draggable & selectable elements; resize handles; margin / padding rulers; alignment guides; snap-to-grid; aspect-lock toggles
- **Crop & framing** — draggable crop rectangle with aspect lock; live cropped preview at production size
- **Animation / transitions** — easing curve editor, duration sliders, scrubber, replay
- **Component variants** — render hover / focus / disabled / loading / dark side by side on one page
- **Iconography** — stroke weight, corner radius, glyph on canvas
- **AI-generated content** — prompt input + param sliders + side-by-side regeneration grid
- **Anything else where "show me, I'll pick"** beats "ask me to specify a number"
## Controls must stay reachable while inspecting
If the studio shows a list, grid, or scroll-long set of variations, **controls must be visible from every scroll position**. The user has to be able to drag a slider while looking at row 14, not scroll back to the top each time.
Two approaches, pick by layout:
- **Sticky bar** (`position: sticky; top: 0`) at the top of the scroll container. Keep the bar visually distinct — paper background + blur backdrop + bottom border — so it doesn't muddy the specimens scrolling behind it. Sticky pins relative to the *nearest scrolling ancestor with a defined boundary*; if you nest it inside a sized parent (a `<header>` with `margin-bottom`, a `<div>` with a fixed height), it stops sticking at that parent's bottom edge. Lift it to be a direct child of `<body>` (or the page-wrap) so stickiness spans the whole page.
- **Floating overlay** (`position: fixed`) for hotkey-toggled controls — e.g. press `d` to reveal. The portfolio's `.debug-ctl` pattern is this: pinned top-left, transparent until summoned. Use when the controls shouldn't occupy permanent screen real estate (final viewers shouldn't see them; the author can summon on demand).
Anti-pattern: a top-of-page control panel that the user scrolls past and never sees again. They will tune blindly, give up, or guess. Either keep the controls in view *or* duplicate a compact control bar next to each variation row.
## Text & media lookdev — direct edit, highlight, comment, annotate
When the artifact is a **blog post, doc, copy deck, script, or media set**, the user is not turning knobs — they're *marking up the work the way an editor marks a manuscript*. The studio renders the **real artifact WYSIWYG** (the actual rendered blog with its real components/media, not a raw-markdown textarea) and lets the user act on it directly. Building this for a doc review is mandatory: **do not paste a long file into chat and ask "what do you think?" — that's the boring wall of text the user is rejecting.** Stand up the annotation studio and let them edit in place.
### The four affordances (build all that apply)
1. **Direct inline editing.** Every text block is editable in place — click a paragraph/heading and type. Use `contentEditable` per block (or click-to-swap-to-`<textarea>`), each block carrying a stable `data-block-id` that maps back to a source location (markdown/MDX line range, JSX node, or content key). Capture the *edited* text per block; the agent applies the diff to source. Don't make them retype in a separate field — they edit the rendered sentence.
2. **Selection highlight.** Select text → toolbar (or hotkey) applies a colored highlight (`<mark>`). Multiple colors = a legend the user defines (e.g. yellow "cut this", green "love it", red "wrong/fact-check"). Each highlight stores `{blockId, startOffset, endOffset, color, optional note}`.
3. **Anchored comments / margin notes.** Select text or click a media region → attach a comment shown in a **margin rail** (pin in the gutter, expand on hover/click) or as a numbered superscript. Comment = `{anchor, text}` where anchor is a block+range or a media region. This is how the user says "diagram goes here", "too long, cut to two sentences", "needs a real screenshot".
4. **Media annotation.** For images/figures: draw a box / drop a pin / arrow on the image and attach a note (`{mediaId, x, y, w, h, note}`); plus a per-media **flag menu** — "replace", "wrong model", "regenerate", "missing — generate one here". Placeholders ("DIAGRAM HERE", "MEDIA?") render as visible drop-zones the user clicks to specify what they want, directly addressing "where are the diagrams / where is the media."
### Round-trip is MANDATORY (same rule as the settings JSON)
The studio is worthless if the agent can't read the markup back out. Every edit, highlight, comment, and media-flag must export as **one machine-readable patch** with a single **Copy** button (and persist to `localStorage`/URL so a refresh doesn't lose work — this is human-labeled data; see `human-labeled-data-rule`). Shape:
```json
{
"edits": [{ "blockId": "p-12", "text": "new rewritten text" }],
"highlights": [{ "blockId": "p-3", "range": [40, 88], "color": "cut", "note": "boring, drop" }],
"comments": [{ "anchor": "p-7", "text": "diagram goes here — flow of the save loop" }],
"media": [{ "mediaId": "fig-2", "flag": "replace", "note": "use a real screenshot, not ASCII" }]
}
```
The agent ingests this and bakes: applies the inline edits to the source file, acts on every comment/flag, swaps/generates the flagged media, resolves the highlights (cut the "cut" spans, etc.). Then re-serve the updated artifact for another pass. **No markup may exist that isn't in the export blob** — otherwise you're back to the user narrating changes by hand.
### Mechanics
- **Render the real thing.** MDX/React blog → mount the actual components; static page → render the real HTML/CSS. WYSIWYG per Architecture #5. An annotation layer over a fake-looking preview lies about the result.
- **Selection → offsets.** Use the `Selection`/`Range` API; store character offsets relative to the block's text content (not DOM node paths, which break on re-render). Re-apply highlights/comments on load by walking each block's text to the stored offsets.
- **Editing toolbar floats with the selection** (a small popover at the selection rect) or a sticky top bar — controls stay reachable (see section above). Hotkeys: highlight on a key (e.g. `h`), comment on `c`.
- **Keep edit/annotate modes distinct** so a stray click doesn't garble text while they meant to highlight — a mode toggle (Edit · Highlight · Comment) or modifier key.
- Everything else — serve locally on a free port, verify headless, tear down after baking — is identical to the visual-parameter workflow below.
## Control patterns
Pick controls by what the decision actually is.
| Decision type | Control |
|---|---|
| Continuous value (intensity, size, opacity, k) | `<input type=range>` **paired with an editable `<input type=number>`** (not a static label) — drag OR click-and-type; they two-way sync |
| Discrete choice (mode, blend, easing kind) | segmented buttons or radio chips |
| Color | `<input type=color>` swatches; pre-extract dominant palette with coverage % when relevant |
| Position / size on a canvas | **drag the element itself** — handles, not numeric inputs |
| Crop region | draggable rectangle + aspect-lock toggle |
| Multiple discrete states | render each in a labeled card on one page |
| Font choice | searchable picker + editable sample-text input |
**Spatial rule:** if the user could point at the thing and drag it, that *is* the control. Don't add an `x:` slider when a drag handle is the obvious affordance.
**Gesture capture — never make the gesturing hand leave the gesture.** When a control toggles a *live mouse action* the user is performing — recording a cursor path, scrubbing, freehand-drawing, demonstrating a motion — the start/stop must **not** be a button they have to click. Clicking it drags the mouse off the path, pollutes the start/end of the very motion being captured, and forces a round-trip back to where they were. Bind start/stop to the **keyboard (spacebar by default)**`keydown` on `Space`, `e.preventDefault()` to kill page scroll, toggle the same handler the button would. Keep the button too (discoverability), but the hotkey is the real control. Generalize: any modal capture where one hand is committed to the primary input gets the *other* modality for mode-switching — gesture→key, and conversely a keyboard-heavy capture gets a foot/mouse toggle. The test: if triggering the control would move the thing you're capturing, it's the wrong modality.
## Coherent control ranges — bounds must propagate
When one control sets a **bound** on another (a min, a max, a threshold, an allowed set), the bounded control's UI must reflect the new bound the instant you change it. A "scrub" slider whose `min`/`max` attributes drift out of sync with its declared bounds is the most common silent bug — the user moves the bounding slider, nothing visible changes downstream, they assume both are broken.
Rules:
- **Single source of truth.** Hold the bound in state once. Every input that *displays* it (its own slider, the dependent control's `min`/`max`, anything else) reads from that state on every update.
- **Re-render `min`/`max` on every state change.** Don't rely on browser-cached attribute values; rewrite them via JS each render. `dependent.min = state.lo; dependent.max = state.hi`.
- **Clamp the dependent value into the new range immediately.** If the user shrinks the upper bound below the current dependent value, the dependent must snap into range, NOT silently stay outside while the slider shows it pinned to the rail.
- **No-op regions are slider bugs.** If dragging a slider past some value has zero downstream effect (because some other control's bound caps it), that's a coherence bug — either narrow this slider's range to where it actually does something, OR change behavior so it does. Sliders with dead zones train the user to think the studio is broken.
- **Test by visualisation, not numeric snapshots.** Take a screenshot, change the bounding slider, take another. The two must look meaningfully different — or the slider is decorative. A numeric `snapshot()` showing state changed doesn't prove the pixels did.
Pattern: every time `applyState()` runs (or its split equivalents), call a `syncBounds()` helper that walks the dependent-input registry and pushes the live bounds into every `min`/`max`/`disabled` attribute. Clamp values into the new bounds in the same pass.
### Paired controls must not cross
A common shape is **two sliders that together define an interval**`min ⟷ max`, `near ⟷ far`, `tightEnd ⟷ wideEnd`, `start ⟷ end`. If the user can drag one past the other, the interval inverts or collapses. Downstream math typically does `(x - lo) / (hi - lo)` which **divides by zero or returns negative `t`** — producing `NaN` coordinates, collapsed views, or inverted lerps. The user sees the studio "break" but no error fires.
Both ends of the defense:
- **UI invariant.** Keep the two sliders from crossing. On every `syncBounds()` pass: `lower.max = upper.value - MIN_SPAN` and `upper.min = lower.value + MIN_SPAN` (small epsilon, e.g. 2 units, so they can't even touch). The user can't physically drag past the other anchor.
- **Math invariant.** The consuming code (lerp, normalisation, ratio) must guard `denominator > 0` and pick a sane fallback for the degenerate case (e.g. clamp `t = 1` or `t = 0`). UI can race the math — always assume the math could be hit with crossed bounds anyway (URL hash, JSON paste-back, programmatic state mutation).
- **Test the boundary explicitly.** When the lookdev exposes both ends of an interval, write a quick check: drag `tightEnd` to the same value as `wideEnd`, verify the scene doesn't break. Drag `tightEnd` past `wideEnd`, verify same. If you can crash the studio with two slider drags, that's a release blocker.
## Architecture
1. **Single-page HTML**`<canvas>` and/or DOM, vanilla JS, a sidebar of controls. No build step, no framework, no deps unless one is genuinely required. Lives in a project-local scratch dir (e.g. `scripts/.lookdev-<name>/` or `scripts/.preview-<name>/`), **gitignored**.
2. **Live re-render** on every `input` event. Debounce heavy work via `requestAnimationFrame`. Keep the loop tight enough to feel like a real slider, not a survey.
- **Every numeric control is dual-input (MANDATORY): a range slider AND an editable `<input type=number>`, two-way synced.** The drag is for exploring; the typed number is for hitting an exact value (and reading the current one). A static `<span>` readout is not enough — the user must be able to click it and type. Sync rule: on slider `input`, write the number field; on number `input`/`change`, update state and re-render — but **do not overwrite a field while it has focus** (guard with `document.activeElement`), or typing gets clobbered mid-keystroke. Clamp to [min,max] on commit (`change`), not on every keystroke, so intermediate values like "1" before "12" aren't snapped.
- **Always include a Reset control** that restores every control to its defaults in one click (keep a `DEFAULTS` object; `Object.assign(state, DEFAULTS)` then re-render). Cheap to add, and essential once the user has wandered far from baseline.
- **Always build undo/redo history (MANDATORY).** Dialing-in is iterative and lossy — the user *will* overshoot a good look and need to step back. Bind **Ctrl/Cmd-Z** (undo) and **Ctrl/Cmd-Shift-Z** / **Ctrl-Y** (redo), and surface visible **↶ Undo / ↷ Redo** buttons. Snapshot the *full* serialized state — every control **plus any drawn/spatial state** (polygons, crop rects, dragged handles, palettes), i.e. the same blob as the settings round-trip (#3), not just slider scalars. Debounce so a continuous drag collapses into **one** history step (snapshot ~350 ms after the last `input`, not per event), keep a bounded stack (~100120 entries), and on a new edit after undo, truncate the redo branch. Restore by re-applying a snapshot through the same `applyState` path the loader uses (so it can't drift). Guard the key handler when focus is in an `<input>`/`<textarea>` so native text-undo still works. A lookdev without undo punishes exploration — the whole point of the tool.
3. **Structured settings round-trip (MANDATORY).** Every lookdev MUST expose its full current state as machine-readable, copy-pasteable text — a settings JSON (or equivalent) covering *every* control, with a one-click **Copy** button and a visible live readout. This is non-negotiable: the agent cannot bake by eyeballing a screenshot, and the user shouldn't have to describe what they dialed in. The round-trip is: user drags → studio serializes the exact state → user pastes the blob back (or it persists to URL/localStorage) → agent bakes from those literal values with identical math. No control may be tweakable without appearing in the export blob. Mirror the state into the URL query so a look is shareable by link, too.
4. **Reproducible export.** Beyond the settings blob, pick by what gets committed:
- **Copy settings JSON** — user pastes back, agent bakes with identical math (port the renderer to Python / build script / etc. and verify the bake matches).
- **Download asset** — page renders the final artifact at full resolution and triggers a download (PNG / SVG / WebP / JSON).
- **Make exported artifacts re-loadable — sidecar + embedded metadata.** When the download is a *non-JSON* artifact (STL, PNG, GLB, SVG, WebP, video…), the look that produced it shouldn't be strandable. Do BOTH, where the format allows:
- **Sidecar:** download a **zip** containing the artifact *and* its `settings.json`, so the exact state ships next to the result.
- **Embed the settings inside the file itself**, so the bare artifact alone restores the look — then add a **drag-drop / file-input loader** that reads it back through the same `applyState` path as the JSON paste. Per-format hooks: **binary STL** → append `MAGIC + uint32 len + JSON` after the triangle data (CAM ignores trailing bytes; parse `count` at byte 80, footer at `84 + count*50`) and drop a human note in the 80-byte header; **PNG** → a `tEXt`/`iTXt` chunk; **SVG/XML** → a `<metadata>` element or comment; **JPEG/MP4** → EXIF/XMP `UserComment`; **GLB** → an `extras` field. The payoff: the user drops last week's STL back on the viewport and the studio re-dials itself — no "which settings made this?" archaeology. Verify the round-trip (export → reset → load → assert state matches) and confirm the artifact still opens in its native tool (the trailing/edge metadata must not corrupt it). Skip only when the format has nowhere safe to stash bytes; the sidecar zip always works as the fallback.
5. **WYSIWYG.** The preview frame must match the production context — same background color, same fonts loaded, same container max-width, same `object-fit`. A generic centered canvas is not WYSIWYG.
6. **Framework-route variant.** When the lookdev is for UI layout inside an existing app, build it as a **temporary route** in the app (`app/dev/...` or equivalent) so the real components, styles, and tokens are in the comparison. **Delete the route once baked.**
## 3D lookdev — orientation gizmo (MANDATORY when the camera orbits)
Any lookdev with a **non-fixed camera** (OrbitControls, trackball, free fly — anything where the user can spin/tumble the view) MUST include a **CAD-style ViewCube** in a corner. Free orbit alone disorients: the user loses which way is up, can't get a repeatable canonical view, and can't tell whether they're looking at the front or the back. The cube fixes both problems — it's an orientation *indicator* and a *controller* in one. **Copy the Autodesk/Fusion 360 ViewCube** — that's the interaction users expect; don't invent a different gizmo.
Required behaviour (this is cheap — ~70 lines of Three.js, no excuse to skip):
- **Live orientation readout.** A small second scene/renderer in a corner draws a labeled cube (FRONT/BACK/LEFT/RIGHT/TOP/BOTTOM). Each frame, drive the gizmo camera from the *main* camera's view direction (`gizmoCam.position = (mainCam.position target).normalize() * d; gizmoCam.up = mainCam.up; gizmoCam.lookAt(0,0,0)`) so the cube always mirrors the scene's current orientation.
- **Click a face / edge / corner to snap** (the defining Fusion behavior; it's 26 preset views — 6 faces, 12 edges, 8 corners). Raycast the gizmo and snap each component of the local hit point (`|c|>0.55 ? sign(c) : 0`) to derive a view direction. One pickable cube then yields **faces → ortho views, edges → 45° edge views, corners → iso views** from a single mesh — no separate hit zones needed. Animate the main camera to `target + dir*currentDist` with a short lerp (~0.28/frame), not an instant cut — the motion is what keeps the user oriented.
- **Drag the cube to orbit freely** (also Fusion, also mandatory — the user WILL try to grab it). Use pointer events with **click-vs-drag discrimination**: on `pointerdown` record the start and `setPointerCapture`; on `pointermove`, once travel exceeds ~4px flip into drag mode and orbit the *main* camera by the pointer delta (convert the camera offset to spherical around the target, `theta -= dx*k; phi = clamp(phi - dy*k, ε, π−ε)`); on `pointerup`, if it never became a drag, treat it as a snap-click. Capture means the drag keeps working when the pointer leaves the little canvas. Cancel any in-flight snap tween when a drag starts.
- **Roll arrows = Fusion's "rotate".** Two curved-arrow buttons (⟲ ⟳) beside the cube that **roll the current view 90°** about the view axis (rotate `camera.up` by ±90° around the normalized `positiontarget` axis). This is the rotate users mean when they say the gizmo "can't rotate" — drag-orbit is *not* a substitute for it. Snap-cleanup the rolled up so near-cardinal components land exactly on 0/±1 (keep genuine diagonals). **Let it roll in ANY view, including iso** — do NOT gate it to face-on views or auto-snap-to-face first. (I tried that "Fusion only rolls in standard views" guard and it backfired: it stops the user rolling an *isometric* view into the exact orientation they want, which is a primary reason they reach for the arrows. Rolling an iso view is a valid, common move.)
- **Perspective ⇄ orthographic toggle.** Any 3D lookdev should expose a projection toggle. Perspective for a natural read; **orthographic for CAD/measure/section work** (parallel edges, true elevation, no foreshortening — essential when judging a thickness or aligning a face). Swap by building the other camera, copying `position`/`up`/`target`, and rebuilding controls; size the ortho frustum from the current target distance (`h = 2·dist·tan(fov/2)`) so the switch doesn't jump scale. Handle resize for both (`isPerspectiveCamera` → set `aspect`; ortho → recompute `left/right` from aspect keeping height).
- **`camera.up` + OrbitControls is a TRAP — read this.** Three's OrbitControls (r160) captures its orbit-axis quaternion from `camera.up` **once**, at construction. If you mutate `camera.up` afterward (e.g. to "fix" a top view, or to roll) and leave it, the main-viewport drag silently breaks — OrbitControls keeps orbiting around the *old* up while the camera renders with the *new* one. Two consequences for the gizmo: **(a)** Do NOT flip `camera.up` for top/bottom snaps. Leave it `(0,1,0)` and instead nudge the snap *direction* a hair off the pole (`dir = (0,±1,0.0009)`) so `lookAt` with `up=+Y` doesn't gimbal-lock. **(b)** When you DO need a new up (the roll arrows), **dispose and recreate OrbitControls** after setting `camera.up`, copying `target` across, so it re-captures the axis. Snaps and Home should reset to `up=(0,1,0)` and rebuild if currently rolled.
- **Home / reset-view button** beside the cube (Fusion's house icon) that re-frames the object, resets `camera.up=(0,1,0)`, and rebuilds controls if rolled.
- **Hover highlight the exact zone, not just the face.** Fusion subdivides each face into a 3×3 grid — center cell = face, edge cells = edges, corner cells = corners — and lights the hovered cell *wrapping across the adjacent faces*. Implement with a small pool of up to 3 translucent quads: from the hovered direction `d` (1/2/3 nonzero axes), for each nonzero axis place one quad on that face at the cell offset `(other-axis sign)*⅔`. A corner lights 3 quads (one per adjacent face), an edge 2, a face 1. A plain whole-face tint is wrong — the user can't tell a corner-pick from a face-pick. Also set a `grab`/`grabbing` cursor so the cube reads as draggable.
**Orient the model so FRONT is the face the user cares about.** The cube's labels are fixed to world axes, so how you place the model decides what "FRONT" shows. For a relief/panel/anything with a hero face, stand it so the hero face points world **+Z** (= FRONT) and image-up points **+Y** — don't lay it flat facing +Y, or FRONT shows a meaningless edge and TOP shows the hero (surprising and "wrong" to the user). Watch the displaced-axis sign too: Three's `PlaneGeometry` pushes `-y`, so vertex row 0 is **+Y (top)** — map image row 0 (top) to it with **no flip**, or your relief comes out upside-down. Verify by snapping FRONT and eyeballing against the source image; don't trust the index math.
**Build solids, not floating sheets.** A displaced `PlaneGeometry` is a single hollow surface — fine for a quick look, wrong the moment the user inspects it. In X-ray (or any side view) the raised bumps read as hollow domes floating above the base with a gap, and it's not watertight for STL/CAM. If the thing is a real object (relief, terrain block, carved panel), build a **solid heightfield**: displaced top surface + perimeter skirt walls + flat bottom, so it's rooted on its base. The user *will* notice "the back doesn't touch the backplate." Set the material `DoubleSide` so hand-wound walls never render black.
**Section / X-ray for hidden internal dimensions.** When a control sets something you can't see from outside — wall thickness, a backing/backplate, internal clearance, draft — add an **X-ray/section toggle** so the user can actually see what they're dialing. Cheapest version: ghost the outer shell (`transparent, opacity~0.15, depthWrite:false`) and render the measured solid (the backplate slab, the remaining wall) as an **opaque distinctly-colored mesh** with a bright edge line at the critical boundary; pair it with a side ortho snap so the dimension reads as a clean band. (A true clipping-plane section with caps is the fancier version; usually not worth the stencil work.) Don't make the user infer a hidden thickness from a number alone when one toggle can show it.
Keep it in world/view space aligned to how the model is *displayed* (account for any root rotation you applied). **Verify by visualization, not math** (these all bit me): click TOP then drag the *main viewport* and screenshot — confirm it still orbits (catches the `camera.up` trap); hover a corner and screenshot the cube — confirm the corner zone lights across faces, not the whole face; click a roll arrow from an iso view and screenshot — confirm it snaps to a face (not a diagonal roll); snap FRONT and confirm the hero face is upright. Genuinely-optional Fusion extras: the adjacent-face triangle arrows (drag-orbit covers them), the N/E/S/W compass ring, and the right-click "set current view as Home" menu — skip unless asked.
## Workflow
1. **Build the studio for the specific question.** Don't make it generic. If the user is choosing a hero crop, the studio shows the actual hero. If they're choosing a font, the studio is reading sample text.
2. **Serve locally.** Never hardcode a port — bind a static server to port 0 (the OS hands back a free port) for static HTML, or use the project's dev server for framework routes. Give the user the URL.
3. **Verify it works headlessly** before handing it over (headless Playwright). Don't ask the user to debug your scaffolding.
4. **User iterates.** They paste back a settings JSON, click a Download button, or say "go with N" / "use this".
5. **Bake.** Render the chosen state into committed assets / production code with reproducible math. Verify the baked result matches what they dialed in (a quick screenshot diff is fair).
6. **Tear down the scaffolding.** Delete the lookdev dir / dev route — it was decision-time scaffolding, not production code. Commit + deploy.
## Anti-patterns
- **Static N×M comparison grid** — limits the user to your guesses; takes longer than a switcher; doesn't give them the in-between point they actually wanted.
- **Numeric prompt before the slider** — "what saturation do you want?" is the wrong question; let them drag.
- **Numeric inputs for spatial decisions** — drag the element. Sliders for opacity, drag handles for position.
- **Drift between preview math and bake math** — when both JS preview and Python bake exist, port one to match the other and verify on a known input.
- **Building inside production routes** — keep scaffolding isolated and trivially deletable. Reach for `app/dev/...` then nuke it.
- **Skipping the WYSIWYG details** — preview without the real font / container / background lies to the user.
- **No structured way to read the state back out** — a studio with no copy-able settings blob forces the agent to bake from a screenshot and the user to narrate values by hand. Every control must round-trip through a machine-readable export (see Architecture #3).
- **Handing back a wall of prose for "review"** — pasting a long doc/blog into chat (or shipping the markdown file) and asking the user to react is NOT lookdev. For any document/copy/media review, build the **text & media annotation studio** (direct edit + highlight + comment + media-flag) so the user marks up the rendered artifact and the markup round-trips back as a patch. A boring text dump the user has to read and reply to in chat is the exact thing this skill exists to replace.
## Working example
A worked example — an image-treatment studio:
extracts a Lab-k-means dominant palette with coverage %, exposes sliders
(resolution, colorize, saturation, gap, glyph, contrast), per-band color
pickers, a luminance-vs-nearest mapping toggle, a Copy-settings-JSON
button, and a `--bake-json` Python path that renders the chosen state to
committed PNG/WebP with math identical to the JS preview. The preview
canvases match the production thumb and hero shapes exactly.
## Related (the studio / narrative family)
lookdev is one of two flagship narratives — **human-in-the-loop** (you, the human, judge and
tune). Its determinism-narrative sibling is deterministic-design (render → *measure* the
UI, numbers not vibes). The family it chains with:
- **deterministic-design** — the other flagship; measure/judge design output deterministically.
- **screenstudio-alternative** — human-in-the-loop video/demo polish studio (NLE timeline).
- **macos-screen-recorder** — capture a studio session or demo (display + system audio).
- **lookdev-auto** — the *automated* counterpart: a vision model judges instead of you.
The foil to lookdev's thesis — use when there's no human to sit the loop.
## Limitations
- Lookdev is useful only when the user can inspect or mark up rendered variants; it is overkill for small deterministic edits.
- A studio must faithfully mirror production fonts, media, containers, and constraints, otherwise the chosen settings can be misleading.
- Human preference remains the source of truth, so the workflow cannot guarantee a universally "best" design or media treatment.
@@ -0,0 +1,59 @@
---
name: macos-screen-recorder
description: "macOS screen recorder that captures the main display PLUS system audio via ScreenCaptureKit — no BlackHole/loopback driver, no sudo, just the standard Screen Recording permission. CLI-driven; fills the headless-screen-recording-with-system-sound gap QuickTime and `screencapture -v` can't."
risk: safe
source: community
source_type: community
source_repo: connerkward/macos-screen-recorder-system-audio
date_added: "2026-06-16"
author: Conner K Ward
license: MIT
tags:
- macos
- screen-recording
- system-audio
- screencapturekit
- cli
- swift
tools:
- claude-code
- antigravity
- cursor
- gemini-cli
- codex-cli
---
## When to Use
Use when you need to script a screen recording WITH system sound on macOS from the CLI (demos, captures, voice-demo recording) — the case QuickTime and `screencapture -v` can't cover without a virtual audio device.
_Source: [connerkward/macos-screen-recorder-system-audio](https://github.com/connerkward/macos-screen-recorder-system-audio) (MIT)._
# macos-screen-recorder (sck-record)
`sck-record.swift` → compiled `sck-record` (binary gitignored; built by `setup-machine`, or
`swiftc -O sck-record.swift -o sck-record`). Records the main display + system audio via
ScreenCaptureKit.
```
./sck-record <out.mp4> <seconds>
```
**The one true differentiator:** system audio from the CLI with **zero install** — no
BlackHole / loopback virtual device, no sudo; only the standard Screen Recording permission
(granted once to whatever app shells out). It is *not* a general "better than OBS/Screen
Studio" tool — it fills exactly the headless-CLI-with-system-audio gap.
`sck-record` is the raw capture primitive — it records, nothing more. To polish a
recording afterward (idle speed-up, auto-zoom, keystroke chips, smoothed cursor,
vertical export), pair it with
[screenstudio-alternative-skill](https://github.com/connerkward/screenstudio-alternative-skill):
record with `sck-record --no-cursor <out.mp4> <seconds>`, then run its post-production
pass on the resulting mp4. (Auto-zoom and keystroke overlays additionally need an
input-event log captured *during* recording, which that skill supplies; `sck-record`'s
pixels alone cover idle speed-up, cursor smoothing, and vertical export.)
## Limitations
- macOS only; it depends on ScreenCaptureKit and the user's Screen Recording permission.
- The recorder captures raw display and system audio but does not provide editing, auto-zoom, captions, or social-format polish by itself.
- Input-event overlays require a separate event log captured during recording; pixels alone cannot reconstruct keystrokes or precise click metadata.
@@ -0,0 +1,116 @@
---
name: pr-merge-champion
description: "Optimize pull requests for quick approval and merging by ensuring clean diffs, comprehensive self-reviews, and structured documentation."
category: workflow
risk: safe
source: self
source_type: self
date_added: "2026-06-16"
author: himanshu-2l
tags: [git, github, pull-request, code-review, workflow]
tools: [claude, cursor, gemini, antigravity]
---
# PR Merge Champion
## Overview
A systematic playbook for preparing, reviewing, and documenting pull requests to ensure they are high-quality, free of common oversights, and optimized for instant maintainer approval and merging.
## When to Use This Skill
- Use when preparing to open a new pull request on GitHub or any Git hosting platform.
- Use when self-auditing a feature or bug-fix branch for code cleanliness and consistency.
- Use when trying to minimize review cycles and speed up the integration of your changes.
## How It Works
### Step 1: Pre-Flight Clean Up & Rebase
Before presenting your code to reviewers, clean up any workspace noise and ensure your branch is up to date:
1. Rebase your feature branch on top of the latest target branch (e.g., `main` or `master`) to resolve conflicts early.
2. Clean up untracked, temp, or swap files from your repository.
3. Run local linters, formatters, and compilers to ensure no stylistic or syntax errors exist.
### Step 2: Critical Self-Review
Review your own diff line-by-line as if you were the reviewer. Look out for:
1. Leftover debugging statements (e.g., `console.log`, `print`, breakpoints, or custom debug flags).
2. Unnecessary changes, white-space only diffs, or commented-out code blocks.
3. Incomplete `TODO` comments that should be resolved or turned into tracked issues.
4. Correctness of error handling and edge cases.
### Step 3: Local Verification & Test Suite
Verify that all changes work as expected:
1. Run the project's automated test suite locally to verify no regressions are introduced.
2. Check test coverage for any new code blocks you added.
3. Manually test the critical paths and edge cases of your feature or bug fix.
### Step 4: Crafting the Pull Request Description
Write a high-signal, structured PR description. A great description tells the story of the changes:
1. **Summary**: A concise explanation of the changes.
2. **Context / Why**: Why this change is necessary and what problem it solves.
3. **Verification**: Explicit details on how you tested it (test commands, screenshots, or step-by-step reproduction).
4. **Checklist**: Conform to the repository's contributing guidelines and checklist requirements.
## Examples
### Example 1: Creating a Clean PR Description
```markdown
# Pull Request: Implement Rate Limiting on Authentication Endpoint
## Summary
Introduces an IP-based rate limiter on the `/api/v1/auth/login` endpoint using Redis to prevent brute-force attacks.
## Why
We identified a high volume of login attempts targeting single accounts. This rate limiting window slows down attackers while keeping the system responsive for genuine users.
## Verification
- Ran unit tests: `npm run test tests/auth.test.js` (all green)
- Manually verified using Postman: sending 15 requests in under 60 seconds returns `429 Too Many Requests`.
## Checklist
- [x] Code follows the style guide
- [x] Unit tests added/updated
- [x] Documentation updated
```
### Example 2: Self-Review Clean Up Commands
Before committing, run these commands to inspect the diff for accidental additions:
```bash
# Check the names of files changed to ensure no unwanted files are staged
git status --porcelain
# Review the actual diff for any leftover print statements or debuggers
git diff | grep -E "(console\.log|debugger|print\(|var_dump|binding\.pry)"
```
## Best Practices
-**Keep PRs Small and Focused**: A PR with fewer than 200 lines of changes gets reviewed and merged significantly faster than a large one.
-**Perform a Self-Review first**: Finding your own bugs and formatting issues first builds trust with the maintainers.
-**Respect Repository Guidelines**: Check the project's `CONTRIBUTING.md` and pull request templates, and adhere to them strictly.
-**Do Not Bundle Unrelated Changes**: Avoid sneaking refactoring or unrelated bug fixes into a feature PR. Create separate PRs instead.
-**Do Not Ignore CI Failures**: Always fix failing tests, linters, or security scans on your branch before requesting a review.
## Limitations
- This skill does not replace project-specific CI/CD validation, automated testing, or domain-expert reviews.
- It assumes a standard Git and GitHub-like environment, though the core principles apply to GitLab, Bitbucket, and other platforms.
## Common Pitfalls
- **Problem:** A PR is left open for a long time due to minor formatting or style comments.
**Solution:** Always run the repository's local formatter (e.g., Prettier, ESLint, Black) before committing.
- **Problem:** Merge conflicts occur immediately after opening the PR.
**Solution:** Pull the latest main branch and rebase or merge it into your branch daily.
## Related Skills
- `@pr-writer` - For Sentry-specific PR writing guidelines.
- `@clean-code` - To ensure code quality before submitting.
@@ -113,6 +113,17 @@ A high score indicates _structural suitability_, not guaranteed rankings.
---
### Scoring Guidance per Category
For each of the six scoring categories, allot points within the category's weight band using these anchors:
- **015% of band:** No alignment — the site/topic clearly does not meet the criterion (e.g. fewer than 10 candidate entities for a directory-style PSEO).
- **1640% of band:** Partial alignment — the criterion is partially met, OR met for a small subset of pages only.
- **4180% of band:** Strong alignment — the criterion holds for most of the planned page set.
- **81100% of band:** Exemplary — the criterion holds universally and is reinforced by a structural data source (DB, API, validated CSV).
Sum the per-category scores to compute the Feasibility Index used in §"Feasibility Bands" below.
### Feasibility Bands (Required)
| Score | Verdict | Interpretation |
@@ -110,6 +110,17 @@ This is a **diagnostic score**, not a promise of rich results.
---
### Scoring Guidance per Category
For each of the six scoring categories, allot points within the category's weight band using these anchors:
- **015% of band:** Schema describes none of the visible content (e.g. you would mark `description` for a `Recipe` page that has no recipe markup yet).
- **1640% of band:** Partial alignment — the schema describes some but not all of the visible content, OR maps to a less-common schema.org type.
- **4180% of band:** Strong alignment — the schema describes the bulk of the visible content with a common schema.org type.
- **81100% of band:** Exemplary — the schema covers all visible content, uses a Google-supported rich-result type, and includes all required properties.
Sum the per-category scores to compute the Eligibility Index used in §"Eligibility Bands" below.
### Eligibility Bands (Required)
| Score | Verdict | Interpretation |
@@ -0,0 +1,91 @@
---
name: screenstudio-alt
description: "Open-source headless Screen Studio alternative: auto speed-up of idle, auto-zoom on click clusters, keystroke overlay chips, smoothed synthetic cursor, and 9:16 vertical export that follows the action — post-production for screen recordings from the CLI."
risk: safe
source: community
source_type: community
source_repo: connerkward/screenstudio-alternative-skill
date_added: "2026-06-16"
author: Conner K Ward
license: MIT
tags:
- screen-recording
- video
- post-production
- auto-zoom
- vertical-video
- ffmpeg
tools:
- claude-code
- antigravity
- cursor
- gemini-cli
- codex-cli
---
## When to Use
Use when polishing a screen recording / demo video for sharing, when the user mentions Screen Studio, auto-zoom, idle speed-up, or vertical/social video from a screen capture, and for any social-facing demo (vertical output is the default for those).
_Source: [connerkward/screenstudio-alternative-skill](https://github.com/connerkward/screenstudio-alternative-skill) (MIT)._
# screenstudio-alt
The skill's code lives in this directory (`polish.py`, `render.py`, `studio.py`,
`events-log.swift`, test fixtures, etc.). Published publicly as
`connerkward/screen-studio-alternative` via the publish-skill skill.
Two components:
- `events-log` (Swift) — capture-side input logger (cursor 60Hz, clicks, keys;
drops keys during macOS secure input). Runs ONLY while recording. Needs
Accessibility/Input Monitoring for the terminal. **Auto-zoom/keys/cursor need
this data at capture time — it cannot be recovered from pixels later.**
- `polish.py` (Python, ffmpeg + PIL) — the post-production pass:
```bash
python3 src/polish.py in.mp4 --events in.events.jsonl \
--speedup # compress idle (input-gap ∩ frozen-pixels; animations stay 1x)
--zoom # eased auto-zoom on click clusters (zoompan)
--keys # accumulating keystroke chips (PIL overlays, no drawtext dep)
--smooth-cursor # synthetic eased cursor (best with sck-record --no-cursor)
--vertical # ALSO emit 1080x1920 following the action
```
`--speedup` works WITHOUT events (freezedetect only) — usable on the whole
existing dailies corpus.
- `render.py`**high-quality non-destructive renderer** (preferred): single-pass
spring-physics camera over the original high-res frames, LANCZOS into a smaller
target (crisp zoom, ~1.3× sharper than the ffmpeg upscale path), 60fps, H + 9:16 V.
Tunable `--freq`/`--zeta` (spring), `--fps`, `--target-w`. Takes explicit
`--regions [{t0,t1,z,cx,cy}]`. `polish.py` is the older ffmpeg-filter fallback.
- `studio.py [recording.mp4]` — local web UI, **NLE-style fixed-ruler timeline** (bar =
source duration, never rescales → upstream always planted): zoom regions are draggable
blocks (move / retime edges / click to add / double-click delete); idle spans are
**speed blocks with rate-only editing** — source range locked, rate set via inspector
slider on select or right-edge **rate-stretch** drag (FCP retime / Premiere Rate
Stretch); rate changes ripple downstream only. Tunable cosine-ease ramp, default zoom,
aspect, frame styling. Always-smooth synthetic cursor + click ripple + real recorded
click sound (CC0 #735771). Export uses render.py. Free port, local. (Keystroke overlay
exists in the engine but is off by default.)
## Easy path
`screencast.sh --demo` (screencast skill) does the whole chain: starts the event
logger, records, then polishes + emits the 9:16 vertical automatically. Vertical
is the DEFAULT for social-facing demos.
## Gotchas (learned the hard way, kept here so they're not relearned)
- ffmpeg CANNOT do animated `scale=eval=frame``crop` (link reinit wedges crop's
per-frame exprs). That's why zoom uses `zoompan` (no `t` var there — use `on/FPS`).
- This machine's ffmpeg lacks `drawtext`; all text/cursor overlays are PIL-rendered
PNGs + `overlay`.
- Test rig: `make-fixture.py` synthesizes a fake screen recording + ground-truth
events.jsonl — validate any change against it before trusting real footage.
## Limitations
- The workflow assumes FFmpeg plus the companion scripts are available locally; it is not a hosted video editor.
- Polished cursor, click, and keystroke effects depend on event logs captured during recording; missing logs limit what can be reconstructed.
- Auto-zoom and idle speed-up still need human review for pacing, framing, and platform-specific taste.
@@ -0,0 +1,209 @@
---
name: super-code
description: "Standing house style to enforce dense, correct, and idiomatic code on all coding tasks. Minimizes code bloat and agent operation overhead."
risk: safe
source: community
date_added: "2026-06-16"
---
# Super Code Skill
## Overview
Produce code that is short, correct, idiomatic, and maintainable — in that priority order.
This skill addresses two distinct inefficiency types that must be fixed independently:
1. **Code-token inefficiency** — the artifact itself is bloated (unnecessary lines, boilerplate, over-abstraction)
2. **Generation-token inefficiency** — how the agent operates during a session (full-file rewrites, unrequested files, prose padding before/after changes)
Both matter. Fixing only one is not enough.
## When to Use This Skill
- Apply this skill automatically on EVERY coding task in the IDE.
- Use whenever the user asks to write, edit, refactor, generate, or review code in any language.
- This is a standing house style, not an on-demand pass — always apply it.
---
## Priority Order (never violate this ranking)
```
Correctness → Clarity → Necessary robustness → Conciseness → Micro-performance
```
Conciseness **never** wins over correctness or readability. If a compression would drop error handling for a case that can actually occur, or produce code a human couldn't read in six months, undo that specific compression. Short bad code is worse than long correct code.
---
## Workflow — apply to every coding task
### Step 1: Commit to minimal correct shape BEFORE writing
Before touching a file, decide:
- What is the smallest surface area that correctly solves this problem?
- What does the caller actually need from this function/class/module?
- Is there a stdlib/framework primitive that already does this?
Write that down mentally (not in a prose block to the user). This is the target shape.
### Step 2: Write using language-idiomatic patterns
Read the relevant reference file for the language in use:
- Bash/Shell → `bash/SKILL.md`
- C → `c/SKILL.md`
- C++ → `cpp/SKILL.md`
- C# → `csharp/SKILL.md`
- Dart/Flutter → `dart/SKILL.md`
- Elixir/Erlang → `elixir/SKILL.md`
- Go → `go/SKILL.md`
- Java → `java/SKILL.md`
- Kotlin/Compose → `kotlin/SKILL.md`
- PHP → `php/SKILL.md`
- Python → `python/SKILL.md`
- Ruby → `ruby/SKILL.md`
- Rust → `rust/SKILL.md`
- Scala → `scala/SKILL.md`
- Swift → `swift/SKILL.md`
- TypeScript/JavaScript → `typescript/SKILL.md`
Apply idiomatic patterns from that file. They replace verbose imperative code with correct, concise equivalents that are still readable.
### Step 3: Compression pass on your own draft
Before presenting any code, scan it for:
| Anti-pattern | Fix |
|---|---|
| Comment restates what code does | Delete comment, or rewrite to say *why* |
| Single-use helper function/class | Inline it |
| Stdlib/framework already does this | Replace with the primitive |
| Defensive handling for impossible case | Remove |
| Verbose loop replaceable by idiomatic expression | Replace |
| Logging/print nobody asked for | Remove |
| Extra config/files/parameters not requested | Remove |
| Unused import or variable | Remove |
### Step 4: Guardrail check — run this before presenting
Ask yourself (silently):
- [ ] Did I remove handling for a case that **can** actually happen?
- [ ] Did I make this harder to read for a human six months from now?
- [ ] Did I drop correctness or security to save lines?
If yes to any: undo that specific compression and keep the rest.
### Step 5: Present output — generation-token rules
**Always:**
- Edit files via targeted patches/diffs, not full rewrites, unless the file is new or the change touches >70% of lines
- Present only what was asked for
**Never:**
- Generate unrequested files (tests, READMEs, configs, types) unless the user asked
- Add prose blocks before/after code explaining what you're about to do or just did — just do it
- Re-explain the user's own requirement back to them before writing
- Restate what changed in a paragraph after showing the diff — the diff is self-evident
---
## Universal Anti-Pattern Checklist
These apply in every language. The language-specific files extend this list.
### Comments
-`// Loop through the list and add each item` → ❌ delete
-`// Order matters: process refunds before charges` → ✅ keep (explains *why*)
- Rule: if the comment could be generated mechanically from reading the code, it adds no value
### Defensive coding
- Only handle error cases that can actually occur given the call site
- If the caller guarantees non-null, don't null-check inside the function
- If a catch block can only log and rethrow, consider removing the try/catch
### Abstractions
- Don't extract a function for logic used exactly once in one place
- Don't create a wrapper class around a primitive used only once
- Threshold: abstraction earns its place when it's used 2+ times OR when it has a meaningful name that genuinely clarifies domain logic
### Scaffolding
- No placeholder TODOs unless the user asked for a scaffold
- No `// TODO: add error handling` — either add it or don't
- No empty catch blocks "just in case"
- No parameters the current callers don't use
---
## Generation-Token Rules (agentic IDE specific)
These govern how you operate inside the session, not just what you produce:
### File edits
- Prefer surgical patches: show only the changed lines + minimal context
- Full file rewrite is acceptable only for: new files, files shorter than ~30 lines, or changes touching >70% of the file
- Never repeat unchanged portions of a file to "show the full context"
### Unrequested artifacts
- Do not create test files, README updates, type definition files, config files, or CI scripts unless explicitly requested
- If you believe a test file would be valuable, offer it in one sentence after the main output — don't generate it unasked
### Prose overhead
- No "Here's what I'm going to do:" preambles
- No "I've made the following changes:" postambles (the diff shows this)
- No "Let me know if you'd like me to..." closers
- One-line clarification is acceptable if a requirement is genuinely ambiguous; otherwise, make a reasonable choice and note the assumption in a comment inside the code if it matters
---
## Language Reference Files
| Language / Stack | File |
|---|---|
| Bash / Shell | `bash/SKILL.md` |
| C | `c/SKILL.md` |
| C++ | `cpp/SKILL.md` |
| C# / .NET | `csharp/SKILL.md` |
| Dart / Flutter | `dart/SKILL.md` |
| Elixir / Erlang | `elixir/SKILL.md` |
| Go | `go/SKILL.md` |
| Java | `java/SKILL.md` |
| Kotlin + Compose (Android) | `kotlin/SKILL.md` |
| PHP | `php/SKILL.md` |
| Python | `python/SKILL.md` |
| Ruby | `ruby/SKILL.md` |
| Rust | `rust/SKILL.md` |
| Scala | `scala/SKILL.md` |
| Swift (iOS/macOS) | `swift/SKILL.md` |
| TypeScript / JavaScript | `typescript/SKILL.md` |
Read the relevant file at Step 2. If the language isn't listed, apply the universal checklist above and use the language's own idioms for loops, error handling, and data transformation.
## Examples
### Example 1: Refactoring a verbose loop
```java
// Anti-pattern
List<String> names = new ArrayList<>();
for (User u : users) {
if (u.isActive()) {
names.add(u.getName());
}
}
// Super-code idiomatic (Java)
List<String> names = users.stream().filter(User::isActive).map(User::getName).toList();
```
## Troubleshooting
### Problem: Code is too dense to read
**Symptoms:** Reviewer complains or logic is unreadable.
**Solution:** Revert the overly compressed section. Clarity and correctness always win over conciseness.
## Related Skills
- `@karpathy-guidelines` - For behavioral guidelines on surgical changes and simplicity.
## Limitations
- **Language Support:** Language-specific idioms require reference files.
- **Readability Tradeoffs:** Extreme compression can sometimes harm readability if not careful.
@@ -0,0 +1,292 @@
---
name: bash
description: "Language-specific super-code guidelines for bash."
risk: safe
source: community
date_added: "2026-06-16"
---
# Bash / Shell: Idiomatic Efficiency Reference
## Table of Contents
1. [Quoting & Word Splitting](#quoting)
2. [Conditionals & Tests](#conditionals)
3. [Loops & Iteration](#loops)
4. [Pipes & Process Substitution](#pipes)
5. [Functions & Return Values](#functions)
6. [Error Handling](#errors)
7. [Anti-patterns specific to Bash](#antipatterns)
---
## 1. Quoting & Word Splitting {#quoting}
```bash
# ❌ Unquoted variable (word splitting + globbing)
for f in $files; do rm $f; done
# ✅
for f in "${files[@]}"; do rm -- "$f"; done
```
```bash
# ❌ Unquoted command substitution
path=$(find . -name config)
cat $path # breaks on spaces
# ✅
path="$(find . -name config)"
cat "$path"
```
```bash
# ❌ Using backticks for command substitution
result=`echo hello`
# ✅ — $() nests cleanly
result=$(echo hello)
```
```bash
# ❌ String comparison without quotes
if [ $var = "hello" ]; then # breaks if var is empty or has spaces
# ✅
if [[ "$var" = "hello" ]]; then
```
**Rule: double-quote every `$variable` and `$(command)` unless you specifically need splitting.**
---
## 2. Conditionals & Tests {#conditionals}
```bash
# ❌ Single bracket test (POSIX but fragile)
if [ -f "$file" -a -r "$file" ]; then
# ✅ — [[ is safer, supports &&/||, no word splitting inside
if [[ -f "$file" && -r "$file" ]]; then
```
```bash
# ❌ Testing command exit status with if [ $? -eq 0 ]
grep -q pattern file
if [ $? -eq 0 ]; then echo "found"; fi
# ✅ — test command directly
if grep -q pattern file; then echo "found"; fi
```
```bash
# ❌ String equality with == outside [[
if [ "$a" == "$b" ]; then # == not POSIX in [ ]
# ✅
if [[ "$a" == "$b" ]]; then # Bash
# or POSIX:
if [ "$a" = "$b" ]; then
```
```bash
# ❌ Arithmetic with [ ] and string comparison
if [ "$count" -gt 10 ]; then
# ✅ — (( )) for arithmetic
if (( count > 10 )); then
```
---
## 3. Loops & Iteration {#loops}
```bash
# ❌ Parsing ls output
for f in $(ls *.txt); do process "$f"; done
# ✅ — glob directly
for f in *.txt; do
[[ -e "$f" ]] || continue # handle no-match
process "$f"
done
```
```bash
# ❌ Reading file line-by-line with for
for line in $(cat file.txt); do # splits on words, not lines
# ✅
while IFS= read -r line; do
process "$line"
done < file.txt
```
```bash
# ❌ Seq for counting
for i in $(seq 1 10); do
# ✅ — brace expansion (Bash)
for i in {1..10}; do
# or C-style:
for (( i = 1; i <= 10; i++ )); do
```
```bash
# ❌ Processing command output line-by-line with pipe (subshell trap)
count=0
cat file.txt | while read -r line; do
(( count++ )) # count resets after loop — subshell
done
echo "$count" # always 0
# ✅ — redirect, not pipe
count=0
while IFS= read -r line; do
(( count++ ))
done < file.txt
echo "$count"
```
---
## 4. Pipes & Process Substitution {#pipes}
```bash
# ❌ Chained grep | grep for AND
grep "error" log.txt | grep "timeout"
# ✅ — single grep with pattern
grep -E "error.*timeout|timeout.*error" log.txt
# or awk for complex logic:
awk '/error/ && /timeout/' log.txt
```
```bash
# ❌ cat + pipe (UUOC — Useless Use of Cat)
cat file.txt | grep pattern
# ✅
grep pattern file.txt
```
```bash
# ❌ Temp file for diff between commands
cmd1 > /tmp/a.txt
cmd2 > /tmp/b.txt
diff /tmp/a.txt /tmp/b.txt
rm /tmp/a.txt /tmp/b.txt
# ✅ — process substitution
diff <(cmd1) <(cmd2)
```
```bash
# ❌ Ignoring pipe failures (only last command's exit code)
false | true
echo $? # 0 — false's failure hidden
# ✅
set -o pipefail
false | true
echo $? # 1
```
---
## 5. Functions & Return Values {#functions}
```bash
# ❌ Using return for string values
get_name() {
return "Alice" # return is for exit codes (0-255)
}
# ✅ — echo + capture
get_name() {
echo "Alice"
}
name=$(get_name)
```
```bash
# ❌ Global variables modified inside functions
result=""
compute() { result="done"; }
# ✅ — use local, return via stdout
compute() {
local tmp
tmp=$(do_work)
echo "$tmp"
}
result=$(compute)
```
```bash
# ❌ Function keyword (not POSIX)
function my_func {
# ✅
my_func() {
```
---
## 6. Error Handling {#errors}
```bash
# ❌ No error handling — script continues after failure
cd /some/dir
rm -rf * # if cd fails, deletes from wrong directory
# ✅
set -euo pipefail
cd /some/dir || { echo "cd failed" >&2; exit 1; }
rm -rf ./*
```
```bash
# ❌ No cleanup on exit
tmpfile=$(mktemp)
# ... script might exit early, leaving tmpfile
# ✅ — trap for cleanup
tmpfile=$(mktemp)
trap 'rm -f "$tmpfile"' EXIT
```
```bash
# ❌ Silencing errors blindly
command 2>/dev/null
# ✅ — redirect only when you know what you're suppressing
command 2>/dev/null || true # explicit: we expect and accept failure
```
**Start every script with `set -euo pipefail`. Remove selectively where needed.**
---
## 7. Anti-patterns specific to Bash {#antipatterns}
| Anti-pattern | Preferred |
|---|---|
| Parsing `ls` output | glob: `for f in *.txt` |
| `cat file \| grep` | `grep pattern file` |
| Unquoted `$var` | `"$var"` always |
| `[ ]` for complex tests | `[[ ]]` |
| Backtick substitution | `$(command)` |
| `$?` check after command | `if command; then` |
| `echo` for debug | `printf '%s\n'` (portable) |
| No `set -euo pipefail` | always set at script top |
| Temp files without cleanup | `trap 'rm -f "$tmp"' EXIT` |
| `eval` with user input | avoid; use arrays for dynamic commands |
| `#!/bin/sh` with Bash features | `#!/usr/bin/env bash` |
| String math `expr 1 + 1` | `$(( 1 + 1 ))` |
| `test -z` for number comparison | `(( ))` for arithmetic |
## Limitations
- These are language-specific guidelines and do not cover overall architectural decisions.
- Over-compression might reduce readability; apply judgement.
@@ -0,0 +1,263 @@
---
name: c
description: "Language-specific super-code guidelines for c."
risk: safe
source: community
date_added: "2026-06-16"
---
# C: Idiomatic Efficiency Reference
## Table of Contents
1. [Memory Management](#memory)
2. [Pointers & Arrays](#pointers)
3. [Error Handling](#errors)
4. [Strings](#strings)
5. [Structs & Enums](#structs)
6. [Preprocessor & Headers](#preprocessor)
7. [Anti-patterns specific to C](#antipatterns)
---
## 1. Memory Management {#memory}
```c
// ❌ malloc without checking return value
char *buf = malloc(size);
strcpy(buf, src);
// ✅
char *buf = malloc(size);
if (!buf) return -ENOMEM;
memcpy(buf, src, size);
```
```c
// ❌ Casting malloc result (unnecessary in C, hides missing #include)
int *p = (int *)malloc(n * sizeof(int));
// ✅
int *p = malloc(n * sizeof *p);
```
```c
// ❌ free without nulling (dangling pointer risk in long-lived scope)
free(ptr);
// ... later code might use ptr
// ✅
free(ptr);
ptr = NULL;
```
```c
// ❌ Forgetting to free on early-return paths
char *a = malloc(100);
char *b = malloc(200);
if (!b) return -1; // leaks a
// ✅ — single cleanup label
char *a = NULL, *b = NULL;
a = malloc(100);
if (!a) goto cleanup;
b = malloc(200);
if (!b) goto cleanup;
// ... use a, b ...
cleanup:
free(b);
free(a);
```
**Use `sizeof *ptr` instead of `sizeof(Type)` — it stays correct when the type changes.**
---
## 2. Pointers & Arrays {#pointers}
```c
// ❌ Manual array size tracking
void process(int *arr, int len) { ... }
process(data, 10);
// ✅ — pass size alongside pointer, or use a struct
typedef struct { int *data; size_t len; } IntSlice;
```
```c
// ❌ Pointer arithmetic where array indexing is clearer
*(arr + i) = value;
// ✅
arr[i] = value;
```
```c
// ❌ VLA in production code (stack overflow risk, optional in C11+)
int arr[n];
// ✅
int *arr = malloc(n * sizeof *arr);
if (!arr) return -ENOMEM;
// ... use arr ...
free(arr);
```
---
## 3. Error Handling {#errors}
```c
// ❌ Using magic numbers for error returns
if (do_thing() == -1) { ... }
// ✅ — define or use named error codes
#include <errno.h>
if (do_thing() < 0) {
perror("do_thing");
return errno;
}
```
```c
// ❌ Deeply nested error checks
int r1 = step1();
if (r1 == 0) {
int r2 = step2();
if (r2 == 0) {
int r3 = step3();
// ...
}
}
// ✅ — early return / goto cleanup
if (step1() < 0) goto fail;
if (step2() < 0) goto fail;
if (step3() < 0) goto fail;
return 0;
fail:
cleanup();
return -1;
```
**`goto cleanup` is idiomatic C for resource teardown — don't avoid it out of principle.**
---
## 4. Strings {#strings}
```c
// ❌ strcpy without bounds checking
strcpy(dest, src);
// ✅
strncpy(dest, src, sizeof(dest) - 1);
dest[sizeof(dest) - 1] = '\0';
// or better: snprintf(dest, sizeof(dest), "%s", src);
```
```c
// ❌ strcmp misuse
if (str == "hello") { ... } // compares pointers, not content
// ✅
if (strcmp(str, "hello") == 0) { ... }
```
```c
// ❌ Building strings with repeated strcat (O(n²))
char result[1024] = "";
for (int i = 0; i < n; i++) {
strcat(result, items[i]);
}
// ✅ — track write position
char result[1024];
int pos = 0;
for (int i = 0; i < n && pos < (int)sizeof(result); i++) {
pos += snprintf(result + pos, sizeof(result) - pos, "%s", items[i]);
}
```
**Prefer `snprintf` over `sprintf` — always.**
---
## 5. Structs & Enums {#structs}
```c
// ❌ Bare struct requiring `struct` keyword everywhere
struct point { int x, y; };
struct point p = {1, 2};
// ✅
typedef struct { int x, y; } Point;
Point p = {1, 2};
```
```c
// ❌ Uninitialized struct
Point p;
use(p.x); // UB
// ✅
Point p = {0};
```
```c
// ❌ Magic integer constants
if (state == 3) { ... }
// ✅
typedef enum { STATE_IDLE, STATE_RUNNING, STATE_DONE } State;
if (state == STATE_DONE) { ... }
```
---
## 6. Preprocessor & Headers {#preprocessor}
```c
// ❌ Macro where inline function works (no type safety, double eval)
#define MAX(a, b) ((a) > (b) ? (a) : (b))
MAX(x++, y) // x incremented twice if x > y
// ✅
static inline int max_int(int a, int b) { return a > b ? a : b; }
```
```c
// ❌ No include guard
// my_header.h
struct Foo { int x; };
// ✅
#ifndef MY_HEADER_H
#define MY_HEADER_H
struct Foo { int x; };
#endif
// or: #pragma once (widely supported, not standard)
```
**Keep macros for conditional compilation and constants. Use `static inline` for logic.**
---
## 7. Anti-patterns specific to C {#antipatterns}
| Anti-pattern | Preferred |
|---|---|
| `sprintf` | `snprintf` with buffer size |
| `gets` | `fgets` (gets is removed in C11) |
| Casting `malloc` result | let implicit `void*` conversion work |
| `sizeof(Type)` in malloc | `sizeof *ptr` |
| VLA for large/runtime arrays | heap allocation |
| `void*` callbacks without context param | pass `void *ctx` alongside function pointer |
| Global mutable state | pass state through struct pointers |
| `assert` for runtime error handling | proper error return codes |
| Missing `const` on read-only pointer params | `const char *str` |
| Mixing signed/unsigned in comparisons | use consistent types, cast explicitly |
## Limitations
- These are language-specific guidelines and do not cover overall architectural decisions.
- Over-compression might reduce readability; apply judgement.
@@ -0,0 +1,271 @@
---
name: cpp
description: "Language-specific super-code guidelines for cpp."
risk: safe
source: community
date_added: "2026-06-16"
---
# C++: Idiomatic Efficiency Reference
## Table of Contents
1. [Memory & Ownership](#memory)
2. [Modern Types & Containers](#types)
3. [Move Semantics & References](#move)
4. [Templates & Concepts](#templates)
5. [Error Handling](#errors)
6. [Concurrency](#concurrency)
7. [Anti-patterns specific to C++](#antipatterns)
---
## 1. Memory & Ownership {#memory}
```cpp
// ❌ Raw new/delete
Widget* w = new Widget();
// ... 15 lines later ...
delete w;
// ✅
auto w = std::make_unique<Widget>();
```
```cpp
// ❌ Shared ownership when unique suffices
auto w = std::make_shared<Widget>();
transfer(w); // only one owner
// ✅ — unique_ptr; move when transferring
auto w = std::make_unique<Widget>();
transfer(std::move(w));
```
```cpp
// ❌ new[] for dynamic arrays
int* arr = new int[n];
// ... use ...
delete[] arr;
// ✅
std::vector<int> arr(n);
```
```cpp
// ❌ Manual RAII wrapper for file/mutex
FILE* f = fopen(path, "r");
// ... must remember fclose ...
// ✅
std::ifstream f(path);
// closes automatically at scope exit
// For non-standard resources: use unique_ptr with custom deleter
auto f = std::unique_ptr<FILE, decltype(&fclose)>(fopen(path, "r"), fclose);
```
**Rule: if you type `new`, you almost certainly want `make_unique` or `make_shared`.**
---
## 2. Modern Types & Containers {#types}
```cpp
// ❌ C-style string manipulation
char buf[256];
sprintf(buf, "%s:%d", host, port);
// ✅
auto addr = std::format("{}:{}", host, port); // C++20
// or: auto addr = host + ":" + std::to_string(port);
```
```cpp
// ❌ out-parameter for multiple returns
void compute(int input, int& result, std::string& error);
// ✅
struct ComputeResult { int value; std::string error; };
ComputeResult compute(int input);
// or: std::pair / std::tuple with structured bindings
auto [value, error] = compute(input);
```
```cpp
// ❌ Manual loop to find element
int idx = -1;
for (int i = 0; i < vec.size(); i++) {
if (vec[i] == target) { idx = i; break; }
}
// ✅
auto it = std::ranges::find(vec, target); // C++20
// or: std::find(vec.begin(), vec.end(), target);
```
```cpp
// ❌ Checking .find() != .end() then accessing
auto it = map.find(key);
if (it != map.end()) { use(it->second); }
// ✅ (C++20)
if (map.contains(key)) { use(map[key]); }
// or keep iterator version when you need the value without double lookup
```
**Use `std::string_view` for function parameters that don't need ownership.**
---
## 3. Move Semantics & References {#move}
```cpp
// ❌ Copying a large container into a function
void process(std::vector<Data> items) { ... } // copies on call
// ✅ — const ref for read, move for sink
void process(const std::vector<Data>& items) { ... } // read-only
void consume(std::vector<Data> items) { ... } // sink: caller moves in
```
```cpp
// ❌ std::move on const object (silently copies)
const std::string s = "hello";
take(std::move(s)); // still copies
// ✅ — don't const things you intend to move
std::string s = "hello";
take(std::move(s));
```
```cpp
// ❌ Returning std::move from local (prevents NRVO)
std::vector<int> build() {
std::vector<int> v;
// ... fill ...
return std::move(v); // pessimization
// ✅ — just return the local; compiler applies NRVO or implicit move
return v;
}
```
---
## 4. Templates & Concepts {#templates}
```cpp
// ❌ SFINAE soup
template<typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
T square(T x) { return x * x; }
// ✅ (C++20 concepts)
template<std::integral T>
T square(T x) { return x * x; }
```
```cpp
// ❌ Template for one type
template<typename T>
void log(T msg) { std::cout << msg; }
// Only ever called with std::string
// ✅ — don't templatize unless you need multiple types
void log(std::string_view msg) { std::cout << msg; }
```
**Concepts make template errors readable — prefer them over SFINAE and static_assert.**
---
## 5. Error Handling {#errors}
```cpp
// ❌ Error codes via int returns (C-style in C++)
int parse(const std::string& input, Data& out);
// ✅ — std::expected (C++23) or exceptions
std::expected<Data, ParseError> parse(const std::string& input);
// or throw for exceptional conditions
Data parse(const std::string& input); // throws ParseError
```
```cpp
// ❌ Catching by value (slices derived exceptions)
try { ... }
catch (std::exception e) { ... }
// ✅
catch (const std::exception& e) { ... }
```
```cpp
// ❌ Exception in destructor
~MyClass() {
if (cleanup() < 0) throw CleanupError(); // terminates
// ✅ — destructors must be noexcept; log/swallow errors
~MyClass() noexcept {
if (cleanup() < 0) log_error("cleanup failed");
}
```
---
## 6. Concurrency {#concurrency}
```cpp
// ❌ Manual thread + join tracking
std::thread t(work);
// ... must remember t.join() ...
// ✅ (C++20)
std::jthread t(work); // auto-joins on destruction
```
```cpp
// ❌ Lock/unlock manually
mtx.lock();
data.push_back(item);
mtx.unlock(); // missed on exception
// ✅
{
std::scoped_lock lock(mtx);
data.push_back(item);
}
```
```cpp
// ❌ Polling a shared bool for completion
while (!done.load()) { std::this_thread::sleep_for(10ms); }
// ✅ — use std::future or condition_variable
auto future = std::async(std::launch::async, compute);
auto result = future.get();
```
**Use `std::scoped_lock` over `lock_guard` — it handles multiple mutexes and avoids deadlock.**
---
## 7. Anti-patterns specific to C++ {#antipatterns}
| Anti-pattern | Preferred |
|---|---|
| Raw `new`/`delete` | `make_unique` / `make_shared` |
| `(Type)expr` C-style cast | `static_cast<Type>(expr)` |
| `#define` constants | `constexpr` variables |
| `NULL` | `nullptr` |
| `using namespace std;` in headers | explicit `std::` prefix |
| Manual loop for transform/filter | `std::ranges` or `<algorithm>` |
| `std::endl` | `'\n'` (endl flushes — slow) |
| `char*` for string parameters | `std::string_view` |
| Exception specification `throw()` | `noexcept` |
| Inheriting from `std::` containers | composition, not inheritance |
| `volatile` for thread synchronization | `std::atomic` |
| Header-only mega-templates | separate declaration/definition where compile time matters |
## Limitations
- These are language-specific guidelines and do not cover overall architectural decisions.
- Over-compression might reduce readability; apply judgement.
@@ -0,0 +1,276 @@
---
name: csharp
description: "Language-specific super-code guidelines for csharp."
risk: safe
source: community
date_added: "2026-06-16"
---
# C#: Idiomatic Efficiency Reference
## Table of Contents
1. [LINQ & Collections](#linq)
2. [Null Handling](#nulls)
3. [Async/Await](#async)
4. [Records & Pattern Matching](#records)
5. [Error Handling](#errors)
6. [Resource Management](#resources)
7. [Anti-patterns specific to C#](#antipatterns)
---
## 1. LINQ & Collections {#linq}
```csharp
// ❌ Imperative accumulation
var result = new List<string>();
foreach (var item in items) {
if (item.IsActive) result.Add(item.Name.ToUpper());
}
// ✅
var result = items
.Where(i => i.IsActive)
.Select(i => i.Name.ToUpper())
.ToList();
```
```csharp
// ❌ Manual grouping
var grouped = new Dictionary<string, List<Item>>();
foreach (var item in items) {
if (!grouped.ContainsKey(item.Category))
grouped[item.Category] = new List<Item>();
grouped[item.Category].Add(item);
}
// ✅
var grouped = items.GroupBy(i => i.Category)
.ToDictionary(g => g.Key, g => g.ToList());
```
```csharp
// ❌ Checking Any() then First()
if (items.Any(i => i.IsValid)) {
var first = items.First(i => i.IsValid);
}
// ✅
var first = items.FirstOrDefault(i => i.IsValid);
if (first is not null) { ... }
```
**Prefer method syntax for chains of 2+ operations. Query syntax is fine for complex joins.**
---
## 2. Null Handling {#nulls}
```csharp
// ❌ Nested null checks
string city = null;
if (user != null && user.Address != null) {
city = user.Address.City;
}
// ✅
var city = user?.Address?.City;
```
```csharp
// ❌ Ternary for null fallback
var name = user != null ? user.Name : "Unknown";
// ✅
var name = user?.Name ?? "Unknown";
```
```csharp
// ❌ Null check before event invocation
if (OnChanged != null) OnChanged(this, args);
// ✅
OnChanged?.Invoke(this, args);
```
```csharp
// ❌ Throwing ArgumentNullException manually
if (name == null) throw new ArgumentNullException(nameof(name));
// ✅ (C# 10+)
ArgumentNullException.ThrowIfNull(name);
```
**Enable nullable reference types (`<Nullable>enable</Nullable>`) project-wide.**
---
## 3. Async/Await {#async}
```csharp
// ❌ Blocking on async code
var result = GetDataAsync().Result; // deadlock risk
// ✅
var result = await GetDataAsync();
```
```csharp
// ❌ async void (exceptions are unobservable)
async void OnButtonClick() { await DoWork(); }
// ✅ — async Task; only async void for event handlers that truly require it
async Task OnButtonClick() { await DoWork(); }
```
```csharp
// ❌ Sequential awaits for independent work
var a = await FetchA();
var b = await FetchB();
// ✅
var (a, b) = (await Task.WhenAll(FetchA(), FetchB())) switch
{
var r => (r[0], r[1])
};
// or cleaner with ValueTuple:
var taskA = FetchA();
var taskB = FetchB();
var a = await taskA;
var b = await taskB;
```
```csharp
// ❌ Wrapping synchronous code in Task.Run inside a library
public Task<int> GetValue() => Task.Run(() => ComputeSync());
// ✅ — let the caller decide; expose sync method
public int GetValue() => ComputeSync();
```
**Add `ConfigureAwait(false)` in library code. Omit in app/UI code.**
---
## 4. Records & Pattern Matching {#records}
```csharp
// ❌ Manual equality, ToString, Deconstruct for data types
class Point {
public int X { get; init; }
public int Y { get; init; }
// + Equals, GetHashCode, ToString...
}
// ✅ (C# 9+)
record Point(int X, int Y);
```
```csharp
// ❌ if-else chain for type checking
if (shape is Circle) {
var c = (Circle)shape;
return c.Radius * c.Radius * Math.PI;
} else if (shape is Rectangle) { ... }
// ✅
return shape switch {
Circle { Radius: var r } => r * r * Math.PI,
Rectangle { Width: var w, Height: var h } => w * h,
_ => throw new ArgumentException($"Unknown shape: {shape}")
};
```
```csharp
// ❌ Range checking with &&
if (score >= 0 && score <= 100) { ... }
// ✅ (C# 9+)
if (score is >= 0 and <= 100) { ... }
```
---
## 5. Error Handling {#errors}
```csharp
// ❌ Catching Exception to log and swallow
try { Process(); }
catch (Exception ex) { logger.LogError(ex, "error"); }
// ✅ — catch specific, rethrow if you can't handle
try { Process(); }
catch (HttpRequestException ex) {
throw new ServiceException("upstream failure", ex);
}
```
```csharp
// ❌ throw ex (resets stack trace)
catch (Exception ex) { throw ex; }
// ✅
catch (Exception ex) { throw; } // preserves stack trace
// or wrap: throw new AppException("context", ex);
```
```csharp
// ❌ Exceptions for flow control
try { return dict[key]; }
catch (KeyNotFoundException) { return defaultValue; }
// ✅
return dict.TryGetValue(key, out var value) ? value : defaultValue;
```
---
## 6. Resource Management {#resources}
```csharp
// ❌ Manual Dispose
var conn = new SqlConnection(cs);
conn.Open();
// ... use conn ...
conn.Dispose(); // missed on exception
// ✅
using var conn = new SqlConnection(cs);
conn.Open();
// disposed at end of scope
```
```csharp
// ❌ Verbose using block
using (var reader = new StreamReader(path)) {
return reader.ReadToEnd();
}
// ✅ (C# 8+)
using var reader = new StreamReader(path);
return reader.ReadToEnd();
// or just: return File.ReadAllText(path);
```
---
## 7. Anti-patterns specific to C# {#antipatterns}
| Anti-pattern | Preferred |
|---|---|
| `string.Format("{0}", x)` | `$"{x}"` string interpolation |
| `List<T>` as public API return | `IReadOnlyList<T>` or `IEnumerable<T>` |
| `async void` | `async Task` |
| `.Result` / `.Wait()` on Task | `await` |
| `throw ex` | `throw` (preserves stack trace) |
| Manual `IEquatable` on data types | `record` |
| `object` parameters | generics with constraints |
| `DateTime.Now` | `DateTime.UtcNow` or `DateTimeOffset` |
| Mutable public fields | properties with `{ get; set; }` or `{ get; init; }` |
| `catch (Exception) { }` (swallow all) | catch specific exceptions, rethrow unknown |
| `IDisposable` without `using` | `using` declaration |
## Limitations
- These are language-specific guidelines and do not cover overall architectural decisions.
- Over-compression might reduce readability; apply judgement.
@@ -0,0 +1,327 @@
---
name: dart
description: "Language-specific super-code guidelines for dart."
risk: safe
source: community
date_added: "2026-06-16"
---
# Dart: Idiomatic Efficiency Reference
## Table of Contents
1. [Null Safety](#nulls)
2. [Collections & Iteration](#collections)
3. [Classes & Records](#classes)
4. [Async/Await & Streams](#async)
5. [Error Handling](#errors)
6. [Flutter-Specific Patterns](#flutter)
7. [Anti-patterns specific to Dart](#antipatterns)
---
## 1. Null Safety {#nulls}
```dart
// ❌ Manual null check
String display;
if (user.name != null) {
display = user.name!;
} else {
display = 'Unknown';
}
// ✅
final display = user.name ?? 'Unknown';
```
```dart
// ❌ Nested null checks
if (user != null && user.address != null && user.address!.city != null) {
print(user.address!.city!);
}
// ✅
final city = user?.address?.city;
if (city != null) print(city);
```
```dart
// ❌ Late field when nullable is correct
late String name; // crashes if accessed before assignment
// ✅ — use late only when you guarantee initialization before access
String? name; // honestly nullable
// late is fine for: late final _controller = TextEditingController();
```
```dart
// ❌ Bang operator (!) everywhere
final name = user.name!;
final city = user.address!.city!;
// ✅ — promote through null checks
final name = user.name;
if (name == null) return;
// name is now non-null (promoted)
```
---
## 2. Collections & Iteration {#collections}
```dart
// ❌ Imperative accumulation
final result = <String>[];
for (final item in items) {
if (item.isActive) result.add(item.name.toUpperCase());
}
// ✅
final result = items
.where((i) => i.isActive)
.map((i) => i.name.toUpperCase())
.toList();
```
```dart
// ❌ Manual map construction
final map = <String, List<Item>>{};
for (final item in items) {
map.putIfAbsent(item.category, () => []).add(item);
}
// ✅ (using collection-if/for in a different way — but groupBy isn't built-in)
// The loop above is actually idiomatic Dart. Use package:collection for groupBy:
import 'package:collection/collection.dart';
final map = groupBy(items, (Item i) => i.category);
```
```dart
// ❌ Building list with add() calls
final widgets = <Widget>[];
widgets.add(Header());
if (showSubtitle) widgets.add(Subtitle());
widgets.add(Body());
// ✅ — collection-if
final widgets = [
Header(),
if (showSubtitle) Subtitle(),
Body(),
];
```
```dart
// ❌ Spreading manually
final all = <int>[];
all.addAll(listA);
all.addAll(listB);
// ✅
final all = [...listA, ...listB];
```
---
## 3. Classes & Records {#classes}
```dart
// ❌ Manual data class boilerplate
class Point {
final int x, y;
const Point(this.x, this.y);
@override bool operator ==(Object other) => ...
@override int get hashCode => ...
@override String toString() => 'Point($x, $y)';
}
// ✅ (Dart 3.0+)
typedef Point = ({int x, int y});
// or for named class semantics:
class Point {
final int x, y;
const Point(this.x, this.y);
}
// Use package:equatable or Dart records for equality
```
```dart
// ❌ Verbose constructor
class User {
final String name;
final int age;
User(String name, int age) : name = name, age = age;
}
// ✅ — initializing formals
class User {
final String name;
final int age;
const User(this.name, this.age);
}
```
```dart
// ❌ Mutable fields on an immutable object
class Config {
String host;
int port;
Config(this.host, this.port);
}
// ✅
class Config {
final String host;
final int port;
const Config(this.host, this.port);
}
```
```dart
// ❌ Switch on type with if-else chain
if (shape is Circle) {
return (shape as Circle).radius * pi;
} else if (shape is Rectangle) { ... }
// ✅ (Dart 3.0+)
return switch (shape) {
Circle(:final radius) => radius * radius * pi,
Rectangle(:final width, :final height) => width * height,
};
```
---
## 4. Async/Await & Streams {#async}
```dart
// ❌ .then() chains
fetchUser()
.then((user) => fetchPosts(user))
.then((posts) => display(posts))
.catchError((e) => log(e));
// ✅
try {
final user = await fetchUser();
final posts = await fetchPosts(user);
display(posts);
} catch (e) {
log(e);
}
```
```dart
// ❌ Sequential await for independent work
final a = await fetchA();
final b = await fetchB();
// ✅
final results = await Future.wait([fetchA(), fetchB()]);
// or with typed destructuring:
final (a, b) = await (fetchA(), fetchB()).wait; // Dart 3.0+ record
```
```dart
// ❌ StreamBuilder doing too much in build
StreamBuilder(
stream: stream,
builder: (ctx, snap) {
if (snap.hasError) return Error();
if (!snap.hasData) return Loading();
final data = snap.data!;
// 50 lines of widget tree...
},
)
// ✅ — extract widget, or use listen + setState for simple cases
```
---
## 5. Error Handling {#errors}
```dart
// ❌ Catching Exception (too broad)
try { process(); }
on Exception catch (e) { print(e); }
// ✅ — catch specific types
try {
process();
} on FormatException catch (e) {
throw AppException('Invalid format', cause: e);
} on HttpException catch (e) {
throw AppException('Network error', cause: e);
}
```
```dart
// ❌ Returning null for errors
Future<User?> fetchUser() async {
try { return await api.getUser(); }
catch (_) { return null; } // caller doesn't know why
}
// ✅ — let exceptions propagate, or use sealed Result type
sealed class Result<T> {}
class Success<T> extends Result<T> { final T value; Success(this.value); }
class Failure<T> extends Result<T> { final Object error; Failure(this.error); }
```
---
## 6. Flutter-Specific Patterns {#flutter}
```dart
// ❌ Rebuilding entire tree on state change
setState(() {
// changes a single value, but the build() method builds 200 widgets
});
// ✅ — extract subtrees into separate widgets or use ValueListenableBuilder
ValueListenableBuilder<int>(
valueListenable: counter,
builder: (_, value, __) => Text('$value'),
)
```
```dart
// ❌ const-able widget without const
Container(color: Colors.blue)
// ✅
const ColoredBox(color: Colors.blue)
// Mark constructors const when possible; use `const` keyword at call site
```
```dart
// ❌ Navigator.push with MaterialPageRoute everywhere
Navigator.push(context, MaterialPageRoute(builder: (_) => DetailPage()));
// ✅ — named routes or GoRouter
context.go('/detail');
```
---
## 7. Anti-patterns specific to Dart {#antipatterns}
| Anti-pattern | Preferred |
|---|---|
| `!` (bang) operator liberally | null checks and promotion |
| `dynamic` everywhere | proper types |
| `as` cast without check | pattern matching or `is` check |
| Mutable fields on value objects | `final` fields |
| `print()` for logging | `package:logging` or structured logger |
| Manual `==`/`hashCode` | records, equatable, or code generation |
| `setState` for complex state | Riverpod / Bloc / Provider |
| Deep widget nesting | extract widgets into classes |
| String-based routing | typed routing (GoRouter) |
| `late` as escape hatch | nullable types or proper initialization |
| `Future.delayed` for debounce | `Timer` or proper debounce utility |
## Limitations
- These are language-specific guidelines and do not cover overall architectural decisions.
- Over-compression might reduce readability; apply judgement.
@@ -0,0 +1,366 @@
---
name: elixir
description: "Language-specific super-code guidelines for elixir."
risk: safe
source: community
date_added: "2026-06-16"
---
# Elixir / Erlang: Idiomatic Efficiency Reference
## Table of Contents
1. [Pattern Matching & Guards](#patterns)
2. [Pipe Operator & Transforms](#pipes)
3. [Processes & OTP](#otp)
4. [Error Handling](#errors)
5. [Collections & Enum](#collections)
6. [Structs & Protocols](#structs)
7. [Anti-patterns specific to Elixir/Erlang](#antipatterns)
---
## 1. Pattern Matching & Guards {#patterns}
```elixir
# ❌ Extracting with Map.get then checking
value = Map.get(map, :key)
if value != nil do
process(value)
end
# ✅ — pattern match directly
case map do
%{key: value} -> process(value)
_ -> :noop
end
# or with if:
if value = map[:key], do: process(value)
```
```elixir
# ❌ Nested case for multiple conditions
case fetch_user(id) do
{:ok, user} ->
case validate(user) do
{:ok, valid_user} -> save(valid_user)
{:error, reason} -> {:error, reason}
end
{:error, reason} -> {:error, reason}
end
# ✅ — with clause
with {:ok, user} <- fetch_user(id),
{:ok, valid_user} <- validate(user) do
save(valid_user)
end
```
```elixir
# ❌ if/else for known shapes
def area(shape) do
if shape.type == :circle do
:math.pi() * shape.radius * shape.radius
else
shape.width * shape.height
end
end
# ✅ — multi-clause function with pattern match
def area(%{type: :circle, radius: r}), do: :math.pi() * r * r
def area(%{type: :rect, width: w, height: h}), do: w * h
```
```elixir
# ❌ Checking type at runtime
def process(x) do
if is_integer(x) and x > 0 do
x * 2
end
end
# ✅ — guard clause
def process(x) when is_integer(x) and x > 0, do: x * 2
def process(_), do: {:error, :invalid_input}
```
---
## 2. Pipe Operator & Transforms {#pipes}
```elixir
# ❌ Nested function calls
String.trim(String.downcase(String.replace(input, ~r/\s+/, " ")))
# ✅
input
|> String.replace(~r/\s+/, " ")
|> String.downcase()
|> String.trim()
```
```elixir
# ❌ Pipe into anonymous function awkwardly
data
|> (fn x -> x * 2 end).()
# ✅ — use then/1 or named function
data
|> then(&(&1 * 2))
# or better: extract a named function
data |> double()
```
```elixir
# ❌ Single-step pipe (no gain in readability)
result = list |> Enum.count()
# ✅ — direct call for single operation
result = Enum.count(list)
```
**Pipe when 2+ transforms. Direct call for single operation. First arg flows through pipe.**
---
## 3. Processes & OTP {#otp}
```elixir
# ❌ Raw spawn for stateful process
pid = spawn(fn -> loop(%{count: 0}) end)
send(pid, {:increment})
# ✅ — GenServer for stateful processes
defmodule Counter do
use GenServer
def start_link(init \\ 0), do: GenServer.start_link(__MODULE__, init)
def increment(pid), do: GenServer.call(pid, :increment)
@impl true
def init(count), do: {:ok, count}
@impl true
def handle_call(:increment, _from, count), do: {:reply, count + 1, count + 1}
end
```
```elixir
# ❌ Spawning without linking (orphan process on crash)
spawn(fn -> do_work() end)
# ✅ — Task for fire-and-forget with supervision
Task.start(fn -> do_work() end)
# or for awaitable result:
task = Task.async(fn -> do_work() end)
result = Task.await(task)
```
```elixir
# ❌ Manual process registry
Process.register(self(), :my_worker)
# ✅ — use Registry or named GenServer
{:ok, _} = Registry.start_link(keys: :unique, name: MyRegistry)
GenServer.start_link(Worker, arg, name: {:via, Registry, {MyRegistry, :my_worker}})
```
```elixir
# ❌ try/catch in GenServer (breaks supervision)
def handle_call(:work, _from, state) do
try do
result = risky_operation()
{:reply, result, state}
catch
_ -> {:reply, :error, state}
end
end
# ✅ — let it crash; supervisor restarts
def handle_call(:work, _from, state) do
result = risky_operation()
{:reply, result, state}
end
```
**"Let it crash" — supervisors handle recovery. Don't defensively catch inside GenServers.**
---
## 4. Error Handling {#errors}
```elixir
# ❌ Raising for expected failures
def find_user(id) do
case Repo.get(User, id) do
nil -> raise "User not found"
user -> user
end
end
# ✅ — tagged tuples for expected outcomes
def find_user(id) do
case Repo.get(User, id) do
nil -> {:error, :not_found}
user -> {:ok, user}
end
end
```
```elixir
# ❌ Ignoring error tuple
{:ok, result} = might_fail() # crashes on {:error, _}
# ✅ — handle both cases
case might_fail() do
{:ok, result} -> process(result)
{:error, reason} -> Logger.error("Failed: #{inspect(reason)}")
end
```
```elixir
# ❌ String errors
{:error, "something went wrong"}
# ✅ — atom or struct errors (matchable, cheap)
{:error, :timeout}
{:error, %ValidationError{field: :email, reason: :invalid_format}}
```
```elixir
# ❌ Deep nesting of ok/error checks
case step1() do
{:ok, a} ->
case step2(a) do
{:ok, b} ->
case step3(b) do
{:ok, c} -> {:ok, c}
error -> error
end
error -> error
end
error -> error
end
# ✅
with {:ok, a} <- step1(),
{:ok, b} <- step2(a),
{:ok, c} <- step3(b) do
{:ok, c}
else
{:error, reason} -> {:error, reason}
end
```
---
## 5. Collections & Enum {#collections}
```elixir
# ❌ Multiple passes when one suffices
items
|> Enum.filter(&(&1.active))
|> Enum.map(&(&1.name))
# ✅ — for comprehension when filter + transform
for %{active: true, name: name} <- items, do: name
```
```elixir
# ❌ Enum.count for empty check (traverses whole list)
if Enum.count(list) == 0, do: :empty
# ✅
if Enum.empty?(list), do: :empty
# or pattern match:
case list do
[] -> :empty
_ -> :has_items
end
```
```elixir
# ❌ Building map with Enum.reduce when Map.new works
Enum.reduce(users, %{}, fn user, acc -> Map.put(acc, user.id, user) end)
# ✅
Map.new(users, &{&1.id, &1})
```
```elixir
# ❌ Enum on large dataset (eager — builds intermediate lists)
huge_list
|> Enum.map(&transform/1)
|> Enum.filter(&valid?/1)
|> Enum.take(10)
# ✅ — Stream for lazy evaluation
huge_list
|> Stream.map(&transform/1)
|> Stream.filter(&valid?/1)
|> Enum.take(10)
```
**Use `Stream` when chaining transforms on large/infinite collections. `Enum` for small or final step.**
---
## 6. Structs & Protocols {#structs}
```elixir
# ❌ Plain map for domain entities
user = %{name: "Alice", email: "a@b.com", age: 30}
# typo in key goes unnoticed: user.emaail
# ✅ — struct enforces keys
defmodule User do
@enforce_keys [:name, :email]
defstruct [:name, :email, age: 0]
end
user = %User{name: "Alice", email: "a@b.com"}
```
```elixir
# ❌ Protocol with only one implementation (over-abstraction)
defprotocol Renderable do
def render(data)
end
defimpl Renderable, for: HtmlPage do ... end
# ✅ — just a function until you need polymorphism
def render(%HtmlPage{} = page), do: ...
```
```elixir
# ❌ Updating nested struct manually
updated = %{user | address: %{user.address | city: "NYC"}}
# ✅
updated = put_in(user.address.city, "NYC")
# or Kernel.update_in/3 for transforms
```
---
## 7. Anti-patterns specific to Elixir/Erlang {#antipatterns}
| Anti-pattern | Preferred |
|---|---|
| `spawn` without link/monitor | `Task.start_link` or `GenServer` |
| `try/catch` inside GenServer | let it crash; supervisor restarts |
| String error reasons | atom or struct errors |
| `Enum.count(x) == 0` | `Enum.empty?(x)` or `match?([], x)` |
| Mutable-style accumulator | `Enum.reduce` / recursion |
| `if/else` chain on data shape | multi-clause function + pattern match |
| Nested `case` for ok/error | `with` expression |
| `IO.inspect` left in prod | `Logger` with levels |
| Single-step pipe | direct function call |
| `Enum` on huge/infinite data | `Stream` |
| Raw PID passing | named processes / Registry |
| Boolean returns for success/fail | `{:ok, val}` / `{:error, reason}` tuples |
| `length(list) > 0` (O(n)) | pattern match `[_ | _]` |
| Shared mutable state via ETS without wrapper | GenServer or Agent as access layer |
## Limitations
- These are language-specific guidelines and do not cover overall architectural decisions.
- Over-compression might reduce readability; apply judgement.
@@ -0,0 +1,234 @@
---
name: go
description: "Language-specific super-code guidelines for go."
risk: safe
source: community
date_added: "2026-06-16"
---
# Go: Idiomatic Efficiency Reference
## Table of Contents
1. [Error Handling](#errors)
2. [Slices & Maps](#slices)
3. [Goroutines & Channels](#concurrency)
4. [Structs & Interfaces](#structs)
5. [Functions & Closures](#functions)
6. [Anti-patterns specific to Go](#antipatterns)
---
## 1. Error Handling {#errors}
```go
// ❌ Ignoring errors
result, _ := os.Open(path)
// ✅ — always handle; only use _ when error is provably irrelevant
result, err := os.Open(path)
if err != nil {
return fmt.Errorf("open %s: %w", path, err)
}
```
```go
// ❌ Redundant error variable
err := doA()
if err != nil { return err }
err = doB()
if err != nil { return err }
// ✅ — each :=/: is fine; this is idiomatic Go. Don't try to "fix" it.
// What you CAN simplify: collapsing to one-liners where the if body is a single return
if err := doA(); err != nil { return err }
if err := doB(); err != nil { return err }
```
```go
// ❌ Custom error type with no added value
type MyError struct{ msg string }
func (e MyError) Error() string { return e.msg }
// ✅ — use errors.New or fmt.Errorf unless callers need to inspect type
var ErrNotFound = errors.New("not found")
return fmt.Errorf("lookup %q: %w", key, ErrNotFound)
```
**Wrap errors with `%w` (not `%v`) so callers can use `errors.Is` / `errors.As`.**
---
## 2. Slices & Maps {#slices}
```go
// ❌ Growing a slice without pre-allocation when size is known
var result []string
for _, item := range items {
result = append(result, item.Name)
}
// ✅
result := make([]string, 0, len(items))
for _, item := range items {
result = append(result, item.Name)
}
```
```go
// ❌ Manual existence check before map write
if _, ok := m[key]; !ok {
m[key] = []string{}
}
m[key] = append(m[key], value)
// ✅ — append to nil slice is valid Go
m[key] = append(m[key], value)
```
```go
// ❌ Copying a map by assignment (copies reference)
copy := original
// ✅
copy := make(map[K]V, len(original))
for k, v := range original { copy[k] = v }
```
---
## 3. Goroutines & Channels {#concurrency}
```go
// ❌ Fire-and-forget goroutine with no lifecycle
go doWork()
// ✅ — use errgroup or WaitGroup to track completion
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
doWork()
}()
wg.Wait()
```
```go
// ❌ Unbuffered channel causing unnecessary goroutine block
ch := make(chan Result)
go func() { ch <- compute() }()
result := <-ch
// ✅ — for single-result, buffered channel avoids goroutine leak if receiver exits early
ch := make(chan Result, 1)
go func() { ch <- compute() }()
result := <-ch
```
```go
// ❌ select with a busy-wait default
for {
select {
case v := <-ch:
process(v)
default:
// spin
}
}
// ✅ — blocking select unless you genuinely need non-blocking
for v := range ch {
process(v)
}
```
**Use `golang.org/x/sync/errgroup` for fan-out with error collection.**
---
## 4. Structs & Interfaces {#structs}
```go
// ❌ Large interface
type Storage interface {
Get(key string) ([]byte, error)
Set(key string, val []byte) error
Delete(key string) error
List(prefix string) ([]string, error)
// ... 10 more methods
}
// ✅ — small, composable interfaces
type Getter interface { Get(key string) ([]byte, error) }
type Setter interface { Set(key string, val []byte) error }
type Storage interface { Getter; Setter }
```
```go
// ❌ Returning concrete struct from constructor (ties callers to implementation)
func NewStore() *RedisStore { ... }
// ✅ — return interface when you have or anticipate multiple implementations
func NewStore() Storage { return &RedisStore{...} }
```
```go
// ❌ Pointer receiver for tiny value types
func (p *Point) X() float64 { return p.x }
// ✅ — value receiver for small immutable types
func (p Point) X() float64 { return p.x }
```
**Rule: pointer receiver when method mutates state OR struct is large (>3 fields of non-trivial size). Value receiver otherwise.**
---
## 5. Functions & Closures {#functions}
```go
// ❌ Named return values used just to avoid a variable declaration
func divide(a, b float64) (result float64, err error) {
result = a / b
return
}
// ✅ — named returns are worth it only for deferred mutation or documentation
func divide(a, b float64) (float64, error) {
if b == 0 { return 0, errors.New("division by zero") }
return a / b, nil
}
```
```go
// ❌ Closure capturing loop variable (classic Go bug, fixed in Go 1.22+)
// Pre-1.22: each goroutine captures the same i
for i := 0; i < n; i++ {
go func() { use(i) }()
}
// ✅ (Go <1.22 — pass as parameter)
for i := 0; i < n; i++ {
go func(i int) { use(i) }(i)
}
// Go 1.22+: loop variable scoped per iteration, so the original is safe
```
---
## 6. Anti-patterns specific to Go {#antipatterns}
| Anti-pattern | Preferred |
|---|---|
| `if err != nil { return err }` repeated 5+ times | acceptable — it's idiomatic Go |
| `panic` for expected errors | `return err` |
| `init()` with side effects | explicit initialization in `main` or constructors |
| `interface{}` / `any` without generics | use generics (Go 1.18+) or typed interfaces |
| Mutex field not adjacent to the data it protects | put `mu` directly above the field it guards |
| Channel of channels | usually a sign of over-engineering; redesign |
| `time.Sleep` in tests | use `testing` hooks or channels for synchronization |
| Exported types with unexported fields (when fields are the whole point) | `record`-style structs with all-exported fields |
| `log.Fatal` outside `main` | return errors up the stack |
## Limitations
- These are language-specific guidelines and do not cover overall architectural decisions.
- Over-compression might reduce readability; apply judgement.
@@ -0,0 +1,230 @@
---
name: java
description: "Language-specific super-code guidelines for java."
risk: safe
source: community
date_added: "2026-06-16"
---
# Java: Idiomatic Efficiency Reference
## Table of Contents
1. [Streams & Collections](#streams)
2. [Optional](#optional)
3. [Records & Data Classes](#records)
4. [Switch Expressions](#switch)
5. [Concurrency](#concurrency)
6. [Error Handling](#errors)
7. [Anti-patterns specific to Java](#antipatterns)
---
## 1. Streams & Collections {#streams}
```java
// ❌ Imperative accumulation
List<String> result = new ArrayList<>();
for (Item item : items) {
if (item.isActive()) result.add(item.getName().toUpperCase());
}
// ✅
List<String> result = items.stream()
.filter(Item::isActive)
.map(item -> item.getName().toUpperCase())
.toList(); // Java 16+; use .collect(Collectors.toList()) before
```
```java
// ❌ Manual grouping
Map<String, List<Item>> grouped = new HashMap<>();
for (Item item : items) {
grouped.computeIfAbsent(item.getCategory(), k -> new ArrayList<>()).add(item);
}
// ✅
Map<String, List<Item>> grouped = items.stream()
.collect(Collectors.groupingBy(Item::getCategory));
```
```java
// ❌ Manual sum
int total = 0;
for (Order o : orders) total += o.getAmount();
// ✅
int total = orders.stream().mapToInt(Order::getAmount).sum();
```
**Prefer method references (`Item::isActive`) over equivalent lambdas (`item -> item.isActive()`).**
---
## 2. Optional {#optional}
```java
// ❌ Null check chain
String city = null;
if (user != null && user.getAddress() != null) {
city = user.getAddress().getCity();
}
// ✅
String city = Optional.ofNullable(user)
.map(User::getAddress)
.map(Address::getCity)
.orElse(null);
```
```java
// ❌ Optional.get() without isPresent()
String name = optional.get(); // throws if empty
// ✅
String name = optional.orElse("default");
// or: optional.orElseThrow(() -> new IllegalStateException("name required"));
```
```java
// ❌ Optional as a field or parameter (anti-pattern)
class User { private Optional<String> nickname; }
// ✅ — Optional is for return types only
class User { private String nickname; } // nullable field
public Optional<String> getNickname() { return Optional.ofNullable(nickname); }
```
---
## 3. Records & Data Classes {#records}
```java
// ❌ Manual POJO
class Point {
private final int x, y;
public Point(int x, int y) { this.x = x; this.y = y; }
public int getX() { return x; }
public int getY() { return y; }
// + equals, hashCode, toString...
}
// ✅ (Java 16+)
record Point(int x, int y) {}
```
```java
// ❌ Builder pattern for a 2-field object
User user = new User.Builder().name("Alice").age(30).build();
// ✅ — use record or constructor directly for small objects
record User(String name, int age) {}
var user = new User("Alice", 30);
```
**Use `record` for any immutable data carrier. Keep builders only for objects with many optional fields.**
---
## 4. Switch Expressions {#switch}
```java
// ❌ Switch statement with fall-through and break
String label;
switch (status) {
case ACTIVE: label = "Active"; break;
case INACTIVE: label = "Inactive"; break;
default: label = "Unknown";
}
// ✅ (Java 14+)
String label = switch (status) {
case ACTIVE -> "Active";
case INACTIVE -> "Inactive";
default -> "Unknown";
};
```
```java
// ❌ instanceof + cast
if (shape instanceof Circle) {
Circle c = (Circle) shape;
return c.radius() * c.radius() * Math.PI;
}
// ✅ Pattern matching (Java 16+)
if (shape instanceof Circle c) {
return c.radius() * c.radius() * Math.PI;
}
```
---
## 5. Concurrency {#concurrency}
```java
// ❌ Raw Thread creation
Thread t = new Thread(() -> doWork());
t.start();
// ✅
ExecutorService exec = Executors.newVirtualThreadPerTaskExecutor(); // Java 21
exec.submit(() -> doWork());
```
```java
// ❌ synchronized on this for fine-grained state
synchronized(this) { counter++; }
// ✅
AtomicInteger counter = new AtomicInteger();
counter.incrementAndGet();
```
**Prefer `CompletableFuture.allOf()` over blocking `.get()` chains for parallel async work.**
---
## 6. Error Handling {#errors}
```java
// ❌ Catching Exception to log and swallow
try {
risky();
} catch (Exception e) {
log.error("error", e);
}
// ✅ — rethrow as unchecked if you can't handle it
try {
risky();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
```
```java
// ❌ Checked exceptions declared on every method
public void process() throws IOException, SQLException, ParseException { ... }
// ✅ — wrap at the boundary; internal methods throw unchecked
```
---
## 7. Anti-patterns specific to Java {#antipatterns}
| Anti-pattern | Preferred |
|---|---|
| `new ArrayList<String>()` (Java 7+) | `new ArrayList<>()` (diamond) |
| `"string".equals(variable)` (Yoda) | `Objects.equals(variable, "string")` |
| `for (int i = 0; i < list.size(); i++)` | enhanced for or stream |
| `StringBuffer` in single-threaded code | `StringBuilder` |
| `e.printStackTrace()` | `log.error("msg", e)` |
| `null` return for "not found" | `Optional<T>` return type |
| Public fields | private + accessor, or `record` |
| Mutable `static` fields | avoid; use dependency injection |
| `instanceof` + cast without pattern matching | pattern matching (Java 16+) |
## Limitations
- These are language-specific guidelines and do not cover overall architectural decisions.
- Over-compression might reduce readability; apply judgement.
@@ -0,0 +1,281 @@
---
name: kotlin
description: "Language-specific super-code guidelines for kotlin."
risk: safe
source: community
date_added: "2026-06-16"
---
# Kotlin + Compose: Idiomatic Efficiency Reference
## Table of Contents
1. [Collections & Data Transformation](#collections)
2. [Null Safety](#null-safety)
3. [Functions & Lambdas](#functions)
4. [Classes & Objects](#classes)
5. [Coroutines & Flow](#coroutines)
6. [Compose UI](#compose)
7. [Anti-patterns specific to Kotlin](#antipatterns)
---
## 1. Collections & Data Transformation {#collections}
**Prefer scope functions and stdlib transforms over imperative loops.**
```kotlin
// ❌ Verbose
val result = mutableListOf<String>()
for (item in items) {
if (item.isActive) {
result.add(item.name.uppercase())
}
}
// ✅ Idiomatic
val result = items.filter { it.isActive }.map { it.name.uppercase() }
```
```kotlin
// ❌ Manual grouping
val map = mutableMapOf<String, MutableList<Item>>()
for (item in items) {
map.getOrPut(item.category) { mutableListOf() }.add(item)
}
// ✅
val map = items.groupBy { it.category }
```
```kotlin
// ❌ Manual fold
var total = 0
for (order in orders) total += order.amount
// ✅
val total = orders.sumOf { it.amount }
```
**Use `associate`, `associateBy`, `partition`, `flatMap`, `zip` instead of manual equivalents.**
---
## 2. Null Safety {#null-safety}
```kotlin
// ❌ Unnecessary null check when Elvis suffices
val name: String
if (user?.name != null) {
name = user.name
} else {
name = "Guest"
}
// ✅
val name = user?.name ?: "Guest"
```
```kotlin
// ❌ Double null check
if (response != null && response.body != null) {
process(response.body!!)
}
// ✅
response?.body?.let { process(it) }
```
```kotlin
// ❌ !! without guard
val value = nullable!!.doSomething()
// ✅ Make the non-null contract explicit at the boundary
val value = requireNotNull(nullable) { "nullable must be set before calling X" }.doSomething()
// Or return early:
val n = nullable ?: return
```
---
## 3. Functions & Lambdas {#functions}
```kotlin
// ❌ Single-expression function with unnecessary block body
fun double(x: Int): Int {
return x * 2
}
// ✅
fun double(x: Int) = x * 2
```
```kotlin
// ❌ Lambda capturing unused parameter
items.forEach { item -> doSomething() }
// ✅
items.forEach { doSomething() }
```
```kotlin
// ❌ Redundant with/apply nesting
val builder = Builder()
builder.setName("x")
builder.setAge(1)
val result = builder.build()
// ✅
val result = Builder().apply {
setName("x")
setAge(1)
}.build()
```
**Prefer `let`, `run`, `apply`, `also`, `with` over repeated receiver references — but don't nest more than 2 levels deep.**
---
## 4. Classes & Objects {#classes}
```kotlin
// ❌ Mutable class for immutable data
class Point {
var x: Int = 0
var y: Int = 0
}
// ✅
data class Point(val x: Int, val y: Int)
```
```kotlin
// ❌ Companion object just to hold a constant
class Foo {
companion object {
val TAG = "Foo"
}
}
// ✅ — top-level if only used in this file
private const val TAG = "Foo"
class Foo
```
```kotlin
// ❌ Enum with when that has to be updated in two places
enum class Status { ACTIVE, INACTIVE }
fun label(s: Status) = when(s) { Status.ACTIVE -> "Active"; Status.INACTIVE -> "Inactive" }
// ✅ — put display logic on the enum itself
enum class Status(val label: String) { ACTIVE("Active"), INACTIVE("Inactive") }
```
**Sealed classes/interfaces over enum when variants carry different data.**
---
## 5. Coroutines & Flow {#coroutines}
```kotlin
// ❌ Unnecessary async/await pair when result is used immediately
val result = async { fetchData() }.await()
// ✅
val result = fetchData() // just suspend fun, no async needed
```
```kotlin
// ❌ Collecting in a loop
while (true) {
val value = channel.receive()
process(value)
}
// ✅
channel.consumeEach { process(it) }
// or for Flow:
flow.collect { process(it) }
```
```kotlin
// ❌ StateFlow + manual emit boilerplate
private val _state = MutableStateFlow(initial)
val state: StateFlow<State> = _state
// ... in many places: _state.value = newValue
// ✅ — use update{} for atomic mutation
_state.update { it.copy(field = newValue) }
```
**Don't launch coroutines in constructors or init blocks. Don't use GlobalScope.**
---
## 6. Compose UI {#compose}
```kotlin
// ❌ Unnecessary remember for derived state that's cheap to compute
val displayName = remember { user.firstName + " " + user.lastName }
// ✅ — only remember if computation is expensive or involves object creation
val displayName = "${user.firstName} ${user.lastName}"
```
```kotlin
// ❌ Passing entire state object when composable only needs one field
@Composable
fun UserBadge(user: User) { Text(user.name) }
// ✅ — pass only what's needed (stability + minimal recomposition)
@Composable
fun UserBadge(name: String) { Text(name) }
```
```kotlin
// ❌ Inline click handler lambda (creates new instance each recomposition)
Button(onClick = { viewModel.onSave() }) { ... }
// ✅
val onSave = remember { { viewModel.onSave() } }
Button(onClick = onSave) { ... }
// Or pass it down as a parameter already
```
```kotlin
// ❌ Nested Column/Row just to group children
Column {
Column {
Text("a")
Text("b")
}
}
// ✅
Column {
Text("a")
Text("b")
}
```
**Use `LazyColumn`/`LazyRow` for lists of unknown or large size. Never put a `LazyColumn` inside a `Column` with unbounded height.**
---
## 7. Anti-patterns specific to Kotlin {#antipatterns}
| Anti-pattern | Preferred |
|---|---|
| `if (x == true)` | `if (x)` |
| `if (x == null) return else x!!` | `val x = x ?: return` |
| `listOf().toMutableList()` for a known-size list | `mutableListOf()` |
| `when` with a single branch and else | `if/else` |
| `.toString()` on a string | remove it |
| Explicit `Unit` return type on functions | omit (inferred) |
| `object : Runnable { override fun run() { ... } }` | `Runnable { ... }` (SAM) |
| `@JvmStatic` in pure Kotlin code | only needed for Java interop |
| Wrapping every function in try/catch to log | handle at the boundary, not inside |
## Limitations
- These are language-specific guidelines and do not cover overall architectural decisions.
- Over-compression might reduce readability; apply judgement.
@@ -0,0 +1,316 @@
---
name: php
description: "Language-specific super-code guidelines for php."
risk: safe
source: community
date_added: "2026-06-16"
---
# PHP: Idiomatic Efficiency Reference
## Table of Contents
1. [Arrays & Collections](#arrays)
2. [Type Safety](#types)
3. [Error Handling](#errors)
4. [String Handling](#strings)
5. [OOP & Modern PHP](#oop)
6. [Functions & Closures](#functions)
7. [Anti-patterns specific to PHP](#antipatterns)
---
## 1. Arrays & Collections {#arrays}
```php
// ❌ Manual accumulation
$result = [];
foreach ($items as $item) {
if ($item->isActive()) {
$result[] = strtoupper($item->getName());
}
}
// ✅
$result = array_map(
fn($i) => strtoupper($i->getName()),
array_filter($items, fn($i) => $i->isActive())
);
```
```php
// ❌ Manual key-value grouping
$grouped = [];
foreach ($items as $item) {
$grouped[$item->getCategory()][] = $item;
}
// ✅ (PHP 8.1+) — or use the loop above; PHP lacks a built-in groupBy
// The foreach is actually idiomatic PHP for grouping. No need to force array_* here.
```
```php
// ❌ Checking isset then accessing
if (isset($data['key'])) {
$value = $data['key'];
} else {
$value = 'default';
}
// ✅
$value = $data['key'] ?? 'default';
```
```php
// ❌ array_push for single element
array_push($items, $newItem);
// ✅
$items[] = $newItem;
```
**Use `array_map`/`array_filter` for transforms. The `foreach` loop is fine when array functions would be less readable.**
---
## 2. Type Safety {#types}
```php
// ❌ No type declarations
function process($items) {
return $items;
}
// ✅ (PHP 8.0+)
function process(array $items): array {
return $items;
}
```
```php
// ❌ Union type for nullable
function find(string $key): string|null { ... }
// ✅
function find(string $key): ?string { ... }
```
```php
// ❌ Loose comparison
if ($value == '0') { ... } // true for 0, '', false, null
// ✅
if ($value === '0') { ... }
```
```php
// ❌ Type checking with gettype()
if (gettype($x) === 'integer') { ... }
// ✅
if (is_int($x)) { ... }
// or with union types, avoid checks entirely
```
**Enable `declare(strict_types=1)` at the top of every file.**
---
## 3. Error Handling {#errors}
```php
// ❌ Suppressing errors with @
$data = @file_get_contents($path);
// ✅
$data = file_get_contents($path);
if ($data === false) {
throw new RuntimeException("Failed to read: $path");
}
```
```php
// ❌ Catching \Exception and swallowing
try { process(); }
catch (\Exception $e) { /* silence */ }
// ✅
try {
process();
} catch (SpecificException $e) {
$this->logger->error($e->getMessage(), ['exception' => $e]);
throw new AppException('Processing failed', previous: $e);
}
```
```php
// ❌ Returning mixed types for error indication
function divide(int $a, int $b): int|false {
if ($b === 0) return false;
return intdiv($a, $b);
}
// ✅ — throw exception for exceptional cases
function divide(int $a, int $b): int {
if ($b === 0) throw new \DivisionByZeroError();
return intdiv($a, $b);
}
```
---
## 4. String Handling {#strings}
```php
// ❌ Concatenation for variable interpolation
$msg = 'Hello, ' . $name . '! You have ' . $count . ' messages.';
// ✅
$msg = "Hello, {$name}! You have {$count} messages.";
```
```php
// ❌ Manual string contains check
if (strpos($haystack, $needle) !== false) { ... }
// ✅ (PHP 8.0+)
if (str_contains($haystack, $needle)) { ... }
```
```php
// ❌ substr for prefix/suffix check
if (substr($str, 0, 4) === 'http') { ... }
if (substr($str, -4) === '.php') { ... }
// ✅ (PHP 8.0+)
if (str_starts_with($str, 'http')) { ... }
if (str_ends_with($str, '.php')) { ... }
```
---
## 5. OOP & Modern PHP {#oop}
```php
// ❌ Manual constructor property assignment
class User {
private string $name;
private int $age;
public function __construct(string $name, int $age) {
$this->name = $name;
$this->age = $age;
}
}
// ✅ (PHP 8.0+)
class User {
public function __construct(
private readonly string $name,
private readonly int $age,
) {}
}
```
```php
// ❌ Constants as class properties
class Status {
const ACTIVE = 'active';
const INACTIVE = 'inactive';
}
// ✅ (PHP 8.1+)
enum Status: string {
case Active = 'active';
case Inactive = 'inactive';
}
```
```php
// ❌ instanceof chains
if ($shape instanceof Circle) { ... }
elseif ($shape instanceof Rectangle) { ... }
// ✅ (PHP 8.0+)
$area = match(true) {
$shape instanceof Circle => $shape->radius ** 2 * M_PI,
$shape instanceof Rectangle => $shape->width * $shape->height,
default => throw new \InvalidArgumentException("Unknown shape"),
};
```
```php
// ❌ Named constructor via static method returning new self()
class Money {
public static function fromCents(int $cents): self {
$m = new self();
$m->cents = $cents;
return $m;
}
}
// ✅ (PHP 8.0+) — constructor promotion + named arguments
class Money {
public function __construct(
public readonly int $cents,
) {}
}
$m = new Money(cents: 500);
```
---
## 6. Functions & Closures {#functions}
```php
// ❌ Verbose closure for simple operation
$doubled = array_map(function ($x) { return $x * 2; }, $numbers);
// ✅ (PHP 7.4+)
$doubled = array_map(fn($x) => $x * 2, $numbers);
```
```php
// ❌ Passing globals or using `global` keyword
global $db;
function getUser(int $id) {
global $db;
return $db->find($id);
}
// ✅ — dependency injection
function getUser(int $id, PDO $db): ?User {
return $db->find($id);
}
```
```php
// ❌ Named arguments abused for every call
str_pad(string: $s, length: 10, pad_string: ' ', pad_type: STR_PAD_LEFT);
// ✅ — named args are useful for readability on ambiguous params; don't force
str_pad($s, 10, ' ', STR_PAD_LEFT);
// but named args shine for: new User(name: 'Alice', age: 30)
```
---
## 7. Anti-patterns specific to PHP {#antipatterns}
| Anti-pattern | Preferred |
|---|---|
| `==` for comparison | `===` (strict equality) |
| `@` error suppression | explicit error handling |
| `global` keyword | dependency injection |
| `extract()` on user input | access keys explicitly |
| `die()` / `exit()` in library code | throw exception |
| `strpos !== false` for contains | `str_contains()` (PHP 8.0) |
| Manual constructor assignment | constructor promotion (PHP 8.0) |
| Class constants for enums | `enum` (PHP 8.1) |
| `mixed` return types | specific typed returns |
| `array` for everything | typed classes / DTOs |
| `var_dump` / `print_r` debugging | proper logging (PSR-3) |
| Not using `declare(strict_types=1)` | always enable |
## Limitations
- These are language-specific guidelines and do not cover overall architectural decisions.
- Over-compression might reduce readability; apply judgement.
@@ -0,0 +1,315 @@
---
name: python
description: "Language-specific super-code guidelines for python."
risk: safe
source: community
date_added: "2026-06-16"
---
# Python: Idiomatic Efficiency Reference
## Table of Contents
1. [Comprehensions & Generators](#comprehensions)
2. [Unpacking & Destructuring](#unpacking)
3. [Built-ins & stdlib](#builtins)
4. [Functions & Defaults](#functions)
5. [Classes & Dataclasses](#classes)
6. [Error Handling](#errors)
7. [Type Hints](#types)
8. [Anti-patterns specific to Python](#antipatterns)
---
## 1. Comprehensions & Generators {#comprehensions}
```python
# ❌ Imperative accumulation
result = []
for item in items:
if item.active:
result.append(item.name.upper())
# ✅
result = [item.name.upper() for item in items if item.active]
```
```python
# ❌ Dict built in a loop
d = {}
for k, v in pairs:
d[k] = v
# ✅
d = dict(pairs)
# or
d = {k: v for k, v in pairs}
```
```python
# ❌ Generator converted to list unnecessarily
total = sum(list(x * 2 for x in nums))
# ✅ — generator expression works directly in sum()
total = sum(x * 2 for x in nums)
```
**Use generator expressions (not list comprehensions) when the result is consumed once and not stored.**
---
## 2. Unpacking & Destructuring {#unpacking}
```python
# ❌ Index access
first = items[0]
rest = items[1:]
# ✅
first, *rest = items
```
```python
# ❌ Temporary variable for swap
tmp = a
a = b
b = tmp
# ✅
a, b = b, a
```
```python
# ❌ items() with separate indexing
for i in range(len(items)):
print(i, items[i])
# ✅
for i, item in enumerate(items):
print(i, item)
```
```python
# ❌ zip with separate index
for i in range(len(a)):
process(a[i], b[i])
# ✅
for x, y in zip(a, b):
process(x, y)
```
---
## 3. Built-ins & stdlib {#builtins}
```python
# ❌ Manual max search
max_val = items[0]
for item in items[1:]:
if item > max_val:
max_val = item
# ✅
max_val = max(items)
```
```python
# ❌ Manual grouping
from collections import defaultdict
groups = defaultdict(list)
for item in items:
groups[item.category].append(item)
# ✅ — same thing, just be explicit about defaultdict; it IS the right tool
# (this example is already correct — don't replace defaultdict with a loop)
```
```python
# ❌ Manual sentinel for dict default
if key in d:
val = d[key]
else:
val = default
# ✅
val = d.get(key, default)
```
```python
# ❌ Rolling your own counter
counts = {}
for item in items:
counts[item] = counts.get(item, 0) + 1
# ✅
from collections import Counter
counts = Counter(items)
```
**Use `itertools` (chain, islice, groupby, product) before writing nested loops for combinatorial or streaming logic.**
---
## 4. Functions & Defaults {#functions}
```python
# ❌ Mutable default argument (bug, not just style)
def append_to(item, lst=[]):
lst.append(item)
return lst
# ✅
def append_to(item, lst=None):
if lst is None:
lst = []
lst.append(item)
return lst
```
```python
# ❌ Positional args for everything when keyword clarity helps
create_user("Alice", True, False, 30)
# ✅ — use keyword args at call site for boolean/ambiguous params
create_user("Alice", is_admin=True, is_active=False, age=30)
```
```python
# ❌ Long function doing multiple things
def process_and_save(data):
# 40 lines of transform
# 20 lines of DB write
...
# ✅ — split only if each part is reused OR independently testable
def _transform(data): ...
def _save(record): ...
def process_and_save(data): _save(_transform(data))
```
---
## 5. Classes & Dataclasses {#classes}
```python
# ❌ Manual __init__ for data holders
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
# ✅
from dataclasses import dataclass
@dataclass
class Point:
x: float
y: float
```
```python
# ❌ Class just to hold a namespace of functions
class MathUtils:
@staticmethod
def add(a, b): return a + b
# ✅ — module-level functions; classes for state + behavior
def add(a, b): return a + b
```
```python
# ❌ __repr__ written manually when dataclass gives it free
# (see above — use @dataclass)
```
**Use `@dataclass(frozen=True)` for immutable value objects. Use `NamedTuple` when you need tuple unpacking.**
---
## 6. Error Handling {#errors}
```python
# ❌ Bare except
try:
risky()
except:
pass
# ✅ — catch the specific exception; don't swallow silently
try:
risky()
except ValueError as e:
logger.warning("Invalid value: %s", e)
```
```python
# ❌ LBYL (look before you leap) when EAFP is cleaner
if os.path.exists(path):
with open(path) as f:
data = f.read()
# ✅ (EAFP)
try:
with open(path) as f:
data = f.read()
except FileNotFoundError:
data = None
```
```python
# ❌ Re-raising with raise e (loses traceback)
except Exception as e:
raise e
# ✅
except Exception:
raise # bare raise preserves original traceback
```
---
## 7. Type Hints {#types}
```python
# ❌ Overly verbose Union syntax (Python <3.10 style in new code)
from typing import Optional, Union
def f(x: Optional[int]) -> Union[str, None]: ...
# ✅ (Python 3.10+)
def f(x: int | None) -> str | None: ...
```
```python
# ❌ Any where a TypeVar or Protocol would be informative
from typing import Any
def first(lst: list[Any]) -> Any: ...
# ✅
from typing import TypeVar
T = TypeVar("T")
def first(lst: list[T]) -> T: ...
```
**Don't add type hints to every local variable — annotate function signatures and class fields; leave obvious locals inferred.**
---
## 8. Anti-patterns specific to Python {#antipatterns}
| Anti-pattern | Preferred |
|---|---|
| `len(lst) == 0` | `not lst` |
| `if x == True:` | `if x:` |
| `if x == None:` | `if x is None:` |
| `range(len(lst))` for iteration | `enumerate(lst)` |
| String concatenation in a loop | `"".join(parts)` |
| `import *` | explicit imports |
| Catching `Exception` to log and re-raise | bare `raise` or let it propagate |
| `print()` for debug output | `logging.debug()` |
| `os.path.join` (Python 3.4+) | `pathlib.Path / "subpath"` |
| Manual `__eq__` + `__hash__` on value objects | `@dataclass(eq=True, frozen=True)` |
## Limitations
- These are language-specific guidelines and do not cover overall architectural decisions.
- Over-compression might reduce readability; apply judgement.
@@ -0,0 +1,306 @@
---
name: ruby
description: "Language-specific super-code guidelines for ruby."
risk: safe
source: community
date_added: "2026-06-16"
---
# Ruby: Idiomatic Efficiency Reference
## Table of Contents
1. [Enumerable & Collections](#enumerable)
2. [Blocks, Procs & Lambdas](#blocks)
3. [String Handling](#strings)
4. [Error Handling](#errors)
5. [Classes & Modules](#classes)
6. [Ruby Idioms](#idioms)
7. [Anti-patterns specific to Ruby](#antipatterns)
---
## 1. Enumerable & Collections {#enumerable}
```ruby
# ❌ Manual accumulation
result = []
items.each do |item|
result << item.name.upcase if item.active?
end
# ✅
result = items.select(&:active?).map { |i| i.name.upcase }
```
```ruby
# ❌ Manual grouping
grouped = {}
items.each do |item|
grouped[item.category] ||= []
grouped[item.category] << item
end
# ✅
grouped = items.group_by(&:category)
```
```ruby
# ❌ Manual sum
total = 0
orders.each { |o| total += o.amount }
# ✅
total = orders.sum(&:amount)
```
```ruby
# ❌ Checking existence then accessing
if hash.key?(key)
value = hash[key]
end
# ✅
value = hash[key] # returns nil if missing
# or with default:
value = hash.fetch(key, default_value)
# or raising on missing:
value = hash.fetch(key) # raises KeyError
```
**Prefer `map`/`select`/`reject`/`sum` over manual loops. Use `&:method` for single-method blocks.**
---
## 2. Blocks, Procs & Lambdas {#blocks}
```ruby
# ❌ Explicit block-to-proc conversion when unnecessary
items.map { |item| item.to_s }
# ✅
items.map(&:to_s)
```
```ruby
# ❌ Proc.new when lambda is safer (arity check + return behavior)
handler = Proc.new { |x| x * 2 }
# ✅
handler = ->(x) { x * 2 }
```
```ruby
# ❌ Multi-line block with { }
items.map { |item|
result = transform(item)
validate(result)
result
}
# ✅ — do/end for multi-line, { } for single-line
items.map do |item|
result = transform(item)
validate(result)
result
end
```
---
## 3. String Handling {#strings}
```ruby
# ❌ String concatenation in loop
result = ""
items.each { |i| result += i.name + ", " }
# ✅
result = items.map(&:name).join(", ")
```
```ruby
# ❌ String concatenation for assembly
greeting = "Hello, " + name + "! You have " + count.to_s + " messages."
# ✅
greeting = "Hello, #{name}! You have #{count} messages."
```
```ruby
# ❌ Mutable string where frozen is fine (Ruby 3+ encourages frozen)
SEPARATOR = ", "
# ✅
SEPARATOR = ", ".freeze
# or add `# frozen_string_literal: true` at file top
```
**Use heredocs (`<<~HEREDOC`) for multi-line strings. `<<~` strips indentation.**
---
## 4. Error Handling {#errors}
```ruby
# ❌ Rescuing Exception (catches EVERYTHING including SignalException, SystemExit)
begin
risky
rescue Exception => e
log(e)
end
# ✅ — rescue StandardError (the default)
begin
risky
rescue StandardError => e
log(e)
raise
end
# or just: rescue => e (same as StandardError)
```
```ruby
# ❌ Using rescue as flow control
begin
value = hash.fetch(key)
rescue KeyError
value = default
end
# ✅
value = hash.fetch(key, default)
```
```ruby
# ❌ Inline rescue hiding errors
result = dangerous_operation rescue nil
# ✅ — inline rescue only for truly trivial fallbacks
result = Integer(input) rescue nil # acceptable for parsing
```
---
## 5. Classes & Modules {#classes}
```ruby
# ❌ Manual accessors
class User
def name
@name
end
def name=(value)
@name = value
end
end
# ✅
class User
attr_accessor :name
end
```
```ruby
# ❌ Deep inheritance for shared behavior
class Animal; end
class Pet < Animal; end
class Dog < Pet; end
# ✅ — mixins for shared behavior, inheritance for "is-a"
module Trainable
def train = puts("Training #{name}")
end
class Dog
include Trainable
attr_reader :name
def initialize(name) = @name = name
end
```
```ruby
# ❌ Class with only class methods (namespace via class)
class MathUtils
def self.square(x) = x * x
def self.cube(x) = x ** 3
end
# ✅
module MathUtils
module_function
def square(x) = x * x
def cube(x) = x ** 3
end
```
---
## 6. Ruby Idioms {#idioms}
```ruby
# ❌ Explicit boolean return
def active?
if status == :active
true
else
false
end
end
# ✅
def active? = status == :active
```
```ruby
# ❌ nil check before method call
if user && user.name
puts user.name
end
# ✅ (Ruby 2.3+)
puts user&.name if user&.name
# or with safe navigation:
user&.name&.then { |n| puts n }
```
```ruby
# ❌ Conditional assignment verbosely
if @cache.nil?
@cache = expensive_compute
end
# ✅
@cache ||= expensive_compute
```
```ruby
# ❌ Multiple assignment from array manually
first = arr[0]
second = arr[1]
# ✅
first, second = arr
```
---
## 7. Anti-patterns specific to Ruby {#antipatterns}
| Anti-pattern | Preferred |
|---|---|
| `rescue Exception` | `rescue StandardError` |
| `for x in collection` | `collection.each` |
| Manual `attr_reader`/`writer` | `attr_accessor` / `attr_reader` |
| `class` for pure namespace | `module` |
| String concatenation with `+` | string interpolation `#{}` |
| `if !condition` | `unless condition` |
| `== true` / `== false` | truthy/falsy check directly |
| `and`/`or` for control flow | `&&`/`||` (different precedence) |
| `return` at end of method | implicit return (last expression) |
| Monkey-patching core classes in production | refinements or wrapper |
| `eval` / `send` for known methods | direct method call |
## Limitations
- These are language-specific guidelines and do not cover overall architectural decisions.
- Over-compression might reduce readability; apply judgement.
@@ -0,0 +1,289 @@
---
name: rust
description: "Language-specific super-code guidelines for rust."
risk: safe
source: community
date_added: "2026-06-16"
---
# Rust: Idiomatic Efficiency Reference
## Table of Contents
1. [Ownership & Borrowing](#ownership)
2. [Error Handling](#errors)
3. [Iterators](#iterators)
4. [Pattern Matching](#patterns)
5. [Structs & Enums](#structs)
6. [Concurrency](#concurrency)
7. [Anti-patterns specific to Rust](#antipatterns)
---
## 1. Ownership & Borrowing {#ownership}
```rust
// ❌ Cloning to avoid thinking about lifetimes
fn get_name(user: &User) -> String {
user.name.clone()
}
// ✅ — return a reference when the data lives long enough
fn get_name(user: &User) -> &str {
&user.name
}
```
```rust
// ❌ Taking ownership when borrowing suffices
fn print_name(name: String) {
println!("{name}");
}
// ✅
fn print_name(name: &str) {
println!("{name}");
}
```
```rust
// ❌ Unnecessary .to_string() / .to_owned() in hot paths
let key = id.to_string();
map.get(&key)
// ✅ — use Borrow trait; HashMap<String, V> accepts &str as key
map.get(id)
```
**Prefer `&str` over `String` in function parameters unless the function needs to own the data.**
---
## 2. Error Handling {#errors}
```rust
// ❌ .unwrap() in production code
let file = File::open(path).unwrap();
// ✅
let file = File::open(path)
.map_err(|e| AppError::Io { path: path.to_owned(), source: e })?;
```
```rust
// ❌ Manual match on Result for every call
match do_thing() {
Ok(v) => v,
Err(e) => return Err(e),
}
// ✅ — the ? operator
let v = do_thing()?;
```
```rust
// ❌ Box<dyn Error> everywhere (loses type info)
fn run() -> Result<(), Box<dyn std::error::Error>> { ... }
// ✅ — use thiserror for library errors, anyhow for application errors
use anyhow::{Context, Result};
fn run() -> Result<()> {
do_thing().context("failed during run")?;
Ok(())
}
```
```rust
// ❌ Separate error enum variant for every call site
enum Error { FileOpen, FileRead, Parse, Network, ... }
// ✅ — use thiserror with #[from] for automatic conversion
#[derive(thiserror::Error, Debug)]
enum Error {
#[error("io error")] Io(#[from] std::io::Error),
#[error("parse error")] Parse(#[from] serde_json::Error),
}
```
---
## 3. Iterators {#iterators}
```rust
// ❌ Imperative accumulation
let mut result = Vec::new();
for item in &items {
if item.active {
result.push(item.name.to_uppercase());
}
}
// ✅
let result: Vec<_> = items.iter()
.filter(|i| i.active)
.map(|i| i.name.to_uppercase())
.collect();
```
```rust
// ❌ Manual sum
let mut total = 0;
for order in &orders { total += order.amount; }
// ✅
let total: u64 = orders.iter().map(|o| o.amount).sum();
```
```rust
// ❌ Index-based loop
for i in 0..items.len() {
process(&items[i]);
}
// ✅
for item in &items {
process(item);
}
// With index:
for (i, item) in items.iter().enumerate() {
process(i, item);
}
```
**Chain iterators lazily; only `.collect()` when you actually need a concrete collection.**
---
## 4. Pattern Matching {#patterns}
```rust
// ❌ if-let chain that should be match
if let Some(x) = opt {
if x > 0 {
use(x)
}
}
// ✅
if let Some(x) = opt.filter(|&x| x > 0) {
use(x)
}
// or match with guard:
match opt {
Some(x) if x > 0 => use(x),
_ => {}
}
```
```rust
// ❌ match with identical arms
match status {
Status::Active => true,
Status::Pending => true,
Status::Inactive => false,
}
// ✅
matches!(status, Status::Active | Status::Pending)
```
```rust
// ❌ Destructuring in body instead of pattern
fn area(shape: &Shape) -> f64 {
match shape {
Shape::Circle(c) => { let r = c.radius; r * r * PI }
Shape::Rect(r) => { let w = r.width; let h = r.height; w * h }
}
}
// ✅ — destructure in pattern
match shape {
Shape::Circle(Circle { radius, .. }) => radius * radius * PI,
Shape::Rect(Rect { width, height }) => width * height,
}
```
---
## 5. Structs & Enums {#structs}
```rust
// ❌ Enum variant carrying bool for binary state
enum State { Running(bool) } // true = paused?
// ✅ — explicit variants
enum State { Running, Paused, Stopped }
```
```rust
// ❌ Struct with many Option fields (stringly optional)
struct Config {
timeout: Option<u64>,
retries: Option<u32>,
base_url: Option<String>,
}
// ✅ — use Default + builder pattern or #[derive(Default)] with sensible defaults
#[derive(Default)]
struct Config {
timeout: u64, // default 0 = no timeout
retries: u32, // default 0
base_url: String,
}
```
```rust
// ❌ pub fields on a type that needs invariants
pub struct Percentage { pub value: f64 }
// ✅ — private field, constructor enforces invariant
pub struct Percentage(f64);
impl Percentage {
pub fn new(v: f64) -> Option<Self> {
(0.0..=100.0).contains(&v).then_some(Self(v))
}
}
```
---
## 6. Concurrency {#concurrency}
```rust
// ❌ Arc<Mutex<T>> for read-heavy data
let data = Arc::new(Mutex::new(vec![...]));
// ✅ — RwLock for read-heavy
let data = Arc::new(RwLock::new(vec![...]));
```
```rust
// ❌ Spawning OS threads for many small tasks
for item in items {
std::thread::spawn(|| process(item));
}
// ✅ — use rayon for CPU-bound parallel iteration
use rayon::prelude::*;
items.par_iter().for_each(|item| process(item));
```
**For async: prefer `tokio::spawn` + `JoinHandle` over manual channels for structured concurrency. Use `tokio::join!` for concurrent awaits.**
---
## 7. Anti-patterns specific to Rust {#antipatterns}
| Anti-pattern | Preferred |
|---|---|
| `.clone()` to appease borrow checker | reconsider lifetime or restructure |
| `.unwrap()` in non-test code | `?` operator or explicit handling |
| `impl Trait` in return position hiding complex type | name the type or use `Box<dyn Trait>` intentionally |
| `String` parameter when `&str` suffices | `&str` for params, `String` for owned storage |
| Nested `Option<Option<T>>` | rethink the data model |
| `unsafe` block without a safety comment | always document the invariant being upheld |
| `Vec<Box<T>>` when `Vec<T>` works | avoid heap allocation inside collections unless T is unsized |
| Manual `Drop` for cleanup that `?` handles | let RAII + `?` do it |
## Limitations
- These are language-specific guidelines and do not cover overall architectural decisions.
- Over-compression might reduce readability; apply judgement.
@@ -0,0 +1,302 @@
---
name: scala
description: "Language-specific super-code guidelines for scala."
risk: safe
source: community
date_added: "2026-06-16"
---
# Scala: Idiomatic Efficiency Reference
## Table of Contents
1. [Collections & Functional Transforms](#collections)
2. [Pattern Matching](#patterns)
3. [Case Classes & ADTs](#case-classes)
4. [Option & Error Handling](#option)
5. [Implicits & Given/Using](#implicits)
6. [Concurrency](#concurrency)
7. [Anti-patterns specific to Scala](#antipatterns)
---
## 1. Collections & Functional Transforms {#collections}
```scala
// ❌ Imperative accumulation
val result = new ArrayBuffer[String]()
for (item <- items) {
if (item.isActive) result += item.name.toUpperCase
}
// ✅
val result = items.filter(_.isActive).map(_.name.toUpperCase)
```
```scala
// ❌ Manual grouping
val grouped = mutable.Map[String, List[Item]]()
for (item <- items) {
grouped(item.category) = grouped.getOrElse(item.category, Nil) :+ item
}
// ✅
val grouped = items.groupBy(_.category)
```
```scala
// ❌ Manual fold when sum/product works
var total = 0
for (o <- orders) total += o.amount
// ✅
val total = orders.map(_.amount).sum
```
```scala
// ❌ Using head on potentially empty collection
val first = items.head // throws on empty
// ✅
val first = items.headOption // returns Option[T]
```
```scala
// ❌ Chaining filter + head for find
val found = items.filter(_.id == targetId).head
// ✅
val found = items.find(_.id == targetId) // returns Option[T]
```
**Use `view` for lazy evaluation on large collections to avoid intermediate allocations.**
---
## 2. Pattern Matching {#patterns}
```scala
// ❌ if-else chain for type dispatch
if (shape.isInstanceOf[Circle]) {
val c = shape.asInstanceOf[Circle]
c.radius * c.radius * Math.PI
} else if (shape.isInstanceOf[Rect]) { ... }
// ✅
shape match {
case Circle(r) => r * r * Math.PI
case Rect(w, h) => w * h
}
```
```scala
// ❌ Nested match with identical fallthrough
x match {
case 1 => "low"
case 2 => "low"
case 3 => "mid"
case _ => "high"
}
// ✅
x match {
case 1 | 2 => "low"
case 3 => "mid"
case _ => "high"
}
```
```scala
// ❌ Match to extract then use
val result = opt match {
case Some(x) => x.toString
case None => "N/A"
}
// ✅
val result = opt.map(_.toString).getOrElse("N/A")
// or:
val result = opt.fold("N/A")(_.toString)
```
---
## 3. Case Classes & ADTs {#case-classes}
```scala
// ❌ Regular class for data
class User(val name: String, val age: Int) {
override def equals(obj: Any): Boolean = ...
override def hashCode(): Int = ...
override def toString: String = ...
}
// ✅
case class User(name: String, age: Int)
```
```scala
// ❌ Sealed trait with unrelated case objects
sealed trait Result
case class Success(value: Int) extends Result
case class Failure(error: String) extends Result
case object Unknown extends Result // what does "Unknown" mean?
// ✅ — each variant should carry the data it represents
sealed trait Result[+A]
case class Success[A] (value: A) extends Result[A]
case class Failure(error: Throwable) extends Result[Nothing]
```
```scala
// ❌ (Scala 3) Verbose enum
sealed trait Color
object Color {
case object Red extends Color
case object Green extends Color
case object Blue extends Color
}
// ✅ (Scala 3)
enum Color { case Red, Green, Blue }
```
---
## 4. Option & Error Handling {#option}
```scala
// ❌ Null checks
val name: String = if (user != null) user.name else "Unknown"
// ✅
val name = Option(user).map(_.name).getOrElse("Unknown")
```
```scala
// ❌ .get on Option (defeats the purpose)
val name = userOpt.get // throws if None
// ✅
val name = userOpt.getOrElse("default")
// or: userOpt.map(process).getOrElse(fallback)
// or: userOpt match { case Some(u) => ... case None => ... }
```
```scala
// ❌ Try with .get
val result = Try(parse(input)).get // throws on failure
// ✅
val result = Try(parse(input)) match {
case Success(v) => v
case Failure(e) => handleError(e)
}
// or: Try(parse(input)).getOrElse(default)
// or: Try(parse(input)).toEither
```
```scala
// ❌ Using exceptions for expected failures
def findUser(id: String): User = {
val user = db.query(id)
if (user == null) throw new NotFoundException(id)
user
}
// ✅ — Option for absence, Either for expected errors
def findUser(id: String): Option[User] = db.query(id)
// or:
def findUser(id: String): Either[AppError, User]
```
---
## 5. Implicits & Given/Using {#implicits}
```scala
// ❌ (Scala 2) Implicit conversion that hides bugs
implicit def stringToInt(s: String): Int = s.toInt
// ✅ — extension methods instead of implicit conversions
extension (s: String)
def toIntSafe: Option[Int] = s.toIntOption
```
```scala
// ❌ (Scala 2) Implicit parameter with broad type
def query(sql: String)(implicit conn: Connection): ResultSet
// ✅ (Scala 3)
def query(sql: String)(using conn: Connection): ResultSet
```
```scala
// ❌ Importing implicits from everywhere
import com.lib.implicits._
// ✅ — import only what you need
import com.lib.given
// or specific: import com.lib.{given ExecutionContext}
```
---
## 6. Concurrency {#concurrency}
```scala
// ❌ Thread.sleep in production code
Thread.sleep(5000)
// ✅ — use scheduler / timer abstraction
import scala.concurrent.duration._
system.scheduler.scheduleOnce(5.seconds)(doWork())
```
```scala
// ❌ Blocking inside Future
Future {
val result = blockingHttpCall() // starves thread pool
process(result)
}
// ✅
Future {
blocking { val result = blockingHttpCall() }
// or use a dedicated blocking ExecutionContext
}
```
```scala
// ❌ Awaiting futures in a loop
for (f <- futures) Await.result(f, Duration.Inf)
// ✅
val all = Future.sequence(futures)
all.map(results => process(results))
```
**Prefer `Future.sequence`/`Future.traverse` over manual await loops.**
---
## 7. Anti-patterns specific to Scala {#antipatterns}
| Anti-pattern | Preferred |
|---|---|
| `.get` on Option/Try | `.getOrElse` / pattern match |
| `null` | `Option` |
| `isInstanceOf` + `asInstanceOf` | pattern matching |
| Implicit conversions (Scala 2) | extension methods (Scala 3) |
| `var` for accumulation | `val` + functional transforms |
| `return` keyword | last expression is the return value |
| Mutable collections by default | immutable collections |
| `Any` / `AnyRef` parameters | generics with type bounds |
| Deeply nested `for` comprehensions | break into named values |
| Tuple instead of case class | case class for anything with semantic meaning |
| `Await.result` in production | compose with `map`/`flatMap` |
## Limitations
- These are language-specific guidelines and do not cover overall architectural decisions.
- Over-compression might reduce readability; apply judgement.
@@ -0,0 +1,299 @@
---
name: swift
description: "Language-specific super-code guidelines for swift."
risk: safe
source: community
date_added: "2026-06-16"
---
# Swift: Idiomatic Efficiency Reference
## Table of Contents
1. [Optionals](#optionals)
2. [Collections & Functional Transforms](#collections)
3. [Value vs Reference Types](#value-types)
4. [Error Handling](#errors)
5. [Concurrency](#concurrency)
6. [Protocol-Oriented Design](#protocols)
7. [Anti-patterns specific to Swift](#antipatterns)
---
## 1. Optionals {#optionals}
```swift
// Force unwrap
let name = user.name!
// guard or if-let
guard let name = user.name else { return }
```
```swift
// Nested if-let pyramid
if let user = fetchUser() {
if let address = user.address {
if let city = address.city {
display(city)
}
}
}
// chained optional binding
if let city = fetchUser()?.address?.city {
display(city)
}
// or guard-let for early exit
guard let city = fetchUser()?.address?.city else { return }
display(city)
```
```swift
// Ternary for default
let name = user.name != nil ? user.name! : "Unknown"
//
let name = user.name ?? "Unknown"
```
```swift
// Optional map when if-let is clearer for side effects
user.name.map { display($0) }
// map for transforms, if-let for side effects
let upper = user.name.map { $0.uppercased() }
if let name = user.name { display(name) }
```
---
## 2. Collections & Functional Transforms {#collections}
```swift
// Imperative filter + map
var result: [String] = []
for item in items {
if item.isActive { result.append(item.name.uppercased()) }
}
//
let result = items
.filter(\.isActive)
.map { $0.name.uppercased() }
```
```swift
// Manual dictionary construction
var dict: [String: User] = [:]
for user in users { dict[user.id] = user }
//
let dict = Dictionary(uniqueKeysWithValues: users.map { ($0.id, $0) })
// or with possible duplicates:
let dict = Dictionary(grouping: users, by: \.department)
```
```swift
// Checking isEmpty then accessing first
if !items.isEmpty { process(items[0]) }
//
if let first = items.first { process(first) }
```
```swift
// Index-based loop
for i in 0..<items.count { process(items[i]) }
//
for item in items { process(item) }
// with index:
for (i, item) in items.enumerated() { process(i, item) }
```
**Use key paths (`\.isActive`) as closure shorthand where supported.**
---
## 3. Value vs Reference Types {#value-types}
```swift
// Class for plain data (reference semantics where value semantics suffice)
class Point {
var x: Double
var y: Double
init(x: Double, y: Double) { self.x = x; self.y = y }
}
//
struct Point { var x, y: Double }
```
```swift
// Large struct copied repeatedly (performance hit)
struct HugeData { var buffer: [UInt8] /* thousands of elements */ }
func process(_ data: HugeData) { ... } // copies entire buffer
// use class or pass inout for mutation
func process(_ data: inout HugeData) { ... }
// or use copy-on-write wrapper for large value types
```
**Default to `struct`. Use `class` when you need identity, inheritance, or reference semantics.**
---
## 4. Error Handling {#errors}
```swift
// Using optionals to mask errors
func parse(_ input: String) -> Data? { ... } // caller doesn't know why it failed
//
func parse(_ input: String) throws -> Data { ... }
```
```swift
// try! in production code
let data = try! JSONDecoder().decode(User.self, from: jsonData)
//
do {
let data = try JSONDecoder().decode(User.self, from: jsonData)
} catch {
logger.error("decode failed: \(error)")
throw AppError.decodingFailed(underlying: error)
}
```
```swift
// Generic Error type
enum AppError: Error { case generic(String) }
// specific, actionable error cases
enum AppError: Error {
case networkUnreachable
case invalidInput(field: String, reason: String)
case unauthorized
}
```
```swift
// Catching all errors and ignoring
do { try riskyOperation() } catch { }
//
do {
try riskyOperation()
} catch let error as NetworkError {
handleNetworkError(error)
} catch {
throw error // rethrow unknown
}
```
---
## 5. Concurrency {#concurrency}
```swift
// Callback-based async (pyramid of doom)
fetchUser { user in
fetchPosts(for: user) { posts in
fetchComments(for: posts.first!) { comments in
display(comments)
}
}
}
// (Swift 5.5+)
let user = try await fetchUser()
let posts = try await fetchPosts(for: user)
let comments = try await fetchComments(for: posts[0])
display(comments)
```
```swift
// Sequential awaits for independent work
let a = try await fetchA()
let b = try await fetchB()
//
async let a = fetchA()
async let b = fetchB()
let (resultA, resultB) = try await (a, b)
```
```swift
// DispatchQueue.main.async for UI updates in async context
DispatchQueue.main.async { label.text = result }
//
await MainActor.run { label.text = result }
// or mark the function/class @MainActor
```
**Use `actor` for mutable shared state instead of manual locks/queues.**
---
## 6. Protocol-Oriented Design {#protocols}
```swift
// Deep class inheritance hierarchy
class Animal { ... }
class Dog: Animal { ... }
class GuideDog: Dog { ... }
// protocols + composition
protocol Animal { var name: String { get } }
protocol Trainable { func train() }
struct Dog: Animal, Trainable { ... }
```
```swift
// Protocol with default implementations for everything
protocol Renderable {
func render()
}
extension Renderable {
func render() { /* default */ }
}
// Every conformer uses default protocol serves no purpose
// only default implementations that provide genuine shared logic
```
```swift
// Associated type when generic parameter suffices
protocol Container {
associatedtype Element
func get() -> Element
}
// use `some` or generic parameter for simple cases
func process(_ item: some Equatable) { ... }
```
---
## 7. Anti-patterns specific to Swift {#antipatterns}
| Anti-pattern | Preferred |
|---|---|
| Force unwrap `!` in production code | `guard let` / `if let` / `??` |
| `try!` outside tests | `do/catch` |
| `class` for plain data | `struct` |
| Deep inheritance hierarchies | protocol composition |
| `@objc` when pure Swift works | native Swift types |
| `NSArray` / `NSDictionary` | `Array` / `Dictionary` |
| `DispatchQueue` in async/await code | `actor` / `MainActor` |
| Implicitly unwrapped optionals as fields | regular optionals or non-optional with init |
| `Any` / `AnyObject` everywhere | generics with protocol constraints |
| Massive `switch` over string values | enum with raw values |
| Singleton pattern (global mutable state) | dependency injection |
## Limitations
- These are language-specific guidelines and do not cover overall architectural decisions.
- Over-compression might reduce readability; apply judgement.
@@ -0,0 +1,286 @@
---
name: typescript
description: "Language-specific super-code guidelines for typescript."
risk: safe
source: community
date_added: "2026-06-16"
---
# TypeScript / JavaScript: Idiomatic Efficiency Reference
## Table of Contents
1. [Array & Object Operations](#arrays)
2. [Destructuring & Spread](#destructuring)
3. [Async / Promises](#async)
4. [Functions & Closures](#functions)
5. [TypeScript Types](#types)
6. [React (if applicable)](#react)
7. [Anti-patterns specific to TS/JS](#antipatterns)
---
## 1. Array & Object Operations {#arrays}
```ts
// ❌ Imperative push loop
const result: string[] = []
for (const item of items) {
if (item.active) result.push(item.name.toUpperCase())
}
// ✅
const result = items.filter(i => i.active).map(i => i.name.toUpperCase())
```
```ts
// ❌ Manual reduce for sum
let total = 0
for (const o of orders) total += o.amount
// ✅
const total = orders.reduce((sum, o) => sum + o.amount, 0)
```
```ts
// ❌ Manual object copy + override
const updated = Object.assign({}, user)
updated.name = "Alice"
// ✅
const updated = { ...user, name: "Alice" }
```
```ts
// ❌ Existence check before property access
const city = user.address ? user.address.city : undefined
// ✅
const city = user.address?.city
```
---
## 2. Destructuring & Spread {#destructuring}
```ts
// ❌ Separate variable assignments
const name = user.name
const age = user.age
// ✅
const { name, age } = user
```
```ts
// ❌ Index access for array elements
const first = arr[0]
const second = arr[1]
// ✅
const [first, second] = arr
```
```ts
// ❌ Merging arrays with concat
const merged = a.concat(b).concat(c)
// ✅
const merged = [...a, ...b, ...c]
```
```ts
// ❌ Omitting a key by delete (mutates)
const copy = { ...obj }
delete copy.password
// ✅ — destructure to omit
const { password, ...safe } = obj
```
---
## 3. Async / Promises {#async}
```ts
// ❌ Promise chain when async/await is cleaner
fetchUser(id)
.then(user => fetchOrders(user.id))
.then(orders => process(orders))
.catch(handleError)
// ✅
try {
const user = await fetchUser(id)
const orders = await fetchOrders(user.id)
process(orders)
} catch (e) {
handleError(e)
}
```
```ts
// ❌ Sequential awaits for independent operations
const user = await fetchUser(id)
const config = await fetchConfig()
// ✅ — run in parallel
const [user, config] = await Promise.all([fetchUser(id), fetchConfig()])
```
```ts
// ❌ Wrapping already-async function in new Promise
const result = await new Promise((resolve) => {
someAsyncFn().then(resolve)
})
// ✅
const result = await someAsyncFn()
```
**Don't `await` inside a `.map()` without `Promise.all` — it sequences what should be parallel.**
---
## 4. Functions & Closures {#functions}
```ts
// ❌ Arrow function with unnecessary block body
const double = (x: number) => { return x * 2 }
// ✅
const double = (x: number) => x * 2
```
```ts
// ❌ Default parameter with if-guard
function greet(name?: string) {
if (!name) name = "World"
return `Hello, ${name}`
}
// ✅
function greet(name = "World") {
return `Hello, ${name}`
}
```
```ts
// ❌ IIFE for no reason in module scope
;(function() {
const x = compute()
doSomething(x)
})()
// ✅ — just top-level statements in a module
const x = compute()
doSomething(x)
```
---
## 5. TypeScript Types {#types}
```ts
// ❌ Explicit return type when inference is obvious
function add(a: number, b: number): number {
return a + b
}
// ✅ — let TS infer simple return types
function add(a: number, b: number) {
return a + b
}
```
```ts
// ❌ any
function process(data: any) { ... }
// ✅ — use unknown + type guard, or a proper type/generic
function process<T extends Record<string, unknown>>(data: T) { ... }
```
```ts
// ❌ Redundant interface for single-use inline shape
interface UserNameProps { name: string }
function UserName({ name }: UserNameProps) { ... }
// ✅ — inline for single-use
function UserName({ name }: { name: string }) { ... }
// Extract interface when reused in 2+ places
```
```ts
// ❌ Type assertion (as) to silence a real type error
const el = document.getElementById("app") as HTMLDivElement
el.innerText = "hi" // crashes if el is null
// ✅
const el = document.getElementById("app")
if (!(el instanceof HTMLDivElement)) throw new Error("Missing #app")
el.innerText = "hi"
```
**Prefer `type` for unions/intersections/aliases; `interface` for extensible object shapes.**
---
## 6. React (if applicable) {#react}
```tsx
// ❌ Effect for derived state
const [doubled, setDoubled] = useState(0)
useEffect(() => { setDoubled(count * 2) }, [count])
// ✅ — compute during render
const doubled = count * 2
```
```tsx
// ❌ useCallback everywhere by default
const handler = useCallback(() => doSomething(id), [id])
// ✅ — only when passed to memoized child or used as effect dep
// Otherwise: const handler = () => doSomething(id)
```
```tsx
// ❌ Passing object literal as prop (new reference each render)
<Component config={{ debug: true }} />
// ✅
const config = useMemo(() => ({ debug: true }), [])
<Component config={config} />
// Or if truly static: define outside component
const CONFIG = { debug: true }
```
```tsx
// ❌ Index as key in list that can reorder/filter
items.map((item, i) => <Row key={i} {...item} />)
// ✅
items.map(item => <Row key={item.id} {...item} />)
```
---
## 7. Anti-patterns specific to TS/JS {#antipatterns}
| Anti-pattern | Preferred |
|---|---|
| `== null` (loose) | `=== null` or `?? / ?.` |
| `typeof x === "undefined"` | `x === undefined` or `x == null` (when both null/undefined ok) |
| `!!x` when boolean coercion is implied | `Boolean(x)` for clarity, or just `x` in conditionals |
| `var` | `const` by default, `let` when reassigned |
| `for...in` on arrays | `for...of` or array methods |
| String template literal with no interpolation | plain string `'...'` |
| `console.log` left in production code | remove or use a logger |
| `Object.keys(obj).forEach(...)` | `for (const [k, v] of Object.entries(obj))` |
| Nested ternaries beyond 2 levels | if/else or early return |
| `try { ... } catch (e) {}` (silent swallow) | log or rethrow |
## Limitations
- These are language-specific guidelines and do not cover overall architectural decisions.
- Over-compression might reduce readability; apply judgement.
@@ -0,0 +1,119 @@
---
name: web-media-getter
description: "One query across free image / video / GIF APIs (stock + historical/archival + GIF engines), returning normalized, license-tagged results with optional top-K download + attribution sidecar. The retrieval peer to local semantic search and generative media."
risk: safe
source: community
source_type: community
source_repo: connerkward/web-media-getter-skill
date_added: "2026-06-16"
author: Conner K Ward
license: MIT
tags:
- media
- images
- video
- gif
- stock
- archival
- attribution
tools:
- claude-code
- antigravity
- cursor
- gemini-cli
- codex-cli
---
## When to Use
Use when a task needs a REAL or ARCHIVAL photo / clip (hero, texture, reference, historical footage) or a reaction / animated GIF, rather than a generated one — fan out across free image/video/GIF sources in one query and download license-tagged results.
_Source: [connerkward/web-media-getter-skill](https://github.com/connerkward/web-media-getter-skill) (MIT)._
# web-media
Query many free image/video sources in one fan-out, get a normalized result list,
optionally download top-K with an attribution sidecar. Zero-dep stdlib script.
**Script:** `webmedia.py` (in this dir). **Keys:** `PEXELS_API_KEY`, `PIXABAY_API_KEY`
in `central/.env` (optional — the 5 no-key sources work without them).
## Sources
| Source | Key? | Best for | Media |
|--------|------|----------|-------|
| openverse | none | CC web images (Flickr, museums) | image |
| wikimedia | none | factual / historical / landmark photos | image |
| internetarchive | none | **historical/archival** images + films | image, video |
| loc | none | historical US prints/photos | image |
| nasa | none | space imagery + video | image, video |
| pexels | free key | modern stock photos + **short video clips** | image, video |
| pixabay | free key | modern photos/illustrations + **short clips** | image, video |
| klipy | free key | **GIFs** — recommended (free, unlimited, Tenor drop-in) | gif |
| giphy | free key | **GIFs** — biggest library (prod key needs approval) | gif |
GIF sources fire only with `--type gif`. Keys: `KLIPY_API_KEY`, `GIPHY_API_KEY`
in `central/.env`. (tenor adapter removed — Google EOL'd the API 2026-06-30.)
**klipy** is the one to get (free + unlimited);
its adapter is **unverified — assumes Tenor-compatible** request/response;
verify against docs.klipy.com when you key it. `webmedia.py "shrug" --type gif --count 6 --json`
## Usage
```bash
webmedia.py "1950s street scene" --type image --count 8 --json
webmedia.py "rocket launch" --type video --source nasa,internetarchive
webmedia.py "car factory 1930s" --source all --download --out /tmp/cars
```
- `--source all` (default) | `nokey` (no-key only) | comma list (`wikimedia,pexels`)
- `--type image|video` · `--count N` · `--json` · `--download --out DIR`
- `--download` fetches each result's direct media URL and writes `attribution.json`
(source, author, license, url, page_url) alongside the files.
## Record schema
`{source, title, url, thumb, dl, page_url, author, license, w, h, type}`
`dl` is the directly-downloadable media URL (None when only a page exists).
## The video caveat (important)
Archival sources (Internet Archive, Europeana, LoC) host **whole films/documentaries**,
not single shots. So:
- **Modern single clip** → `pexels` / `pixabay` (born as short clips, direct MP4). Done.
- **Historical single shot** → retrieve the IA film here, then extract the shot:
- **Twelve Labs** Marengo search (free 600 min) — pass the IA public MP4 URL, get a
timestamped moment for "car on assembly line", clip with ffmpeg. Semantic, cheap.
- or **PySceneDetect** (free, local) to cut the film into shots, then rank keyframes
with CLIP via the `muser` skill. Fully offline.
## Audio: freesound + audio QA
`webmedia.py` is image/video. For **sound effects** (real, CC-licensed) and for
**judging audio** (since Claude can't hear), two sibling scripts live in
`central/scripts/`:
- **`freesound-fetch.py "<query>" [count] [max_sec] [out_dir]`** — searches freesound.org
and downloads short hq-mp3 previews. Prints one JSON line per file with
`license`/`user` for attribution. Key: `FREESOUND_API_KEY` in `central/.env`
(token-based read; full originals would need OAuth — previews suffice for SFX).
- **`audio-judge.py <file> "<target>"`** — sends the clip to OpenAI `gpt-audio`
(audio-native) and returns JSON `{heard, score, matches, suggestion}`, enabling a
generate/fetch → judge → iterate loop. Auto-sources a real `sk-` `OPENAI_API_KEY`
from `.env` (ignores a local `lm-studio` stub env var). Pads sub-2s clips so the
speech-tuned model doesn't refuse. **Caveat:** it reliably *describes* audio and
filters obvious mismatches, but it is NOT a trustworthy judge of subjective qualities
like "grating" — it labels nearly any beep "sharp/high-pitched". Use it to cull, not
to make the final aesthetic call; confirm by ear.
## Where this fits
This is the **internet-retrieval** capability — peer to `muser` (local semantic search)
and `fal` (generate). A future `media` router would fan out across all three and rank
candidates by relevance (CLIP), handing aesthetic spreads to `lookdev`. Don't build that
router until the model demonstrably mis-routes without it.
## Limitations
- Results depend on third-party API availability, quotas, credentials, and license metadata quality.
- License tags and attribution fields must still be reviewed before commercial or public use.
- Relevance ranking can find plausible assets, but final aesthetic fit, brand safety, and audio suitability require human inspection.
@@ -133,11 +133,11 @@ How · Why · What · Best · Full · Real · Free · New · Step-by-Step · Com
## Tags Strategy
Use for every mode that includes tags (A-F). Generate 15-20 tags using this mix:
Use for long-form modes (A-D) that include tags. Generate 14-19 tags using this mix. For short-form (E-F), see the Shorts section for the 5-8 tag rule.
| Type | Count | Rule |
|---|---|---|
| Exact match primary keyword | 2 | Must match Step 1 target keyword exactly |
| Exact match primary keyword | 1 | Must match Step 1 target keyword exactly |
| Broad topic | 3-4 | 1-2 word umbrella terms |
| Long-tail (3-5 words) | 5-6 | Pulled from Step 1 research |
| Question-based | 2 | "how to [topic]", "what is [topic]" |
@@ -311,7 +311,7 @@ Character count: [N]/70
5. [secondary keyword from Step 1 research]
⑤ TAGS
[tag1], [tag2], [tag3] ... [tag15-20 total]
[tag1], [tag2], [tag3] ... [tag14-19 total]
Total character count: [N]/500
⑥ HASHTAGS
@@ -399,7 +399,7 @@ Chapters in description: Yes / No
Links/CTA present: Yes / No
TAGS ANALYSIS
Count: [N] (ideal: 15-20)
Count: [N] (ideal: 14-19)
Tag type coverage: [which of 7 types are missing]
HASHTAG ANALYSIS
@@ -429,7 +429,7 @@ Character count: [N]/70
[Full 3-block description]
④ REWRITTEN TAGS
[15-20 tags across all 7 types]
[14-19 tags across all 7 types]
⑤ REWRITTEN HASHTAGS
#Tag1 #Tag2 #Tag3 #Tag4 #Tag5 [#Tag6 #Tag7 optional]
@@ -507,7 +507,7 @@ Character count: [N]/70
5. [show name + topic]
⑤ TAGS
[tag1], [tag2] ... [tag15-20 — include show + guest name]
[tag1], [tag2] ... [tag14-19 — include show + guest name]
Total: [N]/500
⑥ HASHTAGS
@@ -605,7 +605,7 @@ Timestamps present: Yes / No
Platform links present: Yes / No
TAGS ANALYSIS
Count: [N] (ideal: 15-20)
Count: [N] (ideal: 14-19)
Show / guest name as tags: Yes / No
TOPICS / TIMESTAMPS ANALYSIS
@@ -628,7 +628,7 @@ Character count: [N]/70
[3-block structure, guest bio in Block 2, platform links in Block 3]
④ REWRITTEN TAGS
[15-20 tags including show + guest name]
[14-19 tags including show + guest name]
⑤ REWRITTEN HASHTAGS
#Tag1 #Tag2 #Tag3 #Tag4 #Tag5 [#Tag6 #Tag7 optional]
@@ -866,7 +866,7 @@ Then run the Quality Checklist below.
- [ ] Hashtags on final line only
### Tags & Hashtags
- [ ] 15-20 tags, under 500 chars, all 7 types represented
- [ ] 14-19 tags, under 500 chars, all 7 types represented
- [ ] No hashtag symbols in the tags field
- [ ] 5-8 hashtags (3-5 for short-form), CamelCase, strongest 3 first
- [ ] Hashtag set differs from recent uploads