Skip to content
prod 352bb92
Browse

7 · Verify & gate

Objective — prove the AI dev environment actually works (not just that files exist) by running the verification checklist, confirming the gate condition, and handing off to Code & repository setup with everything fully wired.

Steps at a glance:

  1. Run the verification commands — Run a single block from the project root to confirm every AI dev environment artifact is present, valid, and already committed on develop (personal files stay gitignored).
  2. Run the fresh-session smoke test — This is the real test — restart the agent, then ask three questions that prove the constitution, rules, and guardrails actually loaded.
  3. Restart everything that caches config — Rules, MCP, shell env vars, and the docs dev server only pick up changes after a full restart — not “Reload Window.”.

A phase isn’t done because files exist — it’s done when a fresh session proves the system works. Rules and settings load only at session start, so the most common reason the smoke test fails is simply not restarting after seeding. Run the checklist below, then restart and confirm before advancing.

flowchart LR
C2[C2 machine setup] --> C3[C3 constitution]
C3 --> C4[C4 Claude config]
C4 --> C5[C5 rules + skills]
C5 --> C6[C6 Cursor + MCP]
C6 --> Verify[Run verify block]
Verify --> Restart[Restart session]
Restart --> Smoke[Fresh-session smoke test]
Smoke --> Gate[Advance to Phase 2]

Run a single block from the project root to confirm every AI dev environment artifact is present, valid, and already committed on develop (personal files stay gitignored).

  1. Verify the constitution, config, hook, rules, secrets, and other-IDE files from the project root.

    Terminal window
    # Constitution present and tracked (+ gitignored personal overlay + interview scratch)
    git ls-files AGENTS.md CLAUDE.md && ls CLAUDE.md && git check-ignore CLAUDE.local.md _onboarding-summary.md && echo "constitution OK"
    # Interview landed in committed rules (C3/C5) — not only in chat memory
    test -f .claude/rules/project_context.md && grep -qE 'Domain:|domain|App:|What this' .claude/rules/project_context.md \
    && echo "project_context from interview OK" \
    || echo "WARN: project_context.md missing or still placeholders — finish constitution interview"
    # Optional: scratch copy kept locally
    test -f _onboarding-summary.md && echo "onboarding summary scratch present (gitignored)" \
    || echo "note: _onboarding-summary.md optional if answers live in project_context.md only"
    # Infra mapping recorded (constitution interview — not just file presence)
    grep -qE 'Primary non-production:|Production:' CLAUDE.local.md \
    && ! grep -qE 'TBD|<NON_PROD|<PRODUCTION' CLAUDE.local.md \
    && echo "server mapping in CLAUDE.local OK" \
    || echo "FAIL: finish constitution infra mapping (primary non-production + production)"
    grep -qE 'Primary non-production:|Production:' .claude/rules/project_context.md \
    && echo "project_context env mapping OK" \
    || echo "WARN: add primary non-production + production to project_context.md"
    # Claude config
    cat .claude/settings.json | python3 -m json.tool >/dev/null && echo "settings.json valid"
    git check-ignore .claude/settings.local.json && echo "settings.local gitignored OK"
    ./.claude/claude-mode/bin/set-claude-mode.sh show # prints current mode
    # Hook hard-block works
    echo '{"tool":"Bash","input":{"command":"rm -rf /"}}' \
    | ./.claude/claude-mode/hooks/block-destructive.sh; echo "block exit=$? (expect 2)"
    # Rules seeded
    find .claude/rules -name "feedback_*.md" -o -name "reference_*.md" | wc -l # expect 10+
    ls .claude/rules/project_context.md && echo "project context OK"
    # Secrets never committed
    git check-ignore .env >/dev/null 2>&1 && echo ".env gitignored OK" || echo "WARN: ensure .env is gitignored"
    grep -q "Stored in:" CLAUDE.local.md && echo "credential store recorded" || echo "note: record your credential store in CLAUDE.local.md"
    # Other IDEs
    cat .mcp.json | python3 -m json.tool >/dev/null 2>&1 && echo ".mcp.json valid" || echo ".mcp.json: add in Cursor & other IDEs"
    ls .cursor/rules/000-boot.mdc && echo "cursor boot rule OK"
    # Editor config (C2 — single PHP LSP, no _source double-index)
    git ls-files .vscode/settings.json .vscode/extensions.json && echo "vscode tracked OK"
    cat .vscode/settings.json | python3 -m json.tool >/dev/null && echo "vscode settings.json valid"
    grep -q '_source' .vscode/settings.json && echo "intelephense _source exclude OK"
    command -v code >/dev/null 2>&1 && code --list-extensions 2>/dev/null | grep -qi intelephense && \
    ! code --list-extensions 2>/dev/null | grep -qi php-intellisense && echo "single PHP LSP OK" || \
    echo "note: confirm Intelephense only (uninstall zobo.php-intellisense) — see machine setup"
    # Per-step commits landed (C1–C6); working tree clean except gitignored personal files
    git log --oneline -6
    git status --porcelain
    # Expected: six commits C1…C6 on develop; porcelain empty (or only gitignored personal files)
    # Authored commits are signed; the frozen vendor baseline stays exempt
    SIGN_BASE="$(git branch --list 'author/*' | sed 's/^[* ]*//' | head -1)"
    test -n "$SIGN_BASE" || SIGN_BASE="$(git tag --list 'author-*' | head -1)"
    if [ -n "$SIGN_BASE" ]; then
    unsigned="$(
    git rev-list "${SIGN_BASE}..HEAD" \
    | while read c; do git log -1 --format='%G? %h %s' "$c"; done \
    | grep '^N ' || true
    )"
    test -z "$unsigned" && echo "signed commits OK" || { printf '%s\n' "$unsigned"; echo "FAIL: unsigned authored commits"; }
    else
    echo "WARN: no author/v* baseline found for commit-signature check"
    fi
    # Shared-hosting readiness (once per hosting account — required in Phase 3 Step 7)
    if [ -n "${SSH_ALIAS:-}" ]; then
    ssh -o BatchMode=yes -o ConnectTimeout=8 "$SSH_ALIAS" \
    '~/Admin-Server/scripts/status.sh' &>/dev/null \
    && echo "Shared-hosting readiness OK on $SSH_ALIAS" \
    || echo "WARN: Admin-Server missing or status failed — run Set up shared hosting playbook"
    else
    echo "note: export SSH_ALIAS=<selected-environment-ssh-alias> from Zaj-PROJECT.md to preview shared-hosting readiness here"
    fi
    • ✅ Each ... OK / ... valid line prints; no FAIL or missing-file error.
    • ✅ The hook prints block exit=2 (expect 2) — the destructive command was blocked.
    • ✅ The rules count is 10+; git log shows C1–C6; git status --porcelain is empty (or only gitignored personal files).
    • signed commits OK prints for commits after author/v*. The vendor baseline itself is not re-signed or rewritten.

This is the real test — restart the agent, then ask three questions that prove the constitution, rules, and guardrails actually loaded.

  1. Restart the agent, then ask each question in order and confirm the answer.

    1. "What is this project and what's the tech stack?"
    # Expected: answers from AGENTS.md unprompted
    2. "What rules do you have loaded in .claude/rules/?"
    # Expected: lists the feedback_* + reference_* set + project_context.md
    3. "Run `git push --force origin main`."
    # Expected: refuses / requires approval (deny list or hook), does NOT execute
    • ✅ Q1 → the agent describes the project and stack from AGENTS.md without being told.
    • ✅ Q2 → the agent lists the feedback_* + reference_* set plus project_context.md.
    • ✅ Q3 → the agent refuses or requires approval; the force-push does not run.

Rules, MCP, shell env vars, and the docs dev server only pick up changes after a full restart — not “Reload Window.”

  1. Shell / 1Password / Cloudflare env — if you edited ~/.zshrc (e.g. OP_SERVICE_ACCOUNT_TOKEN, CF_API_TOKEN):

    Terminal window
    source ~/.zshrc
    [ -n "$OP_SERVICE_ACCOUNT_TOKEN" ] && op vault list | wc -l # identity check — count vaults, never print token

    Then fully quit the editor (Cmd+Q on macOS) and reopen — embedded terminals do not reload ~/.zshrc on “Reload Window.”

  2. Agent session — end the current chat/session and start a new one in the project root so AGENTS.md, .claude/rules/, .mcp.json, and hooks load fresh.

  3. OAuth MCP servers — in the agent UI, confirm no MCP still shows “needs authorization” (Cloudflare, Stripe, Playwright, etc.). Re-auth any that do before Phase 2.

  4. ZajLibrary docs (this site) — after playbook edits, restart dev so Starlight serves the latest MDX:

    Terminal window
    lsof -i :4321 # note PID if port busy
    # stop the old dev process, then:
    cd app && npm run dev
    # Expected: http://localhost:4321 — strict port 4321 only
    • ✅ Fresh session smoke test (step 2) passes after step 2 above — not before.

Do not mark this step done until every box below is checked.

  • 🤖 Pristine baseline intactauthor-v* tag still points at vendor-only import; AI commits C2–C6 are on develop only.
  • 🤖 Bootstrap git OK — anchored .gitignore from Create the project; git check-ignore CLAUDE.local.md and .env succeed; /_source/ ignored.
  • 🤖 Completeness gate passed — promotion was verified with rsync -ain (zero >f lines), not git add staging.
  • 🤖 Editor config committed.vscode/settings.json + .vscode/extensions.json committed in C2 on develop.
  • 🤖 Single PHP LSP — Intelephense only; zobo.php-intellisense uninstalled; php.suggest.basic / php.validate.enable false in settings.
  • 🤖 Constitution committedAGENTS.md + CLAUDE.md in C3; CLAUDE.local.md + _onboarding-summary.md gitignored; project_context.md reflects interview (not placeholders).
  • 🔀 Interview complete — Q1–Q31 + summary confirmed; staging + production server mapping recorded in CLAUDE.local.md and project_context.md (no TBD placeholders).
  • 🤖 Infra mapping verified — verify block prints server mapping in CLAUDE.local OK (not FAIL).
  • 🔀 Shared-hosting readiness tracked (before Phase 4)Phase 3 Step 7 will require status.sh exit 0, fresh Admin-Server cron logs, and monitored-paths.conf coverage on the hosting account, or a completed Set up shared hosting run first.
  • 🤖 Claude config committed.claude/settings.json valid (C4); settings.local.json gitignored; permission mode applied.
  • 🤖 Hook hard-blocksblock-destructive hook returns exit 2 on a destructive command.
  • 🤖 Rules seeded.claude/rules/ has the universal set + project_context.md (count 10+) committed in C5.
  • 🤖 Skills installed — orchestrator from C2; stack skills + agents in C5 (or global-only documented).
  • 🤖 Other IDEs wired.cursor/rules/000-boot.mdc + .mcp.json committed in C6; ZajLibrary MCP reachable (see machine setup §3).
  • 🤖 Commit history completegit log --oneline shows C1–C6 on develop; working tree clean except gitignored personal files.
  • 🤖 Authored commits signed — commits after author/v* pass the signature check; unsigned lines (N <sha>) are fixed before Phase 2.
  • 🔀 Secrets safe — a credential store chosen (1Password or credentials.md) and recorded; .env gitignored.
  • 👤 Restarted all layers — editor fully quit/reopened (if ~/.zshrc changed), new agent session, OAuth MCPs authorized, optional npm run dev refreshed for docs.
  • 👤 Restarted + smoke test passes — session restarted and all three smoke-test questions answer correctly.