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.
Steps at a glance:
- 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.
- Write
AGENTS.md(the contract) — WriteAGENTS.mdat the repo root using the auto-detected stack so the tech-stack section is accurate. - Write
CLAUDE.md(thin pointer) — KeepCLAUDE.mdshort so there’s no drift — it only redirects toAGENTS.mdplus any Claude-specific notes. - 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. - Pick a credential store —
CLAUDE.local.mdonly references credentials; the secrets themselves live in one of three stores. - Operator Provisioning Gate — One upfront 👤 step that gathers everything a human must supply across the
whole playbook — accounts, API keys, license codes — and stores it in
op://now, so later phases read non-blocking instead of stalling mid-task. - Verify the constitution loads — Confirm a fresh agent actually reads the contract before declaring this step done.
- Commit the constitution (C3) — commit the constitution files on develop while keeping the local overlay ignored, so every later agent session has a stable project contract.
Background
Section titled “Background”A CodeCanyon app is unfamiliar vendor code. The constitution is the committed contract plus a personal overlay — each layer has a distinct reader and git fate:
flowchart TB subgraph committed["Committed (team-shared)"] A["AGENTS.md<br/>canonical contract"] R[".claude/rules/*.md<br/>seeded in Rules & skills"] C["CLAUDE.md<br/>thin pointer → AGENTS.md"] end subgraph personal["Personal (gitignored)"] L["CLAUDE.local.md<br/>IPs, zone IDs, credential refs"] end C --> A A --> R Tools["Claude Code · Cursor · Codex · Gemini"] --> A Tools --> R Tools --> C Tools --> L| File | Committed? | Role |
|---|---|---|
AGENTS.md | ✅ | The canonical contract. Tool-agnostic. Tech stack, conventions, safety rules, the read-set. Every modern agent reads it. |
.claude/rules/ | ✅ | Behavioral + reference rules (seeded in Rules & skills). Loaded at session start. |
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.
-
Research the CodeCanyon listing when the user supplies the item URL (optional but high leverage for vendor family, docs links, and install quirks).
👤 Ask: “What is the CodeCanyon URL for this script?” Then fetch the listing and extract name, author, Laravel/PHP requirements, feature bullets, documentation URL, demo URL, and recent changelog notes. Summarize back for confirmation.
- ✅ Listing summary confirmed by the user.
- ✅ Online documentation / support URLs captured for a
## Vendor documentationsection inAGENTS.md(bundled PDFs/HTML in the ZIP are catalogued later in Code & repository setup).
-
Auto-detect infra already on the machine before asking domain/hosting questions — present defaults the user can accept or override.
Terminal window echo "=== Auto-detect (workspace-global — from machine setup) ==="gh auth status 2>&1 | head -3git config --global user.name 2>/dev/null; git config --global user.email 2>/dev/nullgrep "^Host " ~/.ssh/config 2>/dev/null | grep -v '\*' || echo "No SSH Host aliases yet"[ -n "$CF_API_TOKEN" ] && echo "CF_API_TOKEN: set" || echo "CF_API_TOKEN: not set"command -v op >/dev/null && op vault ls >/dev/null 2>&1 && echo "1Password: OK" || echo "1Password: not ready"# Expected: gh auth, git identity, existing SSH aliases, Cloudflare token, op status visible before you ask infra questions- ✅ Detected GitHub identity, SSH aliases, and credential store status are shown as defaults in the infra questions below.
-
Answer the human-only questions and record them — they populate
AGENTS.md,CLAUDE.local.md,project_context.md, and laterZaj-PROJECT.md. The agent asks conversationally (not as an unreadable wall); you answer; the agent writes every answer to_onboarding-summary.md(gitignored scratch) or your note app before drafting any committed file.A · App source (before the numbered bank)
Section titled “A · App source (before the numbered bank)”If this is a CodeCanyon project (default for this playbook) — 👤 the agent asks exactly:
What is the CodeCanyon URL for this script? (e.g.
https://codecanyon.net/item/[name]/[id])🤖 After you answer, the agent fetches the listing, summarizes name/version/author/stack/features/docs/demo/changelog, and you confirm accuracy. Bundled vendor PDFs/HTML are catalogued in Code & repository setup — online docs URL still belongs in the interview now.
If NOT CodeCanyon — 👤 the agent asks exactly:
What kind of project is this? (custom Laravel, GitHub clone, other framework)
B · Business context (Q1–Q10 · 👤 USER only)
Section titled “B · Business context (Q1–Q10 · 👤 USER only)”# Question 1 What is the app/brand name for this deployment? (use the exact public name you want customers to see) 2 What domain will it live on? (for example, your production domain) 3 What type of product is this? (SaaS, marketplace, CMS, directory, tool, API, other) 4 Brief one-line description of what it does? (e.g. “AI content generation platform”) 5 Who is the target audience? (developers, businesses, consumers, enterprise, other) 6 What region/language are you targeting? (global English, Arabic, multi-language, other) 7 Any specific industry focus? (healthcare, education, finance, general, other) 8 How will you charge users? (subscription, one-time, freemium, credits/tokens, other) 9 Which payment gateway(s) do you want? (Stripe, PayPal, both, other) 10 Do you need invoicing or tax compliance? (Stripe Tax, manual, merchant of record, none yet) Also capture for vendor CodeCanyon apps (feeds
## Vendor documentation) — required for every CodeCanyon project:Field Question Vendor family Which CodeCanyon vendor family? (Froiden, LiquidThemes, WorkDo, InfyOm, IqonicDesign, other) Listing URL From step A — confirmed CodeCanyon item URL Docs URL Online documentation URL from listing or author site Demo URL Demo URL if listed (optional) Support URL Author support / ticket URL if known (optional) C · Infrastructure (Q11–Q28 · 👤 USER; agent pre-fills from step 4)
Section titled “C · Infrastructure (Q11–Q28 · 👤 USER; agent pre-fills from step 4)”The agent runs step 4 first, presents a defaults table (GitHub account, git name/email, SSH hosts, Cloudflare, Atlas, Docker), and you may say “use defaults” for the whole row set. Then it asks only what auto-detect cannot know — using these exact prompts when relevant:
# Question 11 Which GitHub account/organization should this repo live under? (personal account, org name, or create new) 12 Repo visibility: private or public? (default: private) 13 What git username and email for commits? (detected: [name] / [email] — keep or change?) 14 What hosting provider will you use? (Hostinger, DigitalOcean, AWS, Hetzner, other) 15 Already have servers? Or need to create staging + production? (If SSH hosts detected: “I see [list]. Which are for this project? Or need new ones?“) 16 If existing: what are the server IPs? (auto-fill from SSH config when host matched) 17 SSH access — reuse detected host alias, or set up from scratch? 18 Non-production environment topology? (one staging, multiple staging/UAT/sandbox/client-demo targets, custom names like previeworqa, or none yet)19 Are you using Cloudflare? (If CF_API_TOKENset: “Cloudflare already configured. Same account?“)20 If Cloudflare: is the domain already added to Cloudflare? (agent may look up zone ID via MCP) 21 What email provider for transactional email? (SendGrid, Mailgun, Brevo, Hostinger SMTP, Titan, undecided) 22 Do you need custom email addresses? (e.g. support@domain.com— already have / need to create / not needed)23 Database preference: MySQL or MariaDB? (default: MySQL) 24 Database GUI preference? (TablePlus if installed, else phpMyAdmin, other) 25 Use Atlas for schema management? (if installed: “Atlas detected — use it?” else skip / mysqldump) 26 Use Bytebase for DB change tracking? (default: skip) 27 Use Redis caching now? (default: skip — can add later) 28 Use Sentry for error tracking? (default: yes if free tier — can add later) Server mapping (required — pairs with Phase 2 hard gate)
Section titled “Server mapping (required — pairs with Phase 2 hard gate)”After Q15–Q17, the agent seeds a candidate mapping from
~/.ssh/config, DNS/dig, and hosting MCP listings — then stops for explicit operator confirmation before writingCLAUDE.local.mdorproject_context.md.Environment Record (after confirmation) Non-production target(s) Environment key ( staging-primary,qa,uat,client-demo, etc.) · URL/domain · SSH alias · IP · hosting username/order ID (if known)Production Environment key ( production) · URL/domain · SSH alias · IP · hosting username/order ID (if known)The agent must not assert conclusions about servers it cannot reach (“production isn’t reachable” / “X is the only VPS”) — ask, don’t guess.
Integrations the agent may pre-fill from
composer.json— 👤 you confirm or override:Topic Confirm Stack Laravel version, PHP requirement, frontend (Livewire / Alpine / Vue / Inertia), CSS (Tailwind / Bootstrap) Auth Sanctum, Passport, JWT, Fortify, Breeze, Jetstream — what the repo actually uses Payments detected Stripe, PayPal, Razorpay, Paddle, Cashier, etc. — match Q8–Q9 Secrets Which integrations hold live secrets today? (Stripe test+live keys, mail API, OAuth, webhooks) D · Team & workflow (Q29–Q31 · 👤 USER only)
Section titled “D · Team & workflow (Q29–Q31 · 👤 USER only)”# Question 29 Solo developer or team? (affects branch strategy, PR workflow, credential conventions) 30 Other AI tools alongside Claude Code? (Cursor, Copilot, Codex, Gemini — drives IDE mirror configs) 31 Any deadline or timeline pressure? (urgent vs relaxed — prioritizes phase sequence and post-launch tracks) - ✅ Every row above has a recorded answer or explicit undecided / use defaults before step 6.
-
Compile the interview summary and get sign-off.
🤖 Draft
_onboarding-summary.md(or paste into chat). 👤 Review and correct — this is the gate before writingAGENTS.md.## Project onboarding summary**App:** [Name] — [one-line description]**Source:** CodeCanyon ([Script Name] v[X.X] by [Author]) / Custom / GitHub**Domain:** [domain.com]**Type:** [SaaS / marketplace / etc.]**Vendor family:** [Froiden / … / other]**Listing / docs:** [codecanyon URL] · [docs URL]**Audience:** [target] | **Region:** [region] | **Language:** [lang]**Industry:** [focus]**Monetization:** [model] via [gateway(s)]**Tax / invoicing:** [Stripe Tax / manual / undecided with trigger]**Infrastructure:**- GitHub: [account/org] | Repo: [private/public] | Git user: [name] <[email]>- Hosting: [provider]- **Primary non-production:** [env key] · [SSH alias] · [IP] · [hosting acct/order if known] — **confirmed by operator**- **Production:** [SSH alias] · [IP] · [hosting acct/order if known] — **confirmed by operator**- DNS/CDN: [Cloudflare/other] | Zone: [ID or "to be set up"]- Email: [provider] | Custom addresses: [yes/no/undecided]- Database: [MySQL/MariaDB] | GUI: [tool]- SSH: [alias(es) / needs setup]**Optional tools:** Atlas [yes/no] | Bytebase [yes/no] | Redis [yes/no] | Sentry [yes/no]**Team:** [solo/team of N] | **AI tools:** [list] | **Timeline:** [urgent/relaxed]**Detected from source code:**- Laravel [version], PHP ^[version]- Auth: [Sanctum/Passport/…]- Payments: [packages detected]- Frontend: [Tailwind/Bootstrap + Alpine/Livewire/Vue]- [X] migrations, [Y] models, [Z] route files- Feature surface (from recon): [modules / plans / orders / …]- ✅ You replied confirmed (or listed corrections). Agent updates the summary before drafting files.
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. `Zaj-PROJECT.md` — non-secret current truth: identity, versions, paths, environments, SSH aliases, and tracker links. If missing in a legacy project, read `Admin-Local/1-Project/1-Info/ProjectCard.md` as fallback, then create `Zaj-PROJECT.md`.3. `.claude/rules/` — behavioral + reference rules (auto-loaded).4. `Zaj-PROGRESS.md` and `Zaj-BACKLOG.md` — current work and deferred work.5. `Zaj-CUSTOMIZATIONS.md` — vendor-deviation ledger (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”).6. `CLAUDE.local.md` — personal env (if present, gitignored).## Vendor documentation- CodeCanyon item: <listing URL>- Online docs: <docs URL> · Demo: <demo URL> · Support: <support URL>- Bundled docs in repo: _(filled after Phase 2 extraction — paths under `_Source/` or `Admin-Local/2-Docs/1-VendorDocs/`)_> **Logging cadence:** append to `Zaj-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- `Zaj-PROJECT.md` is the project bible for current facts. `Zaj-CUSTOMIZATIONS.md` is the vendor-deviation ledger. Do not scatter current toolchain/path/server facts through one-off notes.- 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)- **Default = act, don't hedge:** update trackers, run verification, commit routine completed work, and choose the secure/professional default when it is clearly better. Ask only for secrets, paid/provider dashboards, production cutovers, destructive actions, business/scope/legal choices, or genuinely equal options.- **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 `Zaj-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. - Read before SSH: check
AGENTS.md,CLAUDE.local.md,~/.ssh/config, andserver-environment.conf(if present) — only SSH when the answer is not already local. - Read before asking: check interview notes,
.env.example, and existing configs before re-asking the user. - Prefer MCP/API tools and
ghover hand-run shell where one exists; include dashboard steps only as fallback. - 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 silently skip SHOULD/OPTIONAL tasks — default to doing SHOULD work. Mark OPTIONAL work
N/Aonly when the project state or provisioning table proves it is not adopted, and list everyN/A/skipped item with evidence at phase end. - Orchestration skill: read
~/.claude/skills/zaj-laravel-codecanyon/SKILL.md(or the project’s/deploy-codecanyoncommand) at session start — safety rules apply whether or not the skill is invoked. - 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 §8 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 — orchestration + safety rules apply whether or not the skill is invoked.- After [Rules & skills](/tech-stack/laravel/codecanyon/build/playbooks/setup-new/01-ai-system/05-rules-and-skills/), `.claude/rules/` loads at session start — **restart the IDE/agent session** after seeding rules so they take effect.- `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## Cloudflare- Zone ID: <your-zone-id>- Server IP: <your-server-ip>- DNS token env var: $CF_API_TOKEN## Hosting- Provider: <Hostinger / cPanel / other>- Username: <hosting-user> · Order ID: <order-id> (if applicable)- **Primary non-production:** key `<NON_PROD_ENV_KEY>` · alias `<NON_PROD_SSH>` · IP `<NON_PROD_IP>` · acct/order `<NON_PROD_ACCT>`- **Production:** alias `<PRODUCTION_SSH>` · IP `<PRODUCTION_IP>` · acct/order `<PRODUCTION_ACCT>`- Document root: /home/<user>/domains/<domain>/public_html- Hosting MCP note: <machine-wide token covers this acct / per-project token needed / hPanel-only>## Credentials- Stored in: <1Password vault `<Project>` / Admin-Local/…/credentials.md>- ⚠️ When writing passwords to `.env`, always double-quote them.## Email (populate in deploy / superadmin phases)- Provider: <Titan / SendGrid / Mailgun / …>- admin@<domain> · noreply@<domain> · support@<domain> _(when created)_- ✅ The file references where credentials live, never the secret values.
-
Gitignore personal scratch + confirm.
Terminal window for f in CLAUDE.local.md _onboarding-summary.md; dogrep -qx "$f" .gitignore 2>/dev/null || echo "$f" >> .gitignoredonegit check-ignore CLAUDE.local.md _onboarding-summary.md# Expected: both filenames printed — never staged- ✅
git check-ignoreechoes both filenames — interview scratch and personal overlay stay out of git.
- ✅
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 (app secrets) plus a shared General-Dev vault (account-wide infra creds — see two-vault model below). Option A1 (default): commit a .env.tpl with only op:// refs; teammates run op inject. Option A2: gitignore .env.tpl when solo or vault structure should stay out of git — each dev obtains the template via 1Password/onboarding (see sub-choice below). |
| B · Vault templates | Offline / no 1Password | Per-env files live only in gitignored Admin-Local/1-Project/2-Vault/; 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 ([PROJECT]), item = environment for .env values (Local, Staging, Production), and field = a flat env var. Phase 2 seeds the baseline fields (DB_PASSWORD, APP_KEY, etc.); later phases may add more flat fields (for example STRIPE_SECRET) before a template references them. Example value line: DB_PASSWORD="op://[PROJECT]/Production/DB_PASSWORD".
Two-vault model — project vault + shared infra vault
Section titled “Two-vault model — project vault + shared infra vault”op://<Project>/<env>/<key> assumes every secret is project-scoped. Some creds are account-wide infra — a Hostinger account API token, a Cloudflare token, a registrar key, Admin-Server webhooks, MonSpark heartbeat URLs — and span every project on that account. Storing them per-project duplicates the secret and multiplies rotation points (rotate once, update N vaults). Split into two vaults. The default shared vault name in examples is General-Dev, but the operator may use any name; record the actual shared infra vault in Zaj-PROJECT.md.
| Vault | Holds | Reference shape | Example |
|---|---|---|---|
<Project> (per-project) | Baseline env fields seeded in Phase 2 (APP_KEY, DB_*, env_file) plus later-added app secrets that differ per environment (STRIPE_SECRET, MAIL_PASSWORD) | op://<Project>/<env>/<KEY> | op://[PROJECT]/Production/DB_PASSWORD |
<SharedInfraVault> (default example: General-Dev) | Infra creds shared across projects: hosting account token, Cloudflare token, registrar key, Admin-Server Slack/Discord webhooks, MonSpark/heartbeat URLs — one item per account or provider | op://<SharedInfraVault>/<account-or-provider>/<KEY> | op://General-Dev/Hostinger-zaj.commerce/HOSTINGER_API_TOKEN |
When a project uses multiple hosting accounts (e.g. staging on one Hostinger login, production on another), the shared vault holds one item per account, and each environment’s .env.tpl points at the right account item.
-
(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>"SHARED_INFRA_VAULT="General-Dev" # example; use the actual shared vault name if differentop vault get "$PROJECT" >/dev/null 2>&1 || op vault create "$PROJECT" >/dev/nullop vault get "$PROJECT" >/dev/null && echo "vault OK: $PROJECT"# Shared infra vault — created once for the account, reused by every project (see two-vault model above)op vault get "$SHARED_INFRA_VAULT" >/dev/null 2>&1 || op vault create "$SHARED_INFRA_VAULT" >/dev/nullop vault get "$SHARED_INFRA_VAULT" >/dev/null && echo "vault OK: $SHARED_INFRA_VAULT (shared infra)"# The project SA must read BOTH vaults — verify before proceedingop vault ls | grep -E "^${PROJECT}\b|^${SHARED_INFRA_VAULT}\b" || echo "FAIL: SA cannot see one of the vaults — grant access (see danger box above)"# Expected: vault OK: <ProjectName>; vault OK: <SharedInfraVault>; both listed for the SA- ✅ The vault name is the project name, not the environment and not a generic “Env” bucket.
- ✅ The shared infra vault exists, and the project SA can see both vaults (
op vault lslists both). If only the project vault shows, grant the SA shared-vault read per the danger box above before continuing — infra-cred renders will fail otherwise. - ✅ 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.
-
Standardize the per-environment item shape. Setup uses one Secure Note item per environment inside the project vault:
Local,Staging, andProduction. Phase 2 · Prerequisites §3 seeds these items idempotently before.env.tplor production deploy steps reference them.Content Fields Why Flat env fields — same names everywhere DB_CONNECTION,DB_HOST,DB_PORT,DB_DATABASE,DB_USERNAME,DB_PASSWORD,APP_KEY,env_fileMatches op://<Project>/<Environment>/<Field>exactly, so templates never reference a missing item or dotted sub-field.Concealed fields DB_PASSWORD,APP_KEY,env_fileThese can contain secrets or the rendered .envblob. Never print them; verify by length, exit code, or non-secret fields.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: Phase 2 Prerequisites must seed '$item' in vault '$PROJECT'"done# Expected: each item exists before any op://<Project>/<Environment>/<Field> template is rendered- ✅
Local,Staging, andProductionitems exist before rendering templates. - ✅ Each item has the same flat field set; no active setup path uses a single
Databasesitem with dotted fields likeStaging.password.
- ✅
-
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 — no secret values. - ✅
.env.tplhas no commentedop://references. Comments may describe the environment, but examples containingop://live in docs, not inside the template.
Record A1 vs A2 in
CLAUDE.local.mdbefore render (default A1 for multi-developer repos). - ✅
-
Render + verify without printing values.
Terminal window op inject -f -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.
Sync the
env_fileblob back to 1Password (auto-generated — keeps the one-paste blob current):Terminal window PROJECT="<ProjectName>" ENVIRONMENT="Local"# Render once, then push the whole .env as the item's env_file field (never hand-typed)BLOB="$(op inject -f -i .env.tpl)"op item edit "$ENVIRONMENT" --vault "$PROJECT" "env_file[text]=$BLOB" >/dev/null \&& echo "env_file blob synced for $ENVIRONMENT"unset BLOB# Verify it round-trips (length only — never print the body)op read "op://$PROJECT/$ENVIRONMENT/env_file" | wc -c# Expected: byte count > 0 — blob matches the current secret fields- ✅ The
env_fileblob is a fresh render, so it can never drift from the individual secret fields. Re-run this whenever any secret field changes.
Option A sub-choice — commit vs gitignore
.env.tpl:Sub-choice When Tradeoff A1 — commit .env.tpl(default for teams)Multi-developer repo Shared render template in git; teammates clone and run op injectA2 — gitignore .env.tplSolo owner or refs expose vault structure Remove !.env.tplfrom bootstrap.gitignore; each dev obtains template via 1Password/onboardingTerminal window # A1 (default): trackable templategit check-ignore .env.tpl || echo ".env.tpl trackable OK"# A2 (owner choice): intentionally gitignoredgit check-ignore .env.tpl && echo ".env.tpl gitignored by owner choice OK" - ✅
-
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 -f -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. Operator Provisioning Gate
Section titled “6. Operator Provisioning Gate”One upfront 👤 step that gathers everything a human must supply across the whole playbook — accounts, API keys, license codes — and stores it in op:// now, so later phases read non-blocking instead of stalling mid-task. Without this gate, operator inputs are scattered (hosting at Phase 2, Stripe at Phase 6, production infra at Phase 4) and every phase pauses to ask the human for one more credential.
-
Walk the operator through every input the playbook will need. Group by domain so nothing is missed. Store each secret into the right vault (
op://<Project>/<env>/<KEY>for app secrets,op://<SharedInfraVault>/<account-or-provider>/<KEY>for account-wide infra;General-Devis only the default example). Mark the status of each:✅ provided·⏳ carry-forward (captured, infra not built)·❌ missing — blocks Phase N.Domain Item(s) the human must supply Store at Needed by Status Hosting Account login(s), SSH key/access, server IP(s), DB host/user/password op://General-Dev/<account>/...(token) ·op://<Project>/<env>/DB_*Phase 4–5 (deploy) GitHub Repo account/org, repo visibility, deploy key (if CI) ghauth (machine setup) ·op://General-Dev/GitHub/DEPLOY_KEYPhase 2 (code repo) Domain / DNS Domain name, registrar access, Cloudflare account + token + zone ID op://General-Dev/Cloudflare/CF_API_TOKEN· zone ID inCLAUDE.local.mdPhase 5 (staging DNS) Payments Stripe test+live keys (or PayPal), webhook signing secret op://<Project>/<env>/STRIPE_SECRET,STRIPE_WEBHOOK_SECRETPhase 6 (superadmin billing) Email Transactional provider API key / SMTP creds op://<Project>/<env>/MAIL_PASSWORD(orMAIL_API_KEY)Phase 5–6 Admin-Server alerting Slack/Discord ops/deploy/backups webhook URLs; MonSpark/Healthchecks/Better Stack/Cronitor/UptimeRobot heartbeat URLs op://<SharedInfraVault>/Admin-Server/SLACK_WEBHOOK_OPSetc.; runtime copy only in gitignored~/Admin-Server/config/server.envShared-hosting setup · Phase 4 deploy notifications · Phase 5 monitoring Observability Sentry DSN, PostHog key (if used), MonSpark API key/heartbeat URL if adopted op://<Project>/<env>/SENTRY_DSN,POSTHOG_KEY;op://<SharedInfraVault>/MonSpark/...Phase 7 (monitoring) Vendor license CodeCanyon purchase code / license key, Envato token (if updater) op://<Project>/CodeCanyon License/VENDOR_LICENSE_CODEPhase 2 install / Phase 6 updater 1Password Project vault + shared infra vault (default example: General-Dev) + service account with both granted— (this section’s prereq) All phases (every render) - ✅ Every row has a status. Anything
❌ missingthat blocks an early phase (Phase 2–5) is resolved now; items that only block later phases may stay⏳ carry-forwardwith their inputs captured.
- ✅ Every row has a status. Anything
-
Store each provided secret into
op://immediately — do not leave it in chat. Useop item editto add a field to the matching environment item (orop item createfor a shared-infra-vault item). Verify by length only.Terminal window PROJECT="<ProjectName>"SHARED_INFRA_VAULT="General-Dev" # example; use the actual shared vault name if different# Example: add the Stripe live key as a field on the Production itemop item edit "Production" --vault "$PROJECT" "STRIPE_SECRET[password]=<paste>" >/dev/nullop item get "Production" --vault "$PROJECT" --fields label=STRIPE_SECRET --reveal | wc -c# Expected: byte count > 0 — value stored, never printed# Example: account-wide Cloudflare token in the shared vaultop item edit "Cloudflare" --vault "$SHARED_INFRA_VAULT" "CF_API_TOKEN[password]=<paste>" >/dev/null 2>&1 \|| op item create --vault "$SHARED_INFRA_VAULT" --category "API Credential" --title "Cloudflare" "CF_API_TOKEN[password]=<paste>" >/dev/null- ✅ Each secret lives in
op://, referenced (never inlined) by later phases. The provisioning table is a status board, not a place secrets are written.
- ✅ Each secret lives in
-
Record the gate outcome. Write the status table into
_onboarding-summary.md(gitignored) and add a one-line provisional entry toZaj-CUSTOMIZATIONS.mdnoting which inputs are provided vs carried forward, so a later phase reads the board instead of re-interviewing.- ✅ The provisioning table is recorded; carry-forward items name the phase they unblock; no early-phase blocker remains
❌.
- ✅ The provisioning table is recorded; carry-forward items name the phase they unblock; no early-phase blocker remains
7. Verify the constitution loads
Section titled “7. 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.
Terminal window # From project root — constitution files on disktest -f AGENTS.md && test -f CLAUDE.md && echo "constitution files OK"git check-ignore CLAUDE.local.md && echo "local overlay ignored OK"ls .claude/rules/*.md 2>/dev/null | wc -l # 0 until Rules & skills; note count after seedAsk the new session: “What is this project’s stack, and what safety rules apply to tinker and migrations?” — and, after Rules & skills, “List the
.claude/rules/files you loaded.”- ✅ 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 (memory files load once at start, not mid-chat).
- ✅ Without being told, the agent references the stack/conventions — proof it read
8. Commit the constitution (C3)
Section titled “8. 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 .gitignore# Option A1 (default): commit the op:// template — references onlyif git check-ignore -q .env.tpl 2>/dev/null; thenecho "A2 — .env.tpl gitignored by owner choice; skip git add"elif test -f .env.tpl; thengit add .env.tplfigit 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; .env.tpl added only for A1- ✅
git log -3shows C3;git check-ignore CLAUDE.local.mdstill succeeds. - ✅ A1:
.env.tplin C3 (references only). A2:.env.tplgitignored — confirm withgit check-ignore .env.tpland record the choice inCLAUDE.local.md.
- ✅
Checklist
Section titled “Checklist”Do not mark this step done until every box below is checked.
- 🔀 Interview complete — CodeCanyon URL (or non-CC path) + Q1–Q31 answered or marked undecided/defaults; vendor family + docs URLs captured; stack/integrations confirmed.
- 🔀 Interview summary signed off — step 6 summary reviewed; corrections applied before drafting files.
- 🤖
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; if A, record A1 (commit.env.tpl) or A2 (gitignore.env.tpl). - 🔀 Two-vault model set up (Option A) — project vault (
<Project>) and sharedGeneral-Devvault both exist; project SA can read both (op vault lslists both). - 🔀 Env items shaped (Option A) —
Local/Staging/Productionitems exist with flat fields (DB_CONNECTION,DB_HOST,DB_PORT,DB_DATABASE,DB_USERNAME,DB_PASSWORD,APP_KEY,env_file); concealed fields are verified without printing. - 🤖
.env*write guard scoped — write/edit denies cover real secret files only;.env.tpl/.env.<env>.tpl/.env.exampleare editable. - 🔀 Operator Provisioning Gate run — every input the playbook needs (hosting, GitHub, DNS/Cloudflare, payments, email, observability, vendor license, 1Password) captured into
op://with aneeded-by-Phase / statusrow; no early-phase blocker left❌. - 🔀 Constitution verified — a fresh agent session references the stack/conventions unprompted.
- 🤖 C3 committed on
develop—AGENTS.md+CLAUDE.md+.gitignore(+.env.tplfor A1 only; A2 confirmsgit check-ignore .env.tpl);CLAUDE.local.mdgitignored.