okstra 0.50.0 → 0.51.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 (57) hide show
  1. package/README.kr.md +8 -7
  2. package/README.md +8 -7
  3. package/bin/okstra +2 -0
  4. package/docs/kr/architecture.md +15 -16
  5. package/docs/kr/cli.md +5 -5
  6. package/docs/project-structure-overview.md +10 -6
  7. package/package.json +1 -1
  8. package/runtime/BUILD.json +2 -2
  9. package/runtime/agents/SKILL.md +15 -11
  10. package/runtime/agents/workers/claude-worker.md +3 -3
  11. package/runtime/agents/workers/codex-worker.md +2 -2
  12. package/runtime/agents/workers/gemini-worker.md +2 -2
  13. package/runtime/bin/lib/okstra/cli.sh +8 -1
  14. package/runtime/bin/lib/okstra/globals.sh +3 -0
  15. package/runtime/bin/lib/okstra/interactive.sh +14 -12
  16. package/runtime/bin/lib/okstra/usage.sh +6 -0
  17. package/runtime/bin/okstra-team-reconcile.sh +28 -0
  18. package/runtime/bin/okstra.sh +2 -0
  19. package/runtime/prompts/launch.template.md +3 -1
  20. package/runtime/prompts/profiles/_common-contract.md +4 -4
  21. package/runtime/prompts/profiles/_implementation-executor.md +2 -2
  22. package/runtime/prompts/profiles/_implementation-verifier.md +1 -1
  23. package/runtime/prompts/profiles/implementation-planning.md +1 -0
  24. package/runtime/prompts/profiles/implementation.md +1 -1
  25. package/runtime/python/okstra_ctl/analysis_packet.py +259 -0
  26. package/runtime/python/okstra_ctl/context_cost.py +308 -0
  27. package/runtime/python/okstra_ctl/migrate.py +2 -12
  28. package/runtime/python/okstra_ctl/paths.py +22 -0
  29. package/runtime/python/okstra_ctl/render.py +284 -125
  30. package/runtime/python/okstra_ctl/render_final_report.py +31 -0
  31. package/runtime/python/okstra_ctl/run.py +507 -245
  32. package/runtime/python/okstra_ctl/sequence.py +2 -5
  33. package/runtime/python/okstra_ctl/team_reconcile.py +131 -0
  34. package/runtime/python/okstra_ctl/wizard.py +129 -133
  35. package/runtime/python/okstra_ctl/worktree.py +13 -5
  36. package/runtime/schemas/final-report-v1.0.schema.json +4 -0
  37. package/runtime/skills/okstra-coding-preflight/SKILL.md +69 -0
  38. package/runtime/skills/okstra-coding-preflight/architecture/hexagonal.md +116 -0
  39. package/runtime/skills/okstra-coding-preflight/clean-code.md +254 -0
  40. package/runtime/skills/okstra-coding-preflight/languages/java.md +64 -0
  41. package/runtime/skills/okstra-coding-preflight/languages/javascript-typescript.md +87 -0
  42. package/runtime/skills/okstra-coding-preflight/languages/kotlin.md +69 -0
  43. package/runtime/skills/okstra-coding-preflight/languages/nodejs.md +66 -0
  44. package/runtime/skills/okstra-coding-preflight/languages/python.md +179 -0
  45. package/runtime/skills/okstra-coding-preflight/languages/rust.md +105 -0
  46. package/runtime/skills/okstra-coding-preflight/languages/sql.md +68 -0
  47. package/runtime/skills/okstra-context-loader/SKILL.md +12 -6
  48. package/runtime/skills/okstra-inspect/SKILL.md +100 -1
  49. package/runtime/skills/okstra-report-writer/SKILL.md +5 -1
  50. package/runtime/skills/okstra-run/SKILL.md +1 -1
  51. package/runtime/skills/okstra-team-contract/SKILL.md +7 -4
  52. package/runtime/templates/reports/final-report.template.md +1 -0
  53. package/runtime/templates/worker-prompt-preamble.md +3 -3
  54. package/src/_python-helper.mjs +3 -3
  55. package/src/context-cost.mjs +27 -0
  56. package/src/install.mjs +1 -0
  57. package/src/uninstall.mjs +1 -0
@@ -0,0 +1,116 @@
1
+ # Hexagonal Architecture (Ports & Adapters) Overlay
2
+
3
+ Load this **in addition to** the matching language reference when the project follows ports-and-adapters. Detection signals:
4
+
5
+ - A `domain/` (or `domains/`, `core/`) folder holding entities and value objects.
6
+ - A `ports/` folder, files matching `*.port.*`, or `abstract class` / `interface` files declared at a domain boundary.
7
+ - An `adapters/` (or `infrastructure/`) folder holding implementations of those ports.
8
+ - Build config / framework that names the pattern explicitly (e.g., NestJS module split into `domain` / `application` / `infrastructure`).
9
+
10
+ If unsure, ask the user once: *"Does this project use ports-and-adapters? What folder is the domain in?"* — and record the answer in one line so subsequent edits don't re-discover it.
11
+
12
+ These rules are language-agnostic. The examples use TypeScript/Java-ish syntax; translate to Rust traits, Kotlin interfaces, etc., as needed.
13
+
14
+ ---
15
+
16
+ ## Rule H1 — Ports stay thin
17
+
18
+ A port is the interface (or abstract class) at the domain/application boundary. The port declares **shape**, not behavior.
19
+
20
+ A port file's method body — or the interface contract itself — must **not** contain:
21
+
22
+ - `if` statements that branch on domain state.
23
+ - Validation that throws or returns errors based on input shape.
24
+ - Filtering (`.filter(...)`) over domain collections.
25
+ - Business calculations (math on amounts, date arithmetic that expresses a rule, etc.).
26
+
27
+ If the port file's method body does any of the above, the logic belongs in the domain (an invariant on the entity, a domain service) — or in the adapter if it's truly I/O-shaped.
28
+
29
+ Violations:
30
+
31
+ ```ts
32
+ // bad: validation in a port body
33
+ abstract class UserRepository {
34
+ async save(user: User) {
35
+ if (!user.email) throw new Error('email required'); // rule check in port
36
+ return this.persist(user);
37
+ }
38
+ }
39
+
40
+ // bad: filtering in a port
41
+ abstract class OrderRepository {
42
+ async findPending(orders: Order[]) {
43
+ return orders.filter(o => o.status === 'pending'); // domain rule in port
44
+ }
45
+ }
46
+ ```
47
+
48
+ Not a violation:
49
+
50
+ - A pure interface with no method bodies.
51
+ - An abstract method with no implementation.
52
+ - A port file that *imports* domain objects (that's the correct pattern).
53
+
54
+ ---
55
+
56
+ ## Rule H2 — Adapters do I/O, not business logic
57
+
58
+ Same principle as H1, on the adapter side. The adapter's job is to implement **just** the I/O — a SQL query, an HTTP call, a filesystem read. It is **not** the place to filter by domain state, branch on domain values, or compute business answers.
59
+
60
+ Smells in adapter methods:
61
+
62
+ - A `.filter(x => x.status === 'active')` predicate over domain state, *after fetching rows*.
63
+ - An `if` that rejects results based on a domain rule (*"skip if expired"*, *"exclude shared items"*).
64
+ - A method whose name encodes a business rule — `findValid*` / `findActive* `/ `findEligible*`. The adapter should fetch *things*; the caller (or the query itself) should apply the rule.
65
+ - Multiple joins and conditions assembled specifically to satisfy a compound domain rule — the adapter is answering a business question rather than offering a primitive fetch.
66
+
67
+ Judgment: an adapter **can** push filtering into SQL (`WHERE status = 'active'`) — that's still I/O-shaped. The smell is when:
68
+
69
+ - The filtering is *application code after the fetch*, or
70
+ - The method name itself encodes a domain rule that a reader cannot tell from the name alone.
71
+
72
+ A domain-flavored method name (`findValid*` / `findActive*` / `findEligible*`) is not automatically a violation. Accept it when **either** holds:
73
+
74
+ - The query body is still simple — a single `WHERE` clause on an obvious column, no compound predicate.
75
+ - Moving the predicate out would force an N+1 (the caller would have to fetch all rows and round-trip per row to decide which are valid).
76
+
77
+ Reject it when the query is complex enough that a reader can't tell what "valid"/"active"/"eligible" means without reverse-engineering the SQL. At that point the rule really is hiding in infrastructure — move the predicate to the domain and rename the adapter to `findAll` / `findByX`.
78
+
79
+ Not a smell:
80
+
81
+ - Simple `WHERE` clauses for *data-shape* reasons (`WHERE user_id = ?`) — that's scoping the fetch, not a business rule.
82
+ - Pagination, ordering, basic projection.
83
+ - Mapping raw rows to domain objects — that's the adapter's translation job.
84
+
85
+ Why it matters: when adapters hold business rules, the rules become invisible from the domain. A reviewer reading the domain sees a clean method; the rule is hidden two layers down in a repository. Changes to the rule require editing infrastructure code. Tests of the rule are forced through I/O. The whole point of ports-and-adapters collapses.
86
+
87
+ ---
88
+
89
+ ## Rule H3 — Domain objects live in the domain folder
90
+
91
+ Entities, value objects, domain types / enums, and domain errors must be declared under the project's `domain/` folder (or whatever this repo calls it). They must **not** be declared inside a port file or an adapter file.
92
+
93
+ Violations:
94
+
95
+ - `class Order { ... }` declared in `order.port.ts`.
96
+ - `type OrderStatus = 'pending' | 'shipped'` declared inside `order.repository.adapter.ts`.
97
+ - `class OrderNotFoundError extends Error {}` declared in a port file but never defined in `domain/`.
98
+ - An interface representing a domain concept (not a port contract) declared outside `domain/`.
99
+
100
+ Not a violation:
101
+
102
+ - Domain objects *imported* into a port or adapter file — that's the correct pattern.
103
+ - DTOs (data-transfer types used only by an adapter for wire shape) living next to the adapter.
104
+ - Utility types used only by an adapter's implementation.
105
+
106
+ The test: **is this type/class a thing the business talks about, or is it plumbing?** Business things belong in `domain/`. Plumbing can live near the plumbing.
107
+
108
+ ---
109
+
110
+ ## Quick checklist before declaring a port/adapter change complete
111
+
112
+ - [ ] No `if` / `.filter` / validation / business math inside any port body.
113
+ - [ ] Adapter methods are I/O only — any post-fetch JS filtering moved into the query or back to the caller.
114
+ - [ ] Adapter method names are neutral (`findAll`, `findByUserId`) unless the domain-flavored name passes the H2 judgment test.
115
+ - [ ] Every entity, value object, domain enum, and domain error is declared under `domain/`.
116
+ - [ ] Port files only declare shape and `import` domain types — they don't *define* them.
@@ -0,0 +1,254 @@
1
+ # Clean Code Principles (language-agnostic)
2
+
3
+ Apply these on every change. They override personal taste. They do **not** override project-local rules.
4
+
5
+ ## DRY — Don't Repeat Yourself
6
+
7
+ A second copy of the same logic is a signal to extract. Two callers of the same algorithm should call the same function.
8
+
9
+ Bad:
10
+ ```javascript
11
+ let total = 0;
12
+ for (let i = 0; i < prices.length; i++) total += prices[i];
13
+ // later, elsewhere:
14
+ let t = 0;
15
+ for (let i = 0; i < prices.length; i++) t += prices[i];
16
+ ```
17
+
18
+ Good:
19
+ ```javascript
20
+ const sum = (arr) => arr.reduce((acc, x) => acc + x, 0);
21
+ ```
22
+
23
+ Caveat: do not extract until the duplication is real (rule of three). Premature abstraction is also a code smell.
24
+
25
+ ## KISS — Keep It Simple
26
+
27
+ The simplest solution that meets the requirement wins. Cleverness has a maintenance cost.
28
+
29
+ Bad:
30
+ ```javascript
31
+ if (data !== null || data !== undefined || data !== '') { ... }
32
+ ```
33
+
34
+ Good:
35
+ ```javascript
36
+ if (data) { ... }
37
+ ```
38
+
39
+ ## SOLID
40
+
41
+ - **S**ingle Responsibility — one reason to change per class / module.
42
+ - **O**pen/Closed — open for extension, closed for modification. Prefer composition.
43
+ - **L**iskov Substitution — a subtype must honour the parent's contract.
44
+ - **I**nterface Segregation — many small interfaces beat one fat one.
45
+ - **D**ependency Inversion — depend on abstractions, not concrete implementations.
46
+
47
+ Bad (SRP):
48
+ ```python
49
+ class User:
50
+ def save(self): ...
51
+ def send_email(self, msg): ...
52
+ ```
53
+
54
+ Good (SRP):
55
+ ```python
56
+ class UserRepository:
57
+ def save(self, user): ...
58
+
59
+ class EmailService:
60
+ def send(self, user, msg): ...
61
+ ```
62
+
63
+ ## YAGNI — You Aren't Gonna Need It
64
+
65
+ Implement what is required **now**. Do not add knobs, options, hooks, or layers for a hypothetical future caller.
66
+
67
+ Bad:
68
+ ```javascript
69
+ function calculator(a, b, op) {
70
+ switch (op) {
71
+ case 'add': return a + b;
72
+ case 'multiply': return a * b; // not asked for
73
+ case 'divide': return a / b; // not asked for
74
+ }
75
+ }
76
+ ```
77
+
78
+ Good:
79
+ ```javascript
80
+ const add = (a, b) => a + b;
81
+ ```
82
+
83
+ ## Meaningful naming
84
+
85
+ Names reveal intent. `d` and `crtUsrAcct()` are bugs; `elapsedTimeInDays` and `createUserAccount()` are documentation.
86
+
87
+ ### Names must be truthful
88
+
89
+ The name must describe what the function actually does. Misleading names break reader expectations and survive renames.
90
+
91
+ Flag and rename:
92
+
93
+ - `get*` / `fetch*` / `find*` that also mutate state, throw, or write logs / audits.
94
+ - `is*` / `has*` / `can*` that throw instead of returning a boolean.
95
+ - `find*` that *creates* the entity when missing → `findOrCreate*` or `ensure*`.
96
+ - Names that describe the implementation, not the intent: `loopOverUsers` → `notifyActiveUsers`, `runQuery` → `listOverdueInvoices`.
97
+ - Names that say the opposite of what they do under some branch: `enableFoo` that disables when a flag is off, with no hint in the name.
98
+
99
+ ### Names must stand alone
100
+
101
+ The test: if the name appeared in a stacktrace, an autocomplete list, or a grep result, would the reader know what it does without opening the file? If not, add specificity.
102
+
103
+ - Names missing the noun: `createPending()` → `createPendingInstallation()`. The verb is fine; the object is missing.
104
+ - Generic verbs with no information: `handle`, `process`, `execute`, `doStuff`, `manage`. If the function deletes-then-inserts, name it `replace`, not `update`.
105
+ - Constants that will collide with siblings later: `BUCKET` → `FONTS_BUCKET`, `TIMEOUT` → `HTTP_READ_TIMEOUT_MS`, `URL` → `AUTH_SERVICE_URL`.
106
+ - Repository / port methods named after the rule they enforce, not the data they fetch: `findValid*` / `findActive*` / `findEligible*` — the adjective encodes a business rule the caller can't inspect from the name. Prefer `findDesktopLibrariesForUser(userId)` + a domain predicate applied by the caller.
107
+
108
+ This rule applies most strongly to **names crossing module boundaries**: public methods, exported constants, port methods. Private helpers in a tight local scope get more slack.
109
+
110
+ ## Functions do one thing
111
+
112
+ If the name contains "and", or you must scroll to read it, split it.
113
+
114
+ Bad:
115
+ ```javascript
116
+ function handleData() {
117
+ fetchData();
118
+ processData();
119
+ displayData();
120
+ }
121
+ ```
122
+
123
+ Good: each step is its own function with a clear name and signature.
124
+
125
+ ### Plain-English summary test
126
+
127
+ For every function you add or meaningfully modify, ask:
128
+
129
+ > Can I summarize what this function does, line by line, as a short English sentence per line — and does the resulting summary read as prose?
130
+
131
+ If the answer is "no, I'd have to group several lines together and invent a name for the group", **that grouping should already exist in the code as a named helper or named intermediate value**. The fact that you had to invent the name is the signal to extract.
132
+
133
+ ### Also flag and split
134
+
135
+ - Nested conditionals 3+ levels deep — flatten with early returns or named predicates.
136
+ - Mixed levels of abstraction — a high-level orchestrator suddenly doing string parsing, date arithmetic, or SQL assembly inline.
137
+ - A reader has to track 5+ anonymous temporary values to understand the return.
138
+ - A long `.map().filter().reduce()` chain where each stage does non-obvious work and no stage has a name.
139
+ - Boolean expressions long enough that intent is buried — `if (a && b && !c && (d || e.f > 0) && ...)` without a named predicate.
140
+ - A function that orchestrates 4+ distinct logical phases inline (*fetch → resolve → branch → persist → publish*), each more than a line or two. Even without deep nesting, that's enough work to warrant named sub-methods.
141
+
142
+ ### 50-line cap
143
+
144
+ A single function/method body must stay within **50 lines**, counting only effective code (exclude blank lines, comments, and pure data declarations such as large enums, lookup tables, or constant maps).
145
+
146
+ Crossing the cap is an extraction signal, not a style nit. Before declaring a function complete, count effective body lines; if over 50, split, or surface the violation and confirm with the user before continuing.
147
+
148
+ ### What's fine
149
+
150
+ - Functions that read as a straight sequence of clear, already-named steps (each line is an English sentence).
151
+ - Short functions (< ~10 lines) regardless of shape.
152
+ - Idiomatic framework patterns used clearly (a 30-line controller method obviously doing its job).
153
+ - Guard clauses at the top — they *help* readability, they don't count against you.
154
+
155
+ ## Testing discipline
156
+
157
+ These principles apply to every test file regardless of language or framework. The mechanical detection patterns (which mock library, which spy API) are in each `languages/*.md` Tests section.
158
+
159
+ ### No self-mocking
160
+
161
+ A unit test for class `Foo` must **not** stub, spy on, or replace methods on the instance of `Foo` being tested. Mocking injected collaborators (other services, repositories, adapters, ports, the clock) is fine and expected.
162
+
163
+ Why it matters: when you mock a method on the subject under test (SUT), the test no longer verifies that method's behavior — it verifies that *some* version of the class (the one you wired up) calls the mocked method. If someone deletes the real implementation, the test still passes. The test has become a proof of its own shape.
164
+
165
+ Bad (pseudo, language-agnostic):
166
+ ```
167
+ sut = new Foo(deps)
168
+ mock(sut.calculateTotal).returns(100) // mocking the SUT's own method
169
+ result = sut.checkout()
170
+ assert result.total == 100 // proves wiring, not logic
171
+ ```
172
+
173
+ Good:
174
+ ```
175
+ sut = new Foo(deps) // real Foo, real calculateTotal
176
+ mock(deps.payment.charge).returns(ok) // mock the collaborator at the boundary
177
+ result = sut.checkout()
178
+ assert result.total == expectedTotal // proves the logic
179
+ ```
180
+
181
+ ### Behavioral, not implementation tests
182
+
183
+ Assertions should be about **outcomes** — return values, thrown errors, persisted state, published events, calls made to genuine external boundaries. They should **not** be about *how* the work was done internally — which private helper was called, in what order.
184
+
185
+ Smells:
186
+
187
+ - `expect(spy).toHaveBeenCalledWith(...)` as the *only* or *primary* assertion, when the spy is on an internal helper or a pure collaborator with no side effects.
188
+ - Tests that would break if you renamed a private method without changing behavior.
189
+ - Assertions on private methods reached via reflection, cast-to-any, or bracket access — the issue is *reaching into privates*, not the language escape hatch itself.
190
+ - A test file where most assertions are on spies rather than on results.
191
+
192
+ Legitimately behavioral (these are fine):
193
+
194
+ - `expect(mailerSpy).toHaveBeenCalledWith(...)` — sending mail *is* the behavior.
195
+ - `expect(paymentGateway.charge).toHaveBeenCalledWith(...)` — charging a card *is* the behavior.
196
+ - `expect(orderRepo.save).toHaveBeenCalledWith(...)` in an application-service test — producing the right save call *is* the point of the service.
197
+
198
+ The judgment call: is the mocked thing an **outcome boundary** (port, external service, side-effecting adapter) or an **internal helper**? Asserting on boundaries is behavioral; asserting on helpers is implementation-coupled.
199
+
200
+ ## No magic numbers
201
+
202
+ Replace hardcoded values with named constants.
203
+
204
+ Bad: `if (age > 18)`
205
+ Good: `const LEGAL_AGE = 18; if (age > LEGAL_AGE)`
206
+
207
+ ## Limit nesting depth
208
+
209
+ Flatten with early returns / guard clauses. If you reach four nested blocks, refactor.
210
+
211
+ Bad:
212
+ ```javascript
213
+ function process(user) {
214
+ if (user) {
215
+ if (user.isActive) {
216
+ if (user.hasPermission) {
217
+ // ...
218
+ }
219
+ }
220
+ }
221
+ }
222
+ ```
223
+
224
+ Good:
225
+ ```javascript
226
+ function process(user) {
227
+ if (!user) return;
228
+ if (!user.isActive) return;
229
+ if (!user.hasPermission) return;
230
+ // ...
231
+ }
232
+ ```
233
+
234
+ ## Comments explain *why*, not *what*
235
+
236
+ The code already says **what**. Comments record the hidden constraint, the bug being worked around, the surprising tradeoff. Never narrate the next line.
237
+
238
+ Bad:
239
+ ```javascript
240
+ // increment i
241
+ i++;
242
+ ```
243
+
244
+ Good:
245
+ ```javascript
246
+ // User may be soft-deleted; invalidate cache so stale reads vanish.
247
+ cache.invalidate(user.id);
248
+ ```
249
+
250
+ Default to writing **no comment**. Only add one when removing it would confuse a future reader.
251
+
252
+ ## Boy Scout Rule
253
+
254
+ Leave the file cleaner than you found it. Fix one small inconsistency on the way through, do not embark on a separate refactor.
@@ -0,0 +1,64 @@
1
+ # Java Conventions
2
+
3
+ ## Style guide
4
+
5
+ Google Java Style Guide is the default. If the project ships its own (`CONTRIBUTING.md`, `.editorconfig`, `checkstyle.xml`, `google-java-format` config), follow that instead.
6
+
7
+ ## Naming
8
+
9
+ | Element | Convention | Example |
10
+ |---|---|---|
11
+ | Class / interface / enum | UpperCamelCase | `ImmutableList`, `HashIntegrationTest` |
12
+ | Method / variable / parameter | lowerCamelCase | `sendMessage`, `computedValues` |
13
+ | Constant (`static final` + immutable value) | UPPER_SNAKE_CASE | `MAX_COUNT`, `DEFAULT_TIMEOUT` |
14
+ | Package | lowercase, no underscores | `com.example.deepspace` |
15
+ | Type variable | single letter or short PascalCase | `T`, `E`, `Result` |
16
+ | Test class | `<ClassUnderTest>Test` / `<ClassUnderTest>IT` | `UserServiceTest`, `OrderRepositoryIT` |
17
+
18
+ ## Formatting
19
+
20
+ - Indent: **2 spaces**, never tabs.
21
+ - Column limit: **100** characters.
22
+ - Brace style: K&R — opening brace on the same line.
23
+ - One statement per line.
24
+ - Always brace `if`, `else`, `for`, `while`, `do`, even for single-statement bodies.
25
+ - `switch` statements must be exhaustive — supply a `default` case, or rely on enum/sealed-type coverage.
26
+
27
+ ## Required practices
28
+
29
+ - `@Override` on every override (including interface methods).
30
+ - Never silently swallow exceptions. If you intentionally ignore one, leave a one-line comment stating why.
31
+ - Access static members via the class name (`Foo.bar()`, not `instance.bar()`).
32
+ - Do not use finalizers. Use `try-with-resources` for `AutoCloseable` and `java.lang.ref.Cleaner` for native handles.
33
+ - Declare local variables near first use, with the narrowest possible scope.
34
+ - Prefer `List.of(...)` / `Map.of(...)` / `Set.of(...)` for small immutable collections.
35
+ - Prefer `Optional<T>` as a return type for "might be missing"; never use `Optional` for fields, parameters, or collection elements.
36
+ - Use `record` for plain data carriers (Java 16+).
37
+ - Use `var` only when the right-hand side makes the type obvious to a reader.
38
+
39
+ ## Tests
40
+
41
+ - Framework: **JUnit 5** unless the project uses TestNG or JUnit 4.
42
+ - File suffix: `*Test.java` for unit, `*IT.java` for integration.
43
+ - Method naming: `methodUnderTest_condition_expectedResult` (e.g. `parse_emptyInput_throws`).
44
+ - Use **AssertJ** or JUnit's `assertThat` for fluent assertions; avoid bare `assertTrue(a == b)`.
45
+ - One logical assertion per test. Multiple `assertThat` lines on the *same state* are fine.
46
+ - No `Thread.sleep` in tests — use **Awaitility** for async.
47
+ - Mock at process boundaries only (HTTP, DB, time, randomness). Do not mock value objects or DTOs.
48
+ - Use `@Nested` to group related test cases.
49
+ - Use parameterized tests (`@ParameterizedTest` + `@CsvSource` / `@MethodSource`) for table-driven coverage.
50
+
51
+ ### Self-mock signals to refuse (rule from `clean-code.md` → Testing discipline)
52
+
53
+ - A field annotated with **both** `@InjectMocks` *and* `@Spy` — the SUT is being wrapped as a spy so its own methods can be stubbed. Drop `@Spy`; stub only the `@Mock` collaborators on the same class.
54
+ - `Mockito.spy(realSut)` followed by `doReturn(...).when(spy).someMethod(...)` — same anti-pattern, manually constructed.
55
+ - `MockedStatic<SomeUtil>` when `SomeUtil` is the unit under test rather than a dependency.
56
+ - `ReflectionTestUtils.setField(sut, "privateState", ...)` to drive a branch, or `ReflectionTestUtils.invokeMethod(sut, "privateHelper")` to assert on a private helper — that's reaching into privates and breaks behavioral-testing discipline.
57
+
58
+ What's fine: `@Mock` on collaborators injected into `@InjectMocks`, `verify(collaborator).methodCall(...)` on outcome boundaries (repository save, mailer send, gateway charge).
59
+
60
+ ## Formatter / linter to run
61
+
62
+ - `google-java-format` (preferred) or the `spotless` Gradle/Maven plugin.
63
+ - `checkstyle`, `spotbugs`, `error-prone` if configured by the project.
64
+ - Run before commit: `./gradlew spotlessApply check` (or the project's equivalent).
@@ -0,0 +1,87 @@
1
+ # JavaScript / TypeScript Conventions
2
+
3
+ ## Style guide
4
+
5
+ Airbnb JavaScript Style Guide is the common baseline. The project's `.eslintrc(.json|.cjs|.mjs)`, `.prettierrc`, `tsconfig.json`, and `package.json` `engines` override these defaults — read them **first**.
6
+
7
+ ## Naming
8
+
9
+ | Element | Convention | Example |
10
+ |---|---|---|
11
+ | Variable / function | camelCase | `userCount`, `fetchProfile` |
12
+ | Class / type / interface / enum | PascalCase | `UserProfile`, `HttpClient`, `Status` |
13
+ | Constant (true immutable, module-level) | UPPER_SNAKE_CASE | `MAX_RETRIES` |
14
+ | Boolean variable | `is` / `has` / `should` / `can` prefix | `isLoading`, `hasPermission`, `shouldRetry` |
15
+ | React component | PascalCase | `UserCard` |
16
+ | Hook | `use` prefix | `useAuth` |
17
+ | File: React component | matches component name | `UserCard.tsx` |
18
+ | File: everything else | kebab-case | `format-date.ts` |
19
+
20
+ ## Formatting
21
+
22
+ - Indent: **2 spaces**.
23
+ - Semicolons: pick one project-wide and never mix.
24
+ - Single quotes for strings, backticks for templates and any string needing interpolation.
25
+ - Trailing commas in multi-line literals / params.
26
+ - Prefer arrow functions for callbacks and small utilities; reserve `function` for top-level named declarations and class methods.
27
+ - Destructure when accessing 2 or more properties: `const { id, name } = user`.
28
+
29
+ ## TypeScript-specific (mandatory)
30
+
31
+ - `tsconfig.json`: `"strict": true`. No exceptions. `noUncheckedIndexedAccess` and `exactOptionalPropertyTypes` are recommended.
32
+ - **Never** use `any`. If a type is truly unknown, use `unknown` and narrow.
33
+ - Let inference do its job for locals. Declare explicit types at:
34
+ - Exported function signatures (parameters **and** return type).
35
+ - Public class members.
36
+ - Empty literals where inference would default to `never` or `{}` — `const items: User[] = []`.
37
+ - Use `readonly` for arrays, properties, and parameters that must not mutate.
38
+ - Discriminated unions over loose objects with optional flags. Use a literal `kind` / `type` field.
39
+ - Type guards (`function isX(v): v is X`) over casts (`as X`). Casts are a last resort.
40
+ - `interface` for object shapes that may be extended; `type` for unions / mapped / conditional types.
41
+ - Generics: meaningful names when single letters are unclear (`TUser`, `TResult`).
42
+ - Never disable rules inline (`// eslint-disable`, `// @ts-ignore`, `// @ts-expect-error`) without a one-line comment explaining why.
43
+
44
+ ## Required practices
45
+
46
+ - `const` by default, `let` only when reassigned. `var` is **forbidden**.
47
+ - Use `===` / `!==`, never `==` / `!=`.
48
+ - Use optional chaining `?.` and nullish coalescing `??` instead of long `&&` / `||` ladders.
49
+ - `for...of` over indexed `for` loops on iterables. `map` / `filter` / `reduce` over manual loops when transforming.
50
+ - `async` / `await` over raw `.then` chains. Mixing is forbidden in one function.
51
+ - Throw `Error` instances, never strings. Subclass `Error` for typed domain errors.
52
+ - Avoid `export default` unless the file has exactly one obvious export — named exports are easier to refactor and rename.
53
+ - Do not mutate function parameters or imported modules.
54
+
55
+ ## Tests
56
+
57
+ - Framework: **Vitest**, **Jest**, or Node's built-in `node:test`. Match what the project uses.
58
+ - File suffix: `*.test.ts` or `*.spec.ts`, colocated with source (`src/foo/bar.ts` → `src/foo/bar.test.ts`).
59
+ - Structure: `describe(unit, () => { it('does X when Y', () => { ... }) })`.
60
+ - **One behaviour per `it` block.**
61
+ - Async tests: `await` every promise. Returning a promise without `await` hides assertion failures.
62
+ - Mocking: prefer dependency injection over module mocks. When you must mock, use `vi.mock` / `jest.mock` and reset between tests (`beforeEach(() => vi.clearAllMocks())`).
63
+ - React: **React Testing Library**, not Enzyme. Query by role / label / text, not by class or test-id, unless nothing else works.
64
+ - Snapshot tests only for stable, intentional output. Snapshots that "just changed" are noise.
65
+
66
+ ### Self-mock signals to refuse (rule from `clean-code.md` → Testing discipline)
67
+
68
+ - `jest.spyOn(sut, 'someMethod').mockReturnValue(...)` / `.mockResolvedValue(...)` / `.mockImplementation(...)` where `sut` is the subject under test.
69
+ - `jest.spyOn(FooService.prototype, 'someMethod').mockReturnValue(...)` in `foo.service.spec.ts`.
70
+ - `sut.calculateTotal = jest.fn(...)` — manually overwriting a method on the SUT instance.
71
+ - `vi.mocked(sut)` followed by stubbing the SUT's own methods.
72
+ - Reaching into privates via `(sut as any).privateMethod()` or `sut['privateMethod']()` — the issue is the *private access*, not the cast itself.
73
+
74
+ `any` / `as any` in `*.spec.ts` / `*.test.ts` is otherwise fine — test setup justifies looser typing. Only flag it when it's the vehicle for one of the violations above.
75
+
76
+ What's fine: `jest.fn()` for collaborator stubs passed via constructor DI, `vi.mock('../mailer')` to fake a side-effecting module at the boundary, `expect(mailer.send).toHaveBeenCalledWith(...)` on an outcome boundary.
77
+
78
+ ## Formatter / linter to run
79
+
80
+ - `prettier --write .` (formatting).
81
+ - `eslint --fix .` (style + correctness).
82
+ - `tsc --noEmit` (type check).
83
+ - All three should pass before commit. CI should fail on warnings, not just errors.
84
+
85
+ ## Forbidden and Prohibited
86
+
87
+ - never use any type assertions (`as` or ``)
@@ -0,0 +1,69 @@
1
+ # Kotlin Conventions
2
+
3
+ ## Style guide
4
+
5
+ Kotlin official coding conventions (kotlinlang.org/docs/coding-conventions.html). In IntelliJ / Android Studio enable via *Settings → Editor → Code Style → Kotlin → Set from… → Kotlin style guide*.
6
+
7
+ ## Naming
8
+
9
+ | Element | Convention | Example |
10
+ |---|---|---|
11
+ | Class / object / interface | UpperCamelCase | `DeclarationProcessor` |
12
+ | Function / property / parameter | lowerCamelCase | `processDeclarations`, `isReady` |
13
+ | Constant (`const val` or top-level immutable) | SCREAMING_SNAKE_CASE | `MAX_COUNT`, `USER_NAME_FIELD` |
14
+ | Backing property | leading underscore | `_elementList` (private), `elementList` (public) |
15
+ | Package | lowercase, no underscores | `com.example.feature` |
16
+ | Test function | backticked sentence | `` `parses empty input` `` |
17
+
18
+ ## Formatting
19
+
20
+ - Indent: **4 spaces**.
21
+ - Opening brace at end of line, closing brace on its own line.
22
+ - Lambdas: spaces around braces and arrow — `list.filter { it > 10 }`, `map.forEach { (k, v) -> println("$k=$v") }`.
23
+ - Trailing comma in multi-line lists / parameters / `when` arms (since Kotlin 1.4).
24
+ - Function expression body when the body is a single expression: `fun double(x: Int) = x * 2`.
25
+
26
+ ## Required practices
27
+
28
+ - Prefer `val` over `var`. Mutability is opt-in.
29
+ - Default and named arguments replace constructor / function overloads.
30
+ - Prefer the standard library: `filter`, `map`, `groupBy`, `associateBy`, `chunked`, `windowed` over manual loops.
31
+ - String templates: `"Hello, $name"`, never `"Hello, " + name`.
32
+ - Extension functions are encouraged. Keep them `internal` or file-private unless they belong to a public API.
33
+ - `data class` for value-holders. Do not attach behaviour to them.
34
+ - Sealed classes / sealed interfaces for closed hierarchies — exhaustive `when` catches new cases at compile time.
35
+ - Null safety: never chain `!!`. Use `?.`, `?:`, `let`, `requireNotNull(x) { "why" }`, `checkNotNull(x)`.
36
+ - Use `inline` for higher-order functions that are called frequently and pass lambdas, **not** as a default optimization.
37
+ - Use `object` for true singletons and `companion object` only when JVM-visible static members are needed.
38
+
39
+ ## Coroutines
40
+
41
+ - Pick **one** coroutine framework per module. Do not mix coroutines with `CompletableFuture` chains in the same flow.
42
+ - Never `runBlocking { ... }` inside another coroutine.
43
+ - Pass `CoroutineScope` explicitly; do not use `GlobalScope`.
44
+ - Cancel scopes deterministically on lifecycle teardown.
45
+ - Use `withContext(Dispatchers.IO)` only for blocking I/O — most application code stays on the default dispatcher.
46
+
47
+ ## Tests
48
+
49
+ - Framework: **JUnit 5 + kotlin.test**, or **Kotest**. Match the project.
50
+ - Backticked function names are idiomatic for tests: `` fun \`returns empty list when input is empty\`() `` .
51
+ - Use `kotlinx-coroutines-test`'s `runTest` for suspend tests.
52
+ - Use **MockK** over Mockito for Kotlin (handles `final` classes, coroutines, and extension functions correctly).
53
+ - Prefer property-based tests (Kotest's `forAll`) for pure transformations.
54
+ - Assertion libraries: `kotest-assertions` (`shouldBe`, `shouldThrow`) or AssertJ.
55
+
56
+ ### Self-mock signals to refuse (rule from `clean-code.md` → Testing discipline)
57
+
58
+ - `spyk(sut)` followed by `every { sut.someMethod() } returns ...` — the SUT's own method is replaced; the test verifies wiring, not behavior.
59
+ - `coEvery { sut.suspendMethod() } returns ...` for the suspend equivalent.
60
+ - `mockkObject(SomeSingleton)` / `mockkStatic(...)` when `SomeSingleton` *is* the unit under test.
61
+ - Reflection or `internal` visibility hacks (`@VisibleForTesting`, `callPrivateFunc` extensions) used to assert on private helpers — that's the implementation-coupling smell, not just a self-mock.
62
+
63
+ What's fine: `mockk<Collaborator>()` for injected dependencies, `coEvery {}` on those collaborators, and `verify {}` on outcome boundaries (mailer, gateway, repository write).
64
+
65
+ ## Formatter / linter to run
66
+
67
+ - **ktlint** and / or **detekt**.
68
+ - `./gradlew ktlintFormat detekt` if the plugins are configured.
69
+ - Run before commit.
@@ -0,0 +1,66 @@
1
+ # Node.js Conventions (server-side)
2
+
3
+ Load this **in addition to** [javascript-typescript.md](javascript-typescript.md). JS / TS rules still apply; this file adds the server-specific layer.
4
+
5
+ ## Architecture
6
+
7
+ - Separate layers: **HTTP / transport** → **business / domain** → **data access**. No layer skips downward; no layer reaches upward.
8
+ - Request handlers are thin: parse input → call a service → format response.
9
+ - Business logic is framework-agnostic. Do **not** import Express / Fastify / Nest types in service or domain modules.
10
+ - One file, one concern. A 1000-line `index.ts` is a structural bug.
11
+ - Dependency injection (constructor or factory) over module-level singletons. Singletons are not testable.
12
+
13
+ ## Error handling
14
+
15
+ - Distinguish **operational errors** (network, validation, expected business state) from **programmer errors** (bugs, broken invariants). Recover from operational, crash on programmer.
16
+ - Use `async` / `await` with `try` / `catch` at the layer that knows what to do. Catching just to rethrow with no extra value is a smell.
17
+ - Centralise error handling in middleware. Every route returns through it.
18
+ - Always handle event-emitter `'error'` events — an unhandled emitter error crashes the process.
19
+ - Throw subclasses of `Error` (`class ValidationError extends Error {}`) so handlers can `instanceof` them.
20
+ - Process-level: `process.on('unhandledRejection', ...)` and `process.on('uncaughtException', ...)` should log and exit gracefully. Do not swallow.
21
+
22
+ ## Async discipline
23
+
24
+ - Never mix callbacks with promises in the same flow. Use `util.promisify` at the boundary, once.
25
+ - Prefer `for await...of` over manual cursor / stream loops.
26
+ - `Promise.all` for independent parallel work, `Promise.allSettled` when one failure must not cancel the rest.
27
+ - Set explicit timeouts on **every** outgoing HTTP / DB call (no infinite waits, no defaults).
28
+ - Use `AbortController` to cancel in-flight work when the upstream caller goes away.
29
+
30
+ ## Security baseline
31
+
32
+ - All configuration in environment variables (loaded via `dotenv` in dev only). Validate them at startup with `zod`, `envalid`, or similar — fail fast on missing / invalid values.
33
+ - Never log secrets, auth-endpoint request bodies, full JWTs, or PII.
34
+ - HTTPS only at the edge. Behind a TLS-terminating proxy is fine; never speak plain HTTP across the public internet.
35
+ - Use `helmet` for security headers, `cors` with an explicit allowlist (no `*` in production).
36
+ - Validate every untrusted input (`zod`, `joi`, `valibot`). Validation belongs at the boundary, not inside services.
37
+ - Rate-limit public endpoints (`express-rate-limit` or upstream proxy).
38
+ - Run `npm audit` / `pnpm audit` regularly. Patch promptly.
39
+
40
+ ## Performance
41
+
42
+ - **Streams** (`fs.createReadStream`, `pipeline`) for large payloads — never load multi-MB files fully into memory.
43
+ - Multi-core: `cluster`, PM2, or a process manager. Pure async will not save you from a single-thread CPU bottleneck.
44
+ - Keep request handlers off the event loop. Offload heavy CPU work to a `worker_thread` or a queue.
45
+ - Connection pool for every DB client. `pg`, `mysql2`, and `mongoose` default to a pool — do **not** create a client per request.
46
+ - Cache idempotent reads at an explicit layer (Redis, in-memory LRU). Do not bolt caching into a service method.
47
+ - Use `Buffer`/typed arrays for binary data, not strings.
48
+
49
+ ## Logging
50
+
51
+ - Structured logger (`pino`, `winston`). Emit **JSON**, not human strings.
52
+ - Every request: request id, method, path, status, latency. Propagate the request id through downstream calls via `AsyncLocalStorage`.
53
+ - Levels: `error` (pageable), `warn` (suspicious), `info` (business event), `debug` (off in production).
54
+ - Never `console.log` in production code. `console.log` exists for ad-hoc REPL only.
55
+
56
+ ## Tests
57
+
58
+ - Unit-test services and domain functions with zero Node-runtime dependencies — no `fs`, no `net`, no real DB.
59
+ - Integration-test the HTTP layer with `supertest` against the real Express / Fastify app instance.
60
+ - `testcontainers` for DBs when the production DB has features (locks, full-text, JSONB, partitioning) you depend on. In-memory fakes lie about behaviour.
61
+ - Test the error paths. Coverage of the happy path only is not coverage.
62
+
63
+ ## Formatter / linter / tools
64
+
65
+ - Same as JS / TS: `prettier`, `eslint`, `tsc`.
66
+ - `npm run lint && npm test` (or the project's equivalent) must pass before any commit.