feat(workspace): add PHP CODEX.md template + language-aware prep

Workspace prep now detects repo language and copies the right CODEX.md:
- Go repos get CODEX.md.tmpl (existing — Core primitives, banned imports)
- PHP repos get CODEX-PHP.md.tmpl (CorePHP patterns, lifecycle events,
  Actions, BelongsToWorkspace, Flux Pro, FA Pro, UK English)

Added lib.WorkspaceFile() helper for reading individual template files.

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Snider 2026-03-27 06:50:54 +00:00
parent f7397324b1
commit 95c104f7b3
3 changed files with 183 additions and 0 deletions

View file

@ -466,6 +466,16 @@ func (s *PrepSubsystem) prepWorkspace(ctx context.Context, _ *mcp.CallToolReques
out.Branch = s.gitOutput(ctx, repoDir, "rev-parse", "--abbrev-ref", "HEAD")
}
// Overwrite CODEX.md with language-specific version if needed.
// The default template is Go-focused. PHP repos get CODEX-PHP.md instead.
lang := detectLanguage(repoPath)
if lang == "php" {
if r := lib.WorkspaceFile("default", "CODEX-PHP.md.tmpl"); r.OK {
codexPath := core.JoinPath(wsDir, "CODEX.md")
fs.Write(codexPath, r.Value.(string))
}
}
// Clone workspace dependencies — Core modules needed to build the repo.
// Reads go.mod, finds dappco.re/go/core/* imports, clones from Forge,
// and updates go.work so the agent can build inside the workspace.

View file

@ -238,6 +238,20 @@ func ExtractWorkspace(tmplName, targetDir string, data *WorkspaceData) error {
return nil
}
// WorkspaceFile reads a single file from a workspace template.
// Returns the file content as a string.
//
// r := lib.WorkspaceFile("default", "CODEX-PHP.md.tmpl")
// if r.OK { content := r.Value.(string) }
func WorkspaceFile(tmplName, filename string) core.Result {
r := workspaceFS.Sub(tmplName)
if !r.OK {
return r
}
embed := r.Value.(*core.Embed)
return embed.ReadString(filename)
}
// --- List Functions ---
// ListPrompts returns available system prompt slugs.

View file

@ -0,0 +1,159 @@
# CODEX.md — PHP / CorePHP
Instructions for Codex when working with PHP code in this workspace.
## CorePHP Framework
This project uses CorePHP (`core/php`) as its foundation. CorePHP is a Laravel package that provides:
- Event-driven module loading (modules only load when their events fire)
- Multi-tenant isolation via `BelongsToWorkspace` trait
- Actions pattern for single-purpose business logic
- Lifecycle events for route/panel/command registration
## Architecture
### Module Pattern
```php
// app/Mod/{Name}/Boot.php
class Boot extends ServiceProvider
{
public static array $listens = [
WebRoutesRegistering::class => 'onWebRoutes',
AdminPanelBooting::class => 'onAdmin',
];
}
```
### Website Pattern
```php
// app/Website/{Name}/Boot.php
class Boot extends ServiceProvider
{
public static array $domains = [
'/^api\.lthn\.(ai|test|sh)$/',
];
public static array $listens = [
DomainResolving::class => 'onDomain',
WebRoutesRegistering::class => 'onWebRoutes',
ApiRoutesRegistering::class => 'onApiRoutes',
];
}
```
### Lifecycle Events
| Event | Purpose |
|-------|---------|
| `DomainResolving` | Match domain → register website module |
| `WebRoutesRegistering` | Public web routes (sessions, CSRF, Vite) |
| `ApiRoutesRegistering` | Stateless API routes |
| `AdminPanelBooting` | Admin panel resources |
| `ClientRoutesRegistering` | Authenticated SaaS client routes |
| `ConsoleBooting` | Artisan commands |
| `McpToolsRegistering` | MCP tool handlers |
### Actions Pattern
```php
use Core\Actions\Action;
class CreateOrder
{
use Action;
public function handle(User $user, array $data): Order
{
return Order::create($data);
}
}
// Usage: CreateOrder::run($user, $validated);
```
### Multi-Tenant Isolation
```php
use Core\Mod\Tenant\Concerns\BelongsToWorkspace;
class Memory extends Model
{
use BelongsToWorkspace;
// Auto-scopes queries to current workspace
// Auto-assigns workspace_id on create
}
```
## Mandatory Patterns
### Strict Types — every PHP file
```php
<?php
declare(strict_types=1);
```
### Type Hints — all parameters and return types
```php
// WRONG
function process($data) { }
// CORRECT
function process(array $data): JsonResponse { }
```
### UK English in all comments and strings
```
colour not color
organisation not organization
initialise not initialize
serialise not serialize
centre not center
```
### Testing — PHPUnit with Orchestra Testbench
```php
class BrainServiceTest extends TestCase
{
public function test_remember_stores_memory(): void { }
public function test_remember_validates_input(): void { }
public function test_recall_returns_ranked_results(): void { }
public function test_recall_filters_by_org(): void { }
}
```
### Formatting — Laravel Pint (PSR-12)
```bash
./vendor/bin/pint --dirty # Format changed files only
```
## UI
- **Flux Pro** — component library (NOT vanilla Livewire/Alpine)
- **Font Awesome Pro** — icons (NOT Heroicons)
- **Livewire 3** — server-driven components
- **Alpine.js** — client-side interactivity
## What NOT to do
- Don't use American English spellings
- Don't use Heroicons (use Font Awesome Pro)
- Don't use vanilla Blade components where Flux Pro has an equivalent
- Don't create loose route files — routes belong in Boot.php via lifecycle events
- Don't bypass BelongsToWorkspace — all tenant data must be scoped
- Don't use raw DB queries where Eloquent works
## Build & Test
```bash
composer install
composer test
./vendor/bin/pint --dirty
php artisan test --filter=SpecificTest
```