@oleksandr.rudnychenko/sync_loop 0.2.1

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.
@@ -0,0 +1,113 @@
1
+ # Domain Glossary
2
+
3
+ Canonical terminology for the project codebase. All code, comments, docs, and agent reasoning must use these terms.
4
+ Referenced from [patterns.md](patterns.md). Cross-references use `[→ ID]` pattern IDs from the registry.
5
+
6
+ **Use when:**
7
+ - Naming a new variable, field, class, or parameter
8
+ - Writing comments, docstrings, or documentation
9
+ - Interpreting domain concepts in user requests or specs
10
+ - Resolving ambiguity between similar terms
11
+ - Checking deprecated aliases before using a term in new code
12
+
13
+ ---
14
+
15
+ ## Architecture Terms
16
+
17
+ {Fill in during bootstrap with project-specific layer terms.}
18
+
19
+ | Term | Canonical Symbol | Definition |
20
+ |------|------------------|------------|
21
+ | **service** | `*Service` [→ P2, P10] | Business orchestration unit — contains domain logic and data access. No transport concerns. |
22
+ | **route** | `*_router` / `*_controller` [→ P5] | Transport boundary — handles requests, delegates to services. No business logic. |
23
+ | **adapter** | `*Adapter` [→ P1] | Concrete implementation of an infrastructure port/interface. |
24
+ | **config** | `Settings` / `Config` [→ P11] | Centralized configuration — single env source with startup validation. |
25
+
26
+ {Add project-specific architecture terms here.}
27
+
28
+ ---
29
+
30
+ ## Domain Objects
31
+
32
+ {Fill in during bootstrap with actual domain models.}
33
+
34
+ | Term | Canonical Symbol | Model / Location | Description |
35
+ |------|------------------|------------------|-------------|
36
+ | {entity} | `{ClassName}` | `{models/file.py}` | {what it represents} |
37
+
38
+ ---
39
+
40
+ ## Agent Loop Lifecycle Terms
41
+
42
+ | Term | Meaning |
43
+ |------|---------|
44
+ | **SENSE** | State detection and requirement extraction |
45
+ | **GKP** | Generated Knowledge Pack — context retrieval + compression |
46
+ | **DECIDE+ACT** | Mode selection and immediate implementation |
47
+ | **CHALLENGE-TEST** | Validation loop with retry-driven patching |
48
+ | **UPDATE** | State transition persistence |
49
+ | **LEARN** | Persisting reusable corrections and heuristics |
50
+ | **REPORT** | Writing non-trivial session outcomes |
51
+
52
+ ---
53
+
54
+ ## Naming Rules
55
+
56
+ 1. **One concept → one canonical term** — never use synonyms in code
57
+ 2. **Prefer explicit nouns** over abbreviations (`service` not `svc`)
58
+ 3. **Keep boundary model names stable** once published to consumers
59
+ 4. **Record deprecated aliases** before removal (see table below)
60
+ 5. **Suffix conventions** (adapt to project):
61
+ - `*Service` for business logic
62
+ - `*Router` / `*Controller` for transport handlers
63
+ - `*Adapter` for infrastructure implementations
64
+ - `*Port` / `*Interface` for infrastructure abstractions
65
+
66
+ ---
67
+
68
+ ## Deprecated Alias Table
69
+
70
+ | Deprecated | Canonical | Notes |
71
+ |------------|-----------|-------|
72
+ | `svc` | `service` | Abbreviated form |
73
+ | `handler` | `service` | Avoid ambiguity with route handlers |
74
+
75
+ {Add project-specific deprecated aliases here.}
76
+
77
+ ---
78
+
79
+ ## Data Flow Diagram
80
+
81
+ {Fill in during bootstrap with actual project data flow.}
82
+
83
+ ```
84
+ ┌──────────────────────────────┐
85
+ │ USER INPUT │
86
+ ├──────────────────────────────┤
87
+ │ Request → Transport Layer │
88
+ └─────────────┬────────────────┘
89
+
90
+
91
+ ┌──────────────────────────────┐
92
+ │ SERVICE LAYER │
93
+ ├──────────────────────────────┤
94
+ │ Business Logic → Data Access│
95
+ └─────────────┬────────────────┘
96
+
97
+
98
+ ┌──────────────────────────────┐
99
+ │ OUTPUT │
100
+ ├──────────────────────────────┤
101
+ │ Response → Side Effects │
102
+ └──────────────────────────────┘
103
+ ```
104
+
105
+ ---
106
+
107
+ ## Related Documents
108
+
109
+ | Document | Purpose |
110
+ |----------|---------|
111
+ | [reasoning-kernel.md](reasoning-kernel.md) | Where lifecycle terms are executed |
112
+ | [patterns.md](patterns.md) | Where pattern IDs are defined and routed |
113
+ | [../AGENTS.md](../AGENTS.md) | Main entrypoint with full architecture reference |
@@ -0,0 +1,132 @@
1
+ # API Standards (R3)
2
+
3
+ Standards for consistent boundary contracts: HTTP routes, request/response models, error envelopes, and documentation.
4
+ Referenced from [../patterns.md](../patterns.md).
5
+
6
+ ---
7
+
8
+ ## Requirements
9
+
10
+ 1. **Typed Models**: Every route must define explicit request and response models. No raw `dict` or untyped `JSONResponse` for success cases.
11
+
12
+ 2. **Docstrings**: Every route handler must have a docstring with summary and description.
13
+
14
+ 3. **Tags & Metadata**: Group routes with semantic tags. Provide `summary` / `description` in path operations.
15
+
16
+ 4. **Spec Generation**: After modifying routes, regenerate API documentation (OpenAPI spec, Swagger, or equivalent).
17
+
18
+ ---
19
+
20
+ ## Workflow for New Routes
21
+
22
+ ```
23
+ 1. Define Request/Response models in the module's models file
24
+ 2. Implement the route handler using those models
25
+ 3. Register the router in the app entrypoint with appropriate tags
26
+ 4. Run spec generation script
27
+ 5. Verify generated docs match expectations
28
+ ```
29
+
30
+ ### Example Route
31
+
32
+ ```python
33
+ # models.py — typed boundary contracts
34
+ @dataclass
35
+ class CreateEntityRequest:
36
+ name: str
37
+ entity_type: str
38
+ metadata: dict[str, Any] = field(default_factory=dict)
39
+
40
+ @dataclass
41
+ class EntityResponse:
42
+ id: str
43
+ name: str
44
+ entity_type: str
45
+ status: str
46
+ created_at: str
47
+
48
+ # routes.py — thin transport layer
49
+ @router.post("/entities", response_model=EntityResponse)
50
+ def create_entity(
51
+ data: CreateEntityRequest,
52
+ service: EntityService = Depends(get_service),
53
+ ) -> EntityResponse:
54
+ """Create a new entity for processing.
55
+
56
+ Validates input, delegates to service, returns created entity.
57
+ """
58
+ result = service.create(data)
59
+ return EntityResponse(
60
+ id=result.id,
61
+ name=result.name,
62
+ entity_type=result.entity_type,
63
+ status=result.status,
64
+ created_at=result.created_at.isoformat(),
65
+ )
66
+ ```
67
+
68
+ ---
69
+
70
+ ## Error Envelope
71
+
72
+ All error responses must follow a consistent structure:
73
+
74
+ ```python
75
+ # Standard error response
76
+ @dataclass
77
+ class ErrorResponse:
78
+ error: str # Machine-readable error code
79
+ message: str # Human-readable description
80
+ details: dict = field(default_factory=dict) # Optional context
81
+
82
+ # Usage at route boundary
83
+ @router.get("/entities/{entity_id}")
84
+ def get_entity(entity_id: str, service = Depends(get_service)):
85
+ try:
86
+ return service.get(entity_id)
87
+ except NotFoundError as exc:
88
+ raise HTTPException(status_code=404, detail=str(exc))
89
+ except ValidationError as exc:
90
+ raise HTTPException(status_code=400, detail=str(exc))
91
+ ```
92
+
93
+ ---
94
+
95
+ ## Documentation Rules
96
+
97
+ | Rule | Detail |
98
+ |------|--------|
99
+ | Document each endpoint intent | What does it do, who calls it |
100
+ | Define validation constraints | Required fields, ranges, formats |
101
+ | Include success + error examples | Show typical 200, 400, 404, 500 |
102
+ | Keep schema docs in sync | Regenerate after every route change |
103
+
104
+ ---
105
+
106
+ ## Change Safety
107
+
108
+ | Change Type | Requirements |
109
+ |-------------|-------------|
110
+ | **Additive** (new field, new endpoint) | Must be backward-compatible; new fields optional with defaults |
111
+ | **Modification** (rename field, change type) | Requires migration notes + caller updates + NEIGHBOR validation |
112
+ | **Removal** (delete field, remove endpoint) | Requires deprecation period, caller audit, explicit approval |
113
+
114
+ ---
115
+
116
+ ## Versioning Strategy
117
+
118
+ - Prefer additive changes over breaking changes
119
+ - When breaking changes are unavoidable:
120
+ 1. Document the change in migration notes
121
+ 2. Update all known consumers
122
+ 3. Run NEIGHBOR validation to check boundary contracts
123
+ 4. Consider a version prefix if multiple consumers exist
124
+
125
+ ---
126
+
127
+ ## Related Documents
128
+
129
+ | Document | Purpose |
130
+ |----------|---------|
131
+ | [../validate-n.md](../validate-n.md) | Contract compatibility checks (NEIGHBOR) |
132
+ | [code-patterns.md](code-patterns.md) | P5 (Transport Route), P6 (Typed Models) |
@@ -0,0 +1,300 @@
1
+ # Code Patterns (P1–P11)
2
+
3
+ Reusable implementation patterns for layered application code.
4
+ Referenced from [../patterns.md](../patterns.md).
5
+
6
+ ---
7
+
8
+ ## P1 · Port/Adapter
9
+
10
+ Abstracts infrastructure behind protocol interfaces. Decouples domain logic from external systems.
11
+
12
+ ```python
13
+ # Port (interface/protocol)
14
+ class StoragePort(Protocol):
15
+ def search(
16
+ self,
17
+ collection: str,
18
+ query: str,
19
+ *,
20
+ filters: dict | None = None,
21
+ limit: int = 10,
22
+ ) -> list[Record]: ...
23
+
24
+ # Adapter (concrete implementation)
25
+ class DatabaseAdapter:
26
+ def __init__(self, client: DBClient) -> None:
27
+ self._client = client
28
+
29
+ def search(
30
+ self,
31
+ collection: str,
32
+ query: str,
33
+ *,
34
+ filters: dict | None = None,
35
+ limit: int = 10,
36
+ ) -> list[Record]:
37
+ # Implementation against real infrastructure
38
+ ...
39
+ ```
40
+
41
+ **Key rules:**
42
+ - Port lives in `libs/{component}/port.*`
43
+ - Adapter lives in `libs/{component}/{impl}.*`
44
+ - Services depend on port interfaces, not adapters directly
45
+
46
+ ---
47
+
48
+ ## P2 · Domain Module
49
+
50
+ Each domain module follows a consistent multi-file layout:
51
+
52
+ | File | Purpose |
53
+ |------|---------|
54
+ | `models.*` | Domain entities and value objects |
55
+ | `services.*` | Business logic and orchestration |
56
+ | `routes.*` | Transport endpoints |
57
+ | `tasks.*` | Background tasks (if async processing needed) |
58
+
59
+ ```python
60
+ # services.py — business logic only, no transport concerns
61
+ class OrderService:
62
+ def __init__(self, repository: OrderRepository) -> None:
63
+ self._repository = repository
64
+
65
+ def process(self, *, order_id: str) -> ProcessResult:
66
+ order = self._repository.get(order_id)
67
+ # business logic here
68
+ return ProcessResult(order_id=order.id, status="completed")
69
+ ```
70
+
71
+ ---
72
+
73
+ ## P3 · Background Task Boundary
74
+
75
+ Task handlers stay thin. Business logic always lives in services.
76
+
77
+ ```python
78
+ # tasks.py — thin wrapper, delegates to service
79
+ def process_order_task(runtime: TaskRuntime, order_id: str):
80
+ """Background task that delegates to service."""
81
+ service = runtime.order_service
82
+ service.update_status(order_id, status="processing")
83
+
84
+ try:
85
+ service.process(order_id=order_id)
86
+ service.update_status(order_id, status="completed")
87
+ except Exception as exc:
88
+ service.update_status(order_id, status="failed", error=str(exc))
89
+ raise
90
+ ```
91
+
92
+ **Key rules:**
93
+ - Tasks never contain business logic
94
+ - Dependencies injected via runtime, not imported directly
95
+ - Always update status on success and failure
96
+
97
+ ---
98
+
99
+ ## P4 · App Context / Composition Root
100
+
101
+ Centralized dependency wiring, initialized once at startup:
102
+
103
+ ```python
104
+ @dataclass
105
+ class AppContext:
106
+ config: Config
107
+ session_factory: SessionFactory
108
+ services: ServiceRegistry
109
+ logger: Logger
110
+
111
+ _context: AppContext | None = None
112
+
113
+ def init_app_context(config: Config) -> AppContext:
114
+ global _context
115
+ if _context:
116
+ return _context
117
+ _context = AppContext(config=config, ...)
118
+ return _context
119
+
120
+ def get_app_context() -> AppContext:
121
+ if _context is None:
122
+ return init_app_context(load_config())
123
+ return _context
124
+ ```
125
+
126
+ ---
127
+
128
+ ## P5 · Transport Route
129
+
130
+ Routes only handle transport concerns; all logic delegated to services:
131
+
132
+ ```python
133
+ router = APIRouter()
134
+
135
+ def get_service() -> OrderService:
136
+ ctx = get_app_context()
137
+ return OrderService(ctx.repository)
138
+
139
+ @router.post("/orders")
140
+ def create_order(
141
+ data: CreateOrderRequest,
142
+ service: OrderService = Depends(get_service),
143
+ ) -> OrderResponse:
144
+ result = service.create(data)
145
+ return OrderResponse(id=result.id, status=result.status)
146
+ ```
147
+
148
+ ---
149
+
150
+ ## P6 · Typed Models
151
+
152
+ Domain entities with explicit types and serialization:
153
+
154
+ ```python
155
+ @dataclass(slots=True)
156
+ class OrderItem:
157
+ product_id: str
158
+ quantity: int
159
+ unit_price: float
160
+ tags: list[str] = field(default_factory=list)
161
+
162
+ def to_dict(self) -> dict[str, Any]:
163
+ return {
164
+ "product_id": self.product_id,
165
+ "quantity": self.quantity,
166
+ "unit_price": self.unit_price,
167
+ "tags": self.tags,
168
+ }
169
+ ```
170
+
171
+ ---
172
+
173
+ ## P7 · Collection/Enum Safety
174
+
175
+ Replace magic strings with typed enums:
176
+
177
+ ```python
178
+ class Collection(str, Enum):
179
+ ORDERS = "orders"
180
+ PRODUCTS = "products"
181
+ USERS = "users"
182
+
183
+ # Usage: repository.query(Collection.ORDERS, ...)
184
+ # NOT: repository.query("orders", ...)
185
+ ```
186
+
187
+ ---
188
+
189
+ ## P8 · Error Handling
190
+
191
+ Layered exception hierarchy with boundary translation:
192
+
193
+ ```python
194
+ # Domain exceptions
195
+ class DomainError(Exception):
196
+ """Base error for domain."""
197
+
198
+ class NotFoundError(DomainError):
199
+ """Resource not found."""
200
+
201
+ class ValidationError(DomainError):
202
+ """Invalid input or state."""
203
+
204
+ # Route-level translation
205
+ @router.get("/orders/{order_id}")
206
+ def get_order(order_id: str, service = Depends(get_service)):
207
+ try:
208
+ return service.get(order_id)
209
+ except NotFoundError as exc:
210
+ raise HTTPException(status_code=404, detail=str(exc))
211
+ except ValidationError as exc:
212
+ raise HTTPException(status_code=400, detail=str(exc))
213
+ ```
214
+
215
+ ---
216
+
217
+ ## P9 · Type Hints Everywhere
218
+
219
+ All code must have complete type annotations:
220
+
221
+ ```python
222
+ # ✅ Good — fully typed
223
+ def process(
224
+ order_id: str,
225
+ *,
226
+ callback: Callable[..., Awaitable[Response]] | None = None,
227
+ ) -> tuple[str, dict[str, Any]] | None:
228
+ ...
229
+
230
+ # ❌ Bad — missing annotations
231
+ def process(order_id, callback=None):
232
+ ...
233
+ ```
234
+
235
+ **Common type aliases:**
236
+ ```python
237
+ SessionFactory = Callable[[], Session]
238
+ Filters = Mapping[str, Any]
239
+ ```
240
+
241
+ ---
242
+
243
+ ## P10 · Service Orchestration
244
+
245
+ Services accept all dependencies via constructor — no hidden state:
246
+
247
+ ```python
248
+ # Production code
249
+ class AnalysisService:
250
+ def __init__(
251
+ self,
252
+ repository: Repository,
253
+ evaluator: EvaluationService,
254
+ ):
255
+ self._repository = repository
256
+ self._evaluator = evaluator
257
+
258
+ # Test code — inject mocks
259
+ service = AnalysisService(
260
+ repository=mock_repository,
261
+ evaluator=mock_evaluator,
262
+ )
263
+ ```
264
+
265
+ ---
266
+
267
+ ## P11 · Config Isolation
268
+
269
+ Centralized, environment-based configuration with startup validation:
270
+
271
+ ```python
272
+ @dataclass
273
+ class Config:
274
+ database_url: str
275
+ debug: bool = False
276
+ max_workers: int = 4
277
+
278
+ @classmethod
279
+ def from_env(cls) -> "Config":
280
+ return cls(
281
+ database_url=os.environ["DATABASE_URL"],
282
+ debug=os.environ.get("DEBUG", "0") == "1",
283
+ max_workers=int(os.environ.get("MAX_WORKERS", "4")),
284
+ )
285
+ ```
286
+
287
+ **Key rules:**
288
+ - All config read from environment at startup
289
+ - No scattered `os.environ` calls inside business logic
290
+ - Test config overrides controlled via fixtures
291
+
292
+ ---
293
+
294
+ ## Related Documents
295
+
296
+ | Document | Purpose |
297
+ |----------|---------|
298
+ | [../patterns.md](../patterns.md) | Pattern routing index |
299
+ | [refactoring-workflow.md](refactoring-workflow.md) | Safe structural changes |
300
+ | [testing-guide.md](testing-guide.md) | Verification strategy |
@@ -0,0 +1,114 @@
1
+ # Refactoring Workflow (R1)
2
+
3
+ Checklist and approach for moving files, changing imports, or restructuring modules.
4
+ Referenced from [../patterns.md](../patterns.md).
5
+
6
+ ---
7
+
8
+ ## Refactoring Checklist
9
+
10
+ ```
11
+ Phase 1: PLAN
12
+ ☐ Identify all files to move/rename/extract
13
+ ☐ Map old imports → new imports
14
+ ☐ Check for documentation references (README, docstrings, agent-loop specs)
15
+ ☐ Identify public interfaces that must remain stable
16
+
17
+ Phase 2: EXECUTE
18
+ ☐ Move files
19
+ ☐ Update imports in moved files (internal references)
20
+ ☐ Update all caller imports (use grep to find every reference)
21
+ ☐ Update documentation examples if they contain import paths
22
+ ☐ Update .agent-loop/patterns.md structure section if layout changed
23
+
24
+ Phase 3: VALIDATE (NON-NEGOTIABLE)
25
+ ☐ 1. Type check: run project type checker
26
+ ☐ 2. Run tests: execute test suite with fail-fast
27
+ ☐ 3. Check docs: grep for old import paths across all files
28
+ ☐ 4. Verify no orphaned imports (unused or broken)
29
+
30
+ Phase 4: DOCUMENT
31
+ ☐ Update README structure section
32
+ ☐ Update architecture docs if needed
33
+ ☐ Create report in docs/reports/ if the refactor is major
34
+ ```
35
+
36
+ ---
37
+
38
+ ## Example: Moving a Module File
39
+
40
+ ```bash
41
+ # 1. Move file
42
+ mv src/modules/old_location/processor.py src/modules/new_location/processor.py
43
+
44
+ # 2. Update imports inside the moved file (internal references)
45
+ # e.g., relative imports that changed due to new directory depth
46
+
47
+ # 3. Find ALL references to the old path
48
+ grep -r "from src.modules.old_location.processor" .
49
+ grep -r "import old_location.processor" .
50
+
51
+ # 4. Update each reference found:
52
+ # - tests/unit/test_processor.py
53
+ # - src/modules/new_location/tasks.py
54
+ # - docs/architecture.md (if it mentions import paths)
55
+
56
+ # 5. MANDATORY: Run validation
57
+ # Type check → Tests → Grep for leftover old paths
58
+ ```
59
+
60
+ **Why tests after docs?** Documentation often contains import examples. Tests verify imports actually work and catch typos in updated paths.
61
+
62
+ ---
63
+
64
+ ## Example: Extracting a Function to a New Module
65
+
66
+ ```bash
67
+ # 1. Create the new module
68
+ touch src/libs/parsing/helpers.py
69
+
70
+ # 2. Move the function definition to the new file
71
+ # Update its internal imports
72
+
73
+ # 3. In the original file, replace the function body with an import:
74
+ # from src.libs.parsing.helpers import parse_response
75
+
76
+ # 4. Find all OTHER callers of the original location
77
+ grep -r "from src.modules.analysis.utils import parse_response" .
78
+
79
+ # 5. Update all callers to use the new import path
80
+
81
+ # 6. VALIDATE: type check + tests + grep for old path
82
+ ```
83
+
84
+ ---
85
+
86
+ ## Guardrails
87
+
88
+ - **Prefer reversible steps** — move one file at a time, validate, then move next
89
+ - **Never combine unrelated refactors** — one logical change per commit
90
+ - **Do not hide breaking API changes** — if a public interface moved, update all consumers
91
+ - **Never skip Phase 3** — validation is non-negotiable, even for "simple" moves
92
+ - **Grep is your friend** — always search for the old path after moving; IDEs miss things
93
+
94
+ ---
95
+
96
+ ## Common Pitfalls
97
+
98
+ | Pitfall | Prevention |
99
+ |---------|------------|
100
+ | Circular imports after move | Map dependency graph before moving |
101
+ | Tests pass but type checker fails | Always run type checker first |
102
+ | Docs reference old paths | Grep docs/ and *.md files explicitly |
103
+ | Forgot to update __init__.py re-exports | Check all `__init__.py` files in affected packages |
104
+ | Moved too many files at once | Move one file → validate → repeat |
105
+
106
+ ---
107
+
108
+ ## Related Documents
109
+
110
+ | Document | Purpose |
111
+ |----------|---------|
112
+ | [../validate-env.md](../validate-env.md) | Stage 1 gates (type check, tests) |
113
+ | [../validate-n.md](../validate-n.md) | Stage 2 neighbor checks (boundary impact) |
114
+ | [testing-guide.md](testing-guide.md) | Regression test strategy |