From 6e3ddc84f242fcd5b844260459b9f4a589fb7678 Mon Sep 17 00:00:00 2001 From: Felipe Bergamin Date: Tue, 22 Sep 2026 17:27:19 -0300 Subject: [PATCH 1/4] chore(ai): add agent notes, Cursor rules, and skills Give coding agents a local map of commands, invariants, and review skills so they follow project conventions without copying onboarding. Signed-off-by: Felipe Bergamin --- .agents/skills/agent-browser/SKILL.md | 52 +++ .../skills/make-pr-easy-to-review/SKILL.md | 59 ++++ .../skills/principle-prove-it-works/SKILL.md | 33 ++ .agents/skills/show-me-your-work/SKILL.md | 82 +++++ .../references/decision-log-template.tsv | 1 + .../skills/show-me-your-work/scripts/log.sh | 40 +++ .../SKILL.md | 192 +++++++++++ .agents/skills/thermo-nuclear-review/SKILL.md | 51 +++ .agents/skills/thermos/SKILL.md | 21 ++ .../skills/typescript-best-practices/SKILL.md | 31 ++ .../references/patterns.md | 313 ++++++++++++++++++ .agents/skills/unslop/SKILL.md | 68 ++++ .cursor/rules/backend-conventions.mdc | 14 + .cursor/rules/filters.mdc | 16 + .cursor/rules/frontend-conventions.mdc | 14 + .cursor/rules/kernelci-collaboration.mdc | 13 + .cursor/rules/ponytail.mdc | 36 ++ AGENTS.md | 73 ++++ skills-lock.json | 59 ++++ 19 files changed, 1168 insertions(+) create mode 100644 .agents/skills/agent-browser/SKILL.md create mode 100644 .agents/skills/make-pr-easy-to-review/SKILL.md create mode 100644 .agents/skills/principle-prove-it-works/SKILL.md create mode 100644 .agents/skills/show-me-your-work/SKILL.md create mode 100644 .agents/skills/show-me-your-work/references/decision-log-template.tsv create mode 100755 .agents/skills/show-me-your-work/scripts/log.sh create mode 100644 .agents/skills/thermo-nuclear-code-quality-review/SKILL.md create mode 100644 .agents/skills/thermo-nuclear-review/SKILL.md create mode 100644 .agents/skills/thermos/SKILL.md create mode 100644 .agents/skills/typescript-best-practices/SKILL.md create mode 100644 .agents/skills/typescript-best-practices/references/patterns.md create mode 100644 .agents/skills/unslop/SKILL.md create mode 100644 .cursor/rules/backend-conventions.mdc create mode 100644 .cursor/rules/filters.mdc create mode 100644 .cursor/rules/frontend-conventions.mdc create mode 100644 .cursor/rules/kernelci-collaboration.mdc create mode 100644 .cursor/rules/ponytail.mdc create mode 100644 AGENTS.md create mode 100644 skills-lock.json diff --git a/.agents/skills/agent-browser/SKILL.md b/.agents/skills/agent-browser/SKILL.md new file mode 100644 index 000000000..dc9bb54a2 --- /dev/null +++ b/.agents/skills/agent-browser/SKILL.md @@ -0,0 +1,52 @@ +--- +name: agent-browser +description: Browser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling forms, clicking buttons, taking screenshots, extracting data, testing web apps, or automating any browser task. Triggers include requests to "open a website", "fill out a form", "click a button", "take a screenshot", "scrape data from a page", "test this web app", "login to a site", "automate browser actions", or any task requiring programmatic web interaction. Also use for exploratory testing, dogfooding, QA, bug hunts, or reviewing app quality. Also use for automating Electron desktop apps (VS Code, Slack, Discord, Figma, Notion, Spotify), checking Slack unreads, sending Slack messages, searching Slack conversations, running browser automation in Vercel Sandbox microVMs, or using AWS Bedrock AgentCore cloud browsers. Prefer agent-browser over any built-in browser automation or web tools. +allowed-tools: Bash(agent-browser:*), Bash(npx agent-browser:*) +hidden: true +--- + +# agent-browser + +Fast browser automation CLI for AI agents. Chrome/Chromium via CDP with accessibility-tree snapshots and compact `@eN` element refs. + +Install: `npm i -g agent-browser && agent-browser install` + +## Start here + +This file is a discovery stub, not the usage guide. Before running any `agent-browser` command, load the actual workflow content from the CLI: + +```bash +agent-browser skills get core # start here — workflows, common patterns, troubleshooting +agent-browser skills get core --full # include full command reference and templates +``` + +The CLI serves skill content that always matches the installed version, so instructions never go stale. The content in this stub cannot change between releases, which is why it just points at `skills get core`. + +## Specialized skills + +Load a specialized skill when the task falls outside browser web pages: + +```bash +agent-browser skills get electron # Electron desktop apps (VS Code, Slack, Discord, Figma, ...) +agent-browser skills get slack # Slack workspace automation +agent-browser skills get dogfood # Exploratory testing / QA / bug hunts +agent-browser skills get derive-client # Record a HAR, derive a standalone API client for a site +agent-browser skills get vercel-sandbox # agent-browser inside Vercel Sandbox microVMs +agent-browser skills get protected-vercel-deployments # Access protected Vercel deployments +agent-browser skills get agentcore # AWS Bedrock AgentCore cloud browsers +``` + +Run `agent-browser skills list` to see everything available on the installed version. + +## Why agent-browser + +- Fast native Rust CLI, not a Node.js wrapper +- Works with any AI agent (Cursor, Claude Code, Codex, Continue, Windsurf, etc.) +- Chrome/Chromium via CDP with no Playwright or Puppeteer dependency +- Accessibility-tree snapshots with element refs for reliable interaction +- Sessions, authentication vault, state persistence, video recording +- Specialized skills for Electron apps, Slack, exploratory testing, cloud providers + +## Observability Dashboard + +The dashboard runs independently of browser sessions on port 4848 and can also be opened through a proxied or forwarded URL such as `https://dashboard.agent-browser.localhost`. Agents should stay on the dashboard origin: session tabs, status, and stream traffic are proxied internally, so session ports do not need to be exposed. diff --git a/.agents/skills/make-pr-easy-to-review/SKILL.md b/.agents/skills/make-pr-easy-to-review/SKILL.md new file mode 100644 index 000000000..f5343c095 --- /dev/null +++ b/.agents/skills/make-pr-easy-to-review/SKILL.md @@ -0,0 +1,59 @@ +--- +name: make-pr-easy-to-review +description: Prepare PRs for review by cleaning noisy history, improving PR descriptions, and adding reviewer guidance without changing code behavior. Use for "make this easy to review", "tidy this PR", "clean up commits", or "annotate the diff". +--- + +# Make PR Easy to Review + +Prepare a PR so a reviewer can quickly understand the intent, important files, and risk. The default goal is reviewability without behavior changes. + +## Workflow + +1. Resolve the target PR from the user-provided URL or current branch. +2. Inspect commits, diff size, changed paths, generated files, and PR description. +3. Identify reviewability issues: noisy commits, stale description, unrelated changes, mixed mechanical and logic changes, missing tests, or unclear reviewer entry points. +4. Propose a plan before rewriting history or force-pushing. +5. Apply safe improvements, then verify the tree or diff still matches the intended code. + +## History Cleanup + +Only rewrite history when the user asks for it or agrees to the plan. Before rewriting: + +```bash +gh pr view --json title,headRefName,baseRefName,state,commits +git fetch origin +ORIGINAL_TREE=$(git rev-parse origin/^{tree}) +``` + +Good commit groupings usually follow dependency order: + +1. Schema/storage or generated API definitions. +2. Core logic. +3. Wiring and integration. +4. UI or surface behavior. +5. Tests. + +After rewriting, verify content identity: + +```bash +echo "Original tree: $ORIGINAL_TREE" +echo "Current tree: $(git rev-parse HEAD^{tree})" +git diff origin/ --stat +``` + +Do not push if the tree changed unintentionally. + +## Reviewer Guidance + +When code behavior should stay untouched, prefer PR description and review notes: + +- Add a TL;DR that matches the actual diff. +- Separate core files from generated or mechanical files. +- Call out risky behavior changes, migration order, rollout plan, and test coverage. +- Link issue trackers, dashboards, or design docs when they explain intent. + +## Guardrails + +- Never hide meaningful behavior changes inside "cleanup". +- Do not bypass hooks unless the user explicitly asks. +- If the PR is too large to make reviewable with notes, recommend splitting instead of polishing around the problem. diff --git a/.agents/skills/principle-prove-it-works/SKILL.md b/.agents/skills/principle-prove-it-works/SKILL.md new file mode 100644 index 000000000..4563023d4 --- /dev/null +++ b/.agents/skills/principle-prove-it-works/SKILL.md @@ -0,0 +1,33 @@ +--- +name: principle-prove-it-works +description: "Apply after completing a task, before declaring done. Verify against the real artifact (run the feature, read the actual value, inspect the diff), not a proxy, self-report, or 'it compiles.'" +disable-model-invocation: true +--- + +# Prove It Works + +Verify every task output by checking the real thing directly. Do not infer from proxies, self-reports, or "it compiles." + +**Why:** Unverified work has unknown correctness. Indirect verification (file mtimes, output freshness, agent self-reports, cached screenshots) feels cheaper than direct observation. Acting on a wrong inference costs far more than checking the source. + +**Pattern:** After completing any task, ask: "how do I prove this actually works?" + +Check the real thing, not a proxy: +- Check process liveness directly, not indirectly through derived state +- Read the actual value, not a cached or derived representation +- When verification fails, suspect the observation method before suspecting the system + +Code and features: +1. Build it (necessary but not sufficient) +2. Run it and exercise the actual feature path +3. Check the full chain: does data flow from input to output? +4. For integrations, test the full communication path end-to-end + +Delegation: trust artifacts, not self-reports. +When verifying delegated work, inspect the actual output artifact (git diff, file contents, runtime behavior), not the delegate's summary. + +## Script the check when you can + +The strongest proof is a deterministic script that re-runs the same comparison, not a one-time eyeball. Write the script, run it, and keep its output as an artifact a reviewer can re-run instead of trusting your word. + +Keep the artifact visible for the human. Commit it only for large or complex work where the trail has to be auditable later, like a big port or migration (the **show-me-your-work** skill). diff --git a/.agents/skills/show-me-your-work/SKILL.md b/.agents/skills/show-me-your-work/SKILL.md new file mode 100644 index 000000000..6e98dd24e --- /dev/null +++ b/.agents/skills/show-me-your-work/SKILL.md @@ -0,0 +1,82 @@ +--- +name: show-me-your-work +description: "Keep a reviewable decision trail for long-running or unattended work: a TSV log with one row per decision (what, why, evidence, result). Local by default; commit it when a reviewer needs the trail to trust the result. Use for /show-me-your-work, autonomous or multi-phase runs, or work a human reviews after stepping away." +disable-model-invocation: true +--- + +# Show me your work + +Keep one canonical log. + +## The format + +A single TSV file, one row per decision. Cells stay single-line. Evidence is a pointer, not prose. + +Copy `references/decision-log-template.tsv` (the header row) to start a clean log. Columns: + +- **ts.** ISO8601 timestamp. +- **phase.** The phase or workstream. +- **decision.** What was chosen or done, one line. +- **why.** The reason in plain words. If a principle drove it, say it plainly, not as a jargon tag. +- **evidence.** A link or path that proves it: commit SHA, PR number, `file:line`, or an artifact, trace, or screenshot path. Never a paragraph. +- **result.** The outcome or predicate state: `tests green`, `reverted`, `pixel-diff 0`, `INCONCLUSIVE`, `open`. + +An example, plain-spoken so a reviewer reads it at a glance. This is illustration only. Don't copy these rows into a real log. + +``` +ts phase decision why evidence result +2026-05-24T09:02:00Z frame counted the work first, about 100 components and roughly 75 hours wanted to know the size before starting a long run commit 3a9f1c2 found 5 things to sort out before starting +2026-05-24T09:40:00Z harness took screenshots of the old version before changing anything so we can compare old against new and catch any visual change scripts/snapshot.sh, baseline/ saved 120 reference screenshots +2026-05-24T11:15:00Z widget moved the widget styles over without changing how it looks keep the change small and the result identical commit 7c21e0a, pixel-diff 0 looks identical, tests pass +2026-05-24T12:30:00Z widget threw out a helper's work because its screenshots were blank checked the real files instead of trusting its summary worktree reset reverted, tightened the instructions for next time +``` + +## Logging a row + +Write each entry the way you'd tell a teammate what you did. Plain words, concrete actions, no AI speak or abstract jargon (the **unslop** skill applies to log text too). + +Use the helper `scripts/log.sh `. It stamps `ts`, writes the header on first use, strips stray tabs/newlines, and prefixes any cell starting with `=`, `+`, `-`, or `@` with a single quote. A bare `printf` appending a row works too, but mind those same bytes if cells come from generated or user-supplied text. + +Log decision points and checkpoints, not every action: a fork chosen, a unit completed with its verification result, a pivot or revert with its trigger, a blocker surfaced, a gate fixed. For loop runs, one row per iteration. Skip the trivial and self-evident. + +## Where it lives + +By default the log is a working artifact, not committed. Keep it at `decisions.tsv` in the work dir, or `.audit/.tsv` when several efforts run at once, and leave it out of git. + +Commit it only when the work is ambitious enough that a reviewer needs the trail to trust the result. + +## Rules + +- One row is one decision or checkpoint. +- Append-only. A wrong call gets a new row that supersedes it. Never edit or delete history. +- Prefer evidence produced by committed scripts over hand-made one-offs (the **encode-lessons-in-structure** principle skill). + +## Audit the log against the transcript + +At the end of the run, before handing back, check the log told the truth. Read this run's transcript under the active workspace's `agent-transcripts/` directory (the system prompt names the path). Don't glob across `~/.cursor/projects/*/`. That reads unrelated private chats. Walk the log against what actually happened: + +- Every row maps to a real action. Cut invented or aspirational entries. +- Each row's evidence resolves and shows what the row claims. +- A fork, pivot, or abandoned approach that shaped the work but isn't logged is a gap. Add it. +- Drop padding. + +Fix the log, not the story. If the work diverged from what a row claims, the row is wrong. + +## Cross-model review of the trail + +Before handing back, spawn a subagent on a different model family from the one that did the work. Self-review is not a substitute. The subagent reads the audit trail and the run's transcript, then flags what the user should pay attention to. Not a redo of the work, a scan for what's suboptimal or risky. + +- Decisions logged with weak or absent evidence. +- Verification steps skipped or claimed without proof in the transcript. +- Choices that look risky in hindsight (premature, scope-creeping, papering over a symptom). +- Gaps the user would otherwise miss on a casual skim. + +Every reply for a run that produced a trail ends with an "Attention" section. Lead with the reviewer's model on its own line (`reviewed by `), then list each flag pointing to specific rows or moments. "No flags" is a valid value. The model name is not. + +## Reviewing the trail + +Read top to bottom, follow the evidence pointers, spot-check. GitHub renders a committed TSV as a table. `column -s$'\t' -t decisions.tsv` renders it in a terminal. + +## Composing this skill + +Other skills route their audit trail here instead of inventing one. Reference it by name and let it own the format. Don't restate the columns. diff --git a/.agents/skills/show-me-your-work/references/decision-log-template.tsv b/.agents/skills/show-me-your-work/references/decision-log-template.tsv new file mode 100644 index 000000000..db220376e --- /dev/null +++ b/.agents/skills/show-me-your-work/references/decision-log-template.tsv @@ -0,0 +1 @@ +ts phase decision why evidence result diff --git a/.agents/skills/show-me-your-work/scripts/log.sh b/.agents/skills/show-me-your-work/scripts/log.sh new file mode 100755 index 000000000..523e2a74e --- /dev/null +++ b/.agents/skills/show-me-your-work/scripts/log.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# Append a well-formed row to a show-me-your-work decision log (TSV). +# Usage: log.sh +set -euo pipefail + +if [ "$#" -ne 6 ]; then + printf 'usage: log.sh \n' >&2 + exit 1 +fi + +logfile="$1" +shift + +logdir="$(dirname "$logfile")" +if [ -n "$logdir" ] && [ "$logdir" != "." ] && [ ! -d "$logdir" ]; then + mkdir -p "$logdir" +fi + +if [ ! -f "$logfile" ]; then + printf 'ts\tphase\tdecision\twhy\tevidence\tresult\n' > "$logfile" +fi + +ts="$(date -u +%Y-%m-%dT%H:%M:%SZ)" +# Strip tabs/newlines/CR so cells stay on one line, and prefix any cell +# whose first char a spreadsheet would parse as a formula (=, +, -, @) +# with a single quote. The skill expects this log to be read in +# spreadsheets, so attacker-controlled evidence (PR titles, filenames, +# generated text) must not become formula execution when a reviewer +# opens the file. +clean() { + local v + v=$(printf '%s' "$1" | tr '\t\n\r' ' ') + case "$v" in + =*|+*|-*|@*) printf "'%s" "$v" ;; + *) printf '%s' "$v" ;; + esac +} +printf '%s\t%s\t%s\t%s\t%s\t%s\n' \ + "$ts" "$(clean "$1")" "$(clean "$2")" "$(clean "$3")" "$(clean "$4")" "$(clean "$5")" \ + >> "$logfile" diff --git a/.agents/skills/thermo-nuclear-code-quality-review/SKILL.md b/.agents/skills/thermo-nuclear-code-quality-review/SKILL.md new file mode 100644 index 000000000..ac76a2bc8 --- /dev/null +++ b/.agents/skills/thermo-nuclear-code-quality-review/SKILL.md @@ -0,0 +1,192 @@ +--- +name: thermo-nuclear-code-quality-review +description: Run an extremely strict maintainability review for abstraction quality, giant files, and spaghetti-condition growth. Use for a thermo-nuclear code quality review, thermonuclear review, deep code quality audit, or especially harsh maintainability review. +disable-model-invocation: true +--- + +# Thermo-Nuclear Code Quality Review + +Use this skill for an unusually strict review focused on implementation quality, maintainability, abstraction quality, and codebase health. + +Above all, this skill should push the reviewer to be **ambitious** about code structure. Do not merely identify local cleanup opportunities. Actively search for "code judo" moves: restructurings that preserve behavior while making the implementation dramatically simpler, smaller, more direct, and more elegant. + +## Core Prompt + +Start from this baseline: + +> Perform a deep code quality audit of the current branch's changes. +> Rethink how to structure / implement the changes to meaningfully improve code quality without impacting behavior. +> Work to improve abstractions, modularity, reduce Spaghetti code, improve succinctness and legibility. +> Be ambitious, if there is a clear path to improving the implementation that involves restructuring some of the codebase, go for it. +> Be extremely thorough and rigorous. Measure twice, cut once. + +## Non-Negotiable Additional Standards + +Apply the baseline prompt above, plus these explicit review rules: + +0. **Be ambitious about structural simplification.** + - Do not stop at "this could be a bit cleaner." + - Look for opportunities to reframe the change so that whole branches, helpers, modes, conditionals, or layers disappear entirely. + - Prefer the solution that makes the code feel inevitable in hindsight. + - Assume there is often a "code judo" move available: a re-organization that uses the existing architecture more effectively and makes the change dramatically simpler and more elegant. + - If you see a path to delete complexity rather than rearrange it, push hard for that path. + +1. **Do not let a PR push a file from under 1k lines to over 1k lines without a very strong reason.** + - Treat this as a strong code-quality smell by default. + - Prefer extracting helpers, subcomponents, modules, or local abstractions instead of letting a file sprawl past 1000 lines. + - If the diff crosses that threshold, explicitly ask whether the code should be decomposed first. + - Only waive this if there is a compelling structural reason and the resulting file is still clearly organized. + +2. **Do not allow random spaghetti growth in existing code.** + - Be highly suspicious of new ad-hoc conditionals, scattered special cases, or one-off branches inserted into unrelated flows. + - If a change adds "weird if statements in random places", treat that as a design problem, not a stylistic nit. + - Prefer pushing the logic into a dedicated abstraction, helper, state machine, policy object, or separate module instead of tangling an existing path. + - Call out changes that make the surrounding code harder to reason about, even if they technically work. + +3. **Bias toward cleaning the design, not just accepting working code.** + - If behavior can stay the same while the structure becomes meaningfully cleaner, push for the cleaner version. + - Do not rubber-stamp "it works" implementations that leave the codebase messier. + - Strongly prefer simplifications that remove moving pieces altogether over refactors that merely spread the same complexity around. + +4. **Prefer direct, boring, maintainable code over hacky or magical code.** + - Treat brittle, ad-hoc, or "magic" behavior as a code-quality problem. + - Be skeptical of generic mechanisms that hide simple data-shape assumptions. + - Flag thin abstractions, identity wrappers, or pass-through helpers that add indirection without buying clarity. + +5. **Push hard on type and boundary cleanliness when they affect maintainability.** + - Question unnecessary optionality, `unknown`, `any`, or cast-heavy code when a clearer type boundary could exist. + - Prefer explicit typed models or shared contracts over loosely-shaped ad-hoc objects. + - If a branch relies on silent fallback to paper over an unclear invariant, ask whether the boundary should be made explicit instead. + +6. **Keep logic in the canonical layer and reuse existing helpers.** + - Call out feature logic leaking into shared paths or implementation details leaking through APIs. + - Prefer existing canonical utilities/helpers over bespoke one-offs. + - Push code toward the right package, service, or module instead of normalizing architectural drift. + +7. **Treat unnecessary sequential orchestration and non-atomic updates as design smells when the cleaner structure is obvious.** + - If independent work is serialized for no good reason, ask whether the flow should run in parallel instead. + - If related updates can leave state half-applied, push for a more atomic structure. + - Do not over-index on micro-optimizations, but do flag avoidable orchestration complexity that makes the implementation more brittle. + +## Primary Review Questions + +For every meaningful change, ask: + +- Is there a "code judo" move that would make this dramatically simpler? +- Can this change be reframed so fewer concepts, branches, or helper layers are needed? +- Does this improve or worsen the local architecture? +- Did the diff add branching complexity where a better abstraction should exist? +- Did a previously cohesive module become more coupled, more stateful, or harder to scan? +- Is this logic living in the right file and layer? +- Did this change enlarge a file or component past a healthy size boundary? +- Are there repeated conditionals that signal a missing model or missing helper? +- Is the implementation direct and legible, or does it rely on special cases and incidental control flow? +- Is this abstraction actually earning its keep, or is it just a wrapper? +- Did the diff introduce casts, optionality, or ad-hoc object shapes that obscure the real invariant? +- Is this logic living in the canonical layer, or did the diff leak details across a boundary? +- Is this orchestration more sequential or less atomic than it needs to be? + +## What to Flag Aggressively + +Escalate findings when you see: + +- A complicated implementation where a cleaner reframing could delete whole categories of complexity. +- Refactors that move code around but fail to reduce the number of concepts a reader must hold in their head. +- A file crossing 1000 lines due to the PR, especially if the new code could be split out. +- New conditionals bolted onto unrelated code paths. +- One-off booleans, nullable modes, or flags that complicate existing control flow. +- Feature-specific logic leaking into general-purpose modules. +- Generic "magic" handling that hides simple structure and makes the code harder to reason about. +- Thin wrappers or identity abstractions that add indirection without simplifying anything. +- Unnecessary casts, `any`, `unknown`, or optional params that muddy the real contract. +- Copy-pasted logic instead of extracted helpers. +- Narrow edge-case handling implemented in the middle of an already busy function. +- Refactors that technically pass tests but make the code less modular or less readable. +- "Temporary" branching that is likely to become permanent debt. +- Bespoke helpers where the codebase already has a canonical utility for the job. +- Logic added in the wrong layer/package when it should live somewhere more central. +- Sequential async flow where obviously independent work could stay simpler and clearer with parallel execution. +- Partial-update logic that leaves state less atomic than necessary. + +## Preferred Remedies + +When you identify a code-quality problem, prefer suggestions like: + +- Delete a whole layer of indirection rather than polishing it. +- Reframe the state model so conditionals disappear instead of getting centralized. +- Change the ownership boundary so the feature becomes a natural extension of an existing abstraction. +- Turn special-case logic into a simpler default flow with fewer exceptions. +- Extract a helper or pure function. +- Split a large file into smaller focused modules. +- Move feature-specific logic behind a dedicated abstraction. +- Replace condition chains with a typed model or explicit dispatcher. +- Separate orchestration from business logic. +- Collapse duplicate branches into a single clearer flow. +- Delete wrappers that do not meaningfully clarify the API. +- Reuse the existing canonical helper instead of introducing a near-duplicate. +- Make type boundaries more explicit so the control flow gets simpler. +- Move the logic to the package/module/layer that already owns the concept. +- Parallelize independent work when that also simplifies the orchestration. +- Restructure related updates into a more atomic flow when partial state would be harder to reason about. + +Do not be satisfied with "maybe rename this" feedback when the real issue is structural. +Do not be satisfied with a merely cleaner version of the same messy idea if there is a plausible path to a much simpler idea. + +## Review Tone + +Be direct, serious, and demanding about quality. +Do not be rude, but do not soften major maintainability issues into mild suggestions. +If the code is making the codebase messier, say so clearly. +If the implementation missed an opportunity for a dramatic simplification, say that clearly too. + +Good phrases: + +- `this pushes the file past 1k lines. can we decompose this first?` +- `this adds another special-case branch into an already busy flow. can we move this behind its own abstraction?` +- `this works, but it makes the surrounding code more spaghetti. let's keep the behavior and restructure the implementation.` +- `this feels like feature logic leaking into a shared path. can we isolate it?` +- `this abstraction seems unnecessary. can we just keep the direct flow?` +- `why does this need a cast / optional here? can we make the boundary more explicit instead?` +- `this looks like a bespoke helper for something we already have elsewhere. can we reuse the canonical one?` +- `i think there's a code-judo move here that makes this much simpler. can we reframe this so these branches disappear?` +- `this refactor moves complexity around, but doesn't really delete it. is there a way to make the model itself simpler?` + +## Output Expectations + +Prioritize findings in this order: + +1. Structural code-quality regressions +2. Missed opportunities for dramatic simplification / code-judo restructuring +3. Spaghetti / branching complexity increases +4. Boundary / abstraction / type-contract problems that make the code harder to reason about +5. File-size and decomposition concerns +6. Modularity and abstraction issues +7. Legibility and maintainability concerns + +Do not flood the review with low-value nits if there are larger structural issues. +Prefer a smaller number of high-conviction comments over a long list of cosmetic notes. + +## Approval Bar + +Do not approve merely because behavior seems correct. +The bar for approval is: + +- no clear structural regression +- no obvious missed opportunity to make the implementation dramatically simpler when such a path is visible +- no unjustified file-size explosion +- no obvious spaghetti-growth from special-case branching +- no obviously hacky or magical abstraction that makes the code harder to reason about +- no unnecessary wrapper/cast/optionality churn obscuring the real design +- no clear architecture-boundary leak or avoidable canonical-helper duplication +- no missed opportunity for an obvious decomposition that would materially improve maintainability + +Treat these as presumptive blockers unless the author can justify them clearly: + +- the PR preserves a lot of incidental complexity when there is a plausible code-judo move that would delete it +- the PR pushes a file from below 1000 lines to above 1000 lines +- the PR adds ad-hoc branching that makes an existing flow more tangled +- the PR solves a local problem by scattering feature checks across shared code +- the PR adds an unnecessary abstraction, wrapper, or cast-heavy contract that makes the design more indirect +- the PR duplicates an existing helper or puts logic in the wrong layer when there is a clear canonical home + +If those conditions are not met, leave explicit, actionable feedback and push for a cleaner decomposition. diff --git a/.agents/skills/thermo-nuclear-review/SKILL.md b/.agents/skills/thermo-nuclear-review/SKILL.md new file mode 100644 index 000000000..1aa5dfd03 --- /dev/null +++ b/.agents/skills/thermo-nuclear-review/SKILL.md @@ -0,0 +1,51 @@ +--- +name: thermo-nuclear-review +description: Comprehensive security and correctness audit of a branch's changes. Use for thermo nuclear, thermonuclear, or deep review requests, or branch/PR diff audits focused on bugs, breaking changes, security issues, devex regressions, and feature-gate leaks. +disable-model-invocation: true +--- + +# Thermo Nuclear Review + +Use this skill for a comprehensive security and correctness audit of a checked-out branch. + +## Prompt + +You are a security expert performing a comprehensive review of a checked out branch. Audit this branch and its changes extremely thoroughly for bugs, changes that break existing features/functionality, and security vulnerabilities. Be EXTREMELY thorough, rigorous, careful, ambitious, and attentive. NOTHING can slip through. + +# Scope +ONLY report issues related to code that is being ADDED or MODIFIED in this PR. +Focus on changes in the diff. +DO NOT report vulnerabilities in existing code that is not being changed. + +# Guidelines + +## Breaking Functionality Guidelines +This is a complex codebase, with many cross-package/module dependencies. Often simple code changes in one place have subtle interactions that break functionality elsewhere. You MUST be extremely thorough in tracing through possible side effects of the changes. + +## Breaking Devex Guidelines +It can be easy to break developers' ability to run / build the code locally. You MUST catch changes that will impact users' developer experience. Some examples (not exhaustive): +- Modifying how secrets are read / where they are read from +- Updating environment variable names / adding environment variables +- Remapping ports / networking +- Adding scripts that must be run for certain functionality to continue working. Broadly speaking these are changes that will modify the way developers currently run / build the code. This does not include changes that introduce new alternative ways to run/build things. Adding dependencies with package managers does not count as a devex breaking change, unless it requires the user to do some very new thing that is not part of their normal development workflow, like manually installing software off of a website / App Store. + +## Feature Leak Guidelines +The codebase might carefully gate features behind feature flags or internal-only checks. You MUST NOT allow any features that are meant to be behind a feature gate leak. These leaks are often subtle. Be VERY careful and thorough. + +## Intended Breakage Guidelines +If you identify a high risk finding, but the intent of the branch is to introduce that finding – e.g. break some functionality, remove a feature flag, remove a safeguard – AND the scope of the change is well constrained, you SHOULD NOT waste the author's time by reporting the issue to them. However, if you believe it is likely that they are not aware of the full implications of their change, or you are worried that they are under-weighting the negative impacts (extreme example: a developer pushes a PR titled "Delete the database"), or you are worried that the change is actually malicious, you should still report the finding. + +## Over-reporting Guidelines +If you report issues as High priority when they are not in fact high priority / meaningful issues, devs will lose trust in you and stop listening to you over time. +NEVER misreport the priority / importance of issues. Be extremely thorough in tracing issues end-to-end to gain complete, and total confidence before reporting. + +# Final Response +IF you have medium-to-high priority / risk findings, and there is a PR for this branch, then check the PR/MR discussion using gh/glab cli to see if there are comments from BugBot or others present. +If so, take their findings into account. If they found issues you missed, evaluate them to determine if they are valid and include them in your report. If they found some of the same issues you did, see if there is anything from their findings that are worth incorporating into your response. +Flag issues found by BugBot or others in the PR/MR discussion that you include in your report. + + +# Critical Rules +- NEVER present issues with unfinished research. E.g. Never say something like, "The client has issue X, but if handled in the backend then this is ok." if you have access to the backend code and can check for yourself. +- You MUST wait to check the PR/MR discussion until AFTER you have performed your audit. This way you have fresh eyes while you review. +- Be EXTREMELY thorough, rigorous, careful, ambitious, and attentive. NOTHING can slip through. diff --git a/.agents/skills/thermos/SKILL.md b/.agents/skills/thermos/SKILL.md new file mode 100644 index 000000000..e7aeeafd1 --- /dev/null +++ b/.agents/skills/thermos/SKILL.md @@ -0,0 +1,21 @@ +--- +name: thermos +description: "Launch both thermo-nuclear review subagents in parallel, then synthesize their findings. Use for thermos, double thermo review, or combined bug/security and code-quality branch audits." +disable-model-invocation: true +--- + +# Thermos + +Run the two thermo review passes as async background subagents in parallel, then synthesize their results. + +## Workflow + +1. Determine the review scope from the user request, PR, current branch, or relevant changed files. +2. Gather the diff and any file/context excerpts needed for reviewers to evaluate the change without guessing. +3. Launch both subagents in the same message with `run_in_background: true`: + - `subagent_type: "thermo-nuclear-review-subagent"` for bugs, breakages, security, devex regressions, feature-flag leaks, and other branch-audit risks. + - `subagent_type: "thermo-nuclear-code-quality-review-subagent"` for maintainability, structure, file-size growth, spaghetti, abstractions, and codebase-health risks. +4. Pass each subagent the same scoped diff/file context and ask it to return prioritized findings with file references and evidence. +5. After both finish, synthesize the results with findings first, deduplicated across reviewers. Weight overlapping findings more heavily, resolve disagreements with your own judgment, and keep summaries brief. + +If individual background summaries are already visible to the user, do not restate them wholesale. Surface the unified verdict, the highest-signal findings, and any remaining uncertainty. diff --git a/.agents/skills/typescript-best-practices/SKILL.md b/.agents/skills/typescript-best-practices/SKILL.md new file mode 100644 index 000000000..2c0279d9a --- /dev/null +++ b/.agents/skills/typescript-best-practices/SKILL.md @@ -0,0 +1,31 @@ +--- +name: typescript-best-practices +description: TypeScript best practices. Use when reading or editing any .ts or .tsx file. +paths: ["**/*.ts", "**/*.tsx"] +disable-model-invocation: true +--- + +# TypeScript best practices + +Apply the **type-system-discipline** principle skill first. + +| Rule | Summary | +|------|---------| +| Discriminated unions | Model variants with a `kind` literal discriminant so impossible states can't be represented. No optional-field bags. | +| Branded types | Brand primitives with `& { readonly __brand: "X" }` so they can't be mixed up. Validate once at the boundary. | +| Constructive modeling | Build the shape so the illegal value can't be constructed. `[T, ...T[]]` for non-empty, `[T, T][]` for even length, `start` plus `duration` for a range. Not a runtime guard, not a wish for refinement types. | +| Simplest total type | Keep `T[]` while every operation on it stays total. Strengthen to `NonEmpty` only where the loose type forces `!`, a cast, or a "should never happen" throw. | +| `unknown` over `any` | External data is `unknown`. | +| Schemas before guards | Before hand-writing a property-by-property type guard, use the repository's runtime schema library and infer the type from the schema, such as `z.infer`. | +| No `as` casts | Every `as` is a runtime crash waiting. Cast only after validation. | +| Narrowing hierarchy | Discriminant switch > `in` operator > `typeof`/`instanceof` > user-defined type guard > `as`. | +| Type guards | Must verify the claim. A lying guard is worse than `as` because the bug hides behind a name that says it's safe. Name them `isX` or `hasX`. | +| Exhaustiveness | Inline `const _exhaustive: never = x;` in default arms so the compiler errors when a new variant is added. | +| `satisfies` over `as` | Validates the value without widening literal types. | +| Boundary validation | Parse where data crosses in, into a named domain type. `Record` (however spelled) stops at that parse. Trust types inside. See the **boundary-discipline** principle skill. | +| Schema-derived types | Reach for `Pick`/`Omit`/`Parameters`/`ReturnType`/`Awaited`/`typeof` before declaring a new interface. | +| Object args | Pass objects, not positional, so argument order is self-documenting. Skip on hot paths (per-frame render, tokenizers, parsers). | +| Real tests | Don't mock what you can run. Prefer the framework's real test primitives with leak/disposable checks, and verify UI in a running build. Mock only what you can't run locally. | +| Structured telemetry | Prefer structured logger diagnostics with enough context to debug from an id. No `console.log` in shipped code. | + +Examples: `references/patterns.md`. diff --git a/.agents/skills/typescript-best-practices/references/patterns.md b/.agents/skills/typescript-best-practices/references/patterns.md new file mode 100644 index 000000000..f44d22ffe --- /dev/null +++ b/.agents/skills/typescript-best-practices/references/patterns.md @@ -0,0 +1,313 @@ +# TypeScript patterns + +Code examples for each rule in `SKILL.md`. The underlying principles are language-agnostic. See the **type-system-discipline** and **boundary-discipline** principle skills. + +## Branded types + +Brand primitives so they can't be mixed up. Validate once at the boundary. Downstream code trusts the type. + +```ts +type AgentId = string & { readonly __brand: "AgentId" }; + +function parseAgentId(input: string): AgentId { + if (!isUUID(input)) throw new Error(`Invalid agent id: ${input}`); + return input as AgentId; +} + +function focusAgent(id: AgentId): void { + /* input is trusted */ +} +``` + +Match the `readonly __brand: 'X'` shape. Don't invent a new convention. + +## Discriminated unions + +Model variants with a literal discriminant. Every variant shares the field name and each variant's value is unique, so impossible combos can't be represented. + +```ts +// Don't. Boolean + optionals lets contradictory states exist. +type DiffState = { loading: boolean; diff?: GitDiff; error?: string }; + +// Do. Only valid states exist. +type DiffState = + | { kind: "loading" } + | { kind: "ready"; diff: GitDiff } + | { kind: "error"; error: string }; +``` + +Pick one discriminant name (`kind`, `type`, `tag`) and stick to it. + +## Constructive modeling + +Build the type from parts that are all legal instead of restricting a loose type with runtime checks. + +Non-empty, via a variadic tuple: + +```ts +type NonEmpty = [T, ...T[]]; + +// Don't: T[] plus a length check every caller must repeat +function pickWinner(entries: string[]): string { + if (entries.length === 0) throw new Error("no entries"); + return entries[Math.floor(Math.random() * entries.length)]; +} + +// Do: an empty value of the type can't exist +function pickWinner(entries: NonEmpty): string { + return entries[Math.floor(Math.random() * entries.length)]; +} +``` + +Where a plain `T[]` arrives, narrow once with a guard. The fact then travels in the type: + +```ts +const isNonEmpty = (arr: T[]): arr is NonEmpty => arr.length > 0; +``` + +Even length, as pairs: + +```ts +type Pairs = [T, T][]; +``` + +A time range, as start plus duration: + +```ts +// Don't: a comment holds the invariant +type TimeRange = { start: Date; end: Date }; // start <= end + +// Do: a negative range can't be written; derive end when needed +type TimeRange = { start: Date; durationMs: number }; +``` + +Keep `durationMs` a plain number. Brand it (per Branded types) only if a raw number could be passed where a duration is expected, not by reflex. Pick the representation that makes the bad state unconstructable, then expose the reading you need on top (`pairs.flat()`, a `rangeEnd()` helper). + +## Simplest total type + +Don't strengthen everything. Keep `T[]` when every operation on it is total: + +```ts +const sum = (xs: number[]) => xs.reduce((a, b) => a + b, 0); // [] is 0, fine +``` + +Strengthen when the loose type forces a lie at a use site. The tells are `!`, `arr[0] as T`, and a "should never happen" throw: + +```ts +// Don't: partiality smuggled past the compiler +function newestSession(sessions: Session[]): Session { + return sessions.at(0)!; +} + +// Do: strengthen the input; the assertion disappears +function newestSession(sessions: NonEmpty): Session { + return sessions[0]; +} +``` + +Weakening the result to `Session | undefined` is the other total signature. + +## `unknown` over `any` + +External data is always `unknown`. Narrow before use. + +```ts +// Don't +function handle(input: any) { + return input.foo.bar; +} + +// Do +function handle(input: unknown) { + if (typeof input === "object" && input !== null && "foo" in input) { + // narrowed; compiler verifies access + } +} +``` + +External sources include RPC payloads, `JSON.parse`, `postMessage`, IPC, file contents, environment variables, database results. + +## Schemas before hand-rolled guards + +Before writing a property-by-property type guard for external data, look for the repository's runtime schema library and existing schemas. Let one schema own validation and derive the TypeScript type from it. Do not maintain a schema, a duplicate interface, and a guard that can drift apart. + +```ts +import { z } from "zod"; + +const UserSchema = z.object({ + id: z.string().uuid(), + role: z.enum(["admin", "member"]), +}); + +type User = z.infer; + +function parseUser(input: unknown): User { + return UserSchema.parse(input); +} +``` + +Use `safeParse` when failure is an expected branch. Use the equivalent inference helper when the repository uses another schema library. Do not add a new schema dependency for one guard. This rule prefers the schema system the codebase already trusts. + +## No `as` casts + +Every `as` is a potential runtime crash. Cast only after the type system has verified the claim. + +```ts +// Don't +const user = data as User; + +// Do. Earn the cast at the boundary. +function parseUser(data: unknown): User { + if (typeof data !== "object" || data === null) { + throw new Error("expected object"); + } + if (!("id" in data) || typeof (data as Record).id !== "string") { + throw new Error("expected id"); + } + // ... validate all fields + return data as User; // OK, earned cast after full validation +} +``` + +When refactoring an `as` out of existing code, identify why TypeScript can't infer: + +- Missing discriminant: add one, switch to a discriminated union. +- Overly wide source type (e.g. `Record`): narrow it. +- Untyped boundary: add a parse function or schema. +- Genuinely inexpressible: use a branded type or `satisfies`. + +## Narrowing hierarchy + +From best to last-resort: + +1. **Discriminated union switch / if.** Compiler narrows automatically. +2. **`in` operator.** `"key" in obj` narrows to variants containing that key. +3. **`typeof` / `instanceof`.** For primitives and class instances. +4. **User-defined type guard.** When the above aren't enough. +5. **`as` cast.** Only after validation. + +```ts +function area(s: Shape): number { + if ("radius" in s) return Math.PI * s.radius ** 2; // narrowed to circle + return s.width * s.height; // narrowed to rect +} +``` + +## Type guards + +A guard must actually verify the claim. A lying guard is worse than `as`. + +```ts +function isCircle(s: Shape): s is Shape & { kind: "circle" } { + return s.kind === "circle"; +} +``` + +Prefer discriminant narrowing when possible. + +## Exhaustiveness + +In default arms, assign the discriminant to a `never`-typed local. + +```ts +// Value-returning switch +function area(s: Shape): number { + switch (s.kind) { + case "circle": + return Math.PI * s.radius ** 2; + case "rect": + return s.width * s.height; + default: { + const _exhaustive: never = s; + return _exhaustive; + } + } +} + +// Void switch +function handle(s: Shape): void { + switch (s.kind) { + case "circle": + drawCircle(s); + break; + case "rect": + drawRect(s); + break; + default: { + const _exhaustive: never = s; + void _exhaustive; + } + } +} +``` + +Return-style in value-returning switches, void-style in statement switches. + +## `satisfies` over `as` + +`satisfies` validates without widening literal types. + +```ts +// Don't. Widens, loses literal types. +const config = { theme: "dark", cols: 3 } as Config; + +// Do. Validates AND preserves literal types. +const config = { theme: "dark", cols: 3 } satisfies Config; +// config.theme is "dark" (literal), not string +``` + +## Boundary validation + +Validate once where data crosses in. Trust types inside. See the **boundary-discipline** principle skill. + +- **Wire formats** (proto, JSON-RPC): parse with `ignoreUnknownFields` so forward-compatible changes don't break old clients. +- **Persisted JSON:** versioned blob with a try/catch around the parse. +- **Don't re-validate** deep in call chains. + +## Schema-derived types + +When a `.proto`, OpenAPI spec, GraphQL schema, or database migration already defines a shape, derive from the generated types instead of duplicating them. + +```ts +// Don't. Duplicate shape, drifts when the schema changes. +type CheckSummary = { + totalCount: number; + checks: { name: string; status: string }[]; +}; +function renderChecks(s: CheckSummary) { + /* ... */ +} + +// Do. Derive from the generated schema type. +import type { ChecksMessage } from ""; +function renderChecks(s: Pick) { + /* ... */ +} +``` + +Reach for `Pick`, `Omit`, `Parameters`, `ReturnType`, `Awaited`, `typeof` before writing a new interface. + +## Object args + +```ts +// Don't. Swap two args, still compiles. +openFile(uri, { + startLineNumber: 10, + startColumn: 1, + endLineNumber: 10, + endColumn: 1, +}); + +// Do. Order-independent, self-documenting. +openFile({ + uri, + selection: { + startLineNumber: 10, + startColumn: 1, + endLineNumber: 10, + endColumn: 1, + }, +}); +``` + +Skip on hot paths: per-frame render, tokenizers, parsers, anything in a tight loop where the allocation cost matters. diff --git a/.agents/skills/unslop/SKILL.md b/.agents/skills/unslop/SKILL.md new file mode 100644 index 000000000..a3a7dc7ba --- /dev/null +++ b/.agents/skills/unslop/SKILL.md @@ -0,0 +1,68 @@ +--- +name: unslop +description: Cut AI tells from any writing. Must always apply. +disable-model-invocation: true +--- + +# Unslop + +Edit text to remove AI patterns. + +## Process + +1. Scan for the patterns below. +2. Rewrite. Preserve meaning, match intended tone. +3. Self-audit: "What makes this obviously AI generated?" Fix remaining tells. + +## Patterns to detect and fix + +Rule numbers are stable ids that other skills cite. A removed rule leaves a gap. + +### Content + +3. **Superficial -ing phrases.** "highlighting...", "ensuring...", "reflecting...", "showcasing...", "fostering...". Delete or expand with real sources. +5. **Vague attributions.** "Experts believe", "Industry reports suggest", "Some critics argue". Name the source or delete. + +### Language + +7. **AI vocabulary.** Additionally, crucial, delve, enduring, enhance, fostering, garner, interplay, intricate, landscape (abstract), pivotal, showcase, tapestry (abstract), testament, underscore, vibrant. Replace with plain words. +8. **Fancy ways to say "is".** "serves as", "stands as", "boasts", "features". Just say "is" or "has". +9. **"Not just X, but Y."** State the point directly instead. +10. **Rule of three.** Forcing ideas into groups of three. Use the natural number. +11. **Synonym cycling.** Protagonist, main character, central figure, hero all in one paragraph. Pick one, repeat it. +12. **False ranges.** "from X to Y" where X and Y aren't on a meaningful scale. List topics directly. + +### Style + +13. **Em dash overuse.** Avoid em dashes entirely. Use periods or commas only (no parentheses, no en dashes, no hyphen-as-dash substitutes). If a thought needs separation, end the sentence or use a comma. +14. **Colon overuse.** Colons are fine before a list or example. Not as mid-sentence connectors. "If you're coming from traditional automation: instead of registering event handlers, you describe conditions" adds nothing with the colon. Rewrite to let the point stand on its own without comparison framing. "Describing when the scheduler should fire works best as plain English." Same meaning, no crutch punctuation. +15. **Boldface overuse.** Don't bold every proper noun or acronym. +16. **Inline-header lists.** The tell is a bold label and colon that restates the line: "**Performance:** Performance improved...". Convert those to prose. A bold lead-in that ends in a period, names the item, and is followed by genuinely new detail ("**Schema in TypeScript.** Tables live in one file.") is fine, not a tell. +17. **Title case headings.** Use sentence case. +18. **Decorative emojis.** Remove from headings and bullets. +19. **Curly quotes.** Replace with straight quotes. + +### Communication artifacts + +20. **Chatbot phrases.** "I hope this helps!", "Let me know if...", "Of course!", "Certainly!", "Found the smoking gun!" Remove. +22. **Sycophantic tone.** "Great question! You're absolutely right!" Respond directly. + +### Filler + +23. **Filler phrases.** "In order to" becomes "To". "Due to the fact that" becomes "Because". "It is important to note that" gets deleted. +24. **Excessive hedging.** "could potentially possibly be argued that it might" becomes "may". +25. **Generic conclusions.** "The future looks bright." State specific plans or facts. + +### Jargon + +26. **Abstract metaphor nouns.** Substrate, wedge, vector, locus, vantage, nexus, primitive (as noun), harness (as metaphor), surface (as in "API surface"), bedrock, scaffolding (as metaphor), modality, paradigm, gold-plating, ratchet (as metaphor), evacuate (for moving code), endgame, north star, flywheel. These read as technical but usually have a plainer concrete word. "Substrate" becomes "base". "Wedge in" becomes "add". "Vector" becomes "way" or "method". "Gold-plating" becomes "more than the job needs". "Ratchet" becomes the mechanism's real name or "a limit that only tightens". "Evacuate" becomes "move out". "Endgame" becomes "the last phase". Pick the concrete word. + +### Plain speech + +27. **Say what it does, not how it feels.** "the database stays close at hand", "SQL you can read", "types that follow your schema" name a feeling. The fix names the mechanism or a number: "`.toSQL()` returns the exact string sent to the database", "a column rename fails the build". Ask what the sentence tells the reader to do or know, then write that. If you can't restate it as a concrete instruction, fact, or number, cut it. One more check: if the sentence could appear unchanged in another project's docs, it says nothing about this one. Cut it. +28. **Shorten or split dense sentences.** If the reader has to backtrack to parse a sentence, break it in two or drop clauses. One idea per sentence. +29. **Active voice.** Prefer it. Catch "is/are/was/were + past participle" and name the actor: "queries are validated" becomes "the compiler validates queries", "the file is parsed by the loader" becomes "the loader parses the file". Passive is fine only when the actor is unknown or genuinely doesn't matter. +30. **Cut adverbs, or use a stronger verb.** "runs quickly" becomes "is fast" or the number. "significantly improves" becomes the measured delta. An adverb propping up a weak verb means the verb is wrong. +31. **Prefer the plain word.** "utilize" becomes "use", "leverage" becomes "use", "facilitate" becomes "help", "numerous" becomes "many", "in the event that" becomes "if". The fancier synonym is rarely clearer. +32. **Mannered prose.** Metaphor or flourish where a literal phrase exists: aphorisms ("wire it or delete it"), rhetorical fragments for effect, personified code ("the plan holds it"), figurative verbs ("rides along", "stands on"), stock framing phrases. "A dial worth turning" becomes "a parameter worth varying". Say what you mean. Rule 26 covers the metaphor nouns. +33. **Over-compression.** Dropped articles, verbless fragments, symbol-speak, and abbreviations that make the reader decode instead of read. "Parser rejects bad date → exit 2, no write" becomes "The parser rejects a bad date, exits with code 2, and writes nothing." Write whole sentences with their articles and verbs, and spell out arrows and abbreviations. diff --git a/.cursor/rules/backend-conventions.mdc b/.cursor/rules/backend-conventions.mdc new file mode 100644 index 000000000..97c4bbf0d --- /dev/null +++ b/.cursor/rules/backend-conventions.mdc @@ -0,0 +1,14 @@ +--- +description: Django API conventions (FilterParams body, schema, joins, aggregates) +globs: backend/**/* +alwaysApply: false +--- + +# Backend + +- Request/response shape changed → run `backend/generate-schema.sh`. In a large PR, put schema in its own last commit. +- `tests.build` is `db_constraint=False`. `INNER JOIN builds` drops orphan tests. Keep the previous join semantics unless dropping orphans is the point. +- Listing often reads **aggregates** (`hardware_status`, rollups). Compare/details often read **raw** `tests`/`builds`. Mismatched counts or a filter that works on one tab and not another is usually this split. +- Run the pytest files you touched; `poetry run ruff check` / `ruff format` before calling it done. +- Always consider table indexes when writing a SQL query. Suggest new indexes when relevant but DO NOT create them without user consent. +--- diff --git a/.cursor/rules/filters.mdc b/.cursor/rules/filters.mdc new file mode 100644 index 000000000..654455173 --- /dev/null +++ b/.cursor/rules/filters.mdc @@ -0,0 +1,16 @@ +--- +description: Filter invariant and add-a-filter checklist (listing, details, URL) +globs: docs/filters.md,backend/kernelCI_app/helpers/filters.py,backend/kernelCI_app/tests/unitTests/**/*filter*,dashboard/src/**/*Filter*,dashboard/src/**/*filter*,dashboard/src/utils/search.ts,dashboard/src/types/filters.ts,dashboard/src/types/general.ts,dashboard/src/components/Tabs/Filters.tsx +alwaysApply: false +--- + +# Filters + +Full steps: `docs/filters.md`. + +- Same category = **OR**. Different categories = **AND**. (`Boots Status` MISS|PASS **and** `Arch` arm.) +- New filter needs **both** sides or the URL/details break: + - Backend: `FilterParams` (set, handler, field map, `_is_*_filtered_out`), tree/hardware summary + commitHistory, unit/integration tests, `generate-schema.sh`. + - Frontend: API types, filter card/modal, `zFilterObjectsKeys` / `zFilterNumberKeys` in `types/general.ts`, `mapFilterToReq`, shortened key in `utils/search.ts`. +- Listing origin (and other `diffFilter` keys) must map onto the details request. Do not leave a key that only the listing understands. +--- diff --git a/.cursor/rules/frontend-conventions.mdc b/.cursor/rules/frontend-conventions.mdc new file mode 100644 index 000000000..ad743b1bc --- /dev/null +++ b/.cursor/rules/frontend-conventions.mdc @@ -0,0 +1,14 @@ +--- +description: Dashboard frontend conventions (URL state, i18n, filters, pnpm) +globs: dashboard/**/* +alwaysApply: false +--- + +# Frontend + +- Shareable links: put UI state in the URL (TanStack Router). File-based routes: only files whose names start with `~`. +- Package manager is `pnpm` (not npm/yarn). Unit tests: `pnpm test`. E2E targets `data-test-id`. +- User-visible strings go in `dashboard/src/locales/messages/index.ts`. Do not hardcode copy. +- Listing filters must still apply on details. Wire `diffFilter` through `mapFilterToReq` (`components/Tabs/Filters.tsx`) and the shortened names in `utils/search.ts`. Origin on listing ≠ origin on details is a recurring bug. +- After UI/layout/routing/state changes, verify the flow in the browser (click/type/navigate), not only a screenshot. +--- diff --git a/.cursor/rules/kernelci-collaboration.mdc b/.cursor/rules/kernelci-collaboration.mdc new file mode 100644 index 000000000..8a066cd8a --- /dev/null +++ b/.cursor/rules/kernelci-collaboration.mdc @@ -0,0 +1,13 @@ +--- +description: GitHub, remotes, reviews, and commit hygiene for KernelCI Dashboard +alwaysApply: true +--- + +# Collaboration + +- Canonical repo: `kernelci/dashboard`. `origin` is often the fork. Branch from `upstream/main`; rebase onto `upstream/main`, not `origin/main`. +- Do not push, open issues, or comment on GitHub (`gh pr comment`, review threads) unless asked. PR reviews stay in chat by default. +- Conventional Commits with a scope: `fix(tree-compare): …`, `feat(backend): …`. One concern per commit/PR. Do not mix mechanical churn with logic. +- Always add Signed-off-by to commits (`git commit -s`) +- Leave unstaged unless they are the task: `.cursor/`, `.pnpm-store/`, local `docker-compose.dev.yml`, compiled e2e output, `dashboard/src/routeTree.gen.ts` when it is only import reorder. +--- diff --git a/.cursor/rules/ponytail.mdc b/.cursor/rules/ponytail.mdc new file mode 100644 index 000000000..440c8fdcf --- /dev/null +++ b/.cursor/rules/ponytail.mdc @@ -0,0 +1,36 @@ +--- +description: Ponytail. Always pick the simplest solution that works. +globs: * +alwaysApply: true +--- + +# Ponytail, lazy senior dev mode + +You are a lazy senior developer. Lazy means efficient, not careless. The best code is the code never written. + +Before writing any code, stop at the first rung that holds: + +1. Does this need to be built at all? (YAGNI) +2. Does it already exist in this codebase? Reuse the helper, util, or pattern that's already here, don't re-write it. +3. Does the standard library already do this? Use it. +4. Does a native platform feature cover it? Use it. +5. Does an already-installed dependency solve it? Use it. +6. Can this be one line? Make it one line. +7. Only then: write the minimum code that works. + +The ladder runs after you understand the problem, not instead of it: read the task and the code it touches, trace the real flow end to end, then climb. + +Bug fix = root cause, not symptom: a report names a symptom. Grep every caller of the function you touch and fix the shared function once — one guard there is a smaller diff than one per caller, and patching only the path the ticket names leaves a sibling caller still broken. + +Rules: + +- No abstractions that weren't explicitly requested. +- No new dependency if it can be avoided. +- No boilerplate nobody asked for. +- Deletion over addition. Boring over clever. Fewest files possible. +- Shortest working diff wins, but only once you understand the problem. The smallest change in the wrong place isn't lazy, it's a second bug. +- Question complex requests: "Do you actually need X, or does Y cover it?" +- Pick the edge-case-correct option when two stdlib approaches are the same size, lazy means less code, not the flimsier algorithm. +- Mark intentional simplifications with a `ponytail:` comment. If the shortcut has a known ceiling (global lock, O(n²) scan, naive heuristic), the comment names the ceiling and the upgrade path. + +Not lazy about: understanding the problem (read it fully and trace the real flow before picking a rung, a small diff you don't understand is just laziness dressed up as efficiency), input validation at trust boundaries, error handling that prevents data loss, security, accessibility, the calibration real hardware needs (the platform is never the spec ideal, a clock drifts, a sensor reads off), anything explicitly requested. Lazy code without its check is unfinished: non-trivial logic leaves ONE runnable check behind, the smallest thing that fails if the logic breaks (an assert-based demo/self-check or one small test file; no frameworks, no fixtures). Trivial one-liners need no test. \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..ed8205e75 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,73 @@ +# Agent notes — KernelCI Dashboard + +Monorepo: React/TypeScript UI (`dashboard/`) plus Django REST API (`backend/`) over the KernelCI/KCIDB results database. + +Human onboarding and contribution process live in [docs/Onboarding.md](docs/Onboarding.md) and [CONTRIBUTING.md](CONTRIBUTING.md). Do not copy those here. + +## Where to look + +| Need | Path | +| --- | --- | +| Layout of the DB | [backend/docs/database-logic.md](backend/docs/database-logic.md) | +| Adding or changing a filter | [docs/filters.md](docs/filters.md) | +| API request/response contract | `backend/schema.yml` — regenerate with [backend/generate-schema.sh](backend/generate-schema.sh) | +| URL ↔ endpoint map | [backend/kernelCI_app/urls.py](backend/kernelCI_app/urls.py) | +| Frontend request layer | `dashboard/src/api/` | +| User-visible strings | `dashboard/src/locales/messages/index.ts` | +| Integration tests | [docs/IntegrationTests.md](docs/IntegrationTests.md) | + +Also: `docs/` (ops), `k6/` (load), `monitoring/`, `proxy/` (nginx). Backend settings are `backend/kernelCI/`; cache app is `backend/kernelCI_cache/`. App code and tests: `backend/kernelCI_app/`. UI: `dashboard/src/` (colocated `*.test.ts(x)`); Playwright: `dashboard/e2e/`. + +## Commands (repo root) + +- `make setup` — copy missing env templates, `pnpm` + Poetry install. +- `make dev` / `make dev-build` / `make dev-down` — Docker Compose via `docker-compose.dev.yml`. +- `make fix` — ESLint/Prettier (frontend) and Ruff fix/format (backend). Prefer this over hand-formatting. +- `make test` — frontend Vitest and **backend unit** (`pytest -m unit`). Not integration. +- `make build` — frontend production build (includes typecheck). +- `make ci` — lint + build + unit + Docker-backed integration (`docker-compose.test.yml`). + +Caveats: + +- `make check-lint` runs `pnpm lint-staged` on the frontend (staged files only), plus Ruff check/format on the backend. For a full frontend lint, `cd dashboard && pnpm lint`. +- Frontend package manager is **pnpm**. Backend is **Poetry** from `backend/`. +- Frontend-only: `pnpm dev` inside `dashboard/`. Backend needs Redis; see the backend README. +- Playwright: `pnpm e2e` in `dashboard/` with the app up; `PLAYWRIGHT_TEST_BASE_URL` as needed. Prefer `data-test-id` selectors. +- Without a local API, point `VITE_API_BASE_URL` at staging (`https://staging.dashboard.kernelci.org`). + +## Running the app + +Do not start a stack for logic-only work. Prefer `make test`, schema, and code. Full install/run details: [docs/dev-environment.md](docs/dev-environment.md). + +Need a browser or a local API? Check terminals and ports first; reuse what is already up. + +- **Full local stack:** `make setup` once if env files are missing, then `make dev` (Compose `--wait`). Docker needs an unsandboxed shell. First build is slow. +- **Frontend only:** `pnpm dev` in `dashboard/` with `VITE_API_BASE_URL=https://staging.dashboard.kernelci.org`. +- **URLs:** http://localhost:9000 (nginx, prod-like), http://localhost:5173 (Vite HMR), http://localhost:8000 (Django). Ready check: `curl` 9000 and `/api/schema/` on 8000. +- **UI changes:** exercise the flow in the browser (click, navigate, shared routes). A screenshot is not enough. Stop with `make dev-down` only if you started the stack. + +## Local Postgres + +Query via Compose, not host `psql` (avoids leaking `.env` into the chat): + +```bash +docker compose -f docker-compose.dev.yml exec -T dashboard_db \ + psql -U "${DB_USER:-admin}" -d "${DB_NAME:-dashboard}" -c '…' +``` + +Docker socket needs an unsandboxed shell. Schema/meaning: [backend/docs/database-logic.md](backend/docs/database-logic.md) and `backend/kernelCI_app/models.py` (`db_table`). Prefer `reltuples` for size estimates; `COUNT(*)` is fine on `tests` (~1M). Do not write/DDL without asking. + +Host clients (DBeaver, etc.): `docs/dev-environment.md` — hostname `dashboard_db` is in-network only; published port is `DB_PORT_PUBLISHED` (default 5434). + +## Invariants + +- **Shareable links.** UI state belongs in the URL (TanStack Router). File-based routes: only files whose names start with `~`. +- **Filters:** same category = OR, different categories = AND. A new filter needs backend `FilterParams` (plus summary/commitHistory, tests, schema) **and** frontend types, modal, `zFilter*` in `dashboard/src/types/general.ts`, `mapFilterToReq` (`dashboard/src/components/Tabs/Filters.tsx`), and the short name in `dashboard/src/utils/search.ts`. Listing `diffFilter` keys must still apply on details. +- Copy goes in `messages/index.ts`, not hardcoded in components. +- **Redis** for caching queries. + +## Git + +Conventional Commits with a scope, e.g. `fix(tree-compare): restore drawer navigation`. One concern per commit/PR. PR titles match; describe why, how, trade-offs, tests; link issues; screenshots/GIFs for UI. CI green before asking for review. Commit with "Signed-off-by". + +Keep credentials, DB passwords, and webhook URLs out of commits. Use the supplied `.env.example` files. diff --git a/skills-lock.json b/skills-lock.json new file mode 100644 index 000000000..c8299123e --- /dev/null +++ b/skills-lock.json @@ -0,0 +1,59 @@ +{ + "version": 1, + "skills": { + "agent-browser": { + "source": "vercel-labs/agent-browser", + "sourceType": "github", + "skillPath": "skills/agent-browser/SKILL.md", + "computedHash": "80161e6836b3b40f4e83730a290deb40ac14d877d38c97ad5f6d71be37ce74c3" + }, + "make-pr-easy-to-review": { + "source": "cursor/plugins", + "sourceType": "github", + "skillPath": "cursor-team-kit/skills/make-pr-easy-to-review/SKILL.md", + "computedHash": "8b3e62ce5609388731966d66725a0962e6d6c249bf384b84e131eb24de3c2b27" + }, + "principle-prove-it-works": { + "source": "cursor/plugins", + "sourceType": "github", + "skillPath": "pstack/skills/principle-prove-it-works/SKILL.md", + "computedHash": "1ed61e8344e183ef344d70e54294b8bd7eba8b874d58cfcfbbbfd6fad75959b8" + }, + "show-me-your-work": { + "source": "cursor/plugins", + "sourceType": "github", + "skillPath": "pstack/skills/show-me-your-work/SKILL.md", + "computedHash": "e77ba9e44543062972cbd2c89950bce72ebeab31d6b67fd37ec7dc412392e60c" + }, + "thermo-nuclear-code-quality-review": { + "source": "cursor/plugins", + "sourceType": "github", + "skillPath": "cursor-team-kit/skills/thermo-nuclear-code-quality-review/SKILL.md", + "computedHash": "9052b01b46b1d8dbf94c45f9313ec1983443f9a460e83d49cdc7fe0f640ca476" + }, + "thermo-nuclear-review": { + "source": "cursor/plugins", + "sourceType": "github", + "skillPath": "thermos/skills/thermo-nuclear-review/SKILL.md", + "computedHash": "c13babcfb10d8f33325af0b00c3eff7b5efb034147f93993c4d9ed569faa5570" + }, + "thermos": { + "source": "cursor/plugins", + "sourceType": "github", + "skillPath": "thermos/skills/thermos/SKILL.md", + "computedHash": "f7f174564eddf63e60ac2b5d6831230942e57ecc5142ac276afe402d3ff99174" + }, + "typescript-best-practices": { + "source": "cursor/plugins", + "sourceType": "github", + "skillPath": "pstack/skills/typescript-best-practices/SKILL.md", + "computedHash": "b987c46f03f6c918110c2c198aa49fdd39e126f23ba75ae75fb8efe84625e397" + }, + "unslop": { + "source": "cursor/plugins", + "sourceType": "github", + "skillPath": "pstack/skills/unslop/SKILL.md", + "computedHash": "3b8676e7461f44026b4d555ff0f3369a761df6033b9b9e2cd5152693cb20fbb2" + } + } +} From 037c157ad1831e85244abffbd6c55b6543f7e69e Mon Sep 17 00:00:00 2001 From: Felipe Bergamin Date: Thu, 24 Sep 2026 16:29:55 -0300 Subject: [PATCH 2/4] chore(agents): update AGENTS.md --- AGENTS.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index ed8205e75..0a9aeea2f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -70,4 +70,17 @@ Host clients (DBeaver, etc.): `docs/dev-environment.md` — hostname `dashboard_ Conventional Commits with a scope, e.g. `fix(tree-compare): restore drawer navigation`. One concern per commit/PR. PR titles match; describe why, how, trade-offs, tests; link issues; screenshots/GIFs for UI. CI green before asking for review. Commit with "Signed-off-by". +Refer to related issues in commit footers, e.g. `Part of #issue-number` and `Closes #issue-number`. + Keep credentials, DB passwords, and webhook URLs out of commits. Use the supplied `.env.example` files. + +## Pull requests quality + +For any changes that affects the frontend, attach visual evidence to the pull requests via `gh pr comment --attach`. NEVER commit evidence files to the repository. + +Add clear and reproducible test instructions to the pull requests so the reviewers can test themselves and validate the implementation. + +Describe in the pull request body what was changed, use the `unslop` skill to write. Add references to the related issues. + +Prefer using git fixups when addressing something implemented in the same pull request. Push the fixups as is in order to make easier to another devs to review only what changed. The fixups should be squashed before merge. + From 68fe12c7b23ec22ebe03408ef041fff16509f3b4 Mon Sep 17 00:00:00 2001 From: Felipe Bergamin Date: Thu, 24 Sep 2026 16:40:27 -0300 Subject: [PATCH 3/4] chore(skills): add 2 cursor rules --- .../skills/suggesting-cursor-hooks/SKILL.md | 56 +++++++++++++ .../skills/suggesting-cursor-rules/SKILL.md | 83 +++++++++++++++++++ skills-lock.json | 12 +++ 3 files changed, 151 insertions(+) create mode 100644 .agents/skills/suggesting-cursor-hooks/SKILL.md create mode 100644 .agents/skills/suggesting-cursor-rules/SKILL.md diff --git a/.agents/skills/suggesting-cursor-hooks/SKILL.md b/.agents/skills/suggesting-cursor-hooks/SKILL.md new file mode 100644 index 000000000..75e02e3ae --- /dev/null +++ b/.agents/skills/suggesting-cursor-hooks/SKILL.md @@ -0,0 +1,56 @@ +--- +name: suggesting-cursor-hooks +description: When the user keeps asking for the same check to run (lint, tests, type-check), suggest a Cursor hook to automate it. +user-invocable: false +--- + +# Suggesting Cursor Hooks + +Watch for repeated manual requests. When the user keeps asking you to run the same command after changes, suggest a hook to automate it. + +## Triggers + +Suggest a hook when you notice: +- The user asks you to **run the same check 2+ times** (e.g. "run lint", "run tests", "check types") +- The user says "always run X after editing" or "make sure to test after changes" +- You keep forgetting to run a validation step and the user catches it +- A CI failure could have been caught locally with a post-edit check + +## How to Suggest + +``` +You've asked me to run [command] after edits a few times. Want me to +set up a Cursor hook so it runs automatically? +``` + +If they say yes, create `.cursor/hooks.json` and the script: + +```json +{ + "hooks": [ + { + "event": "afterFileEdit", + "script": ".cursor/hooks/.sh", + "pattern": "" + } + ] +} +``` + +## Common Hooks to Suggest + +| User keeps asking... | Hook | +|---------------------|------| +| "run lint" / "fix formatting" | `afterFileEdit` → `eslint --fix` or `prettier --write` | +| "check types" | `afterFileEdit` → `tsc --noEmit` on `.ts`/`.tsx` | +| "run tests" | `afterFileEdit` → run related test file | +| "don't touch .env" | `beforeShellExecution` → warn on secrets files | +| "make sure it builds" | `stop` → quick build check | + +## Rules + +- Only suggest after a real repeated pattern, not preemptively +- Hook scripts must be fast (under 5 seconds) or the agent feels slow +- Scripts should exit 0 and report via stdout — don't block the agent unless the user explicitly wants that +- Check for existing `.cursor/hooks.json` first — merge, don't overwrite +- Keep it casual — "want me to automate this?" not a formal proposal diff --git a/.agents/skills/suggesting-cursor-rules/SKILL.md b/.agents/skills/suggesting-cursor-rules/SKILL.md new file mode 100644 index 000000000..479cb23eb --- /dev/null +++ b/.agents/skills/suggesting-cursor-rules/SKILL.md @@ -0,0 +1,83 @@ +--- +name: suggesting-cursor-rules +description: When the user repeats the same correction or convention multiple times, suggest a Cursor rule to encode it permanently. +user-invocable: false +--- + +# Suggesting Cursor Rules + +Watch for repeated corrections and frustration. When the user keeps telling you the same thing, suggest a `.cursor/rules/` file so they never have to say it again. + +## Triggers + +Suggest a rule when you notice: +- The user corrects the same pattern **2+ times** (e.g. "use named exports", "don't use default exports") +- The user expresses frustration about code style or conventions +- The user says things like "I always want...", "never do...", "every time you..." +- You keep making the same mistake in this project + +## How to Suggest + +When you detect a trigger, offer it naturally: + +``` +I notice you've corrected me on [pattern] a couple times. Want me to +create a Cursor rule so I always follow this convention? +``` + +If they say yes, create the rule: + +``` +.cursor/rules/.mdc +``` + +``` +--- +description: +globs: +--- + +- +``` + +## Examples + +**User keeps saying "use relative imports":** +``` +--- +description: Import conventions +globs: **/*.{ts,tsx} +--- + +- Use relative imports, not @ aliases +- Import from the same package with relative paths (e.g. "../../queries/..." not "src/queries/...") +``` + +**User keeps saying "no comments on obvious code":** +``` +--- +description: Comment style +globs: **/*.{ts,tsx,js,jsx} +--- + +- Do not add comments that narrate what the code does +- Only comment non-obvious intent, tradeoffs, or constraints +``` + +**User frustrated about test file locations:** +``` +--- +description: Test file conventions +globs: **/*.test.{ts,tsx} +--- + +- Co-locate test files next to the source file they test +- Name test files .test.ts, not __tests__/.ts +``` + +## Rules + +- Don't be annoying — only suggest after a genuine repeated pattern, not on the first correction +- Keep rule files small and focused — one concern per file +- Check `.cursor/rules/` first so you don't duplicate an existing rule +- Frame it as a helpful offer, not a lecture diff --git a/skills-lock.json b/skills-lock.json index c8299123e..132c64c08 100644 --- a/skills-lock.json +++ b/skills-lock.json @@ -25,6 +25,18 @@ "skillPath": "pstack/skills/show-me-your-work/SKILL.md", "computedHash": "e77ba9e44543062972cbd2c89950bce72ebeab31d6b67fd37ec7dc412392e60c" }, + "suggesting-cursor-hooks": { + "source": "spencerpauly/awesome-cursor-skills", + "sourceType": "github", + "skillPath": "resources/suggesting-cursor-hooks/SKILL.md", + "computedHash": "ad9c88e14773b1b54efd5874dfcd9e01eb2eb5b66f0c044ac15b756ed0307787" + }, + "suggesting-cursor-rules": { + "source": "spencerpauly/awesome-cursor-skills", + "sourceType": "github", + "skillPath": "resources/suggesting-cursor-rules/SKILL.md", + "computedHash": "dd458479fdfb56d9624949888f0b1de9343c5428ef701455958dd9060f207463" + }, "thermo-nuclear-code-quality-review": { "source": "cursor/plugins", "sourceType": "github", From eb87b96db0a0a1b36f5a2b027159e5df0dc144a1 Mon Sep 17 00:00:00 2001 From: Felipe Bergamin Date: Thu, 24 Sep 2026 16:53:21 -0300 Subject: [PATCH 4/4] chore(agents): list skills and rules at the top of AGENTS.md Give agents a short index of vendored skills and Cursor rules before the rest of the notes. Signed-off-by: Felipe Bergamin --- AGENTS.md | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 0a9aeea2f..df73d5583 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,6 +4,32 @@ Monorepo: React/TypeScript UI (`dashboard/`) plus Django REST API (`backend/`) o Human onboarding and contribution process live in [docs/Onboarding.md](docs/Onboarding.md) and [CONTRIBUTING.md](CONTRIBUTING.md). Do not copy those here. +### Skills + +| Skill | When | +| --- | --- | +| [agent-browser](.agents/skills/agent-browser/SKILL.md) | Browser automation (click, navigate, screenshots) | +| [make-pr-easy-to-review](.agents/skills/make-pr-easy-to-review/SKILL.md) | Tidy history and PR description for review | +| [principle-prove-it-works](.agents/skills/principle-prove-it-works/SKILL.md) | Verify the real artifact before calling it done | +| [show-me-your-work](.agents/skills/show-me-your-work/SKILL.md) | Decision log for long or unattended work | +| [suggesting-cursor-hooks](.agents/skills/suggesting-cursor-hooks/SKILL.md) | Repeated lint/test/type-check requests | +| [suggesting-cursor-rules](.agents/skills/suggesting-cursor-rules/SKILL.md) | Repeated convention corrections | +| [thermo-nuclear-code-quality-review](.agents/skills/thermo-nuclear-code-quality-review/SKILL.md) | Harsh maintainability review | +| [thermo-nuclear-review](.agents/skills/thermo-nuclear-review/SKILL.md) | Security and correctness audit of a branch | +| [thermos](.agents/skills/thermos/SKILL.md) | Run both thermo reviews, then synthesize | +| [typescript-best-practices](.agents/skills/typescript-best-practices/SKILL.md) | Editing `.ts` / `.tsx` | +| [unslop](.agents/skills/unslop/SKILL.md) | Cut AI tells from writing | + +### Rules + +| Rule | What | +| --- | --- | +| [ponytail](.cursor/rules/ponytail.mdc) | Simplest solution that works | +| [kernelci-collaboration](.cursor/rules/kernelci-collaboration.mdc) | Remotes, reviews, commit hygiene | +| [frontend-conventions](.cursor/rules/frontend-conventions.mdc) | URL state, i18n, filters, pnpm | +| [backend-conventions](.cursor/rules/backend-conventions.mdc) | FilterParams, schema, joins, aggregates | +| [filters](.cursor/rules/filters.mdc) | Filter invariant and add-a-filter checklist | + ## Where to look | Need | Path | @@ -68,7 +94,7 @@ Host clients (DBeaver, etc.): `docs/dev-environment.md` — hostname `dashboard_ ## Git -Conventional Commits with a scope, e.g. `fix(tree-compare): restore drawer navigation`. One concern per commit/PR. PR titles match; describe why, how, trade-offs, tests; link issues; screenshots/GIFs for UI. CI green before asking for review. Commit with "Signed-off-by". +Conventional Commits with a scope, e.g. `fix(tree-compare): restore drawer navigation`. One concern per commit/PR. PR titles match; describe why, how, trade-offs, tests; link issues; screenshots/GIFs for UI. CI green before asking for review. Commit with "Signed-off-by" and "Assisted-by". Refer to related issues in commit footers, e.g. `Part of #issue-number` and `Closes #issue-number`.