---
phase: 1
plan: 3
type: execute
wave: 1
depends_on: []
files_modified:
  - .env.example
  - docs/dogfood-setup.md
  - scripts/dogfood-report.mjs
  - .planning/research/WEEKLY-LOG.md
  - __tests__/unit/dogfood-report.test.js
autonomous: false
requirements: [DOG-01, USR-03]
must_haves:
  truths:
    - "Wes's personal Claude Code environment has DASHCLAW_BASE_URL, DASHCLAW_API_KEY, DASHCLAW_HOOK_MODE, and DASHCLAW_RISK_THRESHOLD set and the .claude/hooks/dashclaw_pretool.py hook fires on every Bash/Edit/Write/MultiEdit tool call in his daily workflow"
    - ".env.example documents all four hook env vars with comments explaining where each points, so any future dogfooder (including Wes re-setting up a new machine) has a reference"
    - "docs/dogfood-setup.md contains the exact shell commands Wes runs to wire his personal Claude Code to his live DashClaw instance, plus the Discord webhook setup steps"
    - "A simple scripts/dogfood-report.mjs queries GET /api/guard?days=7 on Wes's personal org and prints a 7-day summary showing which days had at least one governed decision — the pass/fail metric for the >=5-of-7-days commitment"
    - "A .planning/research/WEEKLY-LOG.md file exists with a weekly ritual template (Friday slot) — the USR-03 artifact"
    - "At least one real weekly-log entry exists (the first Friday after this plan ships) proving the ritual is not vaporware"
  artifacts:
    - path: ".env.example"
      provides: "Documented DASHCLAW_BASE_URL, DASHCLAW_API_KEY, DASHCLAW_HOOK_MODE, DASHCLAW_RISK_THRESHOLD for Claude Code hook"
      contains: "DASHCLAW_HOOK_MODE"
    - path: "docs/dogfood-setup.md"
      provides: "Step-by-step personal dogfood wiring guide"
      contains: "DASHCLAW_HOOK_MODE"
    - path: "scripts/dogfood-report.mjs"
      provides: "7-day activity report querying /api/guard"
      contains: "days=7"
    - path: ".planning/research/WEEKLY-LOG.md"
      provides: "Weekly user-research ritual log with at least one real entry"
      contains: "Friday"
  key_links:
    - from: "scripts/dogfood-report.mjs"
      to: "GET /api/guard?days=7 endpoint (app/api/guard/route.js)"
      via: "HTTP fetch with Bearer DASHCLAW_API_KEY header"
      pattern: "api/guard"
    - from: "docs/dogfood-setup.md"
      to: ".claude/hooks/dashclaw_pretool.py env var reads"
      via: "documented shell config that sets each env var the hook expects"
      pattern: "DASHCLAW_BASE_URL"
    - from: ".env.example"
      to: "docs/dogfood-setup.md reference link"
      via: "inline comment pointing readers to the setup doc"
      pattern: "dogfood-setup"
---

<objective>
Pull Wes's Claude Code -> DashClaw dogfood setup out of the GitHub issue #46 comment (where it's been hiding since 2026-03-18) and turn it into a committed daily ritual with documented instrumentation. RESEARCH.md section H confirms the wiring is COMPLETE — `.claude/settings.json`, `.claude/hooks/dashclaw_pretool.py`, and `app/lib/actionAlerts.js` are all in place. What's missing is: (1) env var documentation so Wes can reproduce his setup and anyone else can follow along later, (2) a proof script that uses the existing `/api/guard?days=7` data to verify the >=5-of-7-days commitment, (3) a weekly user-research ritual file (USR-03).

**Not autonomous.** Tasks 3 and 5 are human actions Wes performs on his own machine and against his personal DashClaw instance. The AI executor scaffolds the docs, builds the report script, and writes tests — Wes runs the hook wiring, verifies one real governed command fires end-to-end, and writes the first real weekly log entry.

Purpose: close DOG-01 (daily dogfood, >=5-of-7 days proof) and USR-03 (weekly research ritual scheduled) — ROADMAP Phase 1 success criteria #6 and #7.

Output: `.env.example` updated, new `docs/dogfood-setup.md`, new `scripts/dogfood-report.mjs` with a unit test, new `.planning/research/WEEKLY-LOG.md` with the template and first real entry.
</objective>

<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/REQUIREMENTS.md
@.planning/ROADMAP.md
@.planning/phases/01-foundation/01-RESEARCH.md
@.claude/hooks/dashclaw_pretool.py
@.claude/hooks/dashclaw_posttool.py
@.claude/settings.json
@app/lib/actionAlerts.js
@app/api/guard/route.js
@.env.example

<interfaces>
<!-- What the hook reads and what /api/guard returns. Do NOT go re-exploring. -->

From `.claude/hooks/dashclaw_pretool.py` (RESEARCH.md section H):
- Reads env: DASHCLAW_BASE_URL, DASHCLAW_API_KEY, DASHCLAW_AGENT_ID, DASHCLAW_HOOK_MODE, DASHCLAW_RISK_THRESHOLD
- Calls POST to {DASHCLAW_BASE_URL}/api/guard on every PreToolUse matching `Bash|Edit|Write|MultiEdit`
- On require_approval: creates a pending action via POST /api/actions, polls GET /api/actions/{id} every 3s for up to 30s
- Blocks with exit code 2 on deny or timeout; allows on approved or status=running

From GET /api/guard?days=7 (app/api/guard/route.js, RESEARCH.md section I):
- Returns `{ decisions: [...], stats: { total_24h, blocks_24h, warns_24h, approvals_24h } }`
- Supports `?from=<iso>&to=<iso>` date filter
- Each decision has a `created_at` timestamp — sufficient to bucket by day
- Requires an API key in the Authorization header that maps to Wes's org

From app/lib/actionAlerts.js:
- On action creation with `status: 'pending_approval'`, fires a Discord webhook embed to the DISCORD_WEBHOOK_URL configured in the org's settings (not an env var — it's a per-org setting stored in DB)

From .env.example (current state):
- Has DASHCLAW_API_KEY for server-side use
- Does NOT have DASHCLAW_BASE_URL, DASHCLAW_HOOK_MODE, DASHCLAW_RISK_THRESHOLD, DASHCLAW_AGENT_ID

This is a documentation gap, not a code gap. The hook runs today on Wes's machine because Wes manually set those env vars. New dogfooders (or Wes on a new laptop) have no reference.
</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Document Claude Code hook env vars in .env.example</name>
  <files>.env.example</files>
  <read_first>
    - .env.example (full file — find where existing DASHCLAW_API_KEY line is to insert nearby)
    - .claude/hooks/dashclaw_pretool.py (confirm the exact env var names the hook actually reads — do not guess)
  </read_first>
  <action>
    Open `.env.example`. Find the existing `DASHCLAW_API_KEY=` line. Immediately BELOW it, add a new section (do not disturb any existing section). The new block should contain:

    1. A comment header titled `Claude Code dogfood hook (.claude/hooks/dashclaw_pretool.py)` explaining these vars are read on the machine where Claude Code runs, not the DashClaw server, and referencing `docs/dogfood-setup.md` for the step-by-step guide.
    2. `DASHCLAW_BASE_URL=https://dashclaw.example.com` with a comment noting "Base URL of YOUR personal DashClaw instance. The hook POSTs /api/guard here." Give both a Vercel example and a local-dev example.
    3. `DASHCLAW_HOOK_MODE=enforce` with a comment explaining enforce vs observe semantics.
    4. `DASHCLAW_RISK_THRESHOLD=0.6` with a comment explaining the 0.0-1.0 range and that 0.6 is the recommended starting value.
    5. `DASHCLAW_AGENT_ID=claude-code-<your-handle>` with a comment that it's optional but groups sessions together in the dashboard.

    Keep `DASHCLAW_API_KEY` on its existing line — the hook reuses it (the same key that the DashClaw server uses for auth also authenticates the hook's /api/guard calls). Do NOT commit any real secret values.
  </action>
  <verify>
    <automated>grep -c 'DASHCLAW_BASE_URL\|DASHCLAW_HOOK_MODE\|DASHCLAW_RISK_THRESHOLD\|DASHCLAW_AGENT_ID' .env.example</automated>
  </verify>
  <acceptance_criteria>
    - `.env.example` contains all four new env vars: DASHCLAW_BASE_URL, DASHCLAW_HOOK_MODE, DASHCLAW_RISK_THRESHOLD, DASHCLAW_AGENT_ID
    - `.env.example` still contains the pre-existing DASHCLAW_API_KEY line unchanged
    - The new section references `docs/dogfood-setup.md`
    - No secret values written — only placeholders like `https://dashclaw.example.com` and `claude-code-<your-handle>`
    - `grep -c` of the four new vars returns 4
    - `git status` shows only `.env.example` modified for this task
  </acceptance_criteria>
  <done>Future dogfood setup is reproducible from `.env.example` alone.</done>
</task>

<task type="auto">
  <name>Task 2: Write docs/dogfood-setup.md — exact wiring guide for Wes's personal dogfood</name>
  <files>docs/dogfood-setup.md</files>
  <read_first>
    - .claude/hooks/dashclaw_pretool.py (the exact env vars it reads and the exit-code semantics)
    - .claude/settings.json (the PreToolUse / PostToolUse matcher config)
    - app/lib/actionAlerts.js (how the Discord webhook URL is sourced — per-org setting, not env var)
    - .env.example (reference the new hook section added in Task 1)
  </read_first>
  <action>
    Create `docs/dogfood-setup.md`. The doc must contain the following sections in this order (write real prose and real commands — not placeholders):

    1. **Title + audience note:** "Audience: Wes. Also anyone else who wants to put their own Claude Code in front of their own DashClaw instance. This IS the beachhead install path — founder-only setup up to now, this doc exists so it's no longer tribal knowledge." State "Closes: DOG-01. Prerequisite for CCI-01 (Phase 2)."

    2. **What this wires together:** An ASCII flow diagram showing Claude Code tool call -> dashclaw_pretool.py -> POST /api/guard -> one of three branches (allow / warn / require_approval), with the require_approval branch continuing to "creates pending action -> polls /api/actions/{id} -> Discord webhook fires -> Wes approves from phone -> tool runs or blocks."

    3. **Prerequisites:** (a) live DashClaw instance Wes controls (Vercel or Postgres self-host), (b) API key from `/connect` — reference `docs/smoke-tests/connect-api-key.md` from FIX-02, (c) Discord webhook URL configured in the DashClaw org settings (path: `/mission-control` -> settings -> Discord — stored in DB, not env), (d) Claude Code installed on the daily-work machine.

    4. **One-time setup (~5 min):**
       - **Step 1 — Set env vars** with both a PowerShell ($PROFILE) block AND a bash/zsh (~/.bashrc, ~/.zshrc) block, both setting DASHCLAW_BASE_URL, DASHCLAW_API_KEY, DASHCLAW_HOOK_MODE=enforce, DASHCLAW_RISK_THRESHOLD=0.6, DASHCLAW_AGENT_ID=claude-code-wes. Note that CLAUDE.md says Wes is on Windows 11, so PowerShell is the primary target.
       - **Step 2 — Confirm Claude Code hook config is active:** State that `.claude/settings.json` is already wired (PreToolUse matches `Bash|Edit|Write|MultiEdit` -> `python .claude/hooks/dashclaw_pretool.py`), no edits needed. Note the `python` vs `python3` PATH caveat for Windows, and state explicitly: "don't commit that per-machine quirk."
       - **Step 3 — Configure the Discord webhook in DashClaw:** UI steps via `/mission-control` settings. Reiterate that the webhook URL lives in DB per-org, not in env.
       - **Step 4 — Fire one real governed command to verify end-to-end:** Give Wes a concrete test command to try in a Claude Code session ("delete the node_modules folder and reinstall" is a good example — moderate risk, should trigger require_approval). List the expected 6-step flow. Include curl-based diagnostics for each failure mode: unreachable BASE_URL, invalid API_KEY, webhook not wired.

    5. **Daily ritual:** Two sentences. "Work normally in Claude Code. When approval is needed, your phone buzzes via Discord. Approve/deny from Discord mobile."

    6. **Weekly proof:** "Run `node scripts/dogfood-report.mjs` every Friday (or let a cron post it). It queries `/api/guard?days=7` on your org and prints: number of days in the last 7 with >=1 governed decision, total decisions broken down by allow/warn/require_approval/block, and a pass/fail against the DOG-01 >=5-of-7 criterion. Paste the output into your weekly Friday slot in `.planning/research/WEEKLY-LOG.md` as evidence."

    7. **Troubleshooting table:** A 6-row markdown table covering: (a) hook always allows -> wrong MODE, (b) hook never fires -> settings.json matcher or python PATH, (c) 401 from /api/guard -> wrong API_KEY, (d) Discord silent -> webhook not wired in org settings, (e) 5xx from /api/guard -> server-side issue, (f) poll timeout -> false positives on RISK_THRESHOLD or phone not near.

    8. **Why this doc exists:** Short paragraph referencing SIGNAL.md + issue #46 (flow has worked since 2026-03-18, undocumented outside a comment). "Phase 1 DOG-01 makes it a committed daily ritual; Phase 2 CCI-01 turns this doc into the external-facing Claude Code install guide."

    The doc must be self-contained — a new dogfooder reading only this file + `.env.example` should be able to wire everything up without exploring other files.
  </action>
  <verify>
    <automated>test -f docs/dogfood-setup.md && grep -q "DASHCLAW_BASE_URL" docs/dogfood-setup.md && grep -q "DASHCLAW_HOOK_MODE" docs/dogfood-setup.md && grep -q "dogfood-report.mjs" docs/dogfood-setup.md && grep -q "DOG-01" docs/dogfood-setup.md</automated>
  </verify>
  <acceptance_criteria>
    - `docs/dogfood-setup.md` exists and is >= 80 lines
    - Contains all four env var names: DASHCLAW_BASE_URL, DASHCLAW_HOOK_MODE, DASHCLAW_RISK_THRESHOLD, DASHCLAW_AGENT_ID
    - Contains BOTH a PowerShell block AND a bash/zsh block for Step 1
    - Contains the string `DOG-01` and `CCI-01`
    - Contains a reference to `scripts/dogfood-report.mjs`
    - Contains a reference to `.planning/research/WEEKLY-LOG.md`
    - Contains a reference to `.claude/settings.json` and explains the PreToolUse matcher
    - Does NOT touch `README.md`, `app/page.js`, or any other file
  </acceptance_criteria>
  <done>A dogfooder can go from "zero" to "first governed command" in 5 minutes following only this doc.</done>
</task>

<task type="auto" tdd="true">
  <name>Task 3: Build scripts/dogfood-report.mjs + unit test</name>
  <files>scripts/dogfood-report.mjs, __tests__/unit/dogfood-report.test.js</files>
  <read_first>
    - scripts/migrate-action-records-compat.mjs (pattern for a standalone mjs script in this repo — ESM imports, process.env access, exit code handling, finally-block teardown)
    - app/api/guard/route.js (confirm the response shape — `{ decisions: [...], stats: {...} }` with `created_at` on each decision)
    - __tests__/unit/validate.test.js or any __tests__/unit/*.route.test.js (reference for Vitest + vi.hoisted + mock patterns)
    - .planning/codebase/CONVENTIONS.md (error handling: try/catch with `console.error('[LABEL] message:', error)`, fail loudly, `process.exitCode = 1` + finally teardown)
  </read_first>
  <behavior>
    - Test 1 `reports pass when >=5 of 7 days have decisions`: mock fetch to return 7 decisions across 5 different calendar days, assert the script prints "PASS" and exits with code 0
    - Test 2 `reports fail when <5 of 7 days have decisions`: mock fetch to return 7 decisions all on the same day, assert the script prints "FAIL" and exits with code 1
    - Test 3 `handles empty decisions array`: mock fetch returning `{ decisions: [], stats: {...} }`, assert "FAIL (0 of 7 days)" printed, exit 1
    - Test 4 `fails clean with helpful message when DASHCLAW_BASE_URL missing`: unset env var, assert the script prints a specific error referencing the env var name and exits with code 1 (not 0, not crash)
    - Test 5 `fails clean when fetch throws (network down / 5xx)`: mock fetch to reject, assert error is printed and exit 1 (not unhandled rejection)
    - Test 6 `bucketing: decisions spanning midnight are counted on their own calendar day`: mock 2 decisions at 23:50 day 1 and 00:10 day 2, assert day count = 2
  </behavior>
  <action>
    1. **Create `scripts/dogfood-report.mjs`:**

    The script must:
    - Be a standalone ESM module (`.mjs`). Import nothing from `app/` — this script runs outside the Next.js app context.
    - Read `DASHCLAW_BASE_URL` and `DASHCLAW_API_KEY` from `process.env`. If either is missing, print a clear error (`[dogfood-report] Missing DASHCLAW_BASE_URL — see docs/dogfood-setup.md`) and set `process.exitCode = 1`, then return.
    - Compute an ISO `from` timestamp 7 days ago (UTC, start of day).
    - Call `GET ${DASHCLAW_BASE_URL}/api/guard?days=7` with header `Authorization: Bearer ${DASHCLAW_API_KEY}`. Use the global `fetch` (Node 20+).
    - On non-2xx response: print the status and a short body preview, set `process.exitCode = 1`, return.
    - On success: parse JSON, extract `decisions` array. Bucket each decision's `created_at` into a `YYYY-MM-DD` key in the local (or UTC — pick one and document it) timezone. Count unique day-keys within the last-7-days window.
    - Tally total decisions plus breakdowns by `decision` field (`allow` / `warn` / `require_approval` / `block` — match whatever the guard route returns; treat unknown values as "other").
    - Print a formatted report:
      ```
      DashClaw Dogfood Report — last 7 days
      =====================================
      Active days: N of 7
      Total decisions: M
         allow:            a
         warn:             w
         require_approval: r
         block:            b
      DOG-01 target: >=5 active days
      Result: PASS / FAIL
      ```
    - Exit code: 0 if PASS, 1 if FAIL. Use `process.exitCode` + `return`, not `process.exit()`, so the `finally` block (connection cleanup) runs cleanly. No DB connections in this script — the only teardown is the fetch, which is self-contained.
    - Wrap everything in an async `main()` with `try/catch/finally`. `catch` logs and sets exit code 1. Top-of-file comment: `// Dogfood proof script. Queries /api/guard?days=7 and reports against DOG-01 >=5-of-7 criterion. See docs/dogfood-setup.md.`
    - Expose the bucketing + pass/fail logic as NAMED EXPORTS so the test can import and test them without running the script: `export function bucketByDay(decisions)`, `export function tally(decisions)`, `export function formatReport({ activeDays, total, breakdown, pass })`. The `main()` default IIFE checks `import.meta.url === \`file://${process.argv[1]}\`` to avoid running on import.

    2. **Create `__tests__/unit/dogfood-report.test.js`:**

    - Import the three named exports: `bucketByDay`, `tally`, `formatReport`.
    - Use `vi.hoisted()` + `vi.stubGlobal('fetch', ...)` to mock the global fetch for the end-to-end test cases (Tests 4 and 5).
    - Structure:
      ```javascript
      import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
      import { bucketByDay, tally, formatReport } from '../../scripts/dogfood-report.mjs';

      describe('dogfood-report', () => {
        describe('bucketByDay', () => { /* Tests 1, 2, 3, 6 */ });
        describe('tally', () => { /* breakdown by decision type */ });
        describe('formatReport', () => { /* pass/fail wording */ });
      });
      ```
    - For Tests 4 and 5 (end-to-end env/fetch behavior), use a dynamic `import('../../scripts/dogfood-report.mjs')` inside the test with the env and fetch mocked first — this re-triggers module evaluation. OR (preferred) refactor the main() logic into an exported `runReport({ env, fetch })` function that takes injectable dependencies, and test that directly. Pick the cleaner approach.
    - Every test must be deterministic — no real network calls, no real env reads, no timing dependencies.

    3. Do NOT touch any other file. Do NOT add a `package.json` script entry in this task (leave that to a later phase that wants `npm run dogfood:report` — keep this plan's scope small).
  </action>
  <verify>
    <automated>test -f scripts/dogfood-report.mjs && test -f __tests__/unit/dogfood-report.test.js && npm test -- --run dogfood-report && node --check scripts/dogfood-report.mjs</automated>
  </verify>
  <acceptance_criteria>
    - `scripts/dogfood-report.mjs` exists, is valid ESM (`node --check scripts/dogfood-report.mjs` exits 0)
    - Exports `bucketByDay`, `tally`, `formatReport` (grep confirms)
    - Contains the strings `DASHCLAW_BASE_URL`, `DASHCLAW_API_KEY`, `/api/guard`, `DOG-01`, `>=5`
    - Does NOT import from `app/`, `@/`, or any Next.js-specific path
    - Reads env only via `process.env`, never from a `.env` file directly
    - `__tests__/unit/dogfood-report.test.js` exists and contains all 6 test cases (matching the `<behavior>` block)
    - `npm test -- --run dogfood-report` exits 0 with all 6 tests passing
    - `npm run lint` exits 0
    - No files outside `files_modified` touched
  </acceptance_criteria>
  <done>Weekly proof mechanism works, is tested, and can be run by Wes or scheduled later.</done>
</task>

<task type="checkpoint:human-verify" gate="blocking">
  <name>Task 4: Wes wires his personal Claude Code and fires one real governed command</name>
  <what-built>
    Tasks 1-3 produced: `.env.example` documentation, `docs/dogfood-setup.md` walkthrough, `scripts/dogfood-report.mjs` proof script + tests. The AI cannot actually set env vars on Wes's machine, cannot configure a Discord webhook on Wes's personal DashClaw instance, and cannot trigger a Claude Code session. This is the one-shot human action that proves the whole pipeline is live end-to-end.
  </what-built>
  <how-to-verify>
    1. Open `docs/dogfood-setup.md` in the repo. Follow the "One-time setup" section (Steps 1-4) exactly.
    2. Step 1: Set all 5 env vars in your PowerShell `$PROFILE` (since you're on Windows 11). Use your REAL DashClaw Vercel URL and your REAL API key — do NOT commit these anywhere. Restart your shell or `.` $PROFILE to reload.
    3. Step 2: Verify `.claude/settings.json` PreToolUse hook is active. No edits expected.
    4. Step 3: Configure the Discord webhook in your personal DashClaw instance via `/mission-control` settings. Test the webhook URL once with curl to confirm it posts to your Discord channel.
    5. Step 4: Open a Claude Code session in any repo. Ask Claude to run a moderate-risk command (e.g., "list files then delete all .tmp files in the cwd"). Expected:
       - Hook fires on the Bash tool call
       - POST /api/guard returns a decision
       - If decision is `require_approval`: pending action created, Discord notification reaches your phone within ~1 second, you approve from Discord mobile, Claude Code proceeds
       - If decision is `allow`: the command runs immediately and a decision is recorded (check `/mission-control` for the event)
    6. After the session, run `node scripts/dogfood-report.mjs` from the repo root. Expect: at least 1 active day (today), at least 1 decision in the total, exit code 1 (FAIL because 1 of 7 days — PASS comes after 5 days of real use).
    7. Screenshot the Discord notification AND the script output. Save to `.planning/phases/01-foundation/evidence/` (create the dir if needed). These two screenshots are the evidence attached to 01-03-SUMMARY.md that DOG-01 is verifiably live.
    8. If ANY step fails: consult the Troubleshooting table in `docs/dogfood-setup.md`, fix the config, retry. If the fix required a code change (not just config): STOP and open a new gap-closure plan — do NOT edit the tasks in this plan retroactively.
  </how-to-verify>
  <resume-signal>Type "wired" when end-to-end has fired successfully and the two screenshots are filed. Type "blocked: <reason>" if something in the wiring requires a code fix.</resume-signal>
</task>

<task type="auto">
  <name>Task 5: Scaffold .planning/research/WEEKLY-LOG.md for USR-03 weekly ritual</name>
  <files>.planning/research/WEEKLY-LOG.md</files>
  <read_first>
    - .planning/phases/01-foundation/01-RESEARCH.md section J (the 3 ritual options and the recommendation: Option 1 Friday Discord presence + Option 2 Monday GitHub outreach slot for the first 4 months)
    - .planning/research/SIGNAL.md (the 4 identified users + the "expand to fork owners who haven't been contacted" note)
    - .planning/REQUIREMENTS.md USR-03 definition
  </read_first>
  <action>
    Create `.planning/research/WEEKLY-LOG.md` with the following structure (real content, not placeholders):

    1. **Title + purpose:** "Weekly user-research ritual log. Closes USR-03. Tracks the minimum-viable weekly research cadence that starts in Phase 1 and continues indefinitely."

    2. **Ritual definition** (Option 1 + Option 2 combined from RESEARCH.md section J):
       - Every **Monday** 30-min slot: one outbound GitHub comment to a user who has interacted with DashClaw (existing issues, PRs, forks). Start with the 4 from OUTREACH.md, then expand to fork owners who haven't been contacted.
       - Every **Friday** 20-min slot: presence in the DashClaw GitHub Discussions (currently empty — flag in the first entry that it needs to be enabled and seeded) and any homelab / Claude Code Discords Wes is in. Log one interaction per week. Wes already has Discord open for approvals, so Friday presence is an additive habit, not a new one.
       - **Hard minimum:** one logged entry per calendar week. Missed weeks must be written up in the log as "MISSED — reason" — do NOT fake back-fills.

    3. **Entry template** (copy-paste block for Wes each week):
       ```
       ## Week of YYYY-MM-DD

       ### Monday outreach slot
       - Reached out to: @<handle>
       - Channel: <link to issue/PR/discussion>
       - Message summary: <2-3 sentences>

       ### Friday presence slot
       - Where: <GitHub Discussions / Authentik Discord / homelab Discord / etc>
       - Interaction: <what happened — who said what, what you learned>

       ### What I learned this week
       - <bullet>
       ```

    4. **Entries section** — START EMPTY. Do NOT seed a fake week. The first real entry gets added in Task 6 as the human-action checkpoint.

    5. **Escalation path:** A short note that says: "If 3 consecutive weeks get skipped, the ritual is broken and USR-03 needs to be re-planned as a retrospective. Don't silently stop logging."
  </action>
  <verify>
    <automated>test -f .planning/research/WEEKLY-LOG.md && grep -q "Monday" .planning/research/WEEKLY-LOG.md && grep -q "Friday" .planning/research/WEEKLY-LOG.md && grep -q "USR-03" .planning/research/WEEKLY-LOG.md</automated>
  </verify>
  <acceptance_criteria>
    - `.planning/research/WEEKLY-LOG.md` exists
    - Contains both `Monday` and `Friday` slot definitions
    - Contains the template block with `## Week of`, `### Monday outreach slot`, `### Friday presence slot`, `### What I learned this week`
    - Contains `USR-03` reference
    - Contains the "MISSED — reason" escalation language
    - Entries section is EMPTY (no fake week seeded by the AI)
  </acceptance_criteria>
  <done>Weekly ritual defined in a living document. Ready for Wes to log the first real week.</done>
</task>

<task type="checkpoint:human-verify" gate="blocking">
  <name>Task 6: Wes logs the first real week in WEEKLY-LOG.md</name>
  <what-built>
    Task 5 produced the ritual template and structure, but USR-03 can't be closed by a template alone — it needs proof that the first week actually happened. Wes's Monday outreach slot for this week is already covered by Plan 01-02 (sending outreach to the 4 SIGNAL.md users), so the first Monday entry can reference those sends. The Friday slot needs to be a real interaction, not a placeholder.
  </what-built>
  <how-to-verify>
    1. On the first Friday after this plan lands, spend 20 minutes in GitHub Discussions (enable them if not already — the ROADMAP/SIGNAL.md notes they're empty) OR in a homelab / Claude Code adjacent Discord channel.
    2. Open `.planning/research/WEEKLY-LOG.md`. Copy the template block. Fill in the real entry for this week — the Monday slot can cite "Sent first-ever outreach to @RyanTJoy, @elpolini, @joryirving, @jsidhu per 01-02-PLAN" with the actual URLs from OUTREACH.md. The Friday slot must be a REAL interaction (a comment you made, a question you answered, a thread you read and learned from — something with a URL or a 2-sentence takeaway, not "lurked").
    3. Under "What I learned this week," write 1-3 bullets with real observations. If the only learning is "zero engagement so far," that's a valid entry — write it.
    4. Commit with message `docs(research): first weekly ritual entry (USR-03)`.
    5. **Anti-fake guardrail:** If you're tempted to write a placeholder entry to make the plan "look done," STOP. USR-03 is about whether the ritual actually survives contact with reality. A committed missed-week log entry ("MISSED — reason: shipping Phase 2 discovery") is more valuable than a faked entry. The plan SUMMARY should honestly report the state.
  </how-to-verify>
  <resume-signal>Type "logged" when the first real week is in WEEKLY-LOG.md. Type "deferred: <reason>" if the first Friday is more than 5 days away and you want to close this plan early with an explicit commitment.</resume-signal>
</task>

</tasks>

<verification>
- `.env.example` contains the 4 new hook env vars and references `docs/dogfood-setup.md`
- `docs/dogfood-setup.md` exists, >= 80 lines, covers all 4 setup steps with PowerShell + bash blocks
- `scripts/dogfood-report.mjs` is a valid ESM module (`node --check` passes), exports `bucketByDay`/`tally`/`formatReport`, runs without errors on `node scripts/dogfood-report.mjs` against a live deployment OR exits 1 cleanly with a helpful error when env is missing
- `__tests__/unit/dogfood-report.test.js` exists with 6 test cases, `npm test -- --run dogfood-report` exits 0
- `npm run lint` exits 0
- `.planning/research/WEEKLY-LOG.md` exists with ritual definition and entry template
- Wes has fired one end-to-end governed command (evidence: screenshots in `.planning/phases/01-foundation/evidence/`)
- Wes has logged the first real week in WEEKLY-LOG.md (or an honest missed-week entry)
- No files outside `files_modified` touched
</verification>

<success_criteria>
- DOG-01: End-to-end Claude Code -> DashClaw -> Discord -> approval flow fires on Wes's machine, with evidence screenshots filed and `scripts/dogfood-report.mjs` available for weekly proof against the >=5-of-7 criterion
- USR-03: Weekly research ritual defined in `.planning/research/WEEKLY-LOG.md`, first real entry committed (Monday outreach via 01-02, Friday presence logged honestly)
- All documentation is self-contained — a new dogfooder can follow `docs/dogfood-setup.md` without reading source code
- `dogfood-report.mjs` is testable and tested — not a "trust me it works" script
- No fake/seeded entries in WEEKLY-LOG.md
</success_criteria>

<output>
After completion, create `.planning/phases/01-foundation/01-03-SUMMARY.md` including:
- DOG-01 status: did end-to-end fire? Screenshot paths. First `dogfood-report.mjs` output (expected: 1 of 7 days PASS, which is "FAIL at start of week, will be PASS by end of week 1 if ritual holds")
- USR-03 status: ritual defined + first week logged? Honest state (logged / partially logged / deferred with reason)
- Any drift from what RESEARCH.md section H said the current wiring looked like (if anything differed on the live machine, flag it)
- Follow-up for Phase 2 CCI-01: `docs/dogfood-setup.md` should graduate into the external-facing Claude Code install doc. Note what sections need to be generalized vs kept Wes-specific
</output>
