[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6EKGRAG062FCM7SA3303GWQ":3,"skill-same:01M6EKGRAG062FCM7SA3303GWQ":109},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":34,"publishedAt":55,"linkOnly":56,"markdownContent":57,"markdownBody":58,"frontMatter":59,"refName":60,"commitSha":61,"skillDirectory":62,"forksCount":63,"repositoryHtmlUrl":64,"repositoryHomepageUrl":65,"ownerHtmlUrl":66,"ownerBlogUrl":6,"ownerDescription":67,"updatedAt":68,"install":69,"resources":82},"01M6EKGRAG062FCM7SA3303GWQ","quality-code-review",null,"Review code for any Frappe application — a checklist distilled from years of engineering practice on correctness, security, performance, concurrency, readability, API design, and testing. Use this when reviewing a diff, a PR, or a piece of code for quality and security, or when you want a reviewer's checklist grounded in hard-won Frappe\u002FERPNext lessons.","Venkateshvenki404224\u002Fbenchpress","Venkateshvenki404224","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F75518037?v=4","01M6EKFVM5P6HRTJBSR8BABRJ2","Venkatesh","01M6EKFVRVS346Q492S7SASWTV",6,"AGPL-3.0","https:\u002F\u002Fgithub.com\u002FVenkateshvenki404224\u002Fbenchpress\u002Fblob\u002Fversion-16\u002F.claude\u002Fskills\u002Fquality-code-review\u002FSKILL.md",[18,23,26,29],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000007","代码审查","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":27,"categoryName":28,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000015","视觉设计",{"categoryNo":30,"categoryName":31,"taxonomyType":32,"taxonomyTypeDesc":33},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54],"quality code review","review","quality code","code","code review","quality","review code","frappe","erpnext","readability","diff","use","reviewer","api","concurrency","pr","performance","security","design","testing","2026-11-10 10:12:30",false,"---\nname: quality-code-review\ndescription: >-\n  Review code for any Frappe application — a checklist distilled from years of\n  engineering practice on correctness, security, performance, concurrency,\n  readability, API design, and testing. Use this when reviewing a diff, a PR, or\n  a piece of code for quality and security, or when you want a reviewer's\n  checklist grounded in hard-won Frappe\u002FERPNext lessons.\n---\n\n# Quality Code Review\n\nA reviewer's checklist for Frappe applications. Protect **correctness, security,\nand the future maintainer**, in that order of consequence. The sections below run\nin that order — spend most attention on §1 and §2. Prefer a root-cause fix over a\nworkaround, and say *why* a finding matters (what breaks, for whom).\n\n---\n\n## 1. Correctness & stability (highest consequence)\n\nThe worst bug is **silent stateful corruption** — wrong ledgers\u002Fstock posted\nwith no error. Treat stateful and legal\u002Faccounting\u002Fcompliance code as **\"failure\nis not an option\"** code.\n\n- **Fail early and loudly.** Use assertions for internal invariants:\n  `assert total_credit == total_debit`. (Assertions are for invariants the code\n  guarantees — not user-facing validation.)\n- **Picture how it breaks.** For every change ask: *Where can this break? How\n  will someone misuse this?* Write fool-proof code. Extensions and overrides\n  should especially consider handling all sorts of failure modes.\n- **No partial commits.** A stray `frappe.db.commit()` \u002F `db.rollback()`\n  mid-transaction ends the transaction and exposes partial state — flag every\n  one. Submitting\u002Fsaving with validation bypassed (docs posted with no SL\u002FGL\n  entry when validation fails) is a critical bug.\n- **Don't sacrifice atomicity for convenience** (e.g. adding `autocommit` to fix\n  bootstrapping makes transactions non-atomic). Autocommit belongs only on schema\n  creation.\n- **Preserve invariants over UX.** \"Compromise UX, but guarantee correctness.\"\n  Don't degrade working code to accommodate broken code.\n- **Validate the issue before fixing it.** Sometimes the correct fix is \"don't\n  fix this\". Identify the root causes first.\n- **Watch for destructive DB APIs with empty\u002F`None` filters.**\n  `set_value(\"Site\", None, ...)` \u002F `db.delete` with no filter updates\u002Fdeletes\n  *every* row. These must error, not silently operate on the whole table. Flag\n  any `set_value`\u002F`delete`\u002F`get_value` where the name\u002Ffilter could be `None` or\n  empty- or attacker-controlled.\n- **Check the types in a condition actually match.** A comparison between\n  mismatched types (string vs `datetime`, string vs int) silently never matches\n  or is always true — cast explicitly (`cint`\u002F`flt`) at the boundary.\n- **Question the return shape.** Before indexing a result, ask whether it can be\n  `None` \u002F `[None, None]`; watch `as_dict=1` (list of dicts) vs scalar confusion.\n- **Don't silently change long-standing semantics.** Behavior callers have relied\n  on for a long time is a contract — altering it is a breaking change in disguise,\n  even when no signature changed.\n\n## 2. Security\n\nAvoiding a vulnerability is far easier than fixing one safely. Audit\nsecurity-critical code (auth, authorization, permissions, user management)\nespecially hard.\n\n**Injection**\n- NEVER build SQL by string concatenation\u002Ff-strings. Use the ORM or query\n  builder. If raw SQL is unavoidable, use parameter substitution\n  (`frappe.db.sql(\"... where name = %s\", (user,))`) — never interpolate yourself.\n  Better still, **avoid introducing new raw SQL at all**: beyond injection risk it\n  ties code to one database, and the framework aims to stay DB-agnostic (Postgres\n  support). Prefer `frappe.qb`.\n- **Type confusion is an injection vector even with the ORM.** Frappe accepts\n  complex types, so a parameter expected to be a string can arrive as a filter\n  list: `{\"key\": [\"!=\", \"\"]}` passed to `db.get_value` bypasses a secret-key\n  check. **Validate input *types* at trust boundaries** — explicit\n  `isinstance(key, str)`. Audit every `@frappe.whitelist` method for this.\n- Never `eval`\u002F`exec` anything yourself. `safe_eval`\u002F`safe_exec` only, in limited\n  volume, and \"safe_exec is not magic.\" Never accept a client-supplied method\n  path to execute.\n\n**Sandboxing & trust boundaries**\n- Sandboxed execution (RestrictedPython\u002F`safe_exec`) is not reliably safe —\n  assume escapes exist. Security toggles must live at the **right trust\n  boundary**: server-script enablement is a *bench*-level config, never\n  site-level (a tenant could enable it and take over the whole server).\n- Prefer **allowlists over blocklists** — blocklists are bypassable. Don't\n  expose everything by default.\n\n**Access control**\n- \"Think 10 times before `allow_guest=True`\" — it is not a shortcut around real\n  authn\u002Fauthz. Web pages must apply permissions *before* reading\u002Fsharing data.\n  Prefer `get_list`\u002F`get_all` over hand-rolled queries.\n- **Scope relaxations precisely.** Verify a rate-limit\u002Fpermission exception\n  targets exactly the intended principal — not, say, all non-guest users.\n\n**Path traversal \u002F filesystem**\n- Prefer the File doctype API. If user input enters a path, ensure it can't\n  traverse (`\u002F..\u002F..\u002F`) outside the site folder.\n\n**Crypto \u002F secrets**\n- Never roll your own crypto; reuse existing implementations. Verify authenticity\n  of guest\u002Fwebhook requests (HMAC).\n- Signed\u002Fone-time URLs: use a truly secret signing value; expire by **both** time\n  and first use; **validate using the URL alone, not merged form data** (Frappe\n  merges URL + form data → replay attacks with one valid signature).\n- Store secrets in password fields; never plain text; never leak secrets in logs\n  or error messages.\n\n**XSS & the rest of OWASP**\n- Don't inject user input into the DOM. Treat XSS as critical even when it looks\n  trivial — HTML\u002FJS injection usually leads to account hijack.\n- Don't fix XSS by sanitizing and throwing away special characters. Prefer escaping right before injecting values in DOM.\n\n## 3. Performance is correctness\n\nThe cheapest time to fix performance is at review; slow code merged sits\nundetected for years. Performance is a feature (Doherty threshold; humans\nperceive ~100ms).\n\n- **Budgets:** common reads \u003C 100ms; reads \u003C 1s; most writes \u003C 5s; **never**\n  exceed ~10s. P99 of a frequent read-only request should be ~1s. A slow\n  synchronous request blocks a worker (head-of-line blocking).\n- **Complexity rule:** a frequently-called endpoint must do O(1) or O(log N)\n  work — a large constant factor at worst, never O(N). Counting rows is O(N), not\n  O(1); `COUNT(*)` over a large\u002Ffiltered table is expensive. Bound unbounded\n  scans (e.g. last 3 months, a \"1000+\" sentinel) rather than scanning everything.\n- **Indexes are code.** Flag any `WHERE`\u002Fjoin\u002Ffilter on an unindexed column.\n  Indexes (and custom indexes) must be **committed in code**, not applied ad-hoc\n  — they get lost on migration otherwise. Form loads that pull\n  comments\u002Fversions\u002Fassignments need *all* those queries indexed; one unindexed\n  query makes everything sluggish.\n- **No DB calls in loops.** \"Don't write validations that call db in LOOPS.\" Flag\n  N+1 patterns. Cache stable values (UOM, docstatus, status) instead of\n  re-querying. This is acceptable in background jobs, but never in requests.\n- **The Remove → Reduce → Reuse ladder** for slow code you can't fix: remove it,\n  invoke it less, or memoize. Pick the right cache scope (\n  `@redis_cache`, `@request_cache`, `@site_cache` — the last balloons memory if\n  overused). DO NOT hand-roll caches in `frappe.local` or `frappe.flags`:\n  \"you'll just be creating brand-new cache-invalidation bugs.\" Don't cache\n  trivially cheap work.\n- **Memory:** don't stuff junk into shared module-level files \u002F `__init__.py` \u002F\n  class-level state — it stays resident forever. Remove unused module-level\n  imports (move into the function that uses them). Watch for leaks.\n- **Reorder conditionals so the DB call is last.** In a boolean expression, put\n  cheap in-memory checks first so short-circuiting can skip the query entirely.\n- **Aggregate in SQL, not Python.** Use `SUM()`\u002F`COUNT()` in the query instead of\n  fetching all rows to reduce them in memory; push filters into the subquery so\n  they apply *before* the join.\n- **Don't fetch a whole doc for one value.** Use `get_value`\u002F`get_single_value`\u002F\n  `set_value` for a single column instead of `get_doc().save()`; use\n  `frappe.delete_doc` instead of `get_doc().delete()` (which fetches the doc only\n  to delete it).\n- **No MyISAM tables in hot paths.** Reading a MyISAM table takes an implicit\n  table-level lock — never touch one in a request path.\n- **Move long work to a background queue.** Long-running work belongs in\n  `enqueue(..., queue=\"long\")`, not a synchronous request that blocks a worker.\n\n## 4. Concurrency\n\n- **Check-then-act is a race.** `if not frappe.db.exists(...): insert()` — two\n  workers both see \"not exists\" and both insert. Prefer a **DB-level unique\n  constraint**; \"outsource integrity to the database.\"\n- **Locking footguns:** `SELECT ... FOR UPDATE` on an unindexed query locks every\n  scanned row (and gaps) — always ensure the filter uses an index, or you lock\n  the whole table. Locking a parent but not its children yields a \"mutant\" doc.\n- **Global mutable state \u002F class attributes are global in Python** — a shared\n  engine\u002Fclass attribute leaking query state across concurrent requests produces\n  garbage. Make query-building **stateless**. Don't do \"weird shit with\n  `frappe.local`\" — `local` is for variables, not static state.\n\n## 5. Readability & maintainability\n\n~50% of dev time is spent reading code; rotten code eventually forces a rewrite.\n\n- **Keep functions pure when they can be pure** — easy to read and test.\n- **Don't pass mutable objects around to be filled in** (\"assembly\" code) —\n  return new values. Passing a mutable to be mutated forces a reader to open two\n  files to understand one thing.\n- A function that mutates its input must be named appropriately.\n- **Prefer the boring construct.** While that functional map-reduce one-liner\n  looks beautiful, please just write a 4-line for-loop. Favor debuggable code\n  over clever code.\n- **Consistency over personal style.** A codebase shouldn't be a hodge-podge of\n  10 styles. Match the surrounding formatting\u002Fnaming\u002Fimport conventions; flag a\n  change that breaks them.\n- **Good taste:** restructure so the edge case becomes the common case, removing\n  special-case branches. Ask: can this be simpler? Less code? Is it\n  over-indented if-else soup?\n- **Prefer extending shared components over copy-paste divergence.** 3–4 forked\n  implementations of one thing → slow long-term velocity. Avoid tight coupling\n  across modules; integrate through clear, documented public APIs.\n- Document **public** modules\u002Fclasses\u002Ffunctions with docstrings; **prefer type\n  annotations over describing types in prose** (\"type hints are 10x better\");\n  type checkers find non-obvious bugs.\n- Docstrings should only mention important things. Keep them short and to the\n  point. Don't explain what's trivially understood from function name. Focus on\n  \"why\".\n- **Split unrelated changes** into separate commits\u002FPRs — keeps review focused\n  and `git blame`\u002Freverts clean.\n\n## 6. API design & backward compatibility\n\n- **Principle of least astonishment:** an API's name + signature should convey\n  ~90% of intent; users shouldn't be surprised by behavior.\n- **Reject loose\u002Foverloaded parameters** that accept many disjoint types\n  (string\u002Fdict\u002Flist\u002FNone). Prefer separate single-purpose functions. Beware\n  implicit fallbacks; use explicit variants. \"APIs whose correct use depends on\n  tribal knowledge are a liability.\"\n- **Build for extension, not override.** Provide hooks; never monkey-patch\n  core at runtime (\"inexcusably horrible\" — breaks future fixes) and never copy a\n  whole core file to change a few lines (fixes won't propagate).\n- **Backward compatibility is an obligation** for mature\u002Fpublic APIs. Follow\n  semver; **minor versions = zero breaking changes**. Breaking changes include:\n  removing public functions\u002Ffields, reordering args, new mandatory args, changed\n  business logic, moved\u002Frenamed files (broken imports), bumped shared deps.\n  Renaming without keeping the old name as an alias is an *unnecessary* break.\n  Every breaking change ships a deprecation warning + docs.\n- **Watch for schema breaking-change footguns:** adding mandatory fields to\n  existing sites, making long-lived fields unique (needs a data patch), changing\n  field types without patches, removing fields.\n- **Schema changes that silently skip existing sites need a data patch.** Single\n  doctypes don't sync new-field defaults to existing sites, and a field-type\n  change (e.g. text→int) doesn't convert existing values — both need an explicit\n  patch, tested against a populated site.\n- **New parameters go last as keyword args with safe defaults** (`None`, not\n  `\"\"`) so existing positional callers don't break. When renaming, keep the old\n  name as a shim: `def old_name(...): return new_name(...)`.\n- **Patch hygiene.** Data patches must be **idempotent** (safe to re-run),\n  **correctly ordered** (run after the field\u002Fdoctype they read exists), and live\n  in the **right app** (a framework change is patched in the framework, not the\n  downstream app).\n- **A modified existing test is a red flag.** If making a change pass required\n  editing an existing test's assertions, you've likely broken a real workflow —\n  justify it explicitly rather than bending the test.\n\n## 7. Testing\n\n- **Each PR needs decent test coverage** — patch coverage on the diff, not just\n  project coverage. (Frappe target: 85% covered lines in the diff.) Tests should\n  capture the most-used business scenarios.\n- **Regression test every fix.** A bug fix without a test that would have caught\n  it invites the regression back. For extreme-consequence (stateful\u002Fcompliance)\n  code, go beyond examples — property-based testing (Hypothesis).\n- **Flag missing migration\u002Fdata-patch coverage.** Schema changes and data patches\n  are the highest-risk, least-tested area; a change that alters fields or\n  migrates data needs a patch tested against a realistic, populated site (empty\n  tables always \"migrate\" successfully even when the change is invalid).\n- **Tests must be deterministic and independent.** No `random` (flaky); no\n  reliance on state left by other tests (order-dependence); use `freeze_time`\n  for time-dependent logic.\n\n## 8. Error messages, logging & observability\n\n- **Error message quality is a legitimate review item.** Titles must be specific\n  and Google-able (never \"Message\"\u002F\"Error\"). Reference field names as fields.\n  State *what changed*: the row, the field, and before→after values\n  (`1 → 2`). The user must know \"qty changed from what to what?\"\n- **Surface failures to the affected party** — \"a broken email setup is the\n  user's problem only if they know it's broken.\"\n- **Log things.** Preserve tracebacks\u002Fexception context (orders of magnitude\n  easier debugging). Log destructive\u002Fadmin actions with attributable identity\n  (who, when, from where), persisted outside ephemeral containers.\n","# Quality Code Review\n\nA reviewer's checklist for Frappe applications. Protect **correctness, security,\nand the future maintainer**, in that order of consequence. The sections below run\nin that order — spend most attention on §1 and §2. Prefer a root-cause fix over a\nworkaround, and say *why* a finding matters (what breaks, for whom).\n\n---\n\n## 1. Correctness & stability (highest consequence)\n\nThe worst bug is **silent stateful corruption** — wrong ledgers\u002Fstock posted\nwith no error. Treat stateful and legal\u002Faccounting\u002Fcompliance code as **\"failure\nis not an option\"** code.\n\n- **Fail early and loudly.** Use assertions for internal invariants:\n  `assert total_credit == total_debit`. (Assertions are for invariants the code\n  guarantees — not user-facing validation.)\n- **Picture how it breaks.** For every change ask: *Where can this break? How\n  will someone misuse this?* Write fool-proof code. Extensions and overrides\n  should especially consider handling all sorts of failure modes.\n- **No partial commits.** A stray `frappe.db.commit()` \u002F `db.rollback()`\n  mid-transaction ends the transaction and exposes partial state — flag every\n  one. Submitting\u002Fsaving with validation bypassed (docs posted with no SL\u002FGL\n  entry when validation fails) is a critical bug.\n- **Don't sacrifice atomicity for convenience** (e.g. adding `autocommit` to fix\n  bootstrapping makes transactions non-atomic). Autocommit belongs only on schema\n  creation.\n- **Preserve invariants over UX.** \"Compromise UX, but guarantee correctness.\"\n  Don't degrade working code to accommodate broken code.\n- **Validate the issue before fixing it.** Sometimes the correct fix is \"don't\n  fix this\". Identify the root causes first.\n- **Watch for destructive DB APIs with empty\u002F`None` filters.**\n  `set_value(\"Site\", None, ...)` \u002F `db.delete` with no filter updates\u002Fdeletes\n  *every* row. These must error, not silently operate on the whole table. Flag\n  any `set_value`\u002F`delete`\u002F`get_value` where the name\u002Ffilter could be `None` or\n  empty- or attacker-controlled.\n- **Check the types in a condition actually match.** A comparison between\n  mismatched types (string vs `datetime`, string vs int) silently never matches\n  or is always true — cast explicitly (`cint`\u002F`flt`) at the boundary.\n- **Question the return shape.** Before indexing a result, ask whether it can be\n  `None` \u002F `[None, None]`; watch `as_dict=1` (list of dicts) vs scalar confusion.\n- **Don't silently change long-standing semantics.** Behavior callers have relied\n  on for a long time is a contract — altering it is a breaking change in disguise,\n  even when no signature changed.\n\n## 2. Security\n\nAvoiding a vulnerability is far easier than fixing one safely. Audit\nsecurity-critical code (auth, authorization, permissions, user management)\nespecially hard.\n\n**Injection**\n- NEVER build SQL by string concatenation\u002Ff-strings. Use the ORM or query\n  builder. If raw SQL is unavoidable, use parameter substitution\n  (`frappe.db.sql(\"... where name = %s\", (user,))`) — never interpolate yourself.\n  Better still, **avoid introducing new raw SQL at all**: beyond injection risk it\n  ties code to one database, and the framework aims to stay DB-agnostic (Postgres\n  support). Prefer `frappe.qb`.\n- **Type confusion is an injection vector even with the ORM.** Frappe accepts\n  complex types, so a parameter expected to be a string can arrive as a filter\n  list: `{\"key\": [\"!=\", \"\"]}` passed to `db.get_value` bypasses a secret-key\n  check. **Validate input *types* at trust boundaries** — explicit\n  `isinstance(key, str)`. Audit every `@frappe.whitelist` method for this.\n- Never `eval`\u002F`exec` anything yourself. `safe_eval`\u002F`safe_exec` only, in limited\n  volume, and \"safe_exec is not magic.\" Never accept a client-supplied method\n  path to execute.\n\n**Sandboxing & trust boundaries**\n- Sandboxed execution (RestrictedPython\u002F`safe_exec`) is not reliably safe —\n  assume escapes exist. Security toggles must live at the **right trust\n  boundary**: server-script enablement is a *bench*-level config, never\n  site-level (a tenant could enable it and take over the whole server).\n- Prefer **allowlists over blocklists** — blocklists are bypassable. Don't\n  expose everything by default.\n\n**Access control**\n- \"Think 10 times before `allow_guest=True`\" — it is not a shortcut around real\n  authn\u002Fauthz. Web pages must apply permissions *before* reading\u002Fsharing data.\n  Prefer `get_list`\u002F`get_all` over hand-rolled queries.\n- **Scope relaxations precisely.** Verify a rate-limit\u002Fpermission exception\n  targets exactly the intended principal — not, say, all non-guest users.\n\n**Path traversal \u002F filesystem**\n- Prefer the File doctype API. If user input enters a path, ensure it can't\n  traverse (`\u002F..\u002F..\u002F`) outside the site folder.\n\n**Crypto \u002F secrets**\n- Never roll your own crypto; reuse existing implementations. Verify authenticity\n  of guest\u002Fwebhook requests (HMAC).\n- Signed\u002Fone-time URLs: use a truly secret signing value; expire by **both** time\n  and first use; **validate using the URL alone, not merged form data** (Frappe\n  merges URL + form data → replay attacks with one valid signature).\n- Store secrets in password fields; never plain text; never leak secrets in logs\n  or error messages.\n\n**XSS & the rest of OWASP**\n- Don't inject user input into the DOM. Treat XSS as critical even when it looks\n  trivial — HTML\u002FJS injection usually leads to account hijack.\n- Don't fix XSS by sanitizing and throwing away special characters. Prefer escaping right before injecting values in DOM.\n\n## 3. Performance is correctness\n\nThe cheapest time to fix performance is at review; slow code merged sits\nundetected for years. Performance is a feature (Doherty threshold; humans\nperceive ~100ms).\n\n- **Budgets:** common reads \u003C 100ms; reads \u003C 1s; most writes \u003C 5s; **never**\n  exceed ~10s. P99 of a frequent read-only request should be ~1s. A slow\n  synchronous request blocks a worker (head-of-line blocking).\n- **Complexity rule:** a frequently-called endpoint must do O(1) or O(log N)\n  work — a large constant factor at worst, never O(N). Counting rows is O(N), not\n  O(1); `COUNT(*)` over a large\u002Ffiltered table is expensive. Bound unbounded\n  scans (e.g. last 3 months, a \"1000+\" sentinel) rather than scanning everything.\n- **Indexes are code.** Flag any `WHERE`\u002Fjoin\u002Ffilter on an unindexed column.\n  Indexes (and custom indexes) must be **committed in code**, not applied ad-hoc\n  — they get lost on migration otherwise. Form loads that pull\n  comments\u002Fversions\u002Fassignments need *all* those queries indexed; one unindexed\n  query makes everything sluggish.\n- **No DB calls in loops.** \"Don't write validations that call db in LOOPS.\" Flag\n  N+1 patterns. Cache stable values (UOM, docstatus, status) instead of\n  re-querying. This is acceptable in background jobs, but never in requests.\n- **The Remove → Reduce → Reuse ladder** for slow code you can't fix: remove it,\n  invoke it less, or memoize. Pick the right cache scope (\n  `@redis_cache`, `@request_cache`, `@site_cache` — the last balloons memory if\n  overused). DO NOT hand-roll caches in `frappe.local` or `frappe.flags`:\n  \"you'll just be creating brand-new cache-invalidation bugs.\" Don't cache\n  trivially cheap work.\n- **Memory:** don't stuff junk into shared module-level files \u002F `__init__.py` \u002F\n  class-level state — it stays resident forever. Remove unused module-level\n  imports (move into the function that uses them). Watch for leaks.\n- **Reorder conditionals so the DB call is last.** In a boolean expression, put\n  cheap in-memory checks first so short-circuiting can skip the query entirely.\n- **Aggregate in SQL, not Python.** Use `SUM()`\u002F`COUNT()` in the query instead of\n  fetching all rows to reduce them in memory; push filters into the subquery so\n  they apply *before* the join.\n- **Don't fetch a whole doc for one value.** Use `get_value`\u002F`get_single_value`\u002F\n  `set_value` for a single column instead of `get_doc().save()`; use\n  `frappe.delete_doc` instead of `get_doc().delete()` (which fetches the doc only\n  to delete it).\n- **No MyISAM tables in hot paths.** Reading a MyISAM table takes an implicit\n  table-level lock — never touch one in a request path.\n- **Move long work to a background queue.** Long-running work belongs in\n  `enqueue(..., queue=\"long\")`, not a synchronous request that blocks a worker.\n\n## 4. Concurrency\n\n- **Check-then-act is a race.** `if not frappe.db.exists(...): insert()` — two\n  workers both see \"not exists\" and both insert. Prefer a **DB-level unique\n  constraint**; \"outsource integrity to the database.\"\n- **Locking footguns:** `SELECT ... FOR UPDATE` on an unindexed query locks every\n  scanned row (and gaps) — always ensure the filter uses an index, or you lock\n  the whole table. Locking a parent but not its children yields a \"mutant\" doc.\n- **Global mutable state \u002F class attributes are global in Python** — a shared\n  engine\u002Fclass attribute leaking query state across concurrent requests produces\n  garbage. Make query-building **stateless**. Don't do \"weird shit with\n  `frappe.local`\" — `local` is for variables, not static state.\n\n## 5. Readability & maintainability\n\n~50% of dev time is spent reading code; rotten code eventually forces a rewrite.\n\n- **Keep functions pure when they can be pure** — easy to read and test.\n- **Don't pass mutable objects around to be filled in** (\"assembly\" code) —\n  return new values. Passing a mutable to be mutated forces a reader to open two\n  files to understand one thing.\n- A function that mutates its input must be named appropriately.\n- **Prefer the boring construct.** While that functional map-reduce one-liner\n  looks beautiful, please just write a 4-line for-loop. Favor debuggable code\n  over clever code.\n- **Consistency over personal style.** A codebase shouldn't be a hodge-podge of\n  10 styles. Match the surrounding formatting\u002Fnaming\u002Fimport conventions; flag a\n  change that breaks them.\n- **Good taste:** restructure so the edge case becomes the common case, removing\n  special-case branches. Ask: can this be simpler? Less code? Is it\n  over-indented if-else soup?\n- **Prefer extending shared components over copy-paste divergence.** 3–4 forked\n  implementations of one thing → slow long-term velocity. Avoid tight coupling\n  across modules; integrate through clear, documented public APIs.\n- Document **public** modules\u002Fclasses\u002Ffunctions with docstrings; **prefer type\n  annotations over describing types in prose** (\"type hints are 10x better\");\n  type checkers find non-obvious bugs.\n- Docstrings should only mention important things. Keep them short and to the\n  point. Don't explain what's trivially understood from function name. Focus on\n  \"why\".\n- **Split unrelated changes** into separate commits\u002FPRs — keeps review focused\n  and `git blame`\u002Freverts clean.\n\n## 6. API design & backward compatibility\n\n- **Principle of least astonishment:** an API's name + signature should convey\n  ~90% of intent; users shouldn't be surprised by behavior.\n- **Reject loose\u002Foverloaded parameters** that accept many disjoint types\n  (string\u002Fdict\u002Flist\u002FNone). Prefer separate single-purpose functions. Beware\n  implicit fallbacks; use explicit variants. \"APIs whose correct use depends on\n  tribal knowledge are a liability.\"\n- **Build for extension, not override.** Provide hooks; never monkey-patch\n  core at runtime (\"inexcusably horrible\" — breaks future fixes) and never copy a\n  whole core file to change a few lines (fixes won't propagate).\n- **Backward compatibility is an obligation** for mature\u002Fpublic APIs. Follow\n  semver; **minor versions = zero breaking changes**. Breaking changes include:\n  removing public functions\u002Ffields, reordering args, new mandatory args, changed\n  business logic, moved\u002Frenamed files (broken imports), bumped shared deps.\n  Renaming without keeping the old name as an alias is an *unnecessary* break.\n  Every breaking change ships a deprecation warning + docs.\n- **Watch for schema breaking-change footguns:** adding mandatory fields to\n  existing sites, making long-lived fields unique (needs a data patch), changing\n  field types without patches, removing fields.\n- **Schema changes that silently skip existing sites need a data patch.** Single\n  doctypes don't sync new-field defaults to existing sites, and a field-type\n  change (e.g. text→int) doesn't convert existing values — both need an explicit\n  patch, tested against a populated site.\n- **New parameters go last as keyword args with safe defaults** (`None`, not\n  `\"\"`) so existing positional callers don't break. When renaming, keep the old\n  name as a shim: `def old_name(...): return new_name(...)`.\n- **Patch hygiene.** Data patches must be **idempotent** (safe to re-run),\n  **correctly ordered** (run after the field\u002Fdoctype they read exists), and live\n  in the **right app** (a framework change is patched in the framework, not the\n  downstream app).\n- **A modified existing test is a red flag.** If making a change pass required\n  editing an existing test's assertions, you've likely broken a real workflow —\n  justify it explicitly rather than bending the test.\n\n## 7. Testing\n\n- **Each PR needs decent test coverage** — patch coverage on the diff, not just\n  project coverage. (Frappe target: 85% covered lines in the diff.) Tests should\n  capture the most-used business scenarios.\n- **Regression test every fix.** A bug fix without a test that would have caught\n  it invites the regression back. For extreme-consequence (stateful\u002Fcompliance)\n  code, go beyond examples — property-based testing (Hypothesis).\n- **Flag missing migration\u002Fdata-patch coverage.** Schema changes and data patches\n  are the highest-risk, least-tested area; a change that alters fields or\n  migrates data needs a patch tested against a realistic, populated site (empty\n  tables always \"migrate\" successfully even when the change is invalid).\n- **Tests must be deterministic and independent.** No `random` (flaky); no\n  reliance on state left by other tests (order-dependence); use `freeze_time`\n  for time-dependent logic.\n\n## 8. Error messages, logging & observability\n\n- **Error message quality is a legitimate review item.** Titles must be specific\n  and Google-able (never \"Message\"\u002F\"Error\"). Reference field names as fields.\n  State *what changed*: the row, the field, and before→after values\n  (`1 → 2`). The user must know \"qty changed from what to what?\"\n- **Surface failures to the affected party** — \"a broken email setup is the\n  user's problem only if they know it's broken.\"\n- **Log things.** Preserve tracebacks\u002Fexception context (orders of magnitude\n  easier debugging). Log destructive\u002Fadmin actions with attributable identity\n  (who, when, from where), persisted outside ephemeral containers.\n",{"name":5,"description":7},"version-16","38e17b09d0a500b359ccf7abf4cc0557dcd605e4",".claude\u002Fskills\u002Fquality-code-review",1,"https:\u002F\u002Fgithub.com\u002FVenkateshvenki404224\u002Fbenchpress","https:\u002F\u002Fbenchpress.cloud","https:\u002F\u002Fgithub.com\u002FVenkateshvenki404224","🚀 Backend Engineer | 🧠 Open Source Contributor | ⚙️ Frappe & ERPNext Expert | 🔬 Lab Infra Builder | 👕 Founder @gemzy.in | 💡 Building Dev Tools","2026-11-09 02:43:13",{"source":8,"skillName":5,"commands":70,"prompt":80,"zipAvailable":81},[71,74,77],{"tool":72,"command":73},"npx","npx skills add Venkateshvenki404224\u002Fbenchpress --skill quality-code-review",{"tool":75,"command":76},"bunx","bunx skills add Venkateshvenki404224\u002Fbenchpress --skill quality-code-review",{"tool":78,"command":79},"pnpm","pnpm dlx skills add Venkateshvenki404224\u002Fbenchpress --skill quality-code-review","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FVenkateshvenki404224\u002Fbenchpress\u002Fblob\u002Fversion-16\u002F.claude\u002Fskills\u002Fquality-code-review\u002FSKILL.md",true,[83,89,93,97,101,105],{"path":84,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":87,"sourceHtmlUrl":88},"references\u002Fblast-radius-calculator.md","REFERENCE","参考文档",10496,"https:\u002F\u002Fgithub.com\u002FTorchiaHub\u002FSkills\u002Fblob\u002Fmaster\u002F01-Inbox\u002Fdownloads\u002Fgithub-awesome-copilot\u002F20260429T115304Z\u002Fsource\u002Fskills\u002Fdata-breach-blast-radius\u002Freferences\u002Fblast-radius-calculator.md",{"path":90,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":91,"sourceHtmlUrl":92},"references\u002Fdata-classification.md",8935,"https:\u002F\u002Fgithub.com\u002FTorchiaHub\u002FSkills\u002Fblob\u002Fmaster\u002F01-Inbox\u002Fdownloads\u002Fgithub-awesome-copilot\u002F20260429T115304Z\u002Fsource\u002Fskills\u002Fdata-breach-blast-radius\u002Freferences\u002Fdata-classification.md",{"path":94,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":95,"sourceHtmlUrl":96},"references\u002Fhardening-playbook.md",14290,"https:\u002F\u002Fgithub.com\u002FTorchiaHub\u002FSkills\u002Fblob\u002Fmaster\u002F01-Inbox\u002Fdownloads\u002Fgithub-awesome-copilot\u002F20260429T115304Z\u002Fsource\u002Fskills\u002Fdata-breach-blast-radius\u002Freferences\u002Fhardening-playbook.md",{"path":98,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":99,"sourceHtmlUrl":100},"references\u002Fregulatory-impact.md",15142,"https:\u002F\u002Fgithub.com\u002FTorchiaHub\u002FSkills\u002Fblob\u002Fmaster\u002F01-Inbox\u002Fdownloads\u002Fgithub-awesome-copilot\u002F20260429T115304Z\u002Fsource\u002Fskills\u002Fdata-breach-blast-radius\u002Freferences\u002Fregulatory-impact.md",{"path":102,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":103,"sourceHtmlUrl":104},"references\u002Freport-format.md",10665,"https:\u002F\u002Fgithub.com\u002FTorchiaHub\u002FSkills\u002Fblob\u002Fmaster\u002F01-Inbox\u002Fdownloads\u002Fgithub-awesome-copilot\u002F20260429T115304Z\u002Fsource\u002Fskills\u002Fdata-breach-blast-radius\u002Freferences\u002Freport-format.md",{"path":106,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":107,"sourceHtmlUrl":108},"references\u002FSOURCES.md",12025,"https:\u002F\u002Fgithub.com\u002FTorchiaHub\u002FSkills\u002Fblob\u002Fmaster\u002F01-Inbox\u002Fdownloads\u002Fgithub-awesome-copilot\u002F20260429T115304Z\u002Fsource\u002Fskills\u002Fdata-breach-blast-radius\u002Freferences\u002FSOURCES.md",[110,126,136,161,193,211],{"skillNo":111,"name":112,"nameZh":6,"description":113,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":114,"categories":115,"keywords":117,"publishedAt":55},"01M6EKHBJ8ZYV9E11T2RE35X4D","benchpress-docs","Read and search the BenchPress documentation. Use when working with BenchPress — its setup, configuration, API, and behavior.","https:\u002F\u002Fgithub.com\u002FVenkateshvenki404224\u002Fbenchpress\u002Fblob\u002Fversion-16\u002Fdocs-site\u002F.well-known\u002Fagent-skills\u002Fbenchpress-docs\u002FSKILL.md",[116],{"categoryNo":30,"categoryName":31,"taxonomyType":32,"taxonomyTypeDesc":33},[118,119,120,46,121,122,48,123,124,125],"benchpress docs","docs","configuration","behavior","read","search","documentation","setup",{"skillNo":127,"name":112,"nameZh":6,"description":128,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":129,"categories":130,"keywords":135,"publishedAt":55},"01M6EKH56APEWPT12654C9HG67","Read and search the benchpress documentation. Use when working with benchpress — its setup, configuration, API, and behavior.","https:\u002F\u002Fgithub.com\u002FVenkateshvenki404224\u002Fbenchpress\u002Fblob\u002Fversion-16\u002Fdocs-bundle\u002FSKILL.md",[131,134],{"categoryNo":132,"categoryName":133,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口",{"categoryNo":30,"categoryName":31,"taxonomyType":32,"taxonomyTypeDesc":33},[119,118,121,120,46,122,48,123,124,125],{"skillNo":137,"name":138,"nameZh":6,"description":139,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":140,"categories":141,"keywords":146,"publishedAt":55},"01M6EKH20VPHEBRS67AMARTJRV","ui-design","General UI\u002FUX judgment for layout, polish, visual hierarchy, spacing, typography, color, and accessibility. Use when no product-specific or Frappe-specific design system skill applies.","https:\u002F\u002Fgithub.com\u002FVenkateshvenki404224\u002Fbenchpress\u002Fblob\u002Fversion-16\u002F.claude\u002Fskills\u002Fui-design\u002FSKILL.md",[142,143],{"categoryNo":27,"categoryName":28,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":144,"categoryName":145,"taxonomyType":32,"taxonomyTypeDesc":33},"01JBSEEDCAT000000000000032","设计师",[147,148,53,149,150,151,46,152,153,154,155,156,157,42,158,159,160],"ui","ui design","polish","design system","system","accessibility","ux","typography","general","product","visual","color","layout","judgment",{"skillNo":162,"name":163,"nameZh":6,"description":164,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":165,"categories":166,"keywords":174,"publishedAt":55},"01M6EKGYTRTQHD402K7BBJG669","technical-writing","Write prose in \"Simplified Technical English\". This applies to documentation, READMEs, commits, pull request, plans and release notes. It does not apply to code, identifiers, marketing copy, essays, or anything that needs a voice.","https:\u002F\u002Fgithub.com\u002FVenkateshvenki404224\u002Fbenchpress\u002Fblob\u002Fversion-16\u002F.claude\u002Fskills\u002Ftechnical-writing\u002FSKILL.md",[167,170,171],{"categoryNo":168,"categoryName":169,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000018","写作与文案",{"categoryNo":30,"categoryName":31,"taxonomyType":32,"taxonomyTypeDesc":33},{"categoryNo":172,"categoryName":173,"taxonomyType":32,"taxonomyTypeDesc":33},"01JBSEEDCAT000000000000036","内容创作者",[175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,38,190,191,192,124],"writing","technical writing","technical","english","it","copy","simplified technical english","voice","pull request","prose","write","marketing","commits","pull","release","release notes","plans","notes",{"skillNo":194,"name":195,"nameZh":6,"description":196,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":197,"categories":198,"keywords":199,"publishedAt":55},"01M6EKGVVJHPXAR0PC0R8SS8Y8","resolve-backport-conflicts","Resolve merge conflicts in a Mergify backport pull request. When a cherry-pick fails, Mergify commits the conflict markers as they are. This skill takes a pull request link or number as an argument.","https:\u002F\u002Fgithub.com\u002FVenkateshvenki404224\u002Fbenchpress\u002Fblob\u002Fversion-16\u002F.claude\u002Fskills\u002Fresolve-backport-conflicts\u002FSKILL.md",[],[200,201,202,188,183,203,204,205,206,207,208,209,210,187],"conflicts","resolve backport conflicts","backport","merge","resolve merge conflicts","markers","link","as","conflict","merge conflicts","pick",{"skillNo":212,"name":213,"nameZh":6,"description":214,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":215,"categories":216,"keywords":220,"publishedAt":55},"01M6EKGKNGNVHYJHZZ456N4DVW","issue-to-phases","Decompose a planning document (plan.md, a spec, or a GitHub issue) into incremental, independently-shippable \"tracer bullet\" phases, write them as phase specs under specs\u002F, and optionally generate a Ralph loop (ralph.sh + prompt.md) that implements them unattended. Use this whenever the user hands you a plan and wants it broken into phases, mentions \"tracer bullets\", \"phase 0\u002F1\u002F2\", \"build this in phases\", \"thin end-to-end slice first\", \"ralph loop\", or wants phased specs in the specs\u002F folder — even if they never say the word \"phase\". Also use it when the user asks to \"implement \u002F work on phase N\" of an existing spec folder, so the branch, the PR and the spec's status all move the way every earlier phase did.","https:\u002F\u002Fgithub.com\u002FVenkateshvenki404224\u002Fbenchpress\u002Fblob\u002Fversion-16\u002F.claude\u002Fskills\u002Fissue-to-phases\u002FSKILL.md",[217],{"categoryNo":218,"categoryName":219,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000002","文档处理",[221,222,223,207,224,225,226,46,185,227,228,229,230,231,232,233,234,235,50,236],"issue to phases","issue","branch","end","decompose","never","broken","work","ralph","Word","work on","plan","loop","planning","generate","status"]