

Also from Kynth Studios


Also from Kynth Studios


Also from Kynth Studios
12345# Linting and Formatting in Doughnut Project67## When to Use This Rule89**Use this rule when:**10- Fixing linting or formatting errors11- Running code quality checks12- Validating OpenAPI specifications13- Formatting code before committing14- Troubleshooting linting failures15- Working with Biome, Redocly, or backend linting1617## Overview1819This project uses multiple linting tools to ensure code quality:20- **Biome**: Frontend, CLI, MCP server, `packages/doughnut-test-fixtures`, Cypress, and other root-level TypeScript/JavaScript code21- **Redocly CLI**: OpenAPI specification validation22- **Backend linting**: Java code quality checks (via Gradle)2324## Linting Philosophy2526**IMPORTANT**: Linting is primarily for CI/CD validation. Developers should use `format:all` instead of `lint:all` during development, as formatting also fixes most linting issues automatically.2728## Running Linting2930Run commands from the repo root through Nix.3132### Lint All Code3334```bash35CURSOR_DEV=true nix develop -c pnpm lint:all36```3738This runs:39- Backend Java linting (`pnpm backend:lint`)40- Frontend TypeScript/Vue linting (`pnpm frontend:lint`)41- CLI TypeScript linting (`pnpm cli:lint`)42- Shared API test fixtures (`packages/doughnut-test-fixtures`) via Biome (`pnpm test-fixtures:lint`)43- Cypress E2E test linting (`pnpm cy:lint`)44- OpenAPI specification linting (`pnpm openapi:lint`)4546### Individual Linting Commands4748- `pnpm backend:lint` - Java code linting49- `pnpm frontend:lint` - Frontend TypeScript/Vue linting50- `pnpm cli:lint` - CLI TypeScript linting51- `pnpm test-fixtures:lint` - Biome on `packages/doughnut-test-fixtures` only52- `pnpm cy:lint` - Cypress E2E test linting53- `pnpm openapi:lint` - OpenAPI spec validation5455## Format vs Lint5657- **Use `CURSOR_DEV=true nix develop -c pnpm format:all`** during development - it fixes formatting and catches most issues58- **Use `CURSOR_DEV=true nix develop -c pnpm lint:all`** for CI/CD validation - it checks without fixing5960## OpenAPI Linting6162The OpenAPI specification (`open_api_docs.yaml`) is validated using Redocly CLI. This file is **auto-generated** from backend controllers.6364### When OpenAPI Linting Fails65661. **DO NOT** edit `open_api_docs.yaml` directly - it is auto-generated672. **Fix the issue** in the backend Java controller that generates the problematic endpoint683. **Regenerate** the OpenAPI spec and frontend client (do not hand-edit generated files):69```bash70 CURSOR_DEV=true nix develop -c pnpm generateTypeScript71```72 This runs OpenAPI generation from Spring controllers (`pnpm backend:generateOpenAPIDocs`) and TypeScript client generation from the spec (`pnpm openAPIToTypeScript`).734. **Verify** by running `pnpm openapi:lint` again7475### Common OpenAPI Issues7677- **Duplicate operationIds**: Ensure each controller method has a unique `@Operation(operationId = "...")` annotation or unique method name78- **Missing path parameters**: Add `@PathVariable` annotation to method parameters that match path variables79- **Identical paths**: Standardize path parameter names across controllers (e.g., use `{notebook}` consistently, not `{notebookId}` in some places)8081After API changes, also run frontend unit tests and fix call sites — see the `generate-api-client` skill.8283## Linting Tools Configuration8485- **Biome**: Configured in `biome.json` (root) and `frontend/biome.json`86- **Redocly**: Configured in `redocly.yaml`87- **Backend**: Configured in `backend/build.gradle` (Spotless for formatting, custom linting rules)8889## Biome Configuration (2.4.x)9091This project uses `@biomejs/biome@2.4.x` across the workspace (`package.json`, `frontend/package.json`, `packages/doughnut-test-fixtures/package.json`). Keep those versions pinned and in sync — `syncpack fix` runs on `postinstall` to help.9293### Monorepo config layout9495- **`biome.json`** (workspace root) — governs `e2e_test/`, `cli/`, `mcp-server/`, `scripts/`, `infra/`, `packages/doughnut-test-fixtures/`, etc. Implicit `root: true`.96- **`frontend/biome.json`** — sets `"root": false`; governs `frontend/` only. Uses Vue-specific rules (`noVueReservedProps`, `noVueDuplicateKeys`, `noVueSetupPropsReactivityLoss`, etc.).9798When running `pnpm biome check` from the workspace root, Biome walks up from each file to find the nearest config. The frontend subtree uses the frontend config; everything else uses the root config.99100### Keep the `$schema` URL in sync with the CLI version101102Both configs must pin `$schema` to the exact CLI version. A mismatch emits a `deserialize` warning on every run:103104```105i The configuration schema version does not match the CLI version 2.4.x106```107108When upgrading `@biomejs/biome`, run `pnpx @biomejs/biome migrate --write` from both the repo root and `frontend/` to bump the schema URLs (and auto-apply any rule renames / config restructuring).109110### Ignore quirk: `biome migrate` suggesting `root: false` in the root config111112Running `biome migrate` from inside `frontend/` sometimes suggests adding `"root": false` to **the workspace root** `biome.json`. **Do not apply it.** The workspace root must remain the root. This is a known artifact of the migrator walking up from a nested package.113114### Path scoping: use `files.includes`, not separate `linter.includes`115116- `files.includes` is the canonical include/exclude list; it scopes **both** formatter and linter.117- `linter.includes` and `formatter.includes` exist but further narrow each tool's scope and are easy to get wrong (and desynced from `files.includes`). Prefer `files.includes` plus scoped overrides when a specific tool needs different treatment.118- To express "format but don't lint this file", use an override with `linter: { enabled: false }` scoped via `includes`, e.g. for `vitest.config.ts` and `src/assets/daisyui.css` in `frontend/biome.json`. **Do not** rely on a hidden-in-`linter.includes` exclusion — that pattern is opaque.119120### CRITICAL: Project-domain (MFA) rules and Vue SFCs121122Biome 2.x introduces "project" / multi-file-analysis (MFA) rules that build a cross-file import graph. As of Biome 2.4.x, **the MFA resolver does not understand Vue SFC `<script setup>` implicit default exports**. Every `import X from "./X.vue"` in the frontend is flagged as unresolved once MFA activates.123124Project-domain rules include (non-exhaustive):125- `correctness/noUnresolvedImports`126- `suspicious/noDeprecatedImports`127- `suspicious/noImportCycles`128- `correctness/noFloatingPromises`129- `correctness/noMisusedPromises`130- `correctness/noPrivateImports`131132**Rule**: Do NOT enable any of these in the workspace root `biome.json`. Enabling them anywhere the project graph reaches Vue SFCs (which includes the root config, since it delegates to `frontend/biome.json` via MFA) produces 400+ false-positive `noUnresolvedImports` errors even in pure `.ts` files that import from `.vue`.133134- `frontend/biome.json` currently has `noUnresolvedImports: "error"`. It is silently a no-op today because MFA is not activated (no project-domain rule is triggering it). If you add one of the rules above at the root, `frontend:lint` will break even though `frontend/biome.json` hasn't changed.135- Safe alternative for TS-only subtrees: put project-domain rules in a TS-only override, or in a scoped nested config (e.g. `cli/biome.json`) — but test carefully, since MFA scope is workspace-wide.136- Revisit this restriction once Biome releases proper Vue SFC module resolution. See Biome issues on `<script setup>` default-export detection.137138### `noUnusedExpressions` and Chai/Cypress assertions139140`suspicious/noUnusedExpressions` is enabled at the root but **turned off for `e2e_test/**`**. Chai's property-chain assertions (e.g. `expect(x).to.not.be.null`, `expect(x).to.be.true`) look like unused expressions to Biome but are real assertions that throw. The override lives in the existing `e2e_test` block inside root `biome.json`.141142### Suppression comment format143144Biome 2.x expects:145146```ts147// biome-ignore lint/<GROUP>/<RULE>: reason148```149150The 1.9.x form `// biome-ignore lint(<GROUP>/<RULE>): reason` no longer parses. `biome migrate --write` handles existing comments; new suppressions must use the slash form.151152Bulk variants new in 2.x:153- `// biome-ignore-all lint/suspicious/noConsole: allowed in this file`154- `// biome-ignore-start lint/complexity/noForEach: legacy block`155- `// biome-ignore-end lint/complexity/noForEach:`156157### Empty overrides / includes: delete them158159`{ "includes": [], "linter": { "rules": {} } }` and overrides whose `rules` object is empty do nothing and only obscure intent. Clean them up during any biome-config edit.160161### Running `pnpm lint:all` non-interactively162163The `lint:all` script's first step is `pnpm --frozen-lockfile --silent recursive install`. Outside an interactive TTY, pnpm refuses to purge stale `node_modules` and aborts with:164165```166ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY167```168169Export `CI=true` (or set `"confirmModulesPurge": false` in `.npmrc`) before running `pnpm lint:all` in agents / scripts / non-TTY shells. CI already sets `CI=true`.170171## Integration with CI/CD172173The `pnpm lint:all` command is used in CI/CD pipelines to ensure code quality. All linting errors must be resolved before merging code.174
One repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| nerds-odd-e/doughnut.cursor/rules/general.mdc · 49 | Cursor rules | styledo-not | 49/100 | 14 days ago | |
| nerds-odd-e/doughnut.clinerules/daisyui.md · 49 | Cline rules | setuplint-formatstyleui+1 | 57/100 | 14 days ago | |
| nerds-odd-e/doughnut.cursor/rules/architecture-decisions.mdc · 49 | Cursor rules | no sections | 16/100 | 14 days ago | |
| nerds-odd-e/doughnut.cursor/rules/backend-code.mdc · 49 | Cursor rules | styletypesdatabasedo-not | 61/100 | 14 days ago | |
| nerds-odd-e/doughnut.cursor/rules/backend-testing.mdc · 49 | Cursor rules | buildteststyletesting-strategy+2 | 73/100 | 14 days ago | |
| nerds-odd-e/doughnut.cursor/rules/cli.mdc · 49 | Cursor rules | setupbuildteststyle+4 | 96/100 | 14 days ago | |
| nerds-odd-e/doughnut.cursor/rules/db-migration.mdc · 49 | Cursor rules | stylearchdatabasedeployment | 64/100 | 14 days ago | |
| nerds-odd-e/doughnut.cursor/rules/e2e-authoring.mdc · 49 | Cursor rules | setupteststylearch+3 | 80/100 | 14 days ago | |
| nerds-odd-e/doughnut.cursor/rules/e2e-ocr.mdc · 49 | Cursor rules | setuptesting-strategydo-not | 46/100 | 14 days ago | |
| nerds-odd-e/doughnut.cursor/rules/frontend-api.mdc · 49 | Cursor rules | styletesting-strategyapido-not | 57/100 | 14 days ago | |
| nerds-odd-e/doughnut.cursor/rules/frontend-component.mdc · 49 | Cursor rules | testlint-formatstylearch+2 | 76/100 | 14 days ago | |
| nerds-odd-e/doughnut.cursor/rules/frontend-storybook.mdc · 49 | Cursor rules | buildteststyletesting-strategy+1 | 69/100 | 14 days ago | |
| nerds-odd-e/doughnut.cursor/rules/frontend-testing.mdc · 49 | Cursor rules | buildteststyletesting-strategy+2 | 89/100 | 14 days ago | |
| nerds-odd-e/doughnut.cursor/rules/gsd-coexistence.mdc · 49 | Cursor rules | style | 60/100 | 14 days ago | |
| nerds-odd-e/doughnut.cursor/rules/mcp-server.mdc · 49 | Cursor rules | buildtestlint-formatarch+2 | 85/100 | 14 days ago | |
| nerds-odd-e/doughnut.cursor/rules/planning.mdc · 49 | Cursor rules | teststylearchdo-not+1 | 75/100 | 14 days ago | |
| nerds-odd-e/doughnut.cursor/rules/script.mdc · 49 | Cursor rules | testarch | 58/100 | 14 days ago | |
| nerds-odd-e/doughnutAGENTS.md · 49 | AGENTS.md | no sections | 47/100 | 14 days ago | |
| nerds-odd-e/doughnutCLAUDE.md · 49 | CLAUDE.md | agent-behaviour | 47/100 | 14 days ago |
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| hiromaily/go-crypto-wallet.cursor/rules/typescript.mdc · 126 | Cursor rules | setupbuildtestlint-format+6 | 100/100 | 14 days ago | |
| TechSquidTV/Hermes.cursor/rules/10-hermes-api.mdc · 46 | Cursor rules | testlint-formatstylearch+5 | 100/100 | 14 days ago | |
| markstev/mark-starter.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+6 | 99/100 | 14 days ago | |
| deifos/clipmira-subtitles.cursor/rules/frontend.mdc · 1 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 14 days ago | |
| dodgecfr/combatfilms-webapp.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 14 days ago | |
| Allymahmoud/case-intake-platform.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 14 days ago | |
| langflow-ai/langflow.cursor/rules/docs_development.mdc · 153k | Cursor rules | setupbuildtestlint-format+7 | 97/100 | 14 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-api-tests.mdc · 46 | Cursor rules | teststyletesting-strategysecurity+3 | 97/100 | 14 days ago |
A badge carrying the measured quality of the strongest agent config file in this repository, out of 100. It reads from this index every time somebody loads your page, so it changes when the measurement changes and there is nothing to keep up to date. Free, no account, and the value is not something you or we can set by hand.
[](https://rulestack.kynth.studio/configs/nerds-odd-e-doughnut-cursor-rules-linting-formating)Would rather not hotlink us? Every badge is also served in shields.io’s endpoint schema, so shields renders the image and your readers never talk to our domain:
Published by Toolproof, the masthead over this index and eight others. The method behind the number is at toolproof.kynth.studio/methodology, and the whole thing is readable as JSON with no key at /api.