7 evolutionary tasks implemented: 1. PHP web development: php-developer agent + 6 skills (Laravel, Symfony, WordPress, security, testing, modular architecture) + 2 pipeline commands (/laravel, /wordpress) 2. Atomic task decomposition: 1 action = 1 task rule, task sizing guide, decomposition protocol for orchestrator, token budgets per complexity 3. Modular code rules: max 100 lines/file, max 30 lines/function, service/repository patterns, cross-module communication via events only 4. Gitea-centric workflow: mandatory issue creation before work, research with links, progress checkboxes, screenshots on test, git history as knowledge base 5. Fix: target project auto-detection — removed all hardcoded UniqueSoft/APAW from API calls, added get_target_repo() via git remote, GITEA_TARGET_REPO env override 6. Agent execution monitoring: agent-executions.jsonl logging, agent-stats.ts statistics script, required fields per invocation, Gitea comment includes duration/tokens 7. Token optimization: 1 action = 1 task principle, token budgets by task type, routing matrix, no scope creep, skip unnecessary pipeline steps
5.4 KiB
5.4 KiB
Modular Code Rules
CRITICAL: Never write giant monolithic files. Split code into modules, libraries, and microservice-ready components.
Problem
Agents write enormous single files that are hard to review, test, debug, and maintain. No clear boundaries between features.
Core Principles
- Maximum file size: 100 lines per file (excluding tests and migrations)
- Maximum function/method size: 30 lines
- Maximum class size: 5 public methods
- One responsibility per file: A file does ONE thing
Module Structure (Mandatory)
Every feature must be organized as an independent module:
{feature}/
├── Controllers/ # HTTP request handling (thin)
├── Services/ # Business logic (fat)
├── Repositories/ # Data access (abstracted)
├── Models/ # Data definitions
├── Routes/ # Route definitions
├── Events/ # Events this module emits
├── Listeners/ # Events this module handles
├── Jobs/ # Async work this module performs
├── Requests/ # Input validation (not in controller)
├── Resources/ # Output transformation (not raw model)
├── Exceptions/ # Module-specific exceptions
├── Tests/ # Module-specific tests
└── ModuleServiceProvider.php # Module registration
Service Layer Rules
// ❌ BAD: Business logic in controller
class ProductController
{
public function store(Request $request)
{
$product = Product::create($request->all());
Cache::forget('products');
event(new ProductCreated($product));
Mail::to($product->vendor)->send(new NewProduct($product));
return response()->json($product);
}
}
// ✅ GOOD: Business logic in service
class ProductController
{
public function __construct(private ProductService $service) {}
public function store(ProductStoreRequest $request): JsonResponse
{
$product = $this->service->create($request->validated());
return response()->json(new ProductResource($product), 201);
}
}
class ProductService
{
public function create(array $data): Product
{
$product = $this->repository->create($data);
$this->clearCache();
ProductCreated::dispatch($product);
$this->notifyVendor($product);
return $product;
}
}
Repository Pattern (Mandatory for Data Access)
// ❌ BAD: Query in controller or service
$products = Product::where('active', true)->paginate(20);
// ✅ GOOD: Query in repository
interface ProductRepositoryInterface
{
public function listActive(int $perPage = 20): LengthAwarePaginator;
}
class ProductRepository implements ProductRepositoryInterface
{
public function __construct(private Product $model) {}
public function listActive(int $perPage = 20): LengthAwarePaginator
{
return $this->model->query()
->where('is_active', true)
->orderBy('created_at', 'desc')
->paginate($perPage);
}
}
Cross-Module Communication
Modules MUST NOT import models or repositories from other modules.
❌ Product module imports Order model directly
❌ Order module calls ProductRepository directly
✅ Product module dispatches ProductCreated event
✅ Order module listens to ProductCreated event
✅ Module boundaries enforced via interfaces
Microservice Readiness
Every module must be extractable as an independent service:
- Own database migrations: Module manages its own tables
- Own routes: Module registers its own routes
- Own config: Module has its own configuration
- Own tests: Module tests run independently
- Interface contracts: Module exposes interfaces, not implementations
File Splitting Rules
When a file exceeds 100 lines:
Original: ProductController.php (250 lines)
↓ Split into:
ProductController.php # index, show (thin delegates)
ProductStoreController.php # store endpoint (thin delegates)
ProductUpdateController.php # update endpoint (thin delegates)
ProductService.php # business logic (called by all)
When a service exceeds 5 methods:
Original: ProductService.php (8 methods)
↓ Split into:
ProductCrudService.php # create, update, delete
ProductSearchService.php # list, search, filter
ProductPricingService.php # calculatePrice, applyDiscount
Language-Specific Module Patterns
Node.js
src/modules/product/
├── routes.js
├── controller.js
├── service.js
├── repository.js
├── model.js
├── validators.js
└── __tests__/
Go
internal/product/
├── handler.go
├── service.go
├── repository.go
├── model.go
└── handler_test.go
Flutter/Dart
lib/features/product/
├── data/
│ ├── repositories/
│ └── models/
├── domain/
│ ├── entities/
│ └── usecases/
└── presentation/
├── pages/
├── widgets/
└── providers/
Checklist
- Every file under 100 lines
- Every function under 30 lines
- Every class under 5 public methods
- Features organized as modules
- Service layer contains business logic
- Repository layer abstracts data access
- Controllers are thin (5-10 lines per method)
- Cross-module communication via events
- Each module testable independently
- Each module extractable to microservice