Skip to content
prod 352bb92
Browse

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:

  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.
  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.
  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.
  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.
  5. Pick a credential storeCLAUDE.local.md only references credentials; the secrets themselves live in one of three stores.
  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.
  7. Verify the constitution loads — Confirm a fresh agent actually reads the contract before declaring this step done.
  8. 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.

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
FileCommitted?Role
AGENTS.mdThe 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.mdA thin pointer that says “read AGENTS.md first,” plus any Claude-specific notes. Keeps a single source of truth.
CLAUDE.local.md❌ gitignoredPersonal: local URL, server IPs, zone IDs, credential-store references. Differs per developer; never in git.

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).

  1. 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 -xz
    bash seed.sh # renames dot-* → real dotfiles; materializes project_context.md

    The tarball unpacks its contents straight into the current directory, then seed.sh wires them up. Prefer to inspect first? Browse the file map or MANIFEST, and read seed.sh after extracting — before you run it.

  2. Fill placeholders — run the onboarding interview below (or answer the kit’s printed prompts) so AGENTS.md and project_context.md match this app.

  3. Pick a permission mode./.claude/claude-mode/bin/set-claude-mode.sh medium (see Claude config).

  4. 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.

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.

  1. Auto-detect the stack from the repo.

    Terminal window
    head -30 composer.json # Laravel version, PHP req, key packages
    head -20 package.json # frontend deps (Livewire / Alpine / Vue / Tailwind)
    ls routes/ # route files → surface area
    ls -d app/Http/Controllers/*/ 2>/dev/null # controller organization
    ls 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.
  2. 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 -80
    find Modules app/Classes -maxdepth 2 -type d 2>/dev/null | head -80
    rg -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.
  3. 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 documentation section in AGENTS.md (bundled PDFs/HTML in the ZIP are catalogued later in Code & repository setup).
  4. 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 -3
    git config --global user.name 2>/dev/null; git config --global user.email 2>/dev/null
    grep "^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.
  5. Answer the human-only questions and record them — they populate AGENTS.md, CLAUDE.local.md, project_context.md, and later Zaj-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
    1What is the app/brand name for this deployment? (use the exact public name you want customers to see)
    2What domain will it live on? (for example, your production domain)
    3What type of product is this? (SaaS, marketplace, CMS, directory, tool, API, other)
    4Brief one-line description of what it does? (e.g. “AI content generation platform”)
    5Who is the target audience? (developers, businesses, consumers, enterprise, other)
    6What region/language are you targeting? (global English, Arabic, multi-language, other)
    7Any specific industry focus? (healthcare, education, finance, general, other)
    8How will you charge users? (subscription, one-time, freemium, credits/tokens, other)
    9Which payment gateway(s) do you want? (Stripe, PayPal, both, other)
    10Do 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:

    FieldQuestion
    Vendor familyWhich CodeCanyon vendor family? (Froiden, LiquidThemes, WorkDo, InfyOm, IqonicDesign, other)
    Listing URLFrom step A — confirmed CodeCanyon item URL
    Docs URLOnline documentation URL from listing or author site
    Demo URLDemo URL if listed (optional)
    Support URLAuthor 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
    11Which GitHub account/organization should this repo live under? (personal account, org name, or create new)
    12Repo visibility: private or public? (default: private)
    13What git username and email for commits? (detected: [name] / [email] — keep or change?)
    14What hosting provider will you use? (Hostinger, DigitalOcean, AWS, Hetzner, other)
    15Already have servers? Or need to create staging + production? (If SSH hosts detected: “I see [list]. Which are for this project? Or need new ones?“)
    16If existing: what are the server IPs? (auto-fill from SSH config when host matched)
    17SSH access — reuse detected host alias, or set up from scratch?
    18Non-production environment topology? (one staging, multiple staging/UAT/sandbox/client-demo targets, custom names like preview or qa, or none yet)
    19Are you using Cloudflare? (If CF_API_TOKEN set: “Cloudflare already configured. Same account?“)
    20If Cloudflare: is the domain already added to Cloudflare? (agent may look up zone ID via MCP)
    21What email provider for transactional email? (SendGrid, Mailgun, Brevo, Hostinger SMTP, Titan, undecided)
    22Do you need custom email addresses? (e.g. support@domain.com — already have / need to create / not needed)
    23Database preference: MySQL or MariaDB? (default: MySQL)
    24Database GUI preference? (TablePlus if installed, else phpMyAdmin, other)
    25Use Atlas for schema management? (if installed: “Atlas detected — use it?” else skip / mysqldump)
    26Use Bytebase for DB change tracking? (default: skip)
    27Use Redis caching now? (default: skip — can add later)
    28Use 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 writing CLAUDE.local.md or project_context.md.

    EnvironmentRecord (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)
    ProductionEnvironment 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:

    TopicConfirm
    StackLaravel version, PHP requirement, frontend (Livewire / Alpine / Vue / Inertia), CSS (Tailwind / Bootstrap)
    AuthSanctum, Passport, JWT, Fortify, Breeze, Jetstream — what the repo actually uses
    Payments detectedStripe, PayPal, Razorpay, Paddle, Cashier, etc. — match Q8–Q9
    SecretsWhich 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
    29Solo developer or team? (affects branch strategy, PR workflow, credential conventions)
    30Other AI tools alongside Claude Code? (Cursor, Copilot, Codex, Gemini — drives IDE mirror configs)
    31Any 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.
  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 writing AGENTS.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.

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.

  1. 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@ss
    DB_PASSWORD=p@ss#w&rd$1
    # ✅ stored intact
    DB_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 into composer.json.
    • Post-change: after any installer/deploy/migration, run git diff --name-only and 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.
    • Read this file and .claude/rules/ before any SSH or deploy action.
    • Read before SSH: check AGENTS.md, CLAUDE.local.md, ~/.ssh/config, and server-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 gh over 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/A only when the project state or provisioning table proves it is not adopted, and list every N/A/skipped item with evidence at phase end.
    • Orchestration skill: read ~/.claude/skills/zaj-laravel-codecanyon/SKILL.md (or the project’s /deploy-codecanyon command) 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.
  2. 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.md is at the repo root (written now; committed at C3).

Keep CLAUDE.md short so there’s no drift — it only redirects to AGENTS.md plus any Claude-specific notes.

  1. 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.md contains only a redirect + Claude-specific notes — no duplicated stack/conventions.
  2. 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.md sits at the repo root alongside AGENTS.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.

  1. 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.
  2. Gitignore personal scratch + confirm.

    Terminal window
    for f in CLAUDE.local.md _onboarding-summary.md; do
    grep -qx "$f" .gitignore 2>/dev/null || echo "$f" >> .gitignore
    done
    git check-ignore CLAUDE.local.md _onboarding-summary.md
    # Expected: both filenames printed — never staged
    • git check-ignore echoes both filenames — interview scratch and personal overlay stay out of git.

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.

OptionBest forHow it works
A · 1Password CLI (op) default / recommendedTeams, repeat deployments, secret rotationSecrets 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 templatesOffline / no 1PasswordPer-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.mdSolo / throwawayA 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.

VaultHoldsReference shapeExample
<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 providerop://<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.

  1. (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-cli
    op --version
    op vault ls >/dev/null && echo "op vault access OK"
    # Expected: op 2.x; vault access OK
    • op vault ls works. Do not use op whoami as the service-account test.
  2. 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 different
    op vault get "$PROJECT" >/dev/null 2>&1 || op vault create "$PROJECT" >/dev/null
    op 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/null
    op vault get "$SHARED_INFRA_VAULT" >/dev/null && echo "vault OK: $SHARED_INFRA_VAULT (shared infra)"
    # The project SA must read BOTH vaults — verify before proceeding
    op 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 ls lists 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 create fails 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.
  3. Standardize the per-environment item shape. Setup uses one Secure Note item per environment inside the project vault: Local, Staging, and Production. Phase 2 · Prerequisites §3 seeds these items idempotently before .env.tpl or production deploy steps reference them.

    ContentFieldsWhy
    Flat env fields — same names everywhereDB_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 fieldsDB_PASSWORD, APP_KEY, env_fileThese can contain secrets or the rendered .env blob. Never print them; verify by length, exit code, or non-secret fields.
    Terminal window
    PROJECT="<ProjectName>"
    for item in Local Staging Production; do
    op 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, and Production items exist before rendering templates.
    • ✅ Each item has the same flat field set; no active setup path uses a single Databases item with dotted fields like Staging.password.
  4. Generate .env.tpl from .env.example keys — 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=$1
    print key "=\"op://" vault "/" item "/" key "\""
    next
    }
    { print }
    ' .env.example > .env.tpl
    grep -q 'op://'"$PROJECT"'/'"$ENVIRONMENT" .env.tpl && echo ".env.tpl refs OK"
    if grep -n '^[[:space:]]*#.*op://' .env.tpl; then
    echo "FAIL: remove op:// references from .env.tpl comments"; exit 1
    fi
    # Expected: .env.tpl exists; every key points at op://<Project>/<Environment>/<KEY>
    • .env.tpl contains references only — no secret values.
    • .env.tpl has no commented op:// references. Comments may describe the environment, but examples containing op:// live in docs, not inside the template.

    Record A1 vs A2 in CLAUDE.local.md before render (default A1 for multi-developer repos).

  5. Render + verify without printing values.

    Terminal window
    op inject -f -i .env.tpl -o .env
    test -s .env && echo ".env rendered"
    grep -q '^APP_KEY=' .env && echo "APP_KEY present (value hidden)"
    git check-ignore .env
    git check-ignore .env.tpl || echo ".env.tpl trackable OK"
    # Expected: .env rendered; .env ignored; .env.tpl not ignored
    • .env is populated and gitignored; .env.tpl is trackable and contains only op:// references.

    Sync the env_file blob 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_file blob 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-choiceWhenTradeoff
    A1 — commit .env.tpl (default for teams)Multi-developer repoShared render template in git; teammates clone and run op inject
    A2 — gitignore .env.tplSolo owner or refs expose vault structureRemove !.env.tpl from bootstrap .gitignore; each dev obtains template via 1Password/onboarding
    Terminal window
    # A1 (default): trackable template
    git check-ignore .env.tpl || echo ".env.tpl trackable OK"
    # A2 (owner choice): intentionally gitignored
    git check-ignore .env.tpl && echo ".env.tpl gitignored by owner choice OK"
  6. 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"; then
    echo "FAIL: remove op:// references from template comments"; exit 1
    fi
    op 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.

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.

  1. 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-Dev is only the default example). Mark the status of each: ✅ provided · ⏳ carry-forward (captured, infra not built) · ❌ missing — blocks Phase N.

    DomainItem(s) the human must supplyStore atNeeded byStatus
    HostingAccount login(s), SSH key/access, server IP(s), DB host/user/passwordop://General-Dev/<account>/... (token) · op://<Project>/<env>/DB_*Phase 4–5 (deploy)
    GitHubRepo account/org, repo visibility, deploy key (if CI)gh auth (machine setup) · op://General-Dev/GitHub/DEPLOY_KEYPhase 2 (code repo)
    Domain / DNSDomain name, registrar access, Cloudflare account + token + zone IDop://General-Dev/Cloudflare/CF_API_TOKEN · zone ID in CLAUDE.local.mdPhase 5 (staging DNS)
    PaymentsStripe test+live keys (or PayPal), webhook signing secretop://<Project>/<env>/STRIPE_SECRET, STRIPE_WEBHOOK_SECRETPhase 6 (superadmin billing)
    EmailTransactional provider API key / SMTP credsop://<Project>/<env>/MAIL_PASSWORD (or MAIL_API_KEY)Phase 5–6
    Admin-Server alertingSlack/Discord ops/deploy/backups webhook URLs; MonSpark/Healthchecks/Better Stack/Cronitor/UptimeRobot heartbeat URLsop://<SharedInfraVault>/Admin-Server/SLACK_WEBHOOK_OPS etc.; runtime copy only in gitignored ~/Admin-Server/config/server.envShared-hosting setup · Phase 4 deploy notifications · Phase 5 monitoring
    ObservabilitySentry DSN, PostHog key (if used), MonSpark API key/heartbeat URL if adoptedop://<Project>/<env>/SENTRY_DSN, POSTHOG_KEY; op://<SharedInfraVault>/MonSpark/...Phase 7 (monitoring)
    Vendor licenseCodeCanyon purchase code / license key, Envato token (if updater)op://<Project>/CodeCanyon License/VENDOR_LICENSE_CODEPhase 2 install / Phase 6 updater
    1PasswordProject 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 ❌ missing that blocks an early phase (Phase 2–5) is resolved now; items that only block later phases may stay ⏳ carry-forward with their inputs captured.
  2. Store each provided secret into op:// immediately — do not leave it in chat. Use op item edit to add a field to the matching environment item (or op item create for 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 item
    op item edit "Production" --vault "$PROJECT" "STRIPE_SECRET[password]=<paste>" >/dev/null
    op 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 vault
    op 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.
  3. Record the gate outcome. Write the status table into _onboarding-summary.md (gitignored) and add a one-line provisional entry to Zaj-CUSTOMIZATIONS.md noting 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 .

Confirm a fresh agent actually reads the contract before declaring this step done.

  1. Start a fresh agent session in the project and watch for unprompted stack/convention references.

    Terminal window
    # From project root — constitution files on disk
    test -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 seed

    Ask 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).
  1. Stage and commit C3 from the project root.

    Terminal window
    git branch --show-current # must be develop
    git add AGENTS.md CLAUDE.md .gitignore
    # Option A1 (default): commit the op:// template — references only
    if git check-ignore -q .env.tpl 2>/dev/null; then
    echo "A2 — .env.tpl gitignored by owner choice; skip git add"
    elif test -f .env.tpl; then
    git add .env.tpl
    fi
    git 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 -3 shows C3; git check-ignore CLAUDE.local.md still succeeds.
    • A1: .env.tpl in C3 (references only). A2: .env.tpl gitignored — confirm with git check-ignore .env.tpl and record the choice in CLAUDE.local.md.

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.md written — at the repo root, tech-stack line matches the auto-detected stack; C3 committed.
  • 🤖 CLAUDE.md written — thin pointer only; no duplicated stack/conventions; included in C3.
  • 👤 CLAUDE.local.md created — personal env filled in; references (not values) for credentials.
  • 🤖 CLAUDE.local.md gitignoredgit check-ignore CLAUDE.local.md echoes the filename.
  • 👤 Credential store chosen — Option A (1Password, default), B (vault templates), or C (credentials.md) recorded in CLAUDE.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 shared General-Dev vault both exist; project SA can read both (op vault ls lists both).
  • 🔀 Env items shaped (Option A)Local / Staging / Production items 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.example are editable.
  • 🔀 Operator Provisioning Gate run — every input the playbook needs (hosting, GitHub, DNS/Cloudflare, payments, email, observability, vendor license, 1Password) captured into op:// with a needed-by-Phase / status row; no early-phase blocker left .
  • 🔀 Constitution verified — a fresh agent session references the stack/conventions unprompted.
  • 🤖 C3 committed on developAGENTS.md + CLAUDE.md + .gitignore (+ .env.tpl for A1 only; A2 confirms git check-ignore .env.tpl); CLAUDE.local.md gitignored.