agentleFS
redhat-community-ai-toolsredhat-community-ai-tools/UnifAI/.cursor/rules/engineering-standards.md

UnifAI / rules

Cursor rule45 starsChanged 4 months agoApache-2.0View on GitHub

Universal engineering standards — SOLID, hexagonal architecture, Pydantic, enums, types, imports, naming, error handling, and Python safety patterns. Non-negotiable for all code.

  • Reads credentials
  • Where files are headingNew Cursor rule files have more rules about planning and tracking tasks, and fewer about code style.

    Of the Cursor rule files we read, the 4,183 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.

  • In this repository2 other files it gives its agents.

The file

We split this engineering-standards.md into the rules it gives an agent

9 rules. About 0 are common, 5 are distinctive, and 4 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈5

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 4

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    After decomposition: each focused service owns one cluster.

    line 28
  2. Distinctive:

    If a domain class does I/O, split it: pure domain class + adapter/loader.

    line 43
  3. Very specific:

    If a service contains from project.adapters.xyz import ConcreteClass, that is a DIP violation.

    line 63
  4. Distinctive:

    Import direction is STRICTLY inward.

    line 73
  5. Distinctive:

    Every adapter class must implement exactly one Port (ABC).

    line 77
  6. Very specific:

    NO inline imports (single exception: lazy imports in the composition root — bootstrap/ — for conditional adapter wiring)

    line 138
  7. Very specific:

    NO wildcard imports (from x import *)

    line 139
  8. Distinctive:

    NO circular imports (refactor if needed)

    line 140
  9. Very specific:

    Every open() must be inside with or contextlib.ExitStack

    line 221

≈ “Common” is an estimate: at least 1 in 5 of the 6,668 Cursor rule files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
engineering-standards.md

Engineering Standards

Non-negotiable rules for ALL code in this repository. Violations must be fixed before merge.


1. SOLID Principles

PrincipleRuleViolation Sign
SRPOne class = one reason to changeClass described with "and"
OCPExtend via new classes, not modifying existingAdding if/elif chains for new types
LSPSubtypes substitutable for baseAdapter breaks port contract
ISPSmall focused interfacesImplementors forced to stub methods
DIPDepend on abstractions, not concretionsImporting adapter from domain

Split an application service when:

  • 8+ public methods clustering into 3+ independent groups
  • Method cluster A never calls methods in cluster B
  • Generic name ("Orchestrator", "Manager", "Handler")

After decomposition: each focused service owns one cluster. A thin facade may remain for backward compatibility but must contain zero business logic.


2. Hexagonal Architecture

Layer Placement Decision Tree

QuestionYesNo
Does it call subprocess, open(), shutil.which(), socket, requests, or any filesystem/network/OS operation?Adapternext
Does it orchestrate multiple ports/domain objects to fulfill a use case?Application Servicenext
Does it define pure data, business rules, or computations with zero I/O?Domainnext
Is it an abstract interface (ABC) defining a contract?PortReconsider design

If a domain class does I/O, split it: pure domain class + adapter/loader.

Import Direction (Dependency Inversion)

domain/       may import: stdlib, other domain modules
              must NOT import: ports/, adapters/, services/, frameworks

ports/        may import: stdlib, domain/
              must NOT import: adapters/, services/, frameworks

services/     may import: stdlib, domain/, ports/
              must NOT import: adapters/ (concrete classes)

adapters/     may import: stdlib, domain/, ports/, frameworks, external libs
              must NOT import: services/ (except composition root)

Composition root (cli.py, app factory, main()) is the ONLY place where concrete adapters are instantiated and injected.

If a service contains from project.adapters.xyz import ConcreteClass, that is a DIP violation.

Import Matrix (rings)

RingCan ImportCannot Import
Domain (lib/mas/)Own domain code onlyAdapters, Bootstrap
Adapters (adapters/)Domain (to implement ports)Bootstrap, other tech dirs
Bootstrap (bootstrap/)Domain + Adapters— (top level)

Import direction is STRICTLY inward. No exceptions.

Port-per-Adapter Rule

Every adapter class must implement exactly one Port (ABC).

  • Adapter without a corresponding Port = missing abstraction
  • Service directly instantiating or importing an adapter = DIP violation

Domain-Specific Overrides

Each service's skill files contain an "Established Patterns" section that documents pre-approved deviations. When a component's skill explicitly lists a pattern as established practice, the domain skill takes precedence for that component. Reviewers MUST check the relevant domain's Established Patterns table before flagging a violation.


3. Pydantic Models — Always

Use ForRule
All structured data crossing boundariesPydantic BaseModel
Immutable value objectsmodel_config = {"frozen": True}
Mutable defaultsField(default_factory=list)
ConstraintsField(ge=0), @field_validator
NeverBare dict for structured data, Any for typed fields

4. Enums — No Magic Strings

RuleExample
Every fixed value set = EnumSessionStatus, ResourceCategory
Use str, Enum for JSON serializationclass Status(str, Enum): ...
Compare against enum, never stringstatus == SessionStatus.RUNNING not status == "running"
New values = explicit enum additionUpdate all consumers

Violations:

  • if status == "healthy" should be if status is ServiceStatus.HEALTHY
  • type: str field used for discrimination should be an Enum field

5. Type Hints — Mandatory

RuleApplies To
Full annotations on all signaturesFunctions, methods, class attributes
Return type always declaredUse -> None for void
Optional[X] explicitNever leave nullable implicit
Use TYPE_CHECKING for type-only importsAvoids circular imports

6. Imports — Top of File Only

PositionContents
FirstStandard library (import logging, from typing import ...)
SecondThird-party (from pydantic import ..., from flask import ...)
ThirdLocal domain (from mas.core.identity import Identity)
FourthLocal adapters (only in adapter/bootstrap code)

Rules:

  • NO inline imports (single exception: lazy imports in the composition root — bootstrap/ — for conditional adapter wiring)
  • NO wildcard imports (from x import *)
  • NO circular imports (refactor if needed)

7. No Hardcoded Values

Instead of...Use...
Magic numbers in logicNamed constants (MAX_FILES = 10)
Connection strings in codeConfig parameters
Feature behavior in codeConfig flags
Raw strings for statusesEnums

8. Error Handling

RuleDetail
Specific domain exceptionsSessionNotFoundError, not generic Exception
Never catch bare Exception in business logicOnly in outermost adapter layer
Adapter wraps tech errorspymongo.errors.X → domain exception
Always include contextraise Error(f"Cannot upload {n} files, max is {max}")

Per-Layer Error Contract

LayerMay raiseMay catchMust NOT do
DomainValueError, KeyError, custom domain exceptionsNothing (let errors propagate)SystemExit, print(), I/O
ServicesRuntimeError, domain exceptionsDomain exceptions (to add context)SystemExit, print() to stderr
Adapters (CLI)SystemExit (at entry point only)RuntimeError, KeyError, FileNotFoundErrorSwallowing exceptions silently
Adapters (API)HTTP status codes via frameworkService/domain exceptionsSystemExit

A single error boundary at the CLI entry point (main()) is preferred over scattered try/except.


9. Naming

ThingConventionExample
ClassesPascalCaseSessionService
Functions/methodssnake_caseupload_batch
ConstantsUPPER_SNAKEMAX_FILES_PER_UPLOAD
PrivateLeading __validate_files
Files/dirssnake_casesession_repository.py
EnumsPascalCase + UPPER valuesSessionStatus.RUNNING

10. Documentation

WhenWhat
Every public classOne-line docstring minimum
Non-obvious behaviorExplain WHY, not WHAT
Constraints/invariantsDocument in docstring or comment
NeverNarrate obvious code, explain what code does line-by-line

11. Python Safety Patterns

Shell commands

  • subprocess.run/call/Popen must use list form, never a single string
  • User-derived values → shlex.quote() each argument
  • Parsing command strings → shlex.split(), never str.split()
  • shell=True → flag as MAJOR unless explicitly justified

File safety

  • Sensitive files (secrets, tokens, .env) → create with os.open(path, flags, 0o600)
  • # noqa comments → investigate root cause, don't suppress

Pattern matching

  • Substring matching (if name in content) for service/process identification → use re.search(rf"\b{re.escape(name)}\b", content)

Resource management

  • Every open() must be inside with or contextlib.ExitStack
  • Large files → stream or chunk, never .read_text() into memory

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.