| Server IP : 35.80.110.71 / Your IP : 216.73.216.221 Web Server : Apache/2.4.58 (Ubuntu) System : Linux ip-172-31-21-44 6.17.0-1019-aws #19~24.04.1-Ubuntu SMP Tue Jun 23 18:53:06 UTC 2026 x86_64 User : ubuntu ( 1000) PHP Version : 8.3.31 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /var/www/codex/releases/20260610213343/ |
Upload File : |
# CLAUDE.md
Project guidance for Claude Code when working in this repo.
## What this repo is
Codex — a portfolio intelligence dashboard. The codex of every project,
capability, and gap that Philip Rehberger has built. One monorepo, two halves:
1. **Laravel 13 API + Filament v4 admin** at the repo root — read-only JSON
API at `api.codex.philiprehberger.com`, Filament admin at `/admin`. MySQL
persistence. ~15 tables, ULID + slug throughout.
2. **Next.js 16 dashboard** under `web/` — public capability heatmap, gap
report, per-project pages, resume-bullet generator at
`codex.philiprehberger.com`. Server-rendered, fetches the API at
build/revalidate time over loopback (`http://127.0.0.1` + injected
`Host:` header).
The whole thing is dogfooded: Codex catalogues its own projects (including
itself). Treat every artifact as something a prospective client might read.
## Source plan and rationale
The plan is the source of truth. It absorbs two earlier revisions; read in
order to reconstruct rationale:
1. `~/projects/income-ops/.scratch/plans/project-intelligence-system-plan.md`
— v1 data model (projects + tag pivots + audit_log + learnings + metrics).
2. `~/projects/income-ops/.scratch/plans/project-intelligence-system-plan-v2.md`
— v2 adds the capabilities layer + `canonical_id` vocabulary moderator.
3. `~/projects/income-ops/.scratch/plans/project_intelligence_codex_portfolio.md`
— this build. Absorbs v1 + v2, adds rollout, names the product, formalises
merge/scope/cache rules + senior-review revisions (rounds 2 and 3).
## Conventions
- **No AI attribution.** No `Co-Authored-By`, no "generated by Claude", no
AI mentions in commits, code, comments, README, marketing copy. Anywhere.
- **ULIDs as primary keys; slugs as URL identifiers.** Every table uses
`CHAR(26)` ULIDs. Public-facing routes use the `slug` column. API
payloads emit `id` (ULID) + `slug` together for client correlation.
- **`utf8mb4_bin` collation on every ULID + slug column.** Default
`utf8mb4_0900_ai_ci` is case-insensitive and silently collides on
Crockford ULIDs and accented slugs.
- **MySQL 8 idioms only.** No `jsonb`, no `tsvector`, no partial unique
indexes (so "one primary per dimension" lives in app-layer + a
transactional action + a feature test). CHECK constraints (MySQL 8.0.16+)
are used for value-range enforcement.
- **RFC 7807 problem details** for API errors — `application/problem+json`.
- **API versioning**: `/api/v1/*` always. Unversioned `/api/*` returns 410.
- **Capability vocabulary is curated.** Merges are one-way, terminal-canonical,
required-reason, undoable via the audit log. Never delete a capability
with tagged projects — merge instead.
- **RedactedScope is the model-layer boundary** for `visibility=redacted`
rows. Strips `client_name` + `internal_notes`. Filament admin opts out.
- **No premature abstraction.** Three similar repositories beats a generic
base class. Bug fixes don't need surrounding cleanup. Trust the framework.
## Repo layout
```
/ Laravel 13 root — php artisan works from here
app/
Actions/ MergeCapability, SetPrimaryTag — transactional services
Console/Commands/ codex:assert-invariants, audit-slug-collisions,
export-portfolio, reset-2fa, reset-admin-password
Filament/ Resources, Pages, Widgets (Filament v4)
Models/ with RedactedScope, observers, SoftDeletes
Services/ Spam moderation, AssetSigner, CacheInvalidator
database/
migrations/ v1+v2 schema, every one with a working down()
factories/
seeders/ Idempotent, production-guarded (two layers)
fixtures/ portfolio-bundles/<slug>.md — CI-safe seed input
web/ Next.js 16 dashboard (separate deploy target)
infra/apache/ Canonical vhosts (mirror /etc/apache2/sites-available/)
infra/cron/ Scheduled jobs
scripts/deploy/ Atomic release-based deploy + preflight + recover
docs/ architecture, api-conventions, invariants, upgrades
```
## Voice and tone
Matches `~/projects/income-ops/CLAUDE.md`:
- Diagnosis before pitch.
- Concrete numbers over adjectives.
- Em-dashes, short declarative sentences, no hedging.
- No emoji. No exclamation points.
- "Production-shaped" not "production-grade." Honest framing sells.
## Deployment
- **Both halves build locally and rsync/atomic-release to the EC2 host**
(`54.190.150.0`). No CI-hosted builds, no Vercel.
- **Apache + Let's Encrypt** on `codex.philiprehberger.com` and
`api.codex.philiprehberger.com`. CAA records pin Let's Encrypt at the apex.
- **PM2** for the Next.js half (`codex-web`, `instances: 1` pinned because
the `/api/revalidate` rate-limit counter is per-process) and the queue
worker (`codex-queue` with `--max-time` + `--max-jobs` recycling).
- **MySQL `codex` schema** on the EC2 host. Credentials in
`/var/www/codex/shared/.env` (`0640 root:www-data`) on the server and
`.local/server-secrets.env` locally — both gitignored.
## Things to avoid
- Don't run `php artisan migrate:fresh` against production without the
`--first-deploy` flag — the seeder production guard is the safety net,
but the flag is the intent signal.
- Don't bypass the fetch wrapper at `web/src/lib/codex-api.ts`. Raw
`fetch()` in pages skips the loopback `Host:` header and silently hits
the wrong Apache vhost.
- Don't call `DB::raw` / `DB::select` / raw query builder outside
`database/migrations` and `app/Console/Commands`. A `phpstan-laravel`
rule forbids it — RedactedScope is the boundary, raw SQL bypasses it.
- Don't delete a capability. Merge into the canonical target via the
Filament action; the audit log records the reason and the unmerge path.
- Don't add a primary on `project_capabilities` / `project_technologies`
by writing the pivot directly. Use the `SetPrimaryTag` action — the
`saved` observer rolls back direct writes that violate the
one-primary-per-dimension invariant.