@gspothq/cli 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/README.md +58 -11
  2. package/configurations/database/postgres/manifest.toml +51 -57
  3. package/configurations/database/postgres/rules/POSTGRES.md +14 -13
  4. package/configurations/framework/astro/manifest.toml +33 -35
  5. package/configurations/framework/astro/rules/ASTRO.md +2 -2
  6. package/configurations/framework/expo/manifest.toml +19 -19
  7. package/configurations/framework/express/manifest.toml +10 -4
  8. package/configurations/framework/express/rules/EXPRESS.md +9 -7
  9. package/configurations/framework/fastapi/manifest.toml +9 -3
  10. package/configurations/framework/fastapi/rules/FASTAPI.md +19 -18
  11. package/configurations/framework/nestjs/manifest.toml +12 -12
  12. package/configurations/framework/nestjs/rules/NESTJS.md +9 -9
  13. package/configurations/framework/nextjs/manifest.toml +39 -79
  14. package/configurations/framework/nextjs/rules/NEXTJS.md +9 -7
  15. package/configurations/framework/nextjs/tool-files/semgrep/nextjs.yml +35 -0
  16. package/configurations/framework/react/manifest.toml +33 -43
  17. package/configurations/framework/react/rules/REACT.md +14 -20
  18. package/configurations/framework/react-dom/manifest.toml +6 -6
  19. package/configurations/framework/react-native/manifest.toml +11 -11
  20. package/configurations/framework/react-native/rules/REACT-NATIVE.md +3 -5
  21. package/configurations/framework/site/manifest.toml +57 -110
  22. package/configurations/framework/site/rules/SITE.md +1 -1
  23. package/configurations/framework/svelte/manifest.toml +29 -31
  24. package/configurations/framework/svelte/rules/SVELTE.md +6 -6
  25. package/configurations/framework/vue/manifest.toml +37 -37
  26. package/configurations/framework/vue/rules/VUE.md +6 -6
  27. package/configurations/general/commits/manifest.toml +20 -84
  28. package/configurations/general/commits/rules/GIT.md +24 -13
  29. package/configurations/general/coverage/manifest.toml +1 -1
  30. package/configurations/general/dependencies/manifest.toml +47 -107
  31. package/configurations/general/dependencies/rules/DEPENDENCIES.md +13 -7
  32. package/configurations/general/docs/manifest.toml +28 -43
  33. package/configurations/general/docs/rules/DOCS.md +16 -9
  34. package/configurations/general/duplication/manifest.toml +12 -14
  35. package/configurations/general/engineering/manifest.toml +6 -2
  36. package/configurations/general/engineering/rules/agent/PLANNING.md +9 -6
  37. package/configurations/general/engineering/rules/agent/SUPPRESSIONS.md +20 -11
  38. package/configurations/general/engineering/rules/agent/TASKS.md +6 -2
  39. package/configurations/general/engineering/rules/agent/WORKING.md +34 -29
  40. package/configurations/general/engineering/rules/code/ACCESSIBILITY.md +9 -7
  41. package/configurations/general/engineering/rules/code/CLI.md +12 -9
  42. package/configurations/general/engineering/rules/code/COMMENTS.md +11 -1
  43. package/configurations/general/engineering/rules/code/CONFIGURATION.md +2 -0
  44. package/configurations/general/engineering/rules/code/ERRORS.md +5 -3
  45. package/configurations/general/engineering/rules/code/HTTP.md +9 -10
  46. package/configurations/general/engineering/rules/code/LOGGING.md +10 -9
  47. package/configurations/general/engineering/rules/code/TESTING.md +18 -11
  48. package/configurations/general/files/manifest.toml +45 -73
  49. package/configurations/general/files/rules/YAML.md +20 -10
  50. package/configurations/general/format/manifest.toml +53 -106
  51. package/configurations/general/gspot/manifest.toml +19 -39
  52. package/configurations/general/gspot/rules/GENERATED.md +14 -4
  53. package/configurations/general/licenses/manifest.toml +12 -14
  54. package/configurations/general/naming/defaults.json +14 -13
  55. package/configurations/general/naming/manifest.toml +17 -17
  56. package/configurations/general/naming/rules/NAMING.md +87 -67
  57. package/configurations/general/prose/manifest.toml +166 -25
  58. package/configurations/general/prose/rules/WRITING.md +6 -2
  59. package/configurations/general/repository/manifest.toml +12 -12
  60. package/configurations/general/secrets/manifest.toml +52 -73
  61. package/configurations/general/secrets/rules/SECRETS.md +3 -1
  62. package/configurations/general/security/manifest.toml +29 -52
  63. package/configurations/general/security/rules/SECURITY.md +8 -6
  64. package/configurations/general/spelling/manifest.toml +15 -25
  65. package/configurations/general/structure/manifest.toml +31 -31
  66. package/configurations/infrastructure/ansible/manifest.toml +15 -15
  67. package/configurations/infrastructure/docker/manifest.toml +55 -67
  68. package/configurations/infrastructure/docker/rules/DOCKER.md +15 -13
  69. package/configurations/infrastructure/github-actions/manifest.toml +21 -41
  70. package/configurations/infrastructure/github-actions/rules/GITHUB-ACTIONS.md +16 -12
  71. package/configurations/infrastructure/github-actions/tool-files/zizmor.yml +5 -0
  72. package/configurations/infrastructure/nginx/manifest.toml +20 -20
  73. package/configurations/infrastructure/nginx/rules/NGINX.md +5 -4
  74. package/configurations/infrastructure/openapi/manifest.toml +20 -20
  75. package/configurations/infrastructure/openapi/rules/OPENAPI.md +3 -1
  76. package/configurations/infrastructure/xcode/manifest.toml +55 -75
  77. package/configurations/infrastructure/xcode/rules/XCODE.md +5 -3
  78. package/configurations/language/bash/manifest.toml +89 -170
  79. package/configurations/language/bash/rules/BASH.md +79 -17
  80. package/configurations/language/bash/rules/LANGUAGE.md +30 -26
  81. package/configurations/language/bash/rules/OPERATIONS.md +10 -8
  82. package/configurations/language/bash/rules/SAFETY.md +39 -35
  83. package/configurations/language/css/manifest.toml +22 -61
  84. package/configurations/language/css/rules/CSS.md +21 -2
  85. package/configurations/language/css/rules/TAILWIND.md +7 -5
  86. package/configurations/language/html/manifest.toml +16 -35
  87. package/configurations/language/html/rules/HTML.md +23 -5
  88. package/configurations/language/javascript/eslint-runtime-names.json +66 -61
  89. package/configurations/language/javascript/eslint-runtime.mjs +118 -0
  90. package/configurations/language/javascript/manifest.toml +135 -207
  91. package/configurations/language/javascript/rules/BUN.md +2 -2
  92. package/configurations/language/javascript/rules/COMPONENTS.md +4 -4
  93. package/configurations/language/javascript/rules/DENO.md +1 -1
  94. package/configurations/language/javascript/rules/JAVASCRIPT.md +30 -17
  95. package/configurations/language/javascript/rules/NODE.md +2 -2
  96. package/configurations/language/javascript/rules/PLAYWRIGHT.md +10 -9
  97. package/configurations/language/markdown/manifest.toml +20 -51
  98. package/configurations/language/python/manifest.toml +231 -342
  99. package/configurations/language/python/rules/DESIGN.md +40 -33
  100. package/configurations/language/python/rules/FLOW.md +8 -6
  101. package/configurations/language/python/rules/PACKAGING.md +14 -7
  102. package/configurations/language/python/rules/PYTHON.md +83 -33
  103. package/configurations/language/python/rules/TYPING.md +25 -23
  104. package/configurations/language/sql/manifest.toml +19 -37
  105. package/configurations/language/sql/rules/SQL.md +90 -2
  106. package/configurations/language/swift/manifest.toml +67 -78
  107. package/configurations/language/swift/rules/SWIFT.md +138 -40
  108. package/configurations/language/swift/rules/SWIFTUI.md +11 -9
  109. package/configurations/language/swift/rules/UIKIT.md +21 -19
  110. package/configurations/language/typescript/manifest.toml +34 -34
  111. package/configurations/language/typescript/rules/TYPESCRIPT.md +62 -10
  112. package/configurations/library/drizzle/manifest.toml +25 -38
  113. package/configurations/library/drizzle/rules/DRIZZLE.md +4 -2
  114. package/configurations/library/react-hook-form/manifest.toml +4 -4
  115. package/configurations/library/react-hook-form/rules/REACT-HOOK-FORM.md +15 -13
  116. package/configurations/library/tanstack-query/manifest.toml +6 -6
  117. package/configurations/library/tanstack-query/rules/TANSTACK-QUERY.md +5 -3
  118. package/configurations/library/testing-library/manifest.toml +6 -6
  119. package/configurations/library/translations/manifest.toml +22 -22
  120. package/configurations/library/translations/rules/NEXT-INTL.md +6 -8
  121. package/configurations/library/translations/rules/TRANSLATIONS.md +22 -23
  122. package/configurations/library/trpc/manifest.toml +3 -3
  123. package/configurations/library/trpc/rules/TRPC.md +8 -6
  124. package/configurations/library/zod/manifest.toml +6 -6
  125. package/configurations/library/zod/rules/ZOD.md +4 -2
  126. package/configurations/library/zustand/manifest.toml +4 -4
  127. package/configurations/library/zustand/rules/ZUSTAND.md +16 -14
  128. package/configurations/platform/cloudflare/manifest.toml +28 -47
  129. package/configurations/platform/cloudflare/rules/CLOUDFLARE.md +11 -6
  130. package/configurations/platform/supabase/manifest.toml +48 -61
  131. package/configurations/platform/supabase/rules/SUPABASE.md +27 -28
  132. package/configurations/testing/jest/manifest.toml +21 -22
  133. package/configurations/testing/pytest/manifest.toml +28 -28
  134. package/configurations/testing/swift-tests/manifest.toml +18 -29
  135. package/configurations/testing/swift-tests/rules/SWIFT-TESTS.md +7 -5
  136. package/configurations/testing/vitest/manifest.toml +32 -32
  137. package/configurations/testing/vitest/rules/VITEST.md +3 -1
  138. package/dist/gspot.js +22297 -24707
  139. package/dist/{linkinator-program.js → linkinator-process.js} +5 -7
  140. package/dist/{purgecss-program.js → purgecss-process.js} +12 -12
  141. package/package.json +7 -9
  142. package/configurations/infrastructure/github-actions/zizmor.yml +0 -3
  143. package/configurations/language/bash/rules/NAMING.md +0 -62
  144. package/configurations/language/css/rules/NAMING.md +0 -17
  145. package/configurations/language/html/rules/NAMING.md +0 -16
  146. package/configurations/language/javascript/rules/NAMING.md +0 -31
  147. package/configurations/language/markdown/markdownlint-cli2.mjs +0 -19
  148. package/configurations/language/python/rules/NAMING.md +0 -53
  149. package/configurations/language/sql/rules/NAMING.md +0 -90
  150. package/configurations/language/swift/rules/NAMING.md +0 -88
  151. package/configurations/language/typescript/rules/NAMING.md +0 -73
  152. package/configurations/library/swift-snapshot-testing/manifest.toml +0 -32
  153. package/dist/THIRD-PARTY-NOTICES.md +0 -25
  154. package/dist/eslint-rule-levels.json +0 -10
  155. package/dist/tool-versions.json +0 -3
  156. package/gspot.schema.json +0 -45991
  157. /package/{dist/LICENSE.md → LICENSE.md} +0 -0
  158. /package/configurations/framework/express/{semgrep → tool-files/semgrep}/express.yml +0 -0
  159. /package/configurations/framework/fastapi/{semgrep → tool-files/semgrep}/fastapi.yml +0 -0
  160. /package/configurations/general/dependencies/{syncpack.json → tool-files/syncpack.json} +0 -0
  161. /package/configurations/general/prose/{vale → tool-files/vale}/styles/LICENSES.md +0 -0
  162. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/ampm.yml +0 -0
  163. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/conscious-language.yml +0 -0
  164. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/contractions.yml +0 -0
  165. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/currency.yml +0 -0
  166. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/dashes.yml +0 -0
  167. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/date-ordinals.yml +0 -0
  168. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/dates.yml +0 -0
  169. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/file-paths.yml +0 -0
  170. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/future.yml +0 -0
  171. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/hedging.yml +0 -0
  172. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/history.yml +0 -0
  173. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/idioms.yml +0 -0
  174. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/interface-verbs.yml +0 -0
  175. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/jargon.yml +0 -0
  176. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/leading-conjunctions.yml +0 -0
  177. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/link-text.yml +0 -0
  178. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/locations.yml +0 -0
  179. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/marketing.yml +0 -0
  180. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/merge-conflict-markers.yml +0 -0
  181. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/militaristic-language.yml +0 -0
  182. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/modals.yml +0 -0
  183. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/numbered-units.yml +0 -0
  184. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/obvious-fields.yml +0 -0
  185. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/ordinal-adverbs.yml +0 -0
  186. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/percentage-numbers.yml +0 -0
  187. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/placeholders.yml +0 -0
  188. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/product-centric-writing.yml +0 -0
  189. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/punctuated-headings.yml +0 -0
  190. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/self-reference.yml +0 -0
  191. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/session-identifiers.yml +0 -0
  192. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/since.yml +0 -0
  193. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/straight-quotes.yml +0 -0
  194. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/suspended-hyphens.yml +0 -0
  195. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/symbols.yml +0 -0
  196. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/time-ranges.yml +0 -0
  197. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/url-prepositions.yml +0 -0
  198. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/version-range.yml +0 -0
  199. /package/configurations/general/secrets/{gitleaks.toml → tool-files/gitleaks.toml} +0 -0
  200. /package/configurations/infrastructure/docker/{trivyignore → tool-files/trivyignore} +0 -0
  201. /package/configurations/language/bash/{semgrep → tool-files/semgrep}/bash.yml +0 -0
  202. /package/configurations/language/javascript/{semgrep → tool-files/semgrep}/javascript.yml +0 -0
  203. /package/configurations/language/swift/{semgrep → tool-files/semgrep}/swift.yml +0 -0
  204. /package/configurations/platform/cloudflare/{semgrep → tool-files/semgrep}/cloudflare.yml +0 -0
  205. /package/configurations/platform/supabase/{semgrep → tool-files/semgrep}/supabase.yml +0 -0
@@ -1,22 +1,27 @@
1
1
  # Python Design
2
2
 
3
+ Read this rule when you write Python functions or classes.
4
+
3
5
  ## Functions
4
6
 
5
- I/O never hides in a helper that looks like a pure transformation. Declare an ordinary no-result case in the return type and return an explicit `None`; input that
6
- violates the API contract raises a specific exception. A generator owns its resource lifetime
7
+ I/O never hides in a helper that looks like a pure transformation. Declare an ordinary no-result case in the signature and return an explicit `None`. Input that
8
+ violates the API contract raises a specific exception.
9
+
10
+ A generator owns its resource lifetime
7
11
  explicitly and documents its yielded values and cleanup contract. A partially consumed
8
12
  generator never holds a resource implicitly. A generator expression serves a consumer that
9
- only needs a lazy projection. Comprehensions serve one mapping or one filter; nested
13
+ only needs a lazy projection. Comprehensions serve a single mapping or filter. Nested
10
14
  logic, several conditions, mutation, or a transformation that needs explanation gets an ordinary loop so
11
- its ordering stays visible.
15
+ its order stays visible.
12
16
 
13
17
  ### Function conventions
14
18
 
15
19
  <!-- level: all -->
16
20
 
17
- A function owns one responsibility. Name it for its action and domain concept, except required framework callback names. Make side effects clear in its name or docstring. Plain
18
- functions and explicit data flow come before classes. When a function passes a limit, split
19
- out a step with a real name. If the limit does not fit the code, ask the user to raise it:
21
+ A function owns one responsibility. Name it for its action and domain concept. Framework callbacks keep their required names. Make side effects clear in its name or docstring. Plain
22
+ functions and explicit data flow come before classes.
23
+
24
+ When a function goes over a size limit, extract one step and name it for what it does. If the limit does not fit the code, ask the user to raise it:
20
25
 
21
26
  ```python
22
27
  """Select recent message text under a validated nonnegative limit."""
@@ -41,11 +46,11 @@ def trim_history_messages(messages: Sequence[str], max_messages: int) -> list[st
41
46
  __all__ = ["trim_history_messages"]
42
47
  ```
43
48
 
44
- A nested function closes over a local value to make the outer function clearer, keeping its
49
+ A nested function closes over a local value to make the outer code clearer, and keeps its
45
50
  captured state and lifetime explicit. It never exists to
46
51
  hide a helper from users or to turn a directly usable expression into a local call. A
47
- module-level private helper serves reusable logic that a nested function hides. A conversion that a named helper only
48
- forwards has its body placed directly at the call site.
52
+ module-level private helper serves reusable logic that a nested function hides. When a named helper only
53
+ forwards a conversion, put its body at the call site.
49
54
 
50
55
  Write a lambda as a one-line expression at its consumer, such as
51
56
  a sorting key `lambda item: item.name`. Prefer a generator expression or `operator` function over
@@ -55,12 +60,13 @@ operations gets `if` branches.
55
60
 
56
61
  ## Classes
57
62
 
58
- A class exists for real state, invariants, or behavior. Keep a coordinating function without
59
- polymorphism as a function. Never group static functions into a class. Related
60
- classes that form one contract, such as schemas, exceptions, protocols, and their records,
61
- stay together, and modules split by independent responsibility rather than class count. Plain
62
- data has public attributes, internal attributes carry one leading
63
- underscore, and a double underscore only protects a base class from subclass collisions.
63
+ A class exists for real state, invariants, or behavior. Do not turn a coordinating function
64
+ without polymorphism into a class. Never group static functions into a class.
65
+
66
+ Keep classes that form one contract together, for example schemas, exceptions, protocols,
67
+ and their records. Split modules by independent responsibility, not by class count. Plain
68
+ data has public attributes. Internal attributes carry one leading underscore. Use a double
69
+ underscore only to protect a base class from subclass collisions.
64
70
 
65
71
  `__init__` stays small, takes the values the class needs rather than the external objects
66
72
  that carry them, and validates invariants. Keep it separate from database rows, object-relational mapping (ORM) objects,
@@ -73,13 +79,14 @@ Loading, serialization, and validation systems stay outside the
73
79
  business object. Keep a derived attribute cheap and deterministic. Build it from initialized
74
80
  fields; a named constructor handles derivation through I/O or complex parsing.
75
81
 
76
- Use a dataclass as a typed plain data record with methods only from the data contract. Mutable
82
+ Use a dataclass as a typed plain record with methods only from its data contract. Mutable
77
83
  defaults use `field(default_factory=...)`, and `__post_init__` holds cheap invariants and
78
- derived fields. A named constructor serves construction that needs parsing, I/O, or
79
- alternate sources. Never use it to disguise a mutable global configuration object. A frozen dataclass
84
+ derived fields. Use a named constructor when creation needs parsing, I/O, or
85
+ alternate sources.
86
+
87
+ Never use it to disguise a mutable global configuration object. A frozen dataclass
80
88
  expresses a value fixed after construction. A project on `attrs` applies the same principles
81
- through its factories, validators, and converters, and do not introduce `attrs` to avoid a
82
- small dataclass or a function.
89
+ through its factories, validators, and converters. Do not add `attrs` in place of a small dataclass or a function.
83
90
 
84
91
  ```python
85
92
  """Represent an independently owned batch of prompt text."""
@@ -105,30 +112,30 @@ __all__ = ["Batch"]
105
112
 
106
113
  <!-- level: all -->
107
114
 
108
- Document public dataclass fields in the configured docstring convention: Google style
109
- uses `Attributes:`, NumPy style an underlined `Attributes` heading.
115
+ Document public dataclass fields in the configured docstring convention: `Attributes:` for
116
+ Google style, an underlined `Attributes` heading for NumPy.
110
117
 
111
118
  A property provides cheap, unsurprising attribute access with no side effects. Never use it as a
112
- getter and setter over an internal attribute, expensive work, a rename of a stored attribute,
113
- or a computation subclasses must override. `@property` beats a hand-written descriptor unless
114
- the public interface requires descriptor behavior.
119
+ getter and setter over an internal attribute, expensive work, a rename of a stored one,
120
+ or a computation subclasses must override. Prefer `@property` to a hand-written descriptor unless
121
+ the public interface requires one.
115
122
 
116
123
  ## Inheritance and decorators
117
124
 
118
- Design a class for inheritance or forbid inheritance. Composition shares code, and
125
+ Design a class for inheritance or forbid it. Composition shares code, and
119
126
  protocols or small abstract base classes define interfaces. Reserve specialization for a
120
- subclass that adds to the base class and works wherever callers expect the base class. A
127
+ subclass that extends the base class and works wherever callers expect its parent. A
121
128
  hierarchy never mixes code sharing, interface definition, and specialization, stays shallow,
122
- and lives physically together. Do not default to the template method pattern: a base class
129
+ and lives physically together. Do not default to the template method pattern. A base class
123
130
  that defines control flow and calls subclass hooks takes more work to read than a function with
124
131
  explicit delegation.
125
132
 
126
- Do not put variants in one class with a type field and optional fields that apply to some values.
133
+ Do not put variants in one class with a type field and optional attributes that apply to some values.
127
134
  Make invalid states unrepresentable. Behavior varying on more than one axis uses composition.
128
135
  Cross-cutting behavior such as tracking, caching, timing, or logging uses a wrapper that
129
- records completion only after the wrapped operation succeeds. An operation that varies by
130
- type without a clear owner may use `functools.singledispatch`. A class meant for subclassing
131
- documents its public API and its subclass API separately when the distinction matters.
136
+ records completion only after the operation succeeds. An operation that varies by
137
+ type without one class that handles it may use `functools.singledispatch`. A class meant for subclassing
138
+ documents its public and subclass APIs separately when the distinction matters.
132
139
 
133
140
  A decorator removes real repetition or expresses a framework contract. It behaves
134
141
  unsurprisingly and preserves function metadata. Because it runs at import time, it never
@@ -1,5 +1,7 @@
1
1
  # Python Flow
2
2
 
3
+ Read this rule when you write Python error handling, conditions, loops, or file handling.
4
+
3
5
  ## Exceptions and error handling
4
6
 
5
7
  Use explicit validation for external inputs and required preconditions. Avoid exceptions for
@@ -40,7 +42,7 @@ Keep the exception's public contract stable when changing its message.
40
42
  A custom exception inherits from `Exception`, describes its condition in the docstring, and
41
43
  lives beside the behavior that raises it rather than in a one-declaration file.
42
44
 
43
- Do not substitute a built-in exception for an external API's declared error type. Follow a
45
+ Do not substitute a built-in exception for the declared error type of an external API. Follow a
44
46
  library's public error contract when callers depend on it.
45
47
 
46
48
  ## Assertions
@@ -78,9 +80,9 @@ requirements, not solely to shorten the implementation.
78
80
 
79
81
  ## Strings and output
80
82
 
81
- Use f-strings for ordinary value interpolation. Preserve formatting required by an external API,
82
- and keep logging arguments separate from the message when the logging API formats them lazily.
83
- Use efficient joining for a sequence of strings instead of repeatedly growing a large string.
83
+ Use f-strings for ordinary value interpolation. Preserve formatting that an external API requires.
84
+ Keep logging arguments apart from the message when the logger formats them lazily.
85
+ Join a sequence of strings efficiently instead of repeatedly growing one large value.
84
86
 
85
87
  Formatting neither validates nor escapes values. Use parameterized database queries, argument arrays
86
88
  for subprocesses, and the appropriate encoder for HTML, JSON, or another output format.
@@ -88,7 +90,7 @@ for subprocesses, and the appropriate encoder for HTML, JSON, or another output
88
90
  ## Files and stateful resources
89
91
 
90
92
  Use context managers for resources with an explicit lifetime. Close files and network resources
91
- on both success and failure. Declare text encoding when opening a text file. Use binary mode
93
+ on both success and failure. Declare the encoding when opening a text file. Use binary mode
92
94
  for bytes instead of relying on an implicit conversion.
93
95
 
94
96
  A generator can keep a resource open while suspended. Make that lifetime clear to callers,
@@ -118,7 +120,7 @@ if __name__ == "__main__":
118
120
  ```
119
121
 
120
122
  For large inputs, provide an explicit streaming interface whose caller owns the open resource
121
- or uses a context manager. Do not materialize the full input merely to avoid documenting its
123
+ or uses a context manager. Do not materialize the full input only to avoid documenting its
122
124
  lifetime.
123
125
 
124
126
  Write to a temporary file in the same folder, validate it, then rename it over the target. Clean up only paths and resources owned by the current operation.
@@ -1,6 +1,6 @@
1
1
  # Python Packaging
2
2
 
3
- Deployment installs and lockfiles.
3
+ Read this rule when you change Python dependencies, lockfiles, or package layout.
4
4
 
5
5
  ## Package installation security
6
6
 
@@ -10,15 +10,22 @@ Install from the uv lockfile:
10
10
  uv sync --frozen
11
11
  ```
12
12
 
13
- At level `all`, justify pip use with `gspot ignore python/pip-installs`. Update lockfiles only
13
+ Update lockfiles only
14
14
  when the task includes dependency maintenance.
15
15
 
16
+ ### pip installs
17
+
18
+ <!-- level: all -->
19
+
20
+ `python/pip-installs` reports each `pip install`. Replace it with uv. When the project requires pip, follow the suppression
21
+ rules.
22
+
16
23
  ## Import correctness
17
24
 
18
25
  - Respect the import-linter contracts the project declares.
19
- - Run entrypoints through the configured environment, installation, or module entrypoint.
20
- - Ensure packaged imports work outside the repository root. Do not depend on an accidental
21
- current-directory import path.
26
+ - Run each entrypoint through the configured environment, the installed package, or `python -m`.
27
+ - Make packaged imports work outside the repository root. Do not depend on an accidental
28
+ current-folder import path.
22
29
 
23
30
  ## Packages and architecture
24
31
 
@@ -29,7 +36,7 @@ when the task includes dependency maintenance.
29
36
  - Do not use package `__init__.py` files to hide expensive imports.
30
37
  - Keep `__init__.py` files small and import-stable.
31
38
  - Barrel `__init__.py` files may contain imports and `__all__`.
32
- - Keep lower-level packages independent of higher-level workflow packages.
39
+ - Keep lower-level packages independent of higher-level workflows.
33
40
 
34
41
  ## Source layout
35
42
 
@@ -46,4 +53,4 @@ src/
46
53
  accounts.py
47
54
  ```
48
55
 
49
- Keep helper scripts that nobody imports outside the package import path.
56
+ Keep unimported helper scripts outside the package import path.
@@ -1,33 +1,34 @@
1
1
  # Python
2
2
 
3
+ Read this rule when you change Python code.
4
+
3
5
  ## Python scope
4
6
 
5
- Write readable Python before clever Python: explicit data flow, clear names, small functions,
7
+ Prefer readable Python to clever tricks: explicit data flow, clear names, small functions,
6
8
  and the formatter's layout.
7
- Importing a module does no I/O, starts no work, and parses no arguments. Prefer the project's rules to generic style guides, and consistency with the
8
- surrounding module where a guide allows more than one style.
9
+ Importing a module does no I/O, starts no work, and parses no arguments. Prefer the project's rules to generic style guides, and match the
10
+ surrounding module where a guide allows several options.
9
11
 
10
12
  ## Runtime and files
11
13
 
12
- Use the syntax of the declared Python version, stated once in the runtime pin, and name
13
- version requirements in examples whose syntax depends on them. Use `.py` for source and
14
+ Use the syntax of the declared Python version, stated once in the runtime pin. Name the
15
+ version requirement of an example whose syntax depends on it. Use `.py` for source and
14
16
  `.pyi` for stubs.
15
17
 
16
18
  ### Comment language
17
19
 
18
20
  <!-- level: all -->
19
21
 
20
- Comments and docstrings use the project's declared language, and valid user content
21
- never restricts names to ASCII.
22
+ Write comments and docstrings in the project's declared language. Accept valid user content outside ASCII.
22
23
 
23
24
  ## Environment and configuration
24
25
 
25
- An importable module never depends on an active shell, a virtual environment, the working directory, or a
26
+ An importable module never depends on an active shell, a virtual environment, the current folder, or a
26
27
  globally installed package. Imports resolve through project configuration, editable
27
28
  installs, `python -m`, or the configured environment. Virtual environments and package caches
28
29
  stay out of commits.
29
30
 
30
- The configuration owner takes the environment as a mapping, so a test passes its own:
31
+ The module that reads the configuration takes the environment as a mapping, so a test passes its own:
31
32
 
32
33
  ```python
33
34
 
@@ -59,24 +60,24 @@ Every runtime module has a docstring describing its present purpose.
59
60
 
60
61
  ## Imports
61
62
 
62
- Imports resolve in the declared project environment, never through the script directory on
63
- `sys.path`, and break cycles by moving shared data or interfaces into a lower-level module.
63
+ Imports resolve in the declared project environment, never through the script folder on
64
+ `sys.path`. Break import cycles by moving shared data or interfaces into a lower-level module.
64
65
 
65
66
  ### Import conventions
66
67
 
67
68
  <!-- level: all -->
68
69
 
69
- Imports sit at the top after the docstring and future imports, one per line, with typing and
70
- `collections.abc` symbols imported directly. Use absolute cross-package imports; explicit
71
- relative imports serve siblings inside a package where surrounding code does that. Never an
72
- implicit relative import, a wildcard import, an import inside a function, method, or class
73
- body in runtime code, or a dynamic import through `importlib.import_module`, `__import__`, or
74
- `builtins.__import__`.
70
+ Place imports at the top after the docstring and future statements, one per line. Take typing and
71
+ `collections.abc` symbols directly. Use absolute cross-package imports. Explicit
72
+ relative ones serve siblings inside a package where surrounding code does that. Never use an
73
+ implicit relative, wildcard, or dynamic import through `importlib.import_module`, `__import__`, or
74
+ `builtins.__import__`. In runtime code, never import inside a function, method, or class body.
75
75
 
76
- Direct symbol imports keep call sites readable; use a module import when
77
- its prefix identifies the module, as `logging.getLogger(__name__)` does. Give every alias a reason. Two modules can share a final name, a module can collide with a local
78
- name, or a name can have too many words for the call site. A standard abbreviation such as `np` and a
79
- disambiguated generic name provide the other reasons.
76
+ Direct symbol imports keep call sites readable. Import the module instead when
77
+ its prefix identifies the source, as `logging.getLogger(__name__)` does. Give every alias a reason.
78
+ Valid reasons include a shared final name between two modules, a collision with a local symbol,
79
+ or too many words for the call site. A standard abbreviation such as `np` and a
80
+ disambiguated generic name also count.
80
81
 
81
82
  ## Docstrings
82
83
 
@@ -94,9 +95,9 @@ here follow Google style.
94
95
 
95
96
  ## Constants, globals, and mutable state
96
97
 
97
- Prefer module constants. Do not expose mutable global state, lazy singletons, or mutable globals as
98
- public API. Required mutable state stays internal with its design reason documented,
99
- and no ordinary call mutates a module global as a hidden side effect. Validated configuration
98
+ Prefer module constants. Do not expose mutable global state or lazy singletons as
99
+ public API. Required mutable state stays internal, with its design reason documented.
100
+ No ordinary call changes a module global as a hidden side effect. Validated configuration
100
101
  passes into the client or operation that consumes it, without a renaming wrapper.
101
102
 
102
103
  ### Constant naming
@@ -104,7 +105,7 @@ passes into the client or operation that consumes it, without a renaming wrapper
104
105
  <!-- level: all -->
105
106
 
106
107
  Name constants `UPPER_SNAKE`, private ones with one leading underscore, and retained module
107
- state takes a name for its concept.
108
+ state for its concept.
108
109
 
109
110
  ## Main programs
110
111
 
@@ -114,23 +115,72 @@ Repository entrypoints run as `python -m package.module`, and shell scripts call
114
115
  modules rather than inline snippets. A shebang appears only on a file meant to execute
115
116
  directly.
116
117
 
117
- Never execute untrusted input as Python code, and dynamic execution never replaces input
118
- validation.
118
+ Never execute untrusted input as Python code. Dynamic execution never replaces validation.
119
119
 
120
120
  ## Power features
121
121
 
122
122
  <!-- level: all -->
123
123
 
124
- Avoid power features unless the project has a clear local pattern and the feature serves
125
- necessary. That covers custom metaclasses, bytecode manipulation, dynamic inheritance, object
124
+ Avoid power features unless the project already uses one and no plain alternative works. That covers custom metaclasses, bytecode manipulation, dynamic inheritance, object
126
125
  reparenting, import hooks, runtime monkeypatching, and reflection-heavy designs. It also
127
126
  covers interpreter internals, `__del__` cleanup, manual descriptors, and dynamic code
128
- generation. A power feature never makes code shorter or hides a dependency cycle; ordinary
129
- functions, dataclasses, explicit imports, and explicit data structures come first.
127
+ generation. Never use a power feature to shorten code or hide a dependency cycle. Ordinary
128
+ functions, dataclasses, and explicit imports and data structures come first.
130
129
 
131
130
  ## Threading
132
131
 
133
- Built-in types and variable assignment do not synchronize threads: use `queue.Queue` for thread
132
+ Built-in types and variable assignment do not synchronize threads. Use `queue.Queue` for
134
133
  communication and locks or conditions for shared state, which stays small and explicit.
135
134
 
136
- Name Python test files `test_<behavior>.py`, grouped by behavior.
135
+ ## Naming
136
+
137
+ ### Command inputs and external names
138
+
139
+ <!-- level: all -->
140
+
141
+ - CLI flags use lowercase kebab case, such as `--account-name` and
142
+ `--output-dir`.
143
+ - Avoid single-letter names except loop counters and math notation.
144
+ - Preserve provider capitalization in external names such as `AWS_REGION` and
145
+ resource IDs.
146
+
147
+ ### Python modules and imports
148
+
149
+ <!-- level: all -->
150
+
151
+ - Name test files `test_<behavior>.py`, grouped by behavior.
152
+
153
+ - Do not create package-level re-export layers only to preserve old names.
154
+ - Keep `__all__` names accurate and ordered according to local lint rules.
155
+
156
+ Import `AccountSettings` under its declared name. An alias such as `Thing`
157
+ hides the contract and provides no collision information.
158
+
159
+ ### Python types and dataclasses
160
+
161
+ <!-- level: all -->
162
+
163
+ - Dataclass names describe the domain value they represent.
164
+ - Field names describe the value inside the owning type without repeating it.
165
+ - Give `Path` variables names that reveal whether they point to a folder,
166
+ file, report, output, or repository root.
167
+ - Use `*_path` for filesystem paths and `*_dir` only for folders.
168
+ - Use `*_id` only for real identifiers. Never use it for arbitrary names or labels.
169
+ - Use `*_name` for display or provider names.
170
+ - Use `*_key` for dictionary and supported variant keys.
171
+
172
+ | Avoid | Prefer | Meaning |
173
+ | ------------------------ | ---------------- | ------------------- |
174
+ | `Invoice.invoice_number` | `Invoice.number` | The invoice number. |
175
+ | `Shipment.shipment_date` | `Shipment.date` | The shipment date. |
176
+
177
+ ### Python boundary names
178
+
179
+ <!-- level: all -->
180
+
181
+ - Keep raw provider or CLI names at the boundary.
182
+ - Translate an external name into the domain term before passing values inward when
183
+ the two differ.
184
+ - Keep path construction with the code that owns the resource instead of repeating it in
185
+ unrelated operations.
186
+ - Name functions that cross boundaries for the operation they perform.
@@ -1,5 +1,7 @@
1
1
  # Python Typing
2
2
 
3
+ Read this rule when you write Python type annotations.
4
+
3
5
  ## Annotations
4
6
 
5
7
  <!-- level: all -->
@@ -23,13 +25,13 @@ def group_records[Key: Hashable](records: Iterable[tuple[Key, str]]) -> dict[Key
23
25
  return grouped
24
26
  ```
25
27
 
26
- The type parameter syntax needs Python 3.12; `Key` connects input keys to output keys, and
28
+ The type parameter syntax needs Python 3.12. `Key` ties the input to the output, and
27
29
  `Hashable` states the dictionary-key requirement. A variable annotation appears where the
28
30
  compiler cannot infer a clear type, such as an empty collection, not on every
29
31
  intermediate value.
30
32
 
31
- `typing.Annotated` attaches framework or validation metadata to a real type, real type
32
- first. The default stays an ordinary parameter default and never repeats in the
33
+ `typing.Annotated` attaches framework or validation metadata to a real type, which comes
34
+ first. The default stays an ordinary parameter value and never repeats in the
33
35
  metadata object. Arbitrary string metadata never replaces a domain type or validator. A
34
36
  FastAPI query field reads `Annotated[str | None, Query(max_length=50)] = None`.
35
37
 
@@ -37,12 +39,12 @@ FastAPI query field reads `Annotated[str | None, Query(max_length=50)] = None`.
37
39
 
38
40
  Use `object` for a value of any type when the function only uses operations
39
41
  every object has, such as passing it to `str()`. Also use it for an ignored callback
40
- result. Reserve `Any` for a type the type system cannot express, a correct type that makes the
41
- API unreasonable, or a value that intentionally escapes checking. Never use it as a shortcut; a
42
+ result. Reserve `Any` for what the type system cannot express, a correct annotation that makes the
43
+ API unreasonable, or a value that escapes checking on purpose. Never use it as a shortcut; a
42
44
  protocol, type variable, overload, or small value object models the contract first.
43
45
 
44
46
  Arguments take protocols and abstract collections from `collections.abc`, such as `Iterable`,
45
- `Sequence`, `Mapping`, or `Callable`, and `object` for any value. Concrete implementations return
47
+ `Sequence`, `Mapping`, or `Callable`, and `object` for any value. Implementations return
46
48
  concrete types such as `list`, `dict`, or a dataclass. Protocols and abstract base classes choose
47
49
  return types by the promised interface. A union return that forces callers into an immediate
48
50
  `isinstance()` branch becomes separate functions, a tagged dataclass, a protocol, or a small
@@ -52,34 +54,34 @@ Use `float` for numeric APIs that also accept integers. Use `str` for text and `
52
54
  `AnyStr` ties several annotations to one text or binary type.
53
55
 
54
56
  A `Path | None = None` input handles its `None` branch
55
- before calling path methods, and the default path belongs to the configuration owner. Generic
57
+ before calling path methods. The default path belongs to the module that reads the configuration. Generic
56
58
  types name their parameters, such as `Sequence[int]`. A bare `Sequence` or `dict` exists only when
57
- the element type intentionally accepts any type and says so with `Any`. A `TypeVar` connects
59
+ the elements accept any value on purpose and say so with `Any`. A `TypeVar` connects
58
60
  input and output types when that relationship matters, as `Key` does above.
59
61
 
60
62
  ## Aliases, variables, and forward references
61
63
 
62
- A type alias names a complex repeated type in CapWords, with a leading underscore when
63
- internal. It uses a `type` statement when the Python version supports it and the module
64
- already uses it, or `TypeAlias` where a syntax change falls outside the task. `MetricMap` for
65
- `Mapping[str, float]` names an alias; `Path = pathlib.Path` names a class alias and `errno.EEXIST` a
66
- value, and neither defines a typing interface.
64
+ A type alias names a complex repeated annotation in CapWords, with a leading underscore when
65
+ internal. Use a `type` statement when the Python version supports it and the module
66
+ already does. Use `TypeAlias` where a syntax change falls outside the task. `MetricMap` for
67
+ `Mapping[str, float]` names an alias. `Path = pathlib.Path` aliases a class and `errno.EEXIST` holds a
68
+ value; neither defines a typing interface.
67
69
 
68
70
  ### Type variable naming
69
71
 
70
72
  <!-- level: all -->
71
73
 
72
- A private unconstrained type variable can use `_T` or `_P`; a public or constrained one has a
73
- descriptive name such as `Key`, with `_co` and `_contra` for variance, and no public
74
- single-letter `T` or `P`. `ParamSpec` forwards a callable's parameters, and declare a `Callable` with
75
- bound to the signature its consumer needs rather than left bare.
74
+ A private unconstrained type variable can use `_T` or `_P`. A public or constrained one has a
75
+ descriptive name such as `Key`, with `_co` and `_contra` for variance, never a
76
+ single-letter `T` or `P`. `ParamSpec` forwards the parameters of a callable. Give a `Callable` the
77
+ signature its consumer needs; do not leave it bare.
76
78
 
77
79
  ### Forward references
78
80
 
79
81
  `from __future__ import annotations` handles forward references and stays as the project's
80
- convention even where newer Python defers evaluation. String annotations exist only where
81
- the runtime does not provide future annotations or a type-checking-only import needs them. A
82
- `TYPE_CHECKING` import avoids a runtime import only when the runtime does not need the
82
+ convention even where newer Python defers evaluation. Quote a type only where
83
+ the runtime does not provide future annotations or a type-checking-only import needs it. Use a
84
+ `TYPE_CHECKING` import only when the runtime does not need the
83
85
  symbol. Validators and frameworks that evaluate annotations need the real import. A type-only circular
84
86
  import signals a need to move the shared contract.
85
87
 
@@ -109,9 +111,9 @@ class Node:
109
111
 
110
112
  ## Protocols
111
113
 
112
- Define a structural interface with a narrow `typing.Protocol` near the consumer, with
113
- only what that consumer needs; implementations satisfy it without importing or subclassing
114
- it. `@runtime_checkable` exists only for a real `isinstance()` need, and an abstract base
114
+ Define a structural interface with a narrow `typing.Protocol` near the consumer, holding
115
+ only what it needs. Implementations satisfy it without importing or subclassing
116
+ it. `@runtime_checkable` exists only for a real `isinstance()` need. An abstract base
115
117
  class serves nominal identity, runtime instantiation checks, or a standard-library contract,
116
118
  never shared implementation mixed with interface definition.
117
119
 
@@ -1,68 +1,50 @@
1
1
  allowed_words = ["SQL"]
2
+
2
3
  [configuration]
3
- title = "SQL"
4
+ description = "SQLFluff checks layout and style in the project dialect. At level all, the configuration also checks line comments and file length."
4
5
  suggests = []
5
- description = "SQLFluff checks layout and style in the project dialect. At all, SQL checks line comments, file length, and PostgreSQL function structure."
6
+ title = "SQL"
6
7
 
7
8
  [files]
8
9
  extensions = [".sql", ".pgsql", ".psql"]
9
10
 
10
11
  [[tool]]
11
- name = "sqlfluff"
12
+ brew = "sqlfluff"
12
13
  crash_pattern = '^Traceback \(most recent call last\):$'
13
- suppression = { marker = "\\bnoqa\\b", reason = "--[^\\n]*?-- ?(?<reason>\\S.*)" }
14
- version = "4.3.0"
15
14
  min_version = "3.0.0"
16
- pypi = "sqlfluff"
17
15
  mise = "pipx:sqlfluff"
18
- brew = "sqlfluff"
19
- [[tool.replace]]
20
- file = ".sqlfluff"
21
- shared = false
22
-
23
- [[tool.replace]]
24
- file = ".sqlfluffignore"
25
- shared = false
16
+ name = "sqlfluff"
17
+ pypi = "sqlfluff"
18
+ replace = [".sqlfluff", ".sqlfluffignore"]
19
+ suppression = { marker = "\\bnoqa\\b", reason = "--[^\\n]*?-- ?(?<reason>\\S.*)" }
20
+ version = "4.3.0"
26
21
 
27
- [[tool.replace]]
22
+ [[tool.replace_key]]
28
23
  file = "pyproject.toml"
29
24
  table = "tool.sqlfluff"
30
- shared = true
31
25
 
32
- [[tool.replace]]
26
+ [[tool.replace_key]]
33
27
  file = "setup.cfg"
34
28
  table = "sqlfluff"
35
- shared = true
36
29
 
37
- [[tool.replace]]
30
+ [[tool.replace_key]]
38
31
  file = "tox.ini"
39
32
  table = "sqlfluff"
40
- shared = true
41
33
 
42
34
  [[tool_file]]
35
+ per_scope = true
43
36
  target = ".gspot/config/sqlfluff.cfg"
44
37
  tool = "sqlfluff"
45
- per_scope = true
46
- rule_keys = ["sqlfluff.exclude_rules", "sqlfluff:rules"]
47
38
 
48
39
  [[check]]
49
- name = "sqlfluff"
50
- example = "The query `select id from user_accounts;` reports CP01 under the configured uppercase keyword policy. Format it as `SELECT id FROM user_accounts;` and rerun using the project dialect."
51
- level = "recommended"
52
- stage = "commit"
53
40
  command = ["sqlfluff", "lint", "--config", "{tool_file:sqlfluff}", "--ignore-local-config", "--disable-progress-bar", "--format", "json", "{files}"]
41
+ example = "The query `select id from user_accounts;` reports CP01 for lowercase keywords. Write it as `SELECT id FROM user_accounts;`."
54
42
  exit_codes = [1]
55
43
  fix = ["sqlfluff", "fix", "--config", "{tool_file:sqlfluff}", "--ignore-local-config", "--disable-progress-bar", "{files}"]
44
+ help = "Run `gspot check --fix`. Turn one rule off with `gspot ignore sql/sqlfluff --rule <code> --reason \"<why>\"`."
45
+ level = "recommended"
46
+ name = "sqlfluff"
56
47
  output = { format = "json", children = "violations", fields = { file = "filepath", line = "start_line_no", column = "start_line_pos", rule = "code", message = "description" } }
57
- summary = "Lints every SQL file for layout, capitalization, aliasing, and ambiguous references, in the dialect the database configuration names."
58
- why = "SQL that reads one way in every file lets a reviewer look at what the statement does."
59
- help = "Run gspot check --fix. Turn one rule off with gspot ignore sql/sqlfluff --rule `<code>` --reason \"`<why>`\"."
60
-
61
- [[check]]
62
- name = "functions"
63
- example = "A SQL function that only forwards one expression reports a trivial-function finding. Inline unnecessary calls and delete the function. If an external API requires the signature, record that with `gspot ignore` and a reason."
64
- level = "all"
65
48
  stage = "commit"
66
- summary = "For the postgres dialect, reports trivial SQL and PL/pgSQL functions and excessive declared input parameters."
67
- why = "Unnecessary functions add indirection; excessive parameters obscure responsibilities."
68
- help = "Inline unnecessary functions. If a required API needs one, run `gspot ignore sql/functions --paths <file> --reason <why>`. Reduce parameters or configure limits.sql.function_parameters."
49
+ summary = "Lints every SQL file for layout, capitalization, aliasing, and ambiguous references, in the dialect the database configuration names."
50
+ why = "When every SQL file follows one style, a reviewer can focus on what each statement does."