CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl-labs/separation-of-concerns

Enforce strict three-layer architecture: thin HTTP routes, pure service logic with domain errors, isolated data access with dependency injection.

94

1.08x
Quality

93%

Does it follow best practices?

Impact

97%

1.08x

Average score across 5 eval scenarios

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

criteria.jsonevals/scenario-5/

{
  "context": "Tests whether the agent correctly splits validation between layers (input shape in routes, business rules in services), places all side effects in the service layer, uses domain errors mapped at route boundaries, creates a separate repository, and re-throws unexpected errors.",
  "type": "weighted_checklist",
  "checklist": [
    {
      "name": "Shape validation in route",
      "description": "The route handler performs input shape validation BEFORE calling the service — checks that required fields exist, email contains '@', amount is a number. These checks return 400 directly without calling the service.",
      "max_score": 10
    },
    {
      "name": "Business rules in service",
      "description": "Business rule validation (age >= 18, deposit range $1-$1M, duplicate email check) is in the service layer, NOT in the route handler",
      "max_score": 10
    },
    {
      "name": "No business logic in route",
      "description": "The route handler does NOT contain business rule checks (e.g., no age calculation, no duplicate email lookup, no deposit range logic)",
      "max_score": 8
    },
    {
      "name": "Side effects in service",
      "description": "All three side effects (welcome email, analytics event, compliance webhook) are triggered from within the service layer — not from the route handler",
      "max_score": 10
    },
    {
      "name": "No side effects in route",
      "description": "The route handler does NOT call email, analytics, or webhook functions directly — these are only in the service",
      "max_score": 8
    },
    {
      "name": "Service throws typed errors",
      "description": "The service throws typed domain error class instances (e.g., ValidationError, DuplicateEmailError, BusinessRuleError) — not generic Error with HTTP codes or status strings",
      "max_score": 10
    },
    {
      "name": "Route maps errors to HTTP",
      "description": "The route handler catches domain errors and maps each type to a specific HTTP status code (e.g., ValidationError -> 400, DuplicateEmailError -> 409)",
      "max_score": 8
    },
    {
      "name": "Route re-throws unexpected errors",
      "description": "The route handler re-throws (or passes to next()) errors that are NOT recognized domain errors, rather than swallowing them with a generic 500 response",
      "max_score": 8
    },
    {
      "name": "Separate repository module",
      "description": "A separate repository/store file exists for user data access — the service does NOT contain inline database queries or direct data store access",
      "max_score": 8
    },
    {
      "name": "No HTTP in service",
      "description": "The service file does NOT import or reference req, res, response, request, ctx, or any HTTP framework module",
      "max_score": 8
    },
    {
      "name": "Side effects notes accurate",
      "description": "side-effects-notes.md correctly identifies all three side effects as being in the service layer and explains why",
      "max_score": 6
    },
    {
      "name": "Route passes plain data",
      "description": "The route handler extracts fields from req.body and passes a plain object (not req/res) to the service",
      "max_score": 6
    }
  ]
}

evals

tile.json