agentleFS
yogvidwankhedeyogvidwankhede/vishwakarma/.claude/skills/code-quality/SKILL.md

code-quality

Skill1 starsChanged 51 days agoApache-2.0View on GitHub

Use when writing tests, configuring linters, type checks, formatters, or CI gates, or after a change that must compile before it is done.

77 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use spring-boot, logging-patterns and 10 more.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • ffsshhttiikkSkill

    spring-boot

    Spring Boot framework and Java enterprise patterns

    Also in 3 projects that use this

  • DIL8654Skill

    logging-patterns

    Keep logs structured, correlated, low-noise, and safe for production support. Use when adding operational logging, reviewing observability, or improving diagnostics in .NET services.

    Also in 3 projects that use this

  • mstuder-slalomSkill

    jpa-patterns

    JPA/Hibernate patterns for entity design, relationships, query optimization, transactions, auditing, indexing, pagination, and pooling in Spring Boot.

    Also in 3 projects that use this

  • ffsshhttiikkSkill

    design-patterns

    Practical application and coding skills for Design Patterns

    Also in 4 projects that use this

  • glommerSkill

    pr-workflow

    General guidelines for Commits, formatting, CI, dependencies, security

    Also in 4 projects that use this

  • vm0-aiSkill

    cli-design

    Design patterns and conventions for the Okou CLI user experience

    Also in 3 projects that use this

The file

We split this skill into the rules it gives an agent

6 rules. 0 are common, 6 are distinctive, and 0 are very specific to this project.

  • 0

    Common

    Most of the 71 edited versions we read keep these.

  • 6

    Distinctive

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Prefer hand-written fakes in a shared test module, and confine mocking libraries to framework types you do not own.

    line 92
  2. Distinctive:

    After three failed hypotheses on a single bug, stop guessing and gather a trace, verify a load-bearing assumption directly, bisect against …

    line 135
  3. Distinctive:

    Run these checks against your own output.

    line 243
  4. Distinctive:

    If you attempted a fix more than once, did you report the failed attempts rather than leaving a tree nobody can reason about?

    line 257
  5. Distinctive:

    Do your UI tests select by accessible name, role and state, or visible text — and where you used a test ID, is the element genuinely a …

    line 264
  6. Distinctive:

    Do any screenshot tests freeze the clock, fix their data, and fake the image loader?

    line 265

“Common” is measured: kept by at least 6 in 10 of the 71 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md
<!-- Generated by Vishwakarma. Do not edit this file directly. Edit the source skill and run `vishwakarma sync` to regenerate. -->

Code Quality

A quality gate only works if it fails the build. Everything else — a warning in a log, a lint rule set to warn, a coverage number printed at the end of a run — is a suggestion, and suggestions lose to deadlines every time. The design question for any quality practice is therefore not "is this a good idea" but what does it block, and who notices when it fires.


1. The verification loop

Make the change. Run the gate. Read the actual error text, not the exit code. Fix. Re-run. Proceed only when it passes.

The failure this prevents is proceeding on an assumed pass, and its cost compounds rather than adds: if change A is never verified and change B lands on top of it, a later failure could originate in either, and the bisect surface has doubled. After four unverified changes there are sixteen interaction possibilities and the cheapest recovery is often discarding all four.

Reading the error is a separate discipline from running the gate. Unresolved reference: viewModel and Cannot access class 'HiltViewModel'; it is defined in a module that is not on the compile classpath describe different problems; the build already computed the diagnosis, and guessing from the first line instead of reading it is choosing to re-derive it slowly. Report failures rather than iterating silently — three undisclosed attempts leave a working tree nobody can reason about.


2. The post-change build gate

A full compile is mandatory before claiming success after a change to module structure (a new module, changed api/implementation visibility, a new package boundary), dependency-injection wiring (a new @Inject constructor, module, binding or scope), a navigation graph (new route, changed argument type), database schema (new entity, changed column, new migration), or toolchain versions (Gradle, AGP, Kotlin, Swift, Node, TypeScript).

The mechanism is shared across all five: they fail at configuration or codegen time, not at edit time. The editor's incremental analysis does not run the annotation processor, resolve the dependency graph, or validate the schema, so it shows green on code that cannot build. A missing Hilt binding looks like a perfectly valid @Inject constructor until a MissingBinding error arrives 40 seconds into the KSP task.


3. Static analysis and formatting

Kotlin and Android: detekt with type resolution enabled — without it detekt sees syntax only and most rules worth having are disabled — plus a Compose ruleset, ktlint through spotless, and Android Lint for platform correctness, whose domain does not overlap detekt's. Web: ESLint with type-aware rules (the parserOptions.project wiring), tsc --noEmit as a gate separate from the bundler — bundlers transpile per file and strip types without checking them, so a bundle can build from code that does not typecheck — and Prettier. Apple: SwiftLint for correctness, SwiftFormat for formatting.

Configuration lives in one file that CI and local runs both read, invoked by the same command. Two config sources guarantee a class of defect where a change passes locally and fails in CI, leaving the developer to debug by pushing commits.

Formatters run automatically and their output is never discussed. A review comment about a blank line or an import order is a formatter that was not wired up, surfacing as human labour: a reviewer has a finite number of comments before the author starts skimming, and formatting comments are the easiest to write, so they crowd out the missing null check.


4. Choosing the test shape

Pick the cheapest shape that actually proves the claim. Pure logic — a calculation, a parser, a reducer — takes a unit test with no framework and no doubles. A state holder — a ViewModel, a hook, a store — takes a unit test with a hand-written fake repository asserting emitted state. Rendering and semantics take a component test using semantics-first selectors. Visual regression takes a screenshot test with a frozen clock, fixed seeded data and a fake image loader. Wiring takes one integration smoke test per critical flow, not one per behaviour. A backend contract takes a contract test against a schema or recorded pact, not a live service.

The mechanism is diagnostic resolution: an integration test that fails tells you something broke; a unit test that fails tells you what. An end-to-end checkout failure implicates cart logic, pricing, auth, navigation, the network layer and the fixtures; a failing test on calculateDiscount implicates calculateDiscount.

Prefer hand-written fakes in a shared test module, and confine mocking libraries to framework types you do not own. A mock encodes the call sequence you expected, so the test fails when the implementation is refactored even though behaviour is unchanged — it measures coupling, not correctness — and it returns whatever you told it to, including values the real collaborator would never produce.

Select elements in UI tests by accessible name, then role and state, then visible text, and only then a test ID. That ordering makes the test double as an accessibility assertion: a test that finds the submit button by its accessible name fails the moment that name disappears, which is exactly when the button becomes unusable with a screen reader.


5. Flakes, coverage, and what fails the build

A test that fails intermittently gets quarantined with a named owner and a dated deadline. It does not get a retry: a retry converts a real intermittent bug into invisible latency, leaving the race in production where it surfaces as a one-in-two-hundred crash nobody can reproduce. Quarantine means the test still runs and reports, and does not block the build.

Report coverage; do not gate on a global percentage. A global threshold is satisfiable by testing the easy surface — data classes, mappers, getters — which moves effort away from the payment reconciliation logic where a defect is expensive. Gate on diff coverage instead, and treat coverage as a detector of untested regions rather than a target.

The build fails on: any type error; any lint error; any formatting drift; any failing test outside quarantine; a size budget breach; a benchmark regression beyond a stated noise band derived from observed CI variance; and any accessibility violation at the verified tier. Anything else is advisory and should be labelled as such. A gate that fires on noise is worse than no gate, because it consumes the trust the real gates depend on.


6. Stop at the irreversible boundary

Hand these back rather than performing them: app store uploads and review-queue submissions, release track promotions and rollout percentage increases, version code and build number changes, production database migrations, force-pushes to a shared branch, and credential rotation. Each is irreversible or externally visible, and the reversal cost is measured in hours or days of several people's time against a saving measured in seconds. Prepare the work fully — write the migration, verify it against a staging copy — and hand over the command. The judgement being reserved is the decision to execute, not the engineering.

After three failed hypotheses on a single bug, stop guessing and gather a trace, verify a load-bearing assumption directly, bisect against a known-good state, or reproduce smaller. This is where the verification loop pays: a history of verified changes bisects cleanly, and one of assumed passes does not.

Rules

MUST NOT — Do not configure a retry for an intermittently failing test; quarantine it with a named owner and a dated deadline so it still runs and reports without blocking the build.

Why: A retry converts a real intermittent bug into invisible latency. The underlying race — an unawaited coroutine, a shared fixture, a real clock, an unmocked call — is still present in production code, where it surfaces as a one-in-two-hundred crash nobody can reproduce, and the retry has deleted the only signal that pointed at it while charging an extra CI run per occurrence forever.

MUST NOT — Do not perform store uploads, release track promotions, version code changes, production migrations, force-pushes to shared branches, or credential rotations; prepare the work and hand over the command.

Why: Each is irreversible or externally visible, and the reversal cost is measured in hours or days of several people’s time against a saving measured in seconds. A promoted release cannot be recalled from devices that installed it, a force-push destroys work that exists nowhere else, and a rotated signing key can permanently orphan an app’s update path.

Incorrect:

Run the migration against production because it passed against a staging copy.

Correct:

Write the migration, verify it against a staging copy, report what was verified, and hand over the exact command to run.

MUST — After running a gate, read the full error text before making the next change, rather than acting on the exit code or the first line of a stack trace.

Why: The build has already computed the diagnosis and printed it, so declining to read it means re-deriving it by guesswork. The distinction is load-bearing: "unresolved reference" and "defined in a module that is not on the compile classpath" name entirely different problems, and only the second tells you the fix is in the build file.

Incorrect:

Build failed -> the symbol is probably misspelled -> rename it -> build again.

Correct:

Build failed -> read the message and the Caused by chain -> the class is on a module not on the compile classpath -> add the dependency -> build again.

MUST — Run a full compile before claiming success after any change to module structure, dependency-injection wiring, a navigation graph, a database schema, or a toolchain version.

Why: All five fail at configuration or codegen time rather than at edit time, and the editor’s incremental analysis runs neither the annotation processor nor the dependency resolver nor the schema validator. It therefore shows green on code that cannot build — a missing binding looks like a valid @Inject constructor until the KSP task reaches it.

Incorrect:

Add a nullable column and a migration; the IDE is green and the in-memory-database unit tests pass, so report it done.

Correct:

Add the column and the migration, then run the full build with schema validation on — which catches the migration written against the old column ordering before every existing user crashes on launch.

MUST — Gate on the type checker itself — tsc --noEmit, or the platform compiler — as a step distinct from the bundler build, and enable type resolution in linters that support it.

Why: Most bundlers transpile per file and strip types without checking them, so a bundle can build cleanly from code that does not typecheck; the bundler succeeding tells you the syntax parsed and nothing more. The same holds for linters: without type information the rules that catch real defects — floating promises, unsafe any propagation — do not run at all.

MUST — Select elements in UI tests by accessible name first, then role and state, then visible text, dropping to a test ID only when none of those exist.

Why: This ordering makes the test double as an accessibility assertion. A test that finds the submit button by its accessible name fails the moment that name disappears, which is exactly when the button becomes unusable with a screen reader; a test that finds it by data-testid passes whether or not the control is exposed to assistive technology at all.

Exceptions:

  • Genuinely nameless structural containers — a list wrapper, a layout region with no semantic role — where inventing a name purely for the test would add noise to the accessibility tree. Reaching for a test ID on an interactive control is instead a finding about the component.

SHOULD NOT — Do not fail the build on a global coverage percentage; report coverage and gate on diff coverage instead.

Why: A global threshold is satisfiable by testing the easy surface — data classes, mappers, getters, code with no branches and no risk — which moves effort away from the logic where a defect is expensive. The number rises, the risk profile does not, and the team learns that coverage work is busywork. Diff coverage is scoped to the author’s actual change and degrades gracefully on renames and config edits.

SHOULD — Write the cheapest test shape that actually proves the claim, and keep integration tests to one per critical flow rather than one per behaviour.

Why: Diagnostic resolution is what you are buying: an integration test that fails tells you something broke, while a unit test that fails tells you what. A failing end-to-end checkout implicates cart logic, pricing, auth, navigation, the network layer and its own fixtures; a failing test on calculateDiscount implicates calculateDiscount.

Incorrect:

Prove the discount arithmetic through an end-to-end checkout run in a browser.

Correct:

Unit-test the discount arithmetic directly; keep one end-to-end checkout test to prove the pieces are wired together.

SHOULD — Write hand-written fakes in a shared test module for collaborators you own, and reserve mocking libraries for framework types you cannot reasonably reimplement.

Why: A mock encodes the call sequence you expected, so asserting on it makes the test fail when the implementation is refactored even though behaviour is unchanged — it measures coupling rather than correctness. A mock also returns whatever you told it to, including values the real collaborator would never produce, so it misses the defects that matter.

Incorrect:

verify(dao).insert(user) then verify(dao).refreshCache() — which goes red when the refresh is correctly moved inside the DAO’s own insert.

Correct:

A FakeUserDao backed by a mutable map, asserting the user is retrievable afterwards — green across that refactor, red if the user is not actually stored.

Before reporting completion

Run these checks against your own output. Answer each question explicitly rather than assuming the answer, because the point of the exercise is to notice what you did not notice while building.

Run the project’s own gate chain — substitute the real commands for this stack. This placeholder covers a TypeScript project; on Android it would be ./gradlew detekt lintDebug testDebugUnitTest, and on Apple swiftlint --strict && xcodebuild test. (blocking)

npm run typecheck && npm run lint && npm test

Confirm no change in the tree was left unverified or silently retried. (blocking)

  • Did you run the relevant gate after every change, and read the full error output rather than the exit code alone?
  • Did any change to module structure, dependency injection, navigation, database schema, or a toolchain version get a full compile before you reported it done?
  • If you attempted a fix more than once, did you report the failed attempts rather than leaving a tree nobody can reason about?
  • Are you claiming any result that you have not actually observed a green run produce?

Confirm each new test is the cheapest shape that proves its claim. (blocking)

  • For each test you added, what claim does it prove, and is there a cheaper shape that would prove the same claim?
  • Are your test doubles hand-written fakes for collaborators you own, with mocking libraries reserved for framework types?
  • Do your UI tests select by accessible name, role and state, or visible text — and where you used a test ID, is the element genuinely a nameless structural container?
  • Do any screenshot tests freeze the clock, fix their data, and fake the image loader?

Confirm the gates you configured still carry information and stop at the right boundary.

  • Is every gate you added able to fail the build, and is everything advisory labelled as advisory?
  • Is any benchmark or accessibility gate tight enough to fire on noise, and is its band derived from observed CI variance rather than a guess?
  • Did you quarantine flaky tests with an owner and a date rather than configuring a retry, and did you avoid gating on a global coverage percentage?
  • Did you stop short of store uploads, track promotions, production migrations, shared-branch force-pushes, and credential rotations, handing over the command instead?

Further reference

These are not loaded by default. Read one only when its question is the question you currently have.

  • references/gates-and-static-analysis.md — Which tools do I configure per ecosystem, what must fail the build, when is a full compile mandatory, which actions do I refuse to perform, and what do I do after three failed hypotheses?
  • references/testing-strategy.md — What kind of test proves this particular claim most cheaply, should this double be a fake or a mock, how should UI tests select elements, and what do I do with a flaky test or a coverage number?

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.