This project extends MetaGPT with Laravel-focused roles that generate a full API codebase for Volopa's Out-of-Pocket (OOP) Expenses feature. The real goal is broader: proving that MetaGPT can be scaled to produce code that integrates with an existing legacy platform — not greenfield, but brownfield. Volopa OOP is the first case study.
The key insight is YAML context injection. Instead of letting the LLM invent requirements, two structured YAML files feed every role with domain rules, platform constraints, database schemas, API contracts, and existing-system interfaces.
pip install -r requirements.txt
# Verify all 5 roles are importable
python -c "from industry.roles import LaravelProductManager, LaravelArchitect, LaravelProjectManager, LaravelEngineer, LaravelQaEngineer; print('All 5 roles imported successfully')"# Copy the example config
cp config/config2.example.yaml config/config2.yaml
# Edit config/config2.yaml and add your API key:
# llm:
# api_type: "openai"
# model: "gpt-4o"
# base_url: "https://api.openai.com/v1"
# api_key: "YOUR_API_KEY"python industry/run_volopa_oop_expenses.pyThe script cleans the workspace, resets git state for the output directory, clears
cached team storage, then runs 5 roles sequentially. Output lands in
workspace/volopa_oop_expenses/.
industry/
roles/
laravel_product_manager.py # Joshua - PRD from YAML requirements
laravel_architect.py # Danny - System design, ER, API contracts
laravel_project_manager.py # Manuel - Task breakdown, dependency ordering
laravel_engineer.py # Lucas - Laravel code generation
laravel_qa_engineer.py # Darius - PHPUnit test generation
__init__.py # Exports all 5 roles
actions/
laravel_write_prd.py # LaravelWritePRD action wired to agent LaravelProductManager
laravel_write_prd_an.py # ActionNode schema for LaravelWritePRD
laravel_design_api.py # LaravelWriteDesign action wired to agent LaravelArchitect and watched by LaravelProductManager
laravel_design_api_an.py # ActionNode schema for action LaravelWriteDesign
laravel_write_code.py # LaravelWriteCode action for agent LaravelEngineer
utils/
context_reader.py # ContextReader - shared YAML accessor
evidence_injector.py # Injects evidence into prompts
check_plan_dispatcher.py # Verification protocol logic
repo_verifier.py # Repository correctness checks
requirements/
project_context.yaml # Feature-specific: requirements, flows, interfaces, constraints
environment_context.yaml # Platform-wide: standards, dos/donts, existing tables/models/interfaces
SD-OOP_User_Management_System.pdf
SD-OOP_Pocket_expense.pdf
SD-OOP_Expense_single_data_capturing.pdf
run_volopa_oop_expenses.py # Entry point
dos_and_donts.pdf # Platform coding standards reference
Volopa - Proposed Architecture.pdf
All roles load context through ContextReader (industry/utils/context_reader.py),
which lazy-loads and caches YAML data. Roles call methods like get_dos_and_donts(),
get_database_tables("full"), get_flows(), etc. to inject structured text into
their system prompts.
Feature-scoped rules for OOP Expenses:
intent— objectives, business rationale, success criteriarequirements— REQ-001 through REQ-012, user journeysconstraints— file limits, validation rules, permission hierarchy, FX rulesdecisions— architectural decisions (DEC-001+), unresolved questionsflows— end-to-end flows (CSV upload, single capture, permission delegation)interfaces— database tables (new + existing), controllers, services, models, API routes, CSV column schema, response schemas, permission matrix
Platform-wide standards that apply to any Volopa projects and features:
intent— platform purpose, mental model (Client -> route -> controller -> FormRequest -> service -> model -> Resource -> JSON), design principlesconstraints— dos and donts (routing, validation, controller logic, resources, testing, auth), verification protocol, Laravel task conventionsdecisions— platform-level architectural decisionsinterfaces— existing database tables, models, middleware, endpoints, user roles, FX query contract
| Round | Role | Action | Input | Output |
|---|---|---|---|---|
| 1 | LaravelProductManager (Joshua) | PrepareDocuments + LaravelWritePRD | User idea + YAML context | docs/prd/volopa_oop_expenses.md |
| 2 | LaravelArchitect (Danny) | LaravelWriteDesign | PRD + YAML context | docs/system_design/volopa_oop_expenses.md |
| 3 | LaravelProjectManager (Manuel) | WriteTasks | System design + YAML context | docs/task/volopa_oop_expenses.json |
| 4 | LaravelEngineer (Lucas) | LaravelWriteCode | Design + tasks + YAML context | app/**/*.php, database/**/*.php, routes/api.php |
| 5 | LaravelQaEngineer (Darius) | WriteTest | All code + YAML context | tests/Feature/**/*Test.php |
Each role subscribes to the previous role's output message and adds YAML-derived context to its system prompt before acting.
workspace/volopa_oop_expenses/
.src_workspace # Prevents nested directory creation
docs/
requirement.txt # Original idea text
prd/
volopa_oop_expenses.md # PRD (from ProductManager)
system_design/
volopa_oop_expenses.md # System design (from Architect)
task/
volopa_oop_expenses.json # Task breakdown (from ProjectManager)
app/
Http/
Controllers/Api/V1/ # API controllers
Requests/ # Form request validation
Resources/ # API resources
Models/ # Eloquent models
Services/ # Business logic services
Policies/ # Authorization policies
database/
migrations/ # Laravel migrations
factories/ # Test factories
routes/
api.php # API route definitions
tests/
Feature/ # PHPUnit feature tests
import asyncio
from metagpt.config2 import config
from metagpt.context import Context, AttrDict
from metagpt.team import Team
from industry.roles import (
LaravelProductManager,
LaravelArchitect,
LaravelProjectManager,
LaravelEngineer,
LaravelQaEngineer,
)
async def main():
workspace_path = "workspace/volopa_oop_expenses"
config.update_via_cli(
project_path=workspace_path,
project_name="volopa_oop_expenses",
inc=False,
reqa_file="",
max_auto_summarize_code=0,
)
ctx = Context(
config=config,
kwargs=AttrDict(project_path=workspace_path),
)
company = Team(context=ctx, use_mgx=False)
company.hire([
LaravelProductManager(context=ctx),
LaravelArchitect(context=ctx),
LaravelProjectManager(context=ctx),
LaravelEngineer(context=ctx),
LaravelQaEngineer(context=ctx),
])
company.invest(investment=10.0)
await company.run(
n_round=5,
idea="Build the Volopa OOP Expenses API System using Laravel 10+ with PHP 8.2+.",
send_to="",
auto_archive=True,
)
asyncio.run(main())engineer.py:319 uses forward-slash path constants (docs/task) but Path.parent
on Windows produces backslashes. The custom LaravelEngineer overrides
_new_coding_context to catch the ValueError and return None (skip).
WriteCode.get_codes() includes the full source of every sibling file as context.
By file ~30+ this exceeds 200K tokens. LaravelEngineer monkey-patches get_codes
with _size_limited_get_codes() — full source up to 100K chars, then filenames-only.
If max_react_loop > 1, the Engineer's _think() reads rc.news[0] without
consuming it, so the same WriteTasks message triggers _new_code_actions() on
every iteration. Fix: max_react_loop=1 (already set in LaravelEngineer).
Custom objects stored as self.xxx on Role subclasses fail Pydantic serialization.
Keep them as local variables in __init__ and pass to methods as parameters.
Evidence text must be injected into design_doc.content, not task_doc.content.
The task document is parsed by WriteCode.get_codes() via json.loads() — appending
non-JSON text to it causes the error.
Run from the project root. The run script adds the project root to sys.path
automatically. If importing manually, ensure you are in MetaGPT-Volopa/.
Check config/config2.yaml exists and has a valid api_key under llm:.
| File | Description |
|---|---|
industry/dos_and_donts.pdf |
Platform coding standards (source for YAML context) |
industry/Volopa - Proposed Architecture.pdf |
Architecture reference |
industry/requirements/SD-OOP_User_Management_System.pdf |
User management spec (source PDF) |
industry/requirements/SD-OOP_Pocket_expense.pdf |
Pocket expense spec (source PDF) |
industry/requirements/SD-OOP_Expense_single_data_capturing.pdf |
Single expense spec (source PDF) |
industry/requirements/environment_context.yaml |
Platform-specific standards (source YAML) |
industry/requirements/project_context.yaml |
Feature specific requirements (source YAML) |