Skip to content

Commit d1b2d40

Browse files
authored
fix(workflow): fold v5.1/v5.2 hotfixes + OC-8 exception into v5.0.20260418 - Structured Phascolarctos (#69)
* chore(release): bump version to v5.2.20260418 - Emergent Colugo (hotfix) * fix(workflow): OC-8 dataclass exception, SELF-DECLARE phase, design-patterns heuristic
1 parent 47e4518 commit d1b2d40

File tree

6 files changed

+28
-12
lines changed

6 files changed

+28
-12
lines changed

.opencode/skills/design-patterns/SKILL.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,3 +393,13 @@ class JsonImporter(Importer):
393393
| Class directly calls B, C, D on state change | Observer |
394394
| Two functions share the same skeleton, differ in one step | Template Method |
395395
| Subsystem is complex and callers need a simple entry point | Facade |
396+
397+
---
398+
399+
## Core Heuristic — Procedural vs OOP
400+
401+
> **When procedural code requires modifying existing functions to add new variants, OOP is the fix.**
402+
403+
Procedural code is open to inspection but open to modification too — every new case touches existing logic.
404+
OOP (via Strategy, State, Observer, etc.) closes existing code to modification and opens it to extension through new types.
405+
The smell is always the same: **a place in the codebase that must change every time the domain grows.**

.opencode/skills/implementation/SKILL.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ Only write an ADR if the decision is non-obvious or has meaningful trade-offs. R
135135
Apply to the stub files just written:
136136

137137
- [ ] No class with >2 responsibilities (SOLID-S)
138-
- [ ] No class with >2 instance variables (OC-8)
138+
- [ ] No behavioural class with >2 instance variables (OC-8; dataclasses, Pydantic models, value objects, and TypedDicts are exempt)
139139
- [ ] All external deps assigned a Protocol (SOLID-D + Hexagonal) — N/A if no external dependencies identified in scope
140140
- [ ] No noun with different meaning across modules (DDD Bounded Context)
141141
- [ ] No missing Creational pattern: repeated construction without Factory/Builder
@@ -254,7 +254,7 @@ As a software-engineer I declare:
254254
* OC-5: one dot per line — AGREE/DISAGREE | file:line
255255
* OC-6: no abbreviations — AGREE/DISAGREE | file:line
256256
* OC-7: ≤20 lines per function, ≤50 per class — AGREE/DISAGREE | longest: file:line
257-
* OC-8: ≤2 instance variables per class — AGREE/DISAGREE | file:line
257+
* OC-8: ≤2 instance variables per class (behavioural classes only; dataclasses, Pydantic models, value objects, and TypedDicts are exempt) — AGREE/DISAGREE | file:line
258258
* OC-9: no getters/setters — AGREE/DISAGREE | file:line
259259
* Patterns: no creational smell — AGREE/DISAGREE | file:line
260260
* Patterns: no structural smell — AGREE/DISAGREE | file:line

.opencode/skills/refactor/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ Before marking the `@id` complete, verify all of the following. Each failed item
291291
| OC-5 | One dot per line | `obj.repo.find(id).name` |
292292
| OC-6 | No abbreviations | `usr`, `mgr`, `cfg`, `val`, `tmp` |
293293
| OC-7 | Classes ≤ 50 lines, methods ≤ 20 lines | Any method requiring scrolling |
294-
| OC-8 | ≤ 2 instance variables per class | `__init__` with 3+ `self.x =` assignments |
294+
| OC-8 | ≤ 2 instance variables per class *(behavioural classes only; dataclasses, Pydantic models, value objects, and TypedDicts are exempt)* | `__init__` with 3+ `self.x =` assignments in a behavioural class |
295295
| OC-9 | No getters/setters | `def get_name(self)` / `def set_name(self, v)` |
296296

297297
### SOLID (Martin 2000)

AGENTS.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ docs/architecture/
9595
9696
tests/
9797
features/<feature-name>/
98-
<rule-slug>_test.py ← one per Rule: block, software-engineer-written
98+
<rule_slug>_test.py ← one per Rule: block, software-engineer-written
9999
unit/
100100
<anything>_test.py ← software-engineer-authored extras (no @id traceability)
101101
```
@@ -109,7 +109,7 @@ Tests in `tests/unit/` are software-engineer-authored extras not covered by any
109109
## Test File Layout
110110

111111
```
112-
tests/features/<feature-name>/<rule-slug>_test.py
112+
tests/features/<feature-name>/<rule_slug>_test.py
113113
```
114114

115115
### Function Naming
@@ -177,7 +177,7 @@ uv run task doc-serve
177177
- **Function length**: ≤ 20 lines
178178
- **Class length**: ≤ 50 lines
179179
- **Max nesting**: 2 levels
180-
- **Instance variables**: ≤ 2 per class
180+
- **Instance variables**: ≤ 2 per class *(exception: dataclasses, Pydantic models, value objects, and TypedDicts are exempt — they may carry as many fields as the domain requires)*
181181
- **Semantic alignment**: tests must operate at the same abstraction level as the acceptance criteria they cover
182182
- **Integration tests**: multi-component features require at least one test in `tests/features/` that exercises the public entry point end-to-end
183183

docs/workflow.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,9 @@ Each step has a designated agent and a specific deliverable. No step is skipped.
163163
│ │
164164
│ ARCHITECTURE SMELL CHECK — hard gate (fix before commit) │
165165
│ [ ] No class with >2 responsibilities (SOLID-S) │
166-
│ [ ] No class with >2 instance variables (OC-8) │
166+
│ [ ] No behavioural class with >2 instance variables (OC-8; │
167+
│ dataclasses, Pydantic models, value objects, TypedDicts │
168+
│ are exempt) │
167169
│ [ ] All external deps assigned a Protocol (SOLID-D + Hexagonal) │
168170
│ N/A if no external dependencies identified in scope │
169171
│ [ ] No noun with different meaning across planned modules │
@@ -221,6 +223,11 @@ Each step has a designated agent and a specific deliverable. No step is skipped.
221223
│ │ │ Load skill refactor — follow its protocol │ │ │
222224
│ │ │ uv run task test-fast after each individual change │ │ │
223225
│ │ │ EXIT: test-fast passes; no smells remain │ │ │
226+
│ │ ├───────────────────────────────────────────────────────┤ │ │
227+
│ │ │ SELF-DECLARE │ │ │
228+
│ │ │ Fill Self-Declaration block in TODO.md │ │ │
229+
│ │ │ AGREE/DISAGREE per principle with file:line │ │ │
230+
│ │ │ DISAGREE requires inline justification │ │ │
224231
│ │ └───────────────────────────────────────────────────────┘ │ │
225232
│ │ │ │
226233
│ │ Mark @id completed in TODO.md │ │
@@ -259,7 +266,7 @@ Each step has a designated agent and a specific deliverable. No step is skipped.
259266
│ * OC-5: one dot per line — AGREE/DISAGREE | file:line │
260267
│ * OC-6: no abbreviations — AGREE/DISAGREE | file:line │
261268
│ * OC-7: ≤20 lines per function — AGREE/DISAGREE | file:line │
262-
│ * OC-8: ≤2 instance variables per class — AGREE/DISAGREE | file:line │
269+
│ * OC-8: ≤2 instance variables per class (behavioural classes only; dataclasses, Pydantic models, value objects, and TypedDicts are exempt) — AGREE/DISAGREE | file:line │
263270
│ * OC-9: no getters/setters — AGREE/DISAGREE | file:line │
264271
│ * Patterns: no creational smell — AGREE/DISAGREE | file:line │
265272
│ * Patterns: no structural smell — AGREE/DISAGREE | file:line │
@@ -471,7 +478,7 @@ Source: docs/features/in-progress/<name>.feature
471478

472479
## Cycle State
473480
Test: @id:<hex> — <description>
474-
Phase: RED | GREEN | REFACTOR
481+
Phase: RED | GREEN | REFACTOR | SELF-DECLARE
475482

476483
## Self-Declaration
477484
As a software-engineer I declare:
@@ -493,7 +500,7 @@ As a software-engineer I declare:
493500
* OC-5: one dot per line — AGREE/DISAGREE | file:line
494501
* OC-6: no abbreviations — AGREE/DISAGREE | file:line
495502
* OC-7: ≤20 lines per function, ≤50 per class — AGREE/DISAGREE | longest: file:line
496-
* OC-8: ≤2 instance variables per class — AGREE/DISAGREE | file:line
503+
* OC-8: ≤2 instance variables per class (behavioural classes only; dataclasses, Pydantic models, value objects, and TypedDicts are exempt) — AGREE/DISAGREE | file:line
497504
* OC-9: no getters/setters — AGREE/DISAGREE | file:line
498505
* Patterns: no creational smell — AGREE/DISAGREE | file:line
499506
* Patterns: no structural smell — AGREE/DISAGREE | file:line
@@ -534,7 +541,7 @@ As a software-engineer I declare:
534541
| Function length | ≤ 20 lines |
535542
| Class length | ≤ 50 lines |
536543
| Max nesting | 2 levels |
537-
| Instance variables per class | ≤ 2 |
544+
| Instance variables per class | ≤ 2 (behavioural classes only; dataclasses, Pydantic models, value objects, TypedDicts are exempt) |
538545
| `noqa` comments | 0 |
539546
| `type: ignore` comments | 0 |
540547
| Orphaned tests | 0 |

tests/unit/app_test.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from app.__main__ import main
88

99

10-
@pytest.mark.unit
1110
@given(verbosity=st.sampled_from(["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]))
1211
@example(verbosity="INFO")
1312
def test_app_main_runs_with_valid_verbosity(verbosity: str) -> None:

0 commit comments

Comments
 (0)