Claude-skill-registry layer-helpers
Use when creating or modifying code in nomarr/helpers/. Helpers are pure utilities with NO nomarr.* imports. Only stdlib and third-party libraries allowed.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/layer-helpers" ~/.claude/skills/majiayu000-claude-skill-registry-layer-helpers && rm -rf "$T"
skills/data/layer-helpers/SKILL.mdHelpers Layer
Purpose: Provide pure utilities and shared data types used across all layers.
Helpers are stateless utilities that:
- Perform generic operations (file handling, time, SQL fragments)
- Define DTOs (data transfer objects)
- Define exceptions
- Have no knowledge of Nomarr's domain
Directory Structure
helpers/ ├── files_helper.py # Path utilities, file discovery ├── file_validation_helper.py # Path validation ├── logging_helper.py # Logging utilities ├── time_helper.py # Time utilities (now_ms, etc.) ├── exceptions.py # Domain exceptions ├── dataclasses.py # Shared dataclasses └── dto/ # Data transfer objects ├── processing_dto.py # FileDict, ProcessingResult, etc. ├── library_dto.py # LibraryDict, etc. ├── analytics_dto.py # AnalyticsResult, etc. └── __init__.py
Import Rules
Helpers may ONLY import:
# ✅ Allowed import os import pathlib from datetime import datetime from typing import TypedDict import yaml # Third-party OK
DTO cross-imports are allowed (one-way only):
# ✅ Allowed - sibling DTO imports within helpers/dto/ from nomarr.helpers.dto.tags_dto import Tags # OK in processing_dto.py from nomarr.helpers.dto.path_dto import LibraryPath # OK in ml_dto.py
The dependency direction must be acyclic. If
A imports B, then B must not import A.
Helpers must NEVER import from higher layers:
# ❌ NEVER import any higher-layer nomarr.* modules from nomarr.persistence import Database from nomarr.services import ConfigService from nomarr.workflows import ... from nomarr.components import ... from nomarr.interfaces import ...
This is a hard rule. Helpers are the foundation—they cannot depend on anything above them.
No Config at Import Time
Helpers must never read config files or environment variables at import time:
# ❌ Wrong - reads at import import os DEFAULT_PATH = os.environ.get("NOMARR_PATH", "/data") # NO! # ✅ Correct - function parameter def get_data_path(config_path: str | None = None) -> str: ...
DTO Pattern
DTOs are typed dictionaries or dataclasses for cross-layer data:
# helpers/dto/processing_dto.py from typing import TypedDict class FileDict(TypedDict): _key: str _id: str file_path: str library_key: str status: str discovered_at: int processed_at: int | None
DTO Placement Rules
- Cross-layer DTOs (used by multiple layers):
helpers/dto/<domain>.py - Single-service DTOs (used only in one service): Define in service file
Pure Utility Functions
Helpers should be pure (no side effects, deterministic output):
# ✅ Good - pure function def normalize_path(path: str) -> str: return str(pathlib.Path(path).resolve()) # ✅ Good - utility with explicit inputs def now_ms() -> int: return int(datetime.now().timestamp() * 1000) # ❌ Bad - hidden state/side effects _cached_time: int | None = None def get_time() -> int: global _cached_time if _cached_time is None: _cached_time = int(datetime.now().timestamp() * 1000) return _cached_time
Exceptions
Domain exceptions live in
helpers/exceptions.py:
# helpers/exceptions.py class NomarrError(Exception): """Base exception for all Nomarr errors.""" class LibraryNotFoundError(NomarrError): """Raised when a library is not found.""" class ConfigurationError(NomarrError): """Raised when configuration is invalid."""
What Belongs Here vs Elsewhere
| If it... | Put it in... |
|---|---|
| Does file path manipulation | |
| Formats time/timestamps | |
| Is a cross-layer DTO | |
| Is a domain exception | |
| Does tag parsing logic | (not helper) |
| Does DB queries | (not helper) |
| Has any business logic | (not helper) |
| Constructs/validates library paths | (not helper) |
Library Path Restriction
Helpers MUST NOT construct, resolve, or validate library paths. All library path construction and validation occurs exclusively in
path_comp via LibraryPath factories.
- Helpers define
DTO inLibraryPathhelpers/dto/path_dto.py - Helpers MUST NOT call
orbuild_library_path_from_input()build_library_path_from_db() - Any helper needing a path must receive a validated
DTO as a parameterLibraryPath - Raw strings are never acceptable substitutes for
once constructedLibraryPath
Validation Checklist
Before committing helper code, verify:
- Does this file import from any
module? → Violation (hard rule)nomarr.* - Does this file read config/env at import time? → Violation
- Does this contain business logic? → Move to components
- Does this construct or validate library paths? → Violation (use path_comp)
- Is this DTO used across layers? → Put in
helpers/dto/ - Are functions pure (no hidden state)? → Preferred
Layer Scripts
This skill includes validation scripts in
.github/skills/layer-helpers/scripts/:
lint.py
lint.pyRuns all linters on the helpers layer:
python .github/skills/layer-helpers/scripts/lint.py
Executes: ruff, mypy, vulture, bandit, radon, lint-imports
check_naming.py
check_naming.pyValidates helpers naming conventions:
python .github/skills/layer-helpers/scripts/check_naming.py
Checks:
- Files must end in
or_helper.py
(exceptions:_dto.py
,exceptions.py
)dataclasses.py - No stateful classes (classes with
storing state)__init__ - No
imports (hard rule)nomarr.*