Working with AI

Claude Code

How the team uses Claude Code with the `winch` skill — the rule files Claude follows, the named agents we run, common workflows, and how to add a new rule.

Claude Code is the CLI the team uses to apply this handbook's rules at the point of writing or reviewing code. It runs against the backend repo, reads project-specific configuration from .claude/, and follows the same patterns this handbook documents — Actions, Services, DTOs, the API envelope, migrations without redundant indexes, and the rest.

This handbook and the .claude/skills/winch/ directory in winchsa/backend are two views of the same rules. The handbook is the human-readable explanation — narrative, examples, the reasoning behind a decision. The skill is the machine-readable version Claude follows when it writes or reviews code. When the two drift, the skill is the source of truth (Claude can't read this handbook at edit time), and this chapter explains how to keep them in sync.

The winch skill

The skill lives at winchsa/backend/.claude/skills/winch/. Its entry point is SKILL.md, which Claude auto-loads whenever a task touches src/Domain/* or src/Presentation/* — the two directories that define the DDD layout. Tasks against legacy app/ code or generic Laravel work do not trigger it.

SKILL.md does three things:

  1. Declares the activation triggers in its frontmatter description — Actions, Contracts, Services, DTOs, Filter data, Controllers, Form Requests, the API envelope, migrations, the ProjectFlavor/Pennant decision, admin resources, and project-specific anti-patterns.
  2. States the Consistency First rule: before applying any rule below, check what neighbouring files in the same domain already do. WINCH has ~40 domains and several presentation panels; established patterns vary by area.
  3. Provides the Quick Reference index — one bullet per rule file in rules/, so Claude loads only the 1–3 files relevant to the task at hand instead of the whole ~10,000-line bundle.

The skill exists because generic Laravel guidance — including Laravel's own laravel/boost skill that ships with the repo — does not know about our DDD layout, our DTO naming convention, our API envelope, or our migration defaults. The winch skill overrides boost on the topics in the precedence table at the top of CLAUDE.md; everything else falls back to boost.

Rule files

There are 24 rule files in winchsa/backend/.claude/skills/winch/rules/. Each one covers a single concern and is short enough to load on demand. Most rule files map to a handbook chapter — when you change one, check the other. A few of the newest rules don't have a chapter yet; those rows are marked below.

Rule fileWhat it coversHandbook chapter
actions.mdfinal class X { public static function handle(...) }, no constructor, DB::transaction for multi-statement writesActions
services.mdService-per-Contract pattern, must delegate to Actions, no direct Eloquent queriesServices and Contracts
dtos.mdCreate/Update/Filter{Entity}Data, public readonly snake_case properties, Arrayable traitDTOs
controllers.mdThin controllers, constructor-inject the Contract, build DTOs from requests, delegate everythingControllers
api.md/api/{version}/{resource} paths, the { status, data } envelope, REST method/status conventionsRESTful API
architecture.mdDDD tree (src/Domain/{Domain}/, src/Presentation/{Cpanel}/), boundary rules, where new files goDDD basics
models.mdThe Models/ folder split — Entities (casts, relationships, computed attributes), filter/global Scopes, Abilities, Pennant FeaturesModels
queries.mdwith() / withCount(), whereByType / whereLikeText macros, scopeFilterBy traits in Models/Scopes/Eloquent and queries
migrations.mdAnonymous-class migrations, no indexes by default, never custom-name FKs/indexesMigrations
anti-patterns.md24 numbered anti-patterns with / / severities — used as the shared vocabulary in reviewsAnti-patterns
code-style.mdOne-line method signatures, minimal PHPDoc, Pint handles the restCode style
security.mdForm Request validation, Abilities + Policies for authorization, no $guarded = [] on user-input modelsSecurity
git.mdConventional Commits, scope = domain or panel, squash-merge with Linear issue referencesGit and commits
review-output.mdThe exact markdown format the winch-code-reviewer agent emits — sections, severities, file:line citationsCode review
admin-resource.mdStep-by-step for a full admin CRUD page: FormRequests, controller (all 7 RESTful methods), routes, views, permissions, translations, sidebar menuSet up an admin CRUD
api-resource.mdStep-by-step for a CRUD JSON endpoint on an API panel: FormRequests, ResponseBuilder, Route::apiResource, API Resource, Contract wiringComplete CRUD API Resource Recipe
editable-tables.mdMulti-row input forms (add / duplicate-with-data / copy-column / delete), the cell-type cookbook, 12 named gotchas(chapter pending)
job-batching.mdBus::batch([...]) fan-out via the Core\JobBatch sub-domain — native progress/cancel, ownership in withOption(), no shadow stateJob batching
frontend-components.mdReusable CP Blade patterns for src/Presentation/Admin/Views/ — layouts, filter forms, tables, badges, action buttons, the cp::common.* reference(chapter pending)
platform-apps.mdEnd-to-end recipe for a third-party integration in src/Domain/Integration/{Vendor}/ — Connector, PlatformConfig, @platformApp, fake mode, failure hooksPlatform Apps
helpers.mdThe global functions in app/helpers.phpres(), flavor checks, formatters — and the bar for adding a new oneHelpers
refactoring-legacy.mdFive-step playbook for migrating app/-style code into the DDD layoutRefactoring legacy
feature-flags.mdThe ProjectFlavor enum vs Pennant decision tree — ProjectFlavor for compile-time / sub-feature gating, Pennant for runtime per-subject flagsFeature flags
state-machine-and-abilities.mdCustom state machine (transitions, conditions, soft transitions, TransitionResultData) and per-instance ability classes; ability vs PolicyState machine

If you change a rule file, change the matching handbook chapter in the same PR. If you change a chapter, change the matching rule file. They drift fast otherwise.

Agents

Two named agents live in winchsa/backend/.claude/agents/. Both are markdown files with frontmatter — Claude picks them up automatically when their name is referenced in a prompt. Agents work with the winch skill: they cite the same rule files, use the same severity vocabulary, and never replace the skill's rules.

winch-code-reviewer

Reviews a diff against the project's DDD and API standards. It loads the rule files (architecture.md, api.md, git.md, anti-patterns.md) and reports findings in the format defined by rules/review-output.md: a markdown block with an overall assessment (✅ / ⚠️ / ❌), grouped sections (Architecture, Naming, API, Quality, Security), file:line citations for every finding, and a severity summary at the bottom. Architectural violations rank above style nits; if a finding matches a numbered anti-pattern, the agent cites it as see rules/anti-patterns.md #6.

Run it before opening a PR — it catches the same things the reviewer would, faster.

laravel-simplifier

Refines recently modified PHP for clarity without changing behaviour. It looks at the working tree (or whatever scope you give it) and applies the project's code-style rules — explicit return types, descriptive names, no nested ternaries, no useless comments. It preserves all functionality; if it can't simplify without changing behaviour, it leaves the code alone.

Use it after a feature lands, before you ship — never the other way around. Simplifying first hides the original intent.

Invocation patterns

The team uses these phrasings (from readme.md):

Review recent change from the last commit and apply the laravel-simplifier agent
Review recent change from the last commit and apply the winch-code-reviewer agent
Review recent change from the last commit and apply the winch-code-reviewer, laravel-simplifier agent
Review recent change from (.....) and apply the (agent-name) agent

You can substitute any scope for "the last commit" — a file path, a branch, a Linear issue ID — and Claude will pick it up. Running both agents in one prompt is common: review first, then simplify the things the review didn't flag.

Common workflows

Reviewing a PR you're about to open

Review the diff against main and apply the winch-code-reviewer agent

This runs against your branch's full diff, not just the last commit. The output goes straight into the PR description.

Scaffolding a new domain

Scaffold a Quotation domain — Actions for CRUD, a QuotationService with a QuotationContract,
and a QuotationServiceProvider that binds the Contract. Follow the patterns in src/Domain/Order.

Claude will load actions.md, services.md, dtos.md, and architecture.md, clone the shape from a neighbouring domain, and respect the precedence rules in CLAUDE.md. See Actions, Services, and Contracts for the patterns it follows.

Refactoring a legacy module

This controller in app/Http/Controllers/Admin/InvoiceController.php still has business logic
in it. Migrate it to the DDD layout following rules/refactoring-legacy.md.

Claude follows the five-step playbook. See Refactoring legacy for the human walkthrough.

Writing a new API endpoint

Add an /api/v1/owner/users CRUD on the Owner panel. Use the api-resource rule.

Claude reads rules/api-resource.md and applies the ResponseBuilder envelope, 201/204 status codes, Route::apiResource, and Contract wiring. See Complete CRUD API Resource Recipe.

Writing a new admin CRUD

Add an admin CRUD for Quotations under the Cpanel panel. Use the admin-resource rule.

Claude reads rules/admin-resource.md and scaffolds the seven RESTful controller methods, FormRequests, routes, Blade views, permissions, translations, and the sidebar menu entry. See Set up an admin CRUD.

Adding a new rule

  1. Decide if it's new or an extension. If the concern fits an existing rule file, edit that file. Don't create a new rule for "another note about Actions" — actions.md is the place. Create a new file only when no existing rule covers the topic.
  2. Match the existing style. Read one of the shorter rules first (security.md or review-output.md are good models). Keep it terse and prescriptive. Use ✅ / ❌ pairs only when the wrong shape is something Claude would otherwise pick. No marketing language, no long preambles.
  3. Update the Quick Reference in SKILL.md. Add a numbered bullet under "Quick Reference" pointing at the new file with a one-line description. The bullet is what tells Claude when to load your rule.
  4. Write or update the matching handbook chapter. If the rule introduces a pattern humans need to understand — not just a Claude instruction — add a chapter under content/1.backend/3.guide/ and link it from the table in this chapter. Both the human and machine versions live or die together.
  5. Open one PR with both changes. The rule file and the handbook chapter belong in the same PR against winchsa/backend and handbook respectively. Reviewers should see the change in both places.

Keep each rule file under ~200 lines so Claude can load it without burning context. If you find yourself writing 300+ lines, split it: one file per sub-concern (the way api.md and api-resource.md are split — one is the standard, the other is the recipe).

When rules conflict

CLAUDE.md is the tiebreaker. It carries a precedence table at the top listing every topic where project rules in .claude/skills/winch/ win over the generic laravel-best-practices skill — comments and PHPDoc, the static Action pattern, Eloquent Model location, Controller location, Services-must-delegate-to-Actions, DTO snake_case properties, and the migration defaults. For anything not in that table, boost rules apply (DI for non-domain classes, query patterns, mass-assignment protection, file generators for non-DDD files).

The short version: project rules in .claude/skills/winch/rules/ override the generic laravel-best-practices skill on listed topics; everything else follows boost. When still in doubt, follow what neighbouring files in the same domain already do — the "Consistency First" rule at the top of SKILL.md outranks any default in this handbook, because the codebase is the ground truth.