@gspothq/cli 0.0.0-stage → 0.1.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 (232) hide show
  1. package/README.md +18 -2
  2. package/configurations/database/postgres/manifest.toml +114 -0
  3. package/configurations/database/postgres/rules/POSTGRES.md +78 -0
  4. package/configurations/framework/astro/eslint-presets.json +138 -0
  5. package/configurations/framework/astro/manifest.toml +95 -0
  6. package/configurations/framework/astro/rules/ASTRO.md +39 -0
  7. package/configurations/framework/expo/manifest.toml +43 -0
  8. package/configurations/framework/express/manifest.toml +17 -0
  9. package/configurations/framework/express/rules/EXPRESS.md +73 -0
  10. package/configurations/framework/express/semgrep/express.yml +71 -0
  11. package/configurations/framework/fastapi/manifest.toml +11 -0
  12. package/configurations/framework/fastapi/rules/FASTAPI.md +100 -0
  13. package/configurations/framework/fastapi/semgrep/fastapi.yml +44 -0
  14. package/configurations/framework/nestjs/eslint-presets.json +62 -0
  15. package/configurations/framework/nestjs/manifest.toml +52 -0
  16. package/configurations/framework/nestjs/rules/NESTJS.md +44 -0
  17. package/configurations/framework/nextjs/eslint-presets.json +73 -0
  18. package/configurations/framework/nextjs/manifest.toml +119 -0
  19. package/configurations/framework/nextjs/rules/NEXTJS.md +43 -0
  20. package/configurations/framework/react/eslint-presets.json +77 -0
  21. package/configurations/framework/react/manifest.toml +93 -0
  22. package/configurations/framework/react/rules/REACT.md +108 -0
  23. package/configurations/framework/react-dom/eslint-presets.json +110 -0
  24. package/configurations/framework/react-dom/manifest.toml +24 -0
  25. package/configurations/framework/react-native/manifest.toml +46 -0
  26. package/configurations/framework/react-native/rules/REACT-NATIVE.md +51 -0
  27. package/configurations/framework/site/manifest.toml +170 -0
  28. package/configurations/framework/site/rules/SITE.md +20 -0
  29. package/configurations/framework/svelte/eslint-presets.json +67 -0
  30. package/configurations/framework/svelte/manifest.toml +83 -0
  31. package/configurations/framework/svelte/rules/SVELTE.md +48 -0
  32. package/configurations/framework/vue/eslint-presets.json +180 -0
  33. package/configurations/framework/vue/manifest.toml +83 -0
  34. package/configurations/framework/vue/rules/VUE.md +41 -0
  35. package/configurations/general/commits/manifest.toml +111 -0
  36. package/configurations/general/commits/rules/GIT.md +31 -0
  37. package/configurations/general/coverage/manifest.toml +3 -0
  38. package/configurations/general/dependencies/manifest.toml +159 -0
  39. package/configurations/general/dependencies/rules/DEPENDENCIES.md +30 -0
  40. package/configurations/general/dependencies/syncpack.json +15 -0
  41. package/configurations/general/docs/manifest.toml +79 -0
  42. package/configurations/general/docs/rules/DOCS.md +86 -0
  43. package/configurations/general/duplication/manifest.toml +32 -0
  44. package/configurations/general/engineering/manifest.toml +8 -0
  45. package/configurations/general/engineering/rules/agent/PLANNING.md +42 -0
  46. package/configurations/general/engineering/rules/agent/SUPPRESSIONS.md +16 -0
  47. package/configurations/general/engineering/rules/agent/TALKING.md +3 -0
  48. package/configurations/general/engineering/rules/agent/TASKS.md +19 -0
  49. package/configurations/general/engineering/rules/agent/WORKING.md +83 -0
  50. package/configurations/general/engineering/rules/code/ACCESSIBILITY.md +32 -0
  51. package/configurations/general/engineering/rules/code/CLI.md +37 -0
  52. package/configurations/general/engineering/rules/code/COMMENTS.md +38 -0
  53. package/configurations/general/engineering/rules/code/CONFIGURATION.md +25 -0
  54. package/configurations/general/engineering/rules/code/ERRORS.md +49 -0
  55. package/configurations/general/engineering/rules/code/HTTP.md +46 -0
  56. package/configurations/general/engineering/rules/code/LOGGING.md +55 -0
  57. package/configurations/general/engineering/rules/code/TESTING.md +58 -0
  58. package/configurations/general/files/manifest.toml +141 -0
  59. package/configurations/general/files/rules/YAML.md +19 -0
  60. package/configurations/general/format/manifest.toml +152 -0
  61. package/configurations/general/gspot/manifest.toml +65 -0
  62. package/configurations/general/gspot/rules/GENERATED.md +10 -0
  63. package/configurations/general/licenses/manifest.toml +33 -0
  64. package/configurations/general/naming/defaults.json +423 -0
  65. package/configurations/general/naming/manifest.toml +35 -0
  66. package/configurations/general/naming/rules/NAMING.md +179 -0
  67. package/configurations/general/prose/manifest.toml +86 -0
  68. package/configurations/general/prose/rules/WRITING.md +31 -0
  69. package/configurations/general/prose/vale/styles/LICENSES.md +55 -0
  70. package/configurations/general/prose/vale/styles/gspot/ampm.yml +8 -0
  71. package/configurations/general/prose/vale/styles/gspot/conscious-language.yml +16 -0
  72. package/configurations/general/prose/vale/styles/gspot/contractions.yml +43 -0
  73. package/configurations/general/prose/vale/styles/gspot/currency.yml +8 -0
  74. package/configurations/general/prose/vale/styles/gspot/dashes.yml +8 -0
  75. package/configurations/general/prose/vale/styles/gspot/date-ordinals.yml +41 -0
  76. package/configurations/general/prose/vale/styles/gspot/dates.yml +7 -0
  77. package/configurations/general/prose/vale/styles/gspot/file-paths.yml +7 -0
  78. package/configurations/general/prose/vale/styles/gspot/future.yml +17 -0
  79. package/configurations/general/prose/vale/styles/gspot/hedging.yml +22 -0
  80. package/configurations/general/prose/vale/styles/gspot/history.yml +16 -0
  81. package/configurations/general/prose/vale/styles/gspot/idioms.yml +21 -0
  82. package/configurations/general/prose/vale/styles/gspot/interface-verbs.yml +11 -0
  83. package/configurations/general/prose/vale/styles/gspot/jargon.yml +11 -0
  84. package/configurations/general/prose/vale/styles/gspot/leading-conjunctions.yml +12 -0
  85. package/configurations/general/prose/vale/styles/gspot/link-text.yml +8 -0
  86. package/configurations/general/prose/vale/styles/gspot/locations.yml +14 -0
  87. package/configurations/general/prose/vale/styles/gspot/marketing.yml +26 -0
  88. package/configurations/general/prose/vale/styles/gspot/merge-conflict-markers.yml +13 -0
  89. package/configurations/general/prose/vale/styles/gspot/militaristic-language.yml +22 -0
  90. package/configurations/general/prose/vale/styles/gspot/modals.yml +9 -0
  91. package/configurations/general/prose/vale/styles/gspot/numbered-units.yml +18 -0
  92. package/configurations/general/prose/vale/styles/gspot/obvious-fields.yml +14 -0
  93. package/configurations/general/prose/vale/styles/gspot/ordinal-adverbs.yml +15 -0
  94. package/configurations/general/prose/vale/styles/gspot/percentage-numbers.yml +8 -0
  95. package/configurations/general/prose/vale/styles/gspot/placeholders.yml +7 -0
  96. package/configurations/general/prose/vale/styles/gspot/product-centric-writing.yml +8 -0
  97. package/configurations/general/prose/vale/styles/gspot/punctuated-headings.yml +11 -0
  98. package/configurations/general/prose/vale/styles/gspot/self-reference.yml +15 -0
  99. package/configurations/general/prose/vale/styles/gspot/session-identifiers.yml +11 -0
  100. package/configurations/general/prose/vale/styles/gspot/since.yml +6 -0
  101. package/configurations/general/prose/vale/styles/gspot/straight-quotes.yml +11 -0
  102. package/configurations/general/prose/vale/styles/gspot/suspended-hyphens.yml +8 -0
  103. package/configurations/general/prose/vale/styles/gspot/symbols.yml +8 -0
  104. package/configurations/general/prose/vale/styles/gspot/time-ranges.yml +14 -0
  105. package/configurations/general/prose/vale/styles/gspot/url-prepositions.yml +10 -0
  106. package/configurations/general/prose/vale/styles/gspot/version-range.yml +8 -0
  107. package/configurations/general/repository/manifest.toml +24 -0
  108. package/configurations/general/secrets/gitleaks.toml +2 -0
  109. package/configurations/general/secrets/manifest.toml +125 -0
  110. package/configurations/general/secrets/rules/SECRETS.md +13 -0
  111. package/configurations/general/security/manifest.toml +77 -0
  112. package/configurations/general/security/rules/SECURITY.md +55 -0
  113. package/configurations/general/spelling/manifest.toml +46 -0
  114. package/configurations/general/structure/manifest.toml +70 -0
  115. package/configurations/infrastructure/ansible/manifest.toml +33 -0
  116. package/configurations/infrastructure/docker/manifest.toml +127 -0
  117. package/configurations/infrastructure/docker/rules/DOCKER.md +135 -0
  118. package/configurations/infrastructure/docker/trivyignore +2 -0
  119. package/configurations/infrastructure/github-actions/manifest.toml +76 -0
  120. package/configurations/infrastructure/github-actions/rules/GITHUB-ACTIONS.md +43 -0
  121. package/configurations/infrastructure/github-actions/zizmor.yml +3 -0
  122. package/configurations/infrastructure/nginx/manifest.toml +51 -0
  123. package/configurations/infrastructure/nginx/rules/NGINX.md +38 -0
  124. package/configurations/infrastructure/openapi/manifest.toml +50 -0
  125. package/configurations/infrastructure/openapi/rules/OPENAPI.md +39 -0
  126. package/configurations/infrastructure/xcode/manifest.toml +131 -0
  127. package/configurations/infrastructure/xcode/rules/XCODE.md +34 -0
  128. package/configurations/language/bash/manifest.toml +261 -0
  129. package/configurations/language/bash/rules/BASH.md +106 -0
  130. package/configurations/language/bash/rules/LANGUAGE.md +213 -0
  131. package/configurations/language/bash/rules/NAMING.md +62 -0
  132. package/configurations/language/bash/rules/OPERATIONS.md +77 -0
  133. package/configurations/language/bash/rules/SAFETY.md +222 -0
  134. package/configurations/language/bash/semgrep/bash.yml +30 -0
  135. package/configurations/language/css/manifest.toml +98 -0
  136. package/configurations/language/css/rules/CSS.md +28 -0
  137. package/configurations/language/css/rules/NAMING.md +17 -0
  138. package/configurations/language/css/rules/TAILWIND.md +26 -0
  139. package/configurations/language/css/stylelint-rule-names.json +89 -0
  140. package/configurations/language/html/manifest.toml +59 -0
  141. package/configurations/language/html/rules/HTML.md +27 -0
  142. package/configurations/language/html/rules/NAMING.md +16 -0
  143. package/configurations/language/javascript/eslint-presets.json +997 -0
  144. package/configurations/language/javascript/eslint-rule-names.json +299 -0
  145. package/configurations/language/javascript/eslint-runtime-names.json +61 -0
  146. package/configurations/language/javascript/manifest.toml +393 -0
  147. package/configurations/language/javascript/rules/BUN.md +13 -0
  148. package/configurations/language/javascript/rules/COMPONENTS.md +18 -0
  149. package/configurations/language/javascript/rules/DENO.md +15 -0
  150. package/configurations/language/javascript/rules/JAVASCRIPT.md +60 -0
  151. package/configurations/language/javascript/rules/NAMING.md +31 -0
  152. package/configurations/language/javascript/rules/NODE.md +21 -0
  153. package/configurations/language/javascript/rules/PLAYWRIGHT.md +34 -0
  154. package/configurations/language/javascript/semgrep/javascript.yml +84 -0
  155. package/configurations/language/markdown/manifest.toml +85 -0
  156. package/configurations/language/markdown/markdownlint-cli2.mjs +19 -0
  157. package/configurations/language/python/manifest.toml +621 -0
  158. package/configurations/language/python/rules/DESIGN.md +136 -0
  159. package/configurations/language/python/rules/FLOW.md +124 -0
  160. package/configurations/language/python/rules/NAMING.md +53 -0
  161. package/configurations/language/python/rules/PACKAGING.md +49 -0
  162. package/configurations/language/python/rules/PYTHON.md +136 -0
  163. package/configurations/language/python/rules/TYPING.md +132 -0
  164. package/configurations/language/sql/manifest.toml +68 -0
  165. package/configurations/language/sql/rules/NAMING.md +90 -0
  166. package/configurations/language/sql/rules/SQL.md +9 -0
  167. package/configurations/language/swift/manifest.toml +164 -0
  168. package/configurations/language/swift/rules/NAMING.md +88 -0
  169. package/configurations/language/swift/rules/SWIFT.md +134 -0
  170. package/configurations/language/swift/rules/SWIFTUI.md +61 -0
  171. package/configurations/language/swift/rules/UIKIT.md +99 -0
  172. package/configurations/language/swift/semgrep/swift.yml +126 -0
  173. package/configurations/language/typescript/eslint-presets.json +215 -0
  174. package/configurations/language/typescript/manifest.toml +72 -0
  175. package/configurations/language/typescript/rules/NAMING.md +73 -0
  176. package/configurations/language/typescript/rules/TYPESCRIPT.md +79 -0
  177. package/configurations/library/drizzle/manifest.toml +68 -0
  178. package/configurations/library/drizzle/rules/DRIZZLE.md +44 -0
  179. package/configurations/library/react-hook-form/manifest.toml +15 -0
  180. package/configurations/library/react-hook-form/rules/REACT-HOOK-FORM.md +48 -0
  181. package/configurations/library/swift-snapshot-testing/manifest.toml +32 -0
  182. package/configurations/library/tanstack-query/eslint-presets.json +21 -0
  183. package/configurations/library/tanstack-query/manifest.toml +26 -0
  184. package/configurations/library/tanstack-query/rules/TANSTACK-QUERY.md +41 -0
  185. package/configurations/library/testing-library/eslint-presets.json +191 -0
  186. package/configurations/library/testing-library/manifest.toml +47 -0
  187. package/configurations/library/translations/manifest.toml +40 -0
  188. package/configurations/library/translations/rules/NEXT-INTL.md +33 -0
  189. package/configurations/library/translations/rules/TRANSLATIONS.md +102 -0
  190. package/configurations/library/trpc/manifest.toml +14 -0
  191. package/configurations/library/trpc/rules/TRPC.md +43 -0
  192. package/configurations/library/zod/manifest.toml +25 -0
  193. package/configurations/library/zod/rules/ZOD.md +44 -0
  194. package/configurations/library/zustand/manifest.toml +18 -0
  195. package/configurations/library/zustand/rules/ZUSTAND.md +106 -0
  196. package/configurations/platform/cloudflare/manifest.toml +94 -0
  197. package/configurations/platform/cloudflare/rules/CLOUDFLARE.md +23 -0
  198. package/configurations/platform/cloudflare/semgrep/cloudflare.yml +12 -0
  199. package/configurations/platform/supabase/manifest.toml +112 -0
  200. package/configurations/platform/supabase/rules/SUPABASE.md +129 -0
  201. package/configurations/platform/supabase/semgrep/supabase.yml +60 -0
  202. package/configurations/testing/jest/manifest.toml +54 -0
  203. package/configurations/testing/pytest/manifest.toml +53 -0
  204. package/configurations/testing/swift-tests/manifest.toml +52 -0
  205. package/configurations/testing/swift-tests/rules/SWIFT-TESTS.md +79 -0
  206. package/configurations/testing/vitest/eslint-presets.json +22 -0
  207. package/configurations/testing/vitest/manifest.toml +69 -0
  208. package/configurations/testing/vitest/rules/VITEST.md +18 -0
  209. package/dist/LICENSE.md +201 -0
  210. package/dist/THIRD-PARTY-NOTICES.md +25 -0
  211. package/dist/eslint-rule-levels.json +10 -0
  212. package/dist/gspot.js +30740 -0
  213. package/dist/linkinator-program.js +65 -0
  214. package/dist/purgecss-program.js +51 -0
  215. package/dist/tool-versions.json +3 -0
  216. package/grammars/bash.wasm +0 -0
  217. package/grammars/css.wasm +0 -0
  218. package/grammars/html.wasm +0 -0
  219. package/grammars/javascript.wasm +0 -0
  220. package/grammars/licenses/tree-sitter-bash.txt +21 -0
  221. package/grammars/licenses/tree-sitter-css.txt +21 -0
  222. package/grammars/licenses/tree-sitter-html.txt +21 -0
  223. package/grammars/licenses/tree-sitter-javascript.txt +21 -0
  224. package/grammars/licenses/tree-sitter-python.txt +21 -0
  225. package/grammars/licenses/tree-sitter-swift.txt +21 -0
  226. package/grammars/licenses/tree-sitter-typescript.txt +21 -0
  227. package/grammars/python.wasm +0 -0
  228. package/grammars/swift.wasm +0 -0
  229. package/grammars/tsx.wasm +0 -0
  230. package/grammars/typescript.wasm +0 -0
  231. package/gspot.schema.json +45991 -0
  232. package/package.json +93 -5
@@ -0,0 +1,136 @@
1
+ # Python Design
2
+
3
+ ## Functions
4
+
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
+ explicitly and documents its yielded values and cleanup contract. A partially consumed
8
+ 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
10
+ logic, several conditions, mutation, or a transformation that needs explanation gets an ordinary loop so
11
+ its ordering stays visible.
12
+
13
+ ### Function conventions
14
+
15
+ <!-- level: all -->
16
+
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:
20
+
21
+ ```python
22
+ """Select recent message text under a validated nonnegative limit."""
23
+
24
+ from __future__ import annotations
25
+
26
+ from typing import TYPE_CHECKING
27
+
28
+ if TYPE_CHECKING:
29
+ from collections.abc import Sequence
30
+
31
+
32
+ def trim_history_messages(messages: Sequence[str], max_messages: int) -> list[str]:
33
+ """Return at most the requested number of recent messages."""
34
+ if max_messages == 0:
35
+ return []
36
+ if len(messages) <= max_messages:
37
+ return list(messages)
38
+ return list(messages[-max_messages:])
39
+
40
+
41
+ __all__ = ["trim_history_messages"]
42
+ ```
43
+
44
+ A nested function closes over a local value to make the outer function clearer, keeping its
45
+ captured state and lifetime explicit. It never exists to
46
+ 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.
49
+
50
+ Write a lambda as a one-line expression at its consumer, such as
51
+ a sorting key `lambda item: item.name`. Prefer a generator expression or `operator` function over
52
+ `map()` or `filter()` with a lambda. Use a `def` for anything longer. A conditional expression
53
+ holds one binary choice, `"stream" if is_streaming else "batch"`, and a path with several
54
+ operations gets `if` branches.
55
+
56
+ ## Classes
57
+
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.
64
+
65
+ `__init__` stays small, takes the values the class needs rather than the external objects
66
+ that carry them, and validates invariants. Keep it separate from database rows, object-relational mapping (ORM) objects,
67
+ API payloads, CLI namespaces, or provider responses. An external representation enters
68
+ through a classmethod named constructor such as `from_row` or `from_payload` when the class
69
+ owns that conversion. Neither `ClassName(**external_attributes)` nor a
70
+ constructor that only renames the initializer handles that conversion.
71
+
72
+ Loading, serialization, and validation systems stay outside the
73
+ business object. Keep a derived attribute cheap and deterministic. Build it from initialized
74
+ fields; a named constructor handles derivation through I/O or complex parsing.
75
+
76
+ Use a dataclass as a typed plain data record with methods only from the data contract. Mutable
77
+ 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
80
+ 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.
83
+
84
+ ```python
85
+ """Represent an independently owned batch of prompt text."""
86
+
87
+ from dataclasses import field, dataclass
88
+
89
+ @dataclass
90
+ class Batch:
91
+ """Prompt text owned by one processing batch.
92
+
93
+ Attributes:
94
+ prompts: The text items assigned to this batch.
95
+
96
+ """
97
+
98
+ prompts: list[str] = field(default_factory=list)
99
+
100
+
101
+ __all__ = ["Batch"]
102
+ ```
103
+
104
+ ### Dataclass conventions
105
+
106
+ <!-- level: all -->
107
+
108
+ Document public dataclass fields in the configured docstring convention: Google style
109
+ uses `Attributes:`, NumPy style an underlined `Attributes` heading.
110
+
111
+ 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.
115
+
116
+ ## Inheritance and decorators
117
+
118
+ Design a class for inheritance or forbid inheritance. Composition shares code, and
119
+ 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
121
+ 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
123
+ that defines control flow and calls subclass hooks takes more work to read than a function with
124
+ explicit delegation.
125
+
126
+ Do not put variants in one class with a type field and optional fields that apply to some values.
127
+ Make invalid states unrepresentable. Behavior varying on more than one axis uses composition.
128
+ 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.
132
+
133
+ A decorator removes real repetition or expresses a framework contract. It behaves
134
+ unsurprisingly and preserves function metadata. Because it runs at import time, it never
135
+ depends on files, sockets, databases, or network calls. `classmethod` marks named constructors and
136
+ class-specific routines.
@@ -0,0 +1,124 @@
1
+ # Python Flow
2
+
3
+ ## Exceptions and error handling
4
+
5
+ Use explicit validation for external inputs and required preconditions. Avoid exceptions for
6
+ ordinary branching when code can check the value directly.
7
+
8
+ The following module accepts only bounded positive decimal integers and preserves the original
9
+ conversion error.
10
+
11
+ Good:
12
+
13
+ ```python
14
+ """Validate limits supplied by a command or environment variable."""
15
+
16
+ MAX_BATCH_SIZE = 1000
17
+
18
+
19
+ def parse_batch_limit(raw: str) -> int:
20
+ """Return a positive batch limit within the supported range."""
21
+ try:
22
+ value = int(raw)
23
+ except ValueError as error:
24
+ message = "Batch limit must be a decimal integer."
25
+ raise ValueError(message) from error
26
+ if not 1 <= value <= MAX_BATCH_SIZE:
27
+ message = f"Batch limit must be between 1 and {MAX_BATCH_SIZE}."
28
+ raise ValueError(message)
29
+ return value
30
+ ```
31
+
32
+ ### Exception message conventions
33
+
34
+ <!-- level: all -->
35
+
36
+ Keep the exception's public contract stable when changing its message.
37
+
38
+ ## Exception contracts
39
+
40
+ A custom exception inherits from `Exception`, describes its condition in the docstring, and
41
+ lives beside the behavior that raises it rather than in a one-declaration file.
42
+
43
+ Do not substitute a built-in exception for an external API's declared error type. Follow a
44
+ library's public error contract when callers depend on it.
45
+
46
+ ## Assertions
47
+
48
+ Keep assertions in tests. Use explicit validation for required application checks.
49
+
50
+ ## Boolean logic and comparisons
51
+
52
+ Compare singleton values with `is None`, `is not None`, `is True`, or `is False` when the check concerns
53
+ identity. Use truth-value testing only when all falsy values mean the same thing.
54
+ Do not treat an empty collection, zero, and a missing value as interchangeable by default.
55
+
56
+ Use `==` for value equality. Use membership tests for supported containers, and avoid chained
57
+ comparisons whose repeated values have side effects. Keep evaluation order and short-circuit
58
+ behavior intact when simplifying a condition.
59
+
60
+ ### Control-flow conventions
61
+
62
+ <!-- level: all -->
63
+
64
+ Prefer direct predicates and early exits when they reduce nesting. Keep multi-step branches
65
+ as statements.
66
+
67
+ ## Iteration and collections
68
+
69
+ Iterate containers through their supported interfaces. Do not mutate a collection in a way
70
+ that invalidates the current iteration.
71
+
72
+ Read file objects as iterators instead of loading all lines when the operation can stream them.
73
+ Use `yield from` to delegate an iterator, and `any()` or `all()` for short-circuit predicate checks.
74
+ Keep comprehensions limited to transformations whose order and side effects remain clear.
75
+
76
+ Choose concrete return values or lazy iteration according to the caller's resource and reuse
77
+ requirements, not solely to shorten the implementation.
78
+
79
+ ## Strings and output
80
+
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.
84
+
85
+ Formatting neither validates nor escapes values. Use parameterized database queries, argument arrays
86
+ for subprocesses, and the appropriate encoder for HTML, JSON, or another output format.
87
+
88
+ ## Files and stateful resources
89
+
90
+ 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
92
+ for bytes instead of relying on an implicit conversion.
93
+
94
+ A generator can keep a resource open while suspended. Make that lifetime clear to callers,
95
+ especially when they can stop consuming it early. This command reads the path supplied as its
96
+ first argument and closes the input before printing the collected records.
97
+
98
+ Good:
99
+
100
+ ```python
101
+ """Read normalized, nonempty records from a text file."""
102
+
103
+ import sys
104
+ from pathlib import Path
105
+
106
+
107
+ def main() -> int:
108
+ """Read the first argument as a path and print its normalized records."""
109
+ path = Path(sys.argv[1])
110
+ with path.open(encoding="utf-8") as source:
111
+ records = [record for line in source if (record := line.strip())]
112
+ _ = sys.stdout.write("\n".join(records))
113
+ return 0
114
+
115
+
116
+ if __name__ == "__main__":
117
+ raise SystemExit(main())
118
+ ```
119
+
120
+ 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
122
+ lifetime.
123
+
124
+ 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.
@@ -0,0 +1,53 @@
1
+ # Python Naming
2
+
3
+ Python naming follows Python Enhancement Proposal (PEP) 8, with the rules below.
4
+
5
+ ## Command inputs and external names
6
+
7
+ <!-- level: all -->
8
+
9
+ - CLI flags use lowercase kebab case, such as `--account-name` and
10
+ `--output-dir`.
11
+ - Avoid single-letter names except loop counters and math notation.
12
+ - Preserve provider capitalization in external names such as `AWS_REGION` and
13
+ provider resource IDs.
14
+
15
+ ## Python modules and imports
16
+
17
+ <!-- level: all -->
18
+
19
+ - Do not create package-level re-export layers only to preserve old names.
20
+ - Keep `__all__` names accurate and ordered according to local lint rules.
21
+
22
+ Import `AccountSettings` under its declared name. An alias such as `Thing`
23
+ hides the contract and provides no collision information.
24
+
25
+ ## Python types and dataclasses
26
+
27
+ <!-- level: all -->
28
+
29
+ - Dataclass names describe the domain value they represent.
30
+ - Field names describe the value inside the owning type without repeating the
31
+ type name.
32
+ - Use `Path` variables with names that reveal whether they point to a directory,
33
+ file, report, output, or repository root.
34
+ - Use `*_path` for filesystem paths and `*_dir` only for directories.
35
+ - Use `*_id` only for real identifiers, not arbitrary names, or labels.
36
+ - Use `*_name` for display or provider names.
37
+ - Use `*_key` for dictionary keys and supported variant keys.
38
+
39
+ | Avoid | Prefer | Meaning |
40
+ | ------------------------ | ---------------- | ------------------- |
41
+ | `Invoice.invoice_number` | `Invoice.number` | The invoice number. |
42
+ | `Shipment.shipment_date` | `Shipment.date` | The shipment date. |
43
+
44
+ ## Python boundary names
45
+
46
+ <!-- level: all -->
47
+
48
+ - Keep raw provider or CLI names at the boundary.
49
+ - Translate external names into domain names before passing values inward when
50
+ the external name differs from the domain concept.
51
+ - Keep path construction with the code that owns the resource instead of repeating it in
52
+ unrelated operations.
53
+ - Name functions that cross boundaries for the operation they perform.
@@ -0,0 +1,49 @@
1
+ # Python Packaging
2
+
3
+ Deployment installs and lockfiles.
4
+
5
+ ## Package installation security
6
+
7
+ Install from the uv lockfile:
8
+
9
+ ```bash
10
+ uv sync --frozen
11
+ ```
12
+
13
+ At level `all`, justify pip use with `gspot ignore python/pip-installs`. Update lockfiles only
14
+ when the task includes dependency maintenance.
15
+
16
+ ## Import correctness
17
+
18
+ - 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.
22
+
23
+ ## Packages and architecture
24
+
25
+ <!-- level: all -->
26
+
27
+ - Keep packages shallow and purposeful.
28
+ - Flatten packages that contain only `__init__.py` and one other module.
29
+ - Do not use package `__init__.py` files to hide expensive imports.
30
+ - Keep `__init__.py` files small and import-stable.
31
+ - Barrel `__init__.py` files may contain imports and `__all__`.
32
+ - Keep lower-level packages independent of higher-level workflow packages.
33
+
34
+ ## Source layout
35
+
36
+ <!-- level: all -->
37
+
38
+ Keep the layout the project has. A new package puts its code under `src/`:
39
+
40
+ ```text
41
+ pyproject.toml
42
+ src/
43
+ order_service/
44
+ __init__.py
45
+ orders.py
46
+ accounts.py
47
+ ```
48
+
49
+ Keep helper scripts that nobody imports outside the package import path.
@@ -0,0 +1,136 @@
1
+ # Python
2
+
3
+ ## Python scope
4
+
5
+ Write readable Python before clever Python: explicit data flow, clear names, small functions,
6
+ 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
+
10
+ ## Runtime and files
11
+
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
+ `.pyi` for stubs.
15
+
16
+ ### Comment language
17
+
18
+ <!-- level: all -->
19
+
20
+ Comments and docstrings use the project's declared language, and valid user content
21
+ never restricts names to ASCII.
22
+
23
+ ## Environment and configuration
24
+
25
+ An importable module never depends on an active shell, a virtual environment, the working directory, or a
26
+ globally installed package. Imports resolve through project configuration, editable
27
+ installs, `python -m`, or the configured environment. Virtual environments and package caches
28
+ stay out of commits.
29
+
30
+ The configuration owner takes the environment as a mapping, so a test passes its own:
31
+
32
+ ```python
33
+
34
+ def get_config(environ: Mapping[str, str]) -> AppConfig:
35
+ """Parse an item limit, using 100 when the setting is absent.
36
+
37
+ Args:
38
+ environ: Environment values supplied by the application boundary.
39
+
40
+ Returns:
41
+ Settings containing a positive item limit.
42
+
43
+ Raises:
44
+ ValueError: The supplied limit is not an integer greater than zero.
45
+
46
+ """
47
+ max_items = int(environ.get("MAX_ITEMS", "100"))
48
+ if max_items < 1:
49
+ message = "MAX_ITEMS must be greater than zero"
50
+ raise ValueError(message)
51
+ return AppConfig(max_items=max_items)
52
+ ```
53
+
54
+ ## Module structure
55
+
56
+ <!-- level: all -->
57
+
58
+ Every runtime module has a docstring describing its present purpose.
59
+
60
+ ## Imports
61
+
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.
64
+
65
+ ### Import conventions
66
+
67
+ <!-- level: all -->
68
+
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__`.
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.
80
+
81
+ ## Docstrings
82
+
83
+ Docstrings match their signatures and behavior at both levels, in the project's declared
84
+ convention. Use the explicit pydoclint style when the settings define one. Otherwise, follow the Google
85
+ or NumPy convention that Ruff carries, and failing that the pydoclint default. The examples
86
+ here follow Google style.
87
+
88
+ ### Docstring conventions
89
+
90
+ <!-- level: all -->
91
+
92
+ - A class docstring says what an instance represents, never `Class that...`.
93
+ - A function that mutates an argument says so.
94
+
95
+ ## Constants, globals, and mutable state
96
+
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
100
+ passes into the client or operation that consumes it, without a renaming wrapper.
101
+
102
+ ### Constant naming
103
+
104
+ <!-- level: all -->
105
+
106
+ Name constants `UPPER_SNAKE`, private ones with one leading underscore, and retained module
107
+ state takes a name for its concept.
108
+
109
+ ## Main programs
110
+
111
+ An executable module puts its behavior in `main()`, runs it under the `__main__` guard, and
112
+ prefers `raise SystemExit(main())` when `main()` returns an exit code.
113
+ Repository entrypoints run as `python -m package.module`, and shell scripts call Python
114
+ modules rather than inline snippets. A shebang appears only on a file meant to execute
115
+ directly.
116
+
117
+ Never execute untrusted input as Python code, and dynamic execution never replaces input
118
+ validation.
119
+
120
+ ## Power features
121
+
122
+ <!-- level: all -->
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
126
+ reparenting, import hooks, runtime monkeypatching, and reflection-heavy designs. It also
127
+ 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.
130
+
131
+ ## Threading
132
+
133
+ Built-in types and variable assignment do not synchronize threads: use `queue.Queue` for thread
134
+ communication and locks or conditions for shared state, which stays small and explicit.
135
+
136
+ Name Python test files `test_<behavior>.py`, grouped by behavior.
@@ -0,0 +1,132 @@
1
+ # Python Typing
2
+
3
+ ## Annotations
4
+
5
+ <!-- level: all -->
6
+
7
+ ```python
8
+ """Group labeled records while preserving their key types."""
9
+
10
+ from __future__ import annotations
11
+
12
+ from typing import TYPE_CHECKING
13
+
14
+ if TYPE_CHECKING:
15
+ from collections.abc import Hashable, Iterable
16
+
17
+
18
+ def group_records[Key: Hashable](records: Iterable[tuple[Key, str]]) -> dict[Key, list[str]]:
19
+ """Group record values under their original keys."""
20
+ grouped: dict[Key, list[str]] = {}
21
+ for key, value in records:
22
+ grouped.setdefault(key, []).append(value)
23
+ return grouped
24
+ ```
25
+
26
+ The type parameter syntax needs Python 3.12; `Key` connects input keys to output keys, and
27
+ `Hashable` states the dictionary-key requirement. A variable annotation appears where the
28
+ compiler cannot infer a clear type, such as an empty collection, not on every
29
+ intermediate value.
30
+
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
+ metadata object. Arbitrary string metadata never replaces a domain type or validator. A
34
+ FastAPI query field reads `Annotated[str | None, Query(max_length=50)] = None`.
35
+
36
+ ## Any, object, and shapes
37
+
38
+ Use `object` for a value of any type when the function only uses operations
39
+ 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
+ protocol, type variable, overload, or small value object models the contract first.
43
+
44
+ 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
46
+ concrete types such as `list`, `dict`, or a dataclass. Protocols and abstract base classes choose
47
+ return types by the promised interface. A union return that forces callers into an immediate
48
+ `isinstance()` branch becomes separate functions, a tagged dataclass, a protocol, or a small
49
+ hierarchy with a common contract.
50
+
51
+ Use `float` for numeric APIs that also accept integers. Use `str` for text and `bytes` for binary data.
52
+ `AnyStr` ties several annotations to one text or binary type.
53
+
54
+ A `Path | None = None` input handles its `None` branch
55
+ before calling path methods, and the default path belongs to the configuration owner. Generic
56
+ 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
58
+ input and output types when that relationship matters, as `Key` does above.
59
+
60
+ ## Aliases, variables, and forward references
61
+
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.
67
+
68
+ ### Type variable naming
69
+
70
+ <!-- level: all -->
71
+
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.
76
+
77
+ ### Forward references
78
+
79
+ `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
83
+ symbol. Validators and frameworks that evaluate annotations need the real import. A type-only circular
84
+ import signals a need to move the shared contract.
85
+
86
+ ```python
87
+ @dataclass(frozen=True)
88
+ class Node:
89
+ """One node whose parent chain is acyclic.
90
+
91
+ Attributes:
92
+ name: Display name of this node.
93
+ parent: Parent node, or None for a root.
94
+
95
+ """
96
+
97
+ name: str
98
+ parent: Node | None = None
99
+
100
+ def list_lineage(self) -> list[str]:
101
+ """Return names from this node toward the root."""
102
+ names: list[str] = []
103
+ current: Node | None = self
104
+ while current is not None:
105
+ names.append(current.name)
106
+ current = current.parent
107
+ return names
108
+ ```
109
+
110
+ ## Protocols
111
+
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
115
+ class serves nominal identity, runtime instantiation checks, or a standard-library contract,
116
+ never shared implementation mixed with interface definition.
117
+
118
+ ```python
119
+ class Reader(Protocol):
120
+ """A source of one text document."""
121
+
122
+ def read(self) -> str:
123
+ """Return the document text."""
124
+ ...
125
+
126
+
127
+ def read_unique_records(reader: Reader) -> list[str]:
128
+ """Read sorted unique records, omitting blank lines."""
129
+ records = {line.strip() for line in reader.read().splitlines()}
130
+ records.discard("")
131
+ return sorted(records)
132
+ ```