Remove from SKILL.md and all reference files: - 'Am I choosing between Legacy and v3 plan workflow?' decision tree - LangChain/LangGraph sections (write-code tree, testing tree, code-style README, testing README) - FakeListLLM / MemorySaver / TypedDict LangGraph references - Backwards-compat pre-v3.0.0 policy block (project-version-specific) - v3 ULID format and Backwards-compat-starts rows from Key Numbers table - v3 Plan Lifecycle vs Legacy table from code-style README - Master-tree branch pointing to the v3/legacy workflow tree Generalise across all reference files: - commits/README: pre-commit checklist uses 'task runner session (e.g. nox -s X)' - pull-requests/README: fix approval count 2->1 with self-approval permitted; remove 'neither approver may be original author' (project allows self-approval); generalise automated-checks table command column - testing/README: remove LangChain/LangGraph Testing section; generalise all bare nox commands with task-runner framing and language note at top - code-style/README: rewrite General Principles to language-agnostic tooling guidance; generalise Import Guidelines with Python/Java/TS examples; rename and generalise Type Safety section; remove entire LangChain/LangGraph Best Practices section; remove entire v3 Plan Lifecycle vs Legacy section - security/README: generalise bare nox -s security_scan reference - issue-tracking/README: generalise subtask examples (Behave/nox) ISSUES CLOSED: #0
Code Style and Best Practices
Language note: Code examples in this file use Python. Apply the equivalent idioms and tools for your language ecosystem.
General Principles
- Specification-First Development:
docs/specification.mdis authoritative. Code discrepancies → align code to spec (never adjust spec to match code). Architectural changes require ADR process first. - Modern Tooling: Use modern, idiomatic build tools and workflows for the project's language ecosystem. Avoid legacy approaches such as Makefiles or wrapper shell scripts. All tooling should be from the current ecosystem and used as designed — not wrapped in custom scripts.
- Prefer Existing Tooling: Don't add new dependencies when existing tools suffice. Keep the toolchain simple and consistent.
- Modular Design: Files under 500 lines. Break large files into focused, cohesive modules.
- Environment Safety: NEVER hardcode secrets or sensitive information.
- Test-First Development: Write tests before implementation.
- Clean Architecture: Separate concerns, clear layer boundaries.
- Documentation: Keep docs updated alongside code changes in same commit.
SOLID Principles (required — use liberally)
- SRP (Single Responsibility): Each class has one reason to change; one responsibility only.
- OCP (Open/Closed): Open for extension, closed for modification.
- LSP (Liskov Substitution): Subtypes substitutable for base types without altering correctness.
- ISP (Interface Segregation): Small, specific interfaces; don't force clients to depend on unused methods.
- DIP (Dependency Inversion): Depend on abstractions, not concretions; high-level modules don't depend on low-level.
Required Design Patterns (use liberally)
Creational
Factory, Abstract Factory, Builder, Prototype, Singleton (sparingly — prefer DI), Object Pool, Dependency Injection
Structural
Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy, Module
Behavioral
Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor, Null Object
Architectural
Repository, Unit of Work, Service Layer, MVC, CQRS, Event Sourcing, Specification
Import / Dependency Declaration Guidelines
- Declare all imports at the top of the file — never inside functions, methods, or blocks.
- Prefer specific imports over wildcard or whole-module imports:
- Python:
from module import ClassName(notimport moduleorimport *) - Java: explicit class imports (not
import java.util.*) - TypeScript: named imports (
import { Foo } from './foo')
- Python:
- No wildcard imports in any language.
- Exception: type-only imports used solely to avoid circular dependencies:
- Python:
if TYPE_CHECKING:block - TypeScript:
import type { Foo } from './foo'
- Python:
Error and Exception Handling
Argument Validation — MANDATORY in all public/protected methods
Always validate arguments FIRST before any other logic (Python example):
def process_data(self, data: list[str], threshold: int) -> None:
# Always validate arguments FIRST before any other logic
if data is None:
raise ValueError("data cannot be None")
if not data:
raise ValueError("data cannot be empty")
if not all(isinstance(item, str) for item in data):
raise TypeError("data must contain only strings")
if threshold < 0 or threshold > 100:
raise ValueError(f"threshold must be between 0 and 100, got {threshold}")
# ... actual logic here
Apply the same fail-fast pattern in any language: check value ranges, null/nil/None, types, empty strings, empty collections, and invalid object states at the entry point.
Exception Propagation Rules
- NEVER suppress errors — let exceptions propagate to top-level.
- NEVER catch just to log and re-raise (let them propagate naturally).
- NEVER use bare catch-all handlers without re-raising unless you have specific recovery logic.
- Only catch when you can meaningfully handle (retry, cleanup, add context).
- Raise exceptions rather than returning null/nil/None on error.
Fail-Fast Principles
- Check preconditions at function entry, not deep in logic.
- No silent failures (no returning null/default when error exists).
- Make failure conditions explicit.
Checks to Perform in Every Public/Protected Method
- Value Range: numeric values within acceptable bounds.
- Null Checks: reject null/nil/None where not expected.
- Type Verification: via static type system where possible; runtime checks otherwise.
- Empty Strings: reject where non-empty required.
- Empty Collections: check if must contain elements.
- Invalid States: verify object state valid for operation.
Type Safety
Use static typing pervasively whenever the language supports it. Run the type checker as part of the standard build pipeline — it must pass before work is considered done.
- Full annotations: every function signature, variable declaration, and return type.
- No suppression: never disable the type checker via config and never use inline
suppression comments:
- Python: no
# type: ignore - TypeScript: no
// @ts-ignoreor// @ts-nocheck - Java/Kotlin: no
@SuppressWarningsfor type issues
- Python: no
- Continuous enforcement: type checking runs on every commit via the task runner.
- Python example:
nox -s typecheck(Pyright) - TypeScript example:
npm run typecheck(tsc --noEmit) - Java example:
./gradlew compileJava
- Python example: