3 · Project constitution
Objective — write the constitution: the small set of files (AGENTS.md, CLAUDE.md, CLAUDE.local.md) that tells every AI tool what this project is, how to behave, and what never to touch — loaded automatically at the start of every session so the agent never starts blind.
Background
Section titled “Background”A CodeCanyon app is unfamiliar vendor code. The constitution is three files, each with a distinct reader and a distinct fate in git:
flowchart LR A["AGENTS.md<br/>cross-tool constitution<br/>(committed)"] C["CLAUDE.md<br/>thin pointer → AGENTS.md<br/>(committed)"] L["CLAUDE.local.md<br/>personal env + secrets refs<br/>(gitignored)"] C -->|points to| A A -.read by.-> Tools["Claude Code · Cursor · Codex · Gemini · others"] L -.personal overlay.-> Claude["the Claude Code session"]| File | Committed? | Role |
|---|---|---|
AGENTS.md | ✅ | The canonical contract. Tool-agnostic. Tech stack, conventions, safety rules, the read-set. Every modern agent reads it. |
CLAUDE.md | ✅ | A thin pointer that says “read AGENTS.md first,” plus any Claude-specific notes. Keeps a single source of truth. |
CLAUDE.local.md | ❌ gitignored | Personal: local URL, server IPs, zone IDs, credential-store references. Differs per developer; never in git. |
Alternative · kit + seed.sh
Section titled “Alternative · kit + seed.sh”Use this instead of hand-authoring constitution through Cursor wiring file-by-file (Claude config, Rules & skills, and Cursor & other IDEs are included in the same drop).
-
Fetch + install in one command — from your project root (the app folder with the vendor tree from Create the project). No repo clone, no manual download:
Terminal window curl -fsSL https://library.zajapps.com/kits/codecanyon-ai-system.tgz | tar -xzbash seed.sh # renames dot-* → real dotfiles; materializes project_context.mdThe tarball unpacks its contents straight into the current directory, then
seed.shwires them up. Prefer to inspect first? Browse the file map or MANIFEST, and readseed.shafter extracting — before you run it. -
Fill placeholders — run the onboarding interview below (or answer the kit’s printed prompts) so
AGENTS.mdandproject_context.mdmatch this app. -
Pick a permission mode —
./.claude/claude-mode/bin/set-claude-mode.sh medium(see Claude config). -
Restart your agent session, then continue to Verify & gate.
What seed.sh lays down in one pass: AGENTS.md, CLAUDE.md, CLAUDE.local.md.example, the full .claude/ tree (settings, rules, skills, hooks, mode switcher), .mcp.json, and .cursor/ mirrors. The individual pages remain the reference if you need to change one subtree later.
1. Run the onboarding interview
Section titled “1. Run the onboarding interview”Before writing files, gather context. The agent should inspect both the stack and the app surface first, then pre-fill evidence-based answers for the user to confirm or correct.
-
Auto-detect the stack from the repo.
Terminal window head -30 composer.json # Laravel version, PHP req, key packageshead -20 package.json # frontend deps (Livewire / Alpine / Vue / Tailwind)ls routes/ # route files → surface areals -d app/Http/Controllers/*/ 2>/dev/null # controller organizationls database/migrations/ | wc -l # migration count → deploy timeout risk# Expected: Laravel version, frontend deps, route/controller layout, migration count- ✅ The auto-detected Laravel version, frontend, CSS, auth, and DB are captured for the interview.
-
Inspect what the app actually does before asking business questions. Generic interview choices miss the mark on CodeCanyon apps. Read the models, migrations, modules, and route groups so the agent can pre-fill the app’s feature surface and monetization model from evidence.
Terminal window find app/Models -maxdepth 1 -type f -name '*.php' 2>/dev/null | sed 's#app/Models/##' | sort | head -80find Modules app/Classes -maxdepth 2 -type d 2>/dev/null | head -80rg -n "Plan|Subscription|Order|Coupon|Module|Trial|Payment|Invoice|Warehouse|Helpdesk|Chat" \app database routes Modules 2>/dev/null | head -120# Expected: feature areas, billing/plan objects, module system, and route groups are visible enough to pre-fill the interview- ✅ The agent can say, “I see subscription plans / orders / coupons / modules / feature areas X-Y-Z; confirm or correct.”
- ✅ Human-only questions stay human-owned, but they are grounded in the actual codebase instead of hardcoded generic options.
-
Answer the human-only questions and record them — they populate the files below.
- App identity: What’s the app called (its name)? What type is it — SaaS, marketplace, directory, or other? Who’s the author/owner (your name + email)? These feed the
<AppName>/ “what this is” lines inAGENTS.mdplus yourgit configand license/branding. - Business: What does the app do? Who’s the customer? What’s the monetization? Start from the agent’s evidence summary (
Plan,Subscription,Order,Coupon, modules, feature routes), then correct what the code cannot know. - Vendor: Which CodeCanyon item / vendor family (Froiden, LiquidThemes, WorkDo, InfyOm, IqonicDesign, other)? This predicts gotchas later.
- Stack confirmation: Confirm the auto-detected Laravel version, frontend, CSS, auth (Sanctum/Passport), and DB.
- Infra: Where does it deploy? Production domain(s)? Staging URL? Hosting provider + plan?
- Integrations: Stripe (test + live)? Mail provider? Anything else holding secrets?
- Team: Solo or multi-developer? (Decides whether the gitignored personal files need a shared convention.)
- ✅ All seven answer-sets recorded in a scratch note before drafting any file.
- App identity: What’s the app called (its name)? What type is it — SaaS, marketplace, directory, or other? Who’s the author/owner (your name + email)? These feed the
2. Write AGENTS.md (the contract)
Section titled “2. Write AGENTS.md (the contract)”Write AGENTS.md at the repo root using the auto-detected stack so the tech-stack section is accurate. The kit ships a complete version; the essential shape is below.
-
Draft the file at the repo root with the canonical shape.
# AGENTS.md — <AppName>> Cross-tool constitution. Every AI agent (Claude Code, Cursor, Codex, Gemini) reads this first.## What this is<AppName> — a <SaaS / marketplace / directory> built on a CodeCanyon Laravel base (<vendor family>).## Read first (the read-set)1. This file (AGENTS.md).2. `.claude/rules/` — behavioral + reference rules (auto-loaded).3. `CLAUDE.local.md` — personal env (if present, gitignored).4. `_CUSTOMIZATIONS.md` — log of every deviation from vendor code (create an empty stub at [Create the project §6](/tech-stack/laravel/codecanyon/build/playbooks/setup-new/01-ai-system/01-project-setup/) or Phase 2 import; until then, treat as “not yet created”).> **Logging cadence:** append to `_CUSTOMIZATIONS.md` **at the moment of the deviation**, not in a batch later — one entry per in-place vendor edit (`ZAJ:BEGIN/END`), net-new file (`ZAJ:FILE`), custom `_zaj` migration, and provisional/strategic decision (e.g. the Stripe account-strategy block). After any installer / deploy / migration / vendor update, run `git diff --name-only` and reconcile anything untracked against the ledger before committing.## Tech stack- Laravel <version> (PHP ^<version>), <Livewire/Alpine/Vue>, <Tailwind/Bootstrap>, MySQL, <Sanctum/Passport>.## Conventions- Custom migrations use a `_zaj` suffix; never edit vendor migrations (overwritten on update).- Wrap in-place vendor edits with `ZAJ:BEGIN` / `ZAJ:END` markers; net-new files start with `ZAJ:FILE`.- Use `gh` for all GitHub operations.## Safety rules (non-negotiable)- **Tinker:** never create/update/delete records via `php artisan tinker` without explicit approval; prefer direct SQL reads.- **Vendor files:** never modify `vendor/`; document deviations in `_CUSTOMIZATIONS.md`.- **Migrations:** guard with `Schema::hasTable()/hasColumn()`; never `migrate:fresh|wipe|reset` on shared data.- **Route protection:** block sensitive paths at the web-server level (`.htaccess` `[F]` / Nginx `deny`), not only in middleware. CodeCanyon apps often boot heavy middleware stacks; a middleware-only installer block can 500 before it blocks. Web-server denial stops the request before PHP boots.- **`.env` quoting:** **always** double-quote *every* `.env` value. Unquoted, the characters `# $ & ^ [ + * ;` and spaces break `.env` parsing. A password like `p@ss#w&rd$1` written unquoted silently truncates to `p@ss` (parsing stops at the first `#`). Applies to `DB_PASSWORD`, `REDIS_PASSWORD`, `MAIL_PASSWORD`, API keys — every value:```dotenv# ❌ silently truncates to p@ssDB_PASSWORD=p@ss#w&rd$1# ✅ stored intactDB_PASSWORD="p@ss#w&rd$1"- Composer auth: for private/paid packages, set
composer config --global github-oauth.github.com <token>— never paste tokens inline intocomposer.json. - Post-change: after any installer/deploy/migration, run
git diff --name-onlyand report before committing. - Secrets: store in a secrets manager or the gitignored credentials file — never echo secret characters into a session.
- Cloudflare budgets: free-plan WAF/rate-limit/cache budgets are tight. Scope WAF/rate-limit to
/admin,/login,/install, and/update; never challenge the public landing page or checkout without a human SEO/business decision.
AI-agent conventions
Section titled “AI-agent conventions”- Read this file and
.claude/rules/before any SSH or deploy action. - Prefer MCP/API tools and
ghover hand-run shell where one exists. - Stay within resource budgets — shared hosting (migration timeouts, memory) and the Cloudflare free plan (≈ 5 WAF rules, 1 rate-limit rule, 10 cache rules). Note usage and remaining capacity after each Cloudflare operation.
- Never challenge the landing page. Scope every WAF / rate-limit rule to specific sensitive paths (
/admin,/login,/install) — never apply a challenge or block to/or a broad URI pattern; that blocks search engines and real visitors. - Never replace the vendor landing page or demo content silently — flag it for a human decision.
- ✅ The tech-stack line matches what Create the project auto-detected; vendor family and business lines match the interview. - Composer auth: for private/paid packages, set
-
Verify on disk — commit as C3 at the end of this page.
Terminal window test -f AGENTS.md && git add -n AGENTS.md# Expected: AGENTS.md listed (dry-run) — commit in §7 below- ✅
AGENTS.mdis at the repo root (written now; committed at C3).
- ✅
3. Write CLAUDE.md (thin pointer)
Section titled “3. Write CLAUDE.md (thin pointer)”Keep CLAUDE.md short so there’s no drift — it only redirects to AGENTS.md plus any Claude-specific notes.
-
Create the pointer at the repo root.
# CLAUDE.md — <AppName>> Single source of truth lives in **AGENTS.md** — read it first.> This file holds only Claude-specific notes to avoid cross-tool drift.## Claude-specific notes- Read `~/.claude/skills/zaj-laravel-codecanyon/SKILL.md` at session start — its orchestration + safety rules apply whether or not the skill is invoked.- `CLAUDE.local.md` (gitignored) holds personal/machine notes.- ✅
CLAUDE.mdcontains only a redirect + Claude-specific notes — no duplicated stack/conventions.
- ✅
-
Verify on disk (same timing as
AGENTS.md— commit at C3).Terminal window test -f CLAUDE.md && git add -n CLAUDE.md# Expected: CLAUDE.md listed (dry-run)- ✅
CLAUDE.mdsits at the repo root alongsideAGENTS.md.
- ✅
4. Create CLAUDE.local.md (personal, gitignored)
Section titled “4. Create CLAUDE.local.md (personal, gitignored)”This file holds the per-developer environment — local URL, server IPs, zone IDs — and only references credentials, never the secrets themselves. It must never enter git.
-
Write the personal file at the repo root.
# CLAUDE.local.md — Personal (NOT committed)## My environment- Local URL: http://<app>.test- DB GUI: TablePlus## Infra- Zone ID: <your-zone-id> Server IP: <your-server-ip>- DNS token env var: $CF_API_TOKEN- Hosting: <provider> · root: /home/<user>/domains/<domain>/public_html## Credentials- Stored in: <1Password vault / credentials file path>- ⚠️ When writing passwords to `.env`, always double-quote them.- ✅ The file references where credentials live, never the secret values.
-
Gitignore it and confirm.
Terminal window grep -q "CLAUDE.local.md" .gitignore || echo "CLAUDE.local.md" >> .gitignoregit check-ignore CLAUDE.local.md # → CLAUDE.local.md# Expected: the command prints "CLAUDE.local.md", proving it's ignored- ✅
git check-ignore CLAUDE.local.mdechoes the filename — git will never stage it.
- ✅
5. Pick a credential store
Section titled “5. Pick a credential store”CLAUDE.local.md only references credentials; the secrets themselves live in one of three stores. The default is 1Password (op) because it keeps secrets encrypted and lets the project commit safe op:// templates instead of plaintext env copies.
| Option | Best for | How it works |
|---|---|---|
A · 1Password CLI (op) default / recommended | Teams, repeat deployments, secret rotation | Secrets stay in a project vault. A committed .env.tpl (or .env.<env>.tpl) holds only op://<Project>/<Environment>/<ENV_VAR> references; op inject renders the real .env on demand. |
| B · Vault templates | Offline / no 1Password | Per-env files live only in gitignored Admin-Local/1-Project/2-ProjectVault/; only .env.example is tracked — wired in Phase 2 · Wire .env templates. |
C · credentials.md | Solo / throwaway | A single gitignored markdown file in the vault lists each secret. Simpler, but unencrypted on disk — never commit it. |
For 1Password references, use this convention everywhere: op://<vault>/<item>/<field> where vault = project (Custojo), item = environment (Local, Staging, Production), and field = env var (DB_PASSWORD, APP_KEY, STRIPE_SECRET). Example value line: DB_PASSWORD="op://Custojo/Production/DB_PASSWORD".
-
(Option A) Install and verify
op. Machine setup has the full CLI walkthrough; here you only confirm the chosen store is usable from this project.Terminal window command -v op >/dev/null || brew install --cask 1password/tap/1password-cliop --versionop vault ls >/dev/null && echo "op vault access OK"# Expected: op 2.x; vault access OK- ✅
op vault lsworks. Do not useop whoamias the service-account test.
- ✅
-
Pick or create the project vault — service account creates it. For the zero-manual-grant path, the same service account from Machine setup §3.5 creates the project vault. Do not reuse a human-created vault unless a human explicitly granted that service account access in 1Password.
Terminal window PROJECT="<ProjectName>"op vault get "$PROJECT" >/dev/null 2>&1 || op vault create "$PROJECT" >/dev/nullop vault get "$PROJECT" >/dev/null && echo "vault OK: $PROJECT"# Expected: vault OK: <ProjectName>- ✅ The vault name is the project name, not the environment and not a generic “Env” bucket.
- ✅ If
op vault createfails with access/permission error, recreate the service account with vault-creation permission or use the 1Password UI to grant access; do not keep retrying item creation against a vault the token cannot see.
-
Create one item per environment. Create or reuse
Local,Staging, andProductionitems inside the project vault. Each env var becomes a field on the matching environment item, so secrets can vary naturally by environment.Terminal window PROJECT="<ProjectName>"for item in Local Staging Production; doop item get "$item" --vault "$PROJECT" >/dev/null 2>&1 \&& echo "item exists: $item" \|| echo "TODO: create 1Password item '$item' in vault '$PROJECT' with one field per env var"done# Expected: each item exists, or an explicit TODO for the user to create/fill it- ✅
Local,Staging, andProductionitems exist before rendering templates.
- ✅
-
Generate
.env.tplfrom.env.examplekeys — don’t hand-write it.Terminal window PROJECT="<ProjectName>"ENVIRONMENT="Local"test -f .env.example || { echo "FAIL: create .env.example first"; exit 1; }awk -F= -v vault="$PROJECT" -v item="$ENVIRONMENT" '/^[[:space:]]*#/ || /^[[:space:]]*$/ { print; next }/^[A-Za-z_][A-Za-z0-9_]*=/ {key=$1print key "=\"op://" vault "/" item "/" key "\""next}{ print }' .env.example > .env.tplgrep -q 'op://'"$PROJECT"'/'"$ENVIRONMENT" .env.tpl && echo ".env.tpl refs OK"if grep -n '^[[:space:]]*#.*op://' .env.tpl; thenecho "FAIL: remove op:// references from .env.tpl comments"; exit 1fi# Expected: .env.tpl exists; every key points at op://<Project>/<Environment>/<KEY>- ✅
.env.tplcontains references only. It is safe to commit because it has no secret values. - ✅
.env.tplhas no commentedop://references. Comments may describe the environment, but examples containingop://live in docs, not inside the template.
- ✅
-
Render + verify without printing values.
Terminal window op inject -i .env.tpl -o .envtest -s .env && echo ".env rendered"grep -q '^APP_KEY=' .env && echo "APP_KEY present (value hidden)"git check-ignore .envgit check-ignore .env.tpl || echo ".env.tpl trackable OK"# Expected: .env rendered; .env ignored; .env.tpl not ignored- ✅
.envis populated and gitignored;.env.tplis trackable and contains onlyop://references.
- ✅
-
Thread staging/production through deploy. For each environment, render from that environment item rather than copying a local
.env.Terminal window # Example: render production from op://<Project>/Production/<KEY>PROJECT="<ProjectName>" ENVIRONMENT="Production"awk -F= -v vault="$PROJECT" -v item="$ENVIRONMENT" '/^[[:space:]]*#/ || /^[[:space:]]*$/ { print; next }/^[A-Za-z_][A-Za-z0-9_]*=/ { key=$1; print key "=\"op://" vault "/" item "/" key "\""; next }{ print }' .env.example > ".env.${ENVIRONMENT}.tpl"if grep -n '^[[:space:]]*#.*op://' ".env.${ENVIRONMENT}.tpl"; thenecho "FAIL: remove op:// references from template comments"; exit 1fiop inject -i ".env.${ENVIRONMENT}.tpl" -o ".env.${ENVIRONMENT}"# Expected: .env.Production rendered locally for upload/deploy, never committed- ✅ Each deployment environment has a reproducible render path from 1Password.
6. Verify the constitution loads
Section titled “6. Verify the constitution loads”Confirm a fresh agent actually reads the contract before declaring this step done.
-
Start a fresh agent session in the project and watch for unprompted stack/convention references.
- ✅ Without being told, the agent references the stack/conventions — proof it read
AGENTS.md. If not, confirm the files are at the repo root and restart the session.
- ✅ Without being told, the agent references the stack/conventions — proof it read
7. Commit the constitution (C3)
Section titled “7. Commit the constitution (C3)”-
Stage and commit C3 from the project root.
Terminal window git branch --show-current # must be developgit add AGENTS.md CLAUDE.md .gitignoretest -f .env.tpl && git add .env.tpl # Option A — references only; safe to commitgit commit -m "feat(ai): project constitution (AGENTS.md + CLAUDE.md)"git log --oneline -3 # C3 atop C2 + C1# Expected: C3 on develop; CLAUDE.local.md not staged; the CLAUDE.local.md ignore rule is committed- ✅
git log -3shows C3;git check-ignore CLAUDE.local.mdstill succeeds. - ✅ When Option A (1Password) was chosen,
.env.tpllands in the same atomic commit — references only, no secret values.
- ✅
Checklist
Section titled “Checklist”Do not mark this step done until every box below is checked.
- 🔀 Interview answers recorded — app identity (name/type/author), business, vendor family, stack, infra, integrations, and team size captured in a scratch note after stack + feature/business recon.
- 🤖
AGENTS.mdwritten — at the repo root, tech-stack line matches the auto-detected stack; C3 committed. - 🤖
CLAUDE.mdwritten — thin pointer only; no duplicated stack/conventions; included in C3. - 👤
CLAUDE.local.mdcreated — personal env filled in; references (not values) for credentials. - 🤖
CLAUDE.local.mdgitignored —git check-ignore CLAUDE.local.mdechoes the filename. - 👤 Credential store chosen — Option A (1Password, default), B (vault templates), or C (
credentials.md) recorded inCLAUDE.local.md. - 🔀 Constitution verified — a fresh agent session references the stack/conventions unprompted.
- 🤖 C3 committed on
develop—AGENTS.md+CLAUDE.md+.gitignore(+.env.tplwhen Option A);CLAUDE.local.mdgitignored.