Performance Enhancer Agent Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Build a generic, committed Claude Code agent prompt at docs/performance-enhancer/performance-enhancer.md that restarts the dev server with clean logs, runs user-supplied Playwright scenarios, captures server + client performance metrics, analyzes the logs for hotspots, and emits a fix-plan prompt whose completion gate is a before/after comparison report.

Architecture: The deliverable is a single Markdown prompt (no code). Users edit its ## Scenarios section, hand the file to Claude Code, and the agent runs three phases: (1) clean-slate server restart + login, (2) execute scenarios while harvesting log/development.log + log/bullet.log + Playwright nav time, (3) analyze and emit findings.md + plan.md under docs/performance-enhancer/runs/. The plan.md is a self-contained handoff prompt that includes a verification gate mandating the after-run comparison.

Tech Stack: Markdown prompt artifact; MCP Playwright (already available in Claude Code); bin/dev (foreman β†’ web on :3004, runs bin/rails logs:clear on boot); bullet gem (already in Gemfile β†’ log/bullet.log); Devise login at /users/sign_in with fields user[email] / user[password]; seeded user [email protected] / 123456.

Spec: docs/superpowers/specs/2026-04-22-performance-enhancer-design.md


File Structure

Created:

  • docs/performance-enhancer/performance-enhancer.md β€” the generic agent prompt (single file, ~400-500 lines)

Modified:

  • .gitignore β€” add one line to exclude generated run artifacts

Generated at runtime (not part of implementation, but specified by the agent):

  • docs/performance-enhancer/runs/<ts>-before.json
  • docs/performance-enhancer/runs/<ts>-findings.md
  • docs/performance-enhancer/runs/<ts>-plan.md
  • docs/performance-enhancer/runs/<ts>-after.json (produced by plan execution)
  • docs/performance-enhancer/runs/<ts>-comparison.md (produced by plan execution)

Since the deliverable is a prompt artifact, traditional unit tests don't apply. Each task ends with a structural verification (grep/Read) to confirm the required content landed, and the final task is an end-to-end smoke test that runs the agent against a real page.


Task 1: Create directory, add gitignore entry, commit

Files:

  • Create: docs/performance-enhancer/ (directory)
  • Create: docs/performance-enhancer/.gitkeep (so the empty dir is committed)
  • Modify: .gitignore (append one line)

  • Step 1: Create the directory and .gitkeep

Run:

mkdir -p /code/life-management/docs/performance-enhancer
touch /code/life-management/docs/performance-enhancer/.gitkeep
  • Step 2: Add gitignore entry for runs/

Append this line to /code/life-management/.gitignore:

docs/performance-enhancer/runs/
  • Step 3: Verify the gitignore entry landed

Run:

grep -n "docs/performance-enhancer/runs/" /code/life-management/.gitignore

Expected: one line showing the entry.

  • Step 4: Commit the scaffolding
cd /code/life-management
git add docs/performance-enhancer/.gitkeep .gitignore
git commit -m "$(cat <<'EOF'
Scaffold docs/performance-enhancer/ and ignore runs/

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
EOF
)"

Task 2: Create the agent .md with the header sections

Writes the top of the file: title, purpose, how-to-invoke, prerequisites, credentials, thresholds. This is the orientation section β€” everything a reader sees before the user-editable Scenarios block.

Files:

  • Create: docs/performance-enhancer/performance-enhancer.md

  • Step 1: Write the header sections

Write the file with exactly this content:

# Performance Enhancer Agent

A reusable prompt for diagnosing and fixing real-world performance issues in this Rails app. You supply an ordered list of UI scenarios; the agent restarts the dev server with clean logs, drives the scenarios via MCP Playwright, captures per-request server metrics (request time, query count, DB time, Bullet N+1 warnings) plus Playwright navigation time, analyzes hotspots in the code, and emits a self-contained **fix-plan prompt** you hand to a fresh Claude session. That plan prompt is required β€” by its own verification gate β€” to re-run the identical scenarios after implementation and produce a full before/after comparison report.

---

## How to invoke

1. Edit the `## Scenarios` section below with the pages and actions you want measured.
2. Open this file in Claude Code.
3. Say: **"Run the performance enhancer against this scenario list."**
4. Claude follows the instructions in `## Agent instructions` verbatim.

You do not edit anything outside `## Scenarios` (and `## Hotspot thresholds` if you want to tune sensitivity). Everything else is the agent's operational contract.

---

## Prerequisites

- The MCP Playwright plugin is active in your Claude Code session (provides `browser_navigate`, `browser_fill_form`, `browser_click`, etc.).
- You have run `bin/rails db:seed` so `[email protected]` exists.
- Port **3004** is free (the agent will kill stray processes; see troubleshooting if stuck).
- You are on a branch where uncommitted work is OK β€” the agent restarts the server but does not mutate code.

---

## Credentials (hardcoded β€” dev only)

- Email: `[email protected]`
- Password: `123456`
- Login URL: `http://localhost:3004/users/sign_in`

These come from `db/seeds.rb`. This file is dev-only; do not use these credentials to describe a production user.

---

## Hotspot thresholds (editable)

The agent flags a request as a hotspot if **any** of these are true:

- `total_ms > 500`
- `query_count > 30`
- `db_ms > 0.6 * total_ms` (DB-bound)
- any Bullet warning emitted during the request
- two or more identical queries within one request (loose dup detection by SQL fingerprint)

Tune these by editing the numbers. The agent reads them from this section at run time.

---
  • Step 2: Verify structural content

Run:

grep -c "^## " /code/life-management/docs/performance-enhancer/performance-enhancer.md

Expected: 4 (How to invoke, Prerequisites, Credentials, Hotspot thresholds).

grep -n "[email protected]\|3004\|bullet" /code/life-management/docs/performance-enhancer/performance-enhancer.md

Expected: matches for the seeded user, port 3004, and Bullet.

  • Step 3: Commit
cd /code/life-management
git add docs/performance-enhancer/performance-enhancer.md
git commit -m "$(cat <<'EOF'
Add performance-enhancer.md header sections

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
EOF
)"

Task 3: Append the Scenarios section (user-edited block)

The only section users touch before each run. Includes grammar rules and a worked example.

Files:

  • Modify: docs/performance-enhancer/performance-enhancer.md (append)

  • Step 1: Append the Scenarios section

Append exactly this content to the file:

## Scenarios

**This is the section you edit before each run.** Each step below becomes one Playwright action executed by the agent, in order. The after-run (triggered by the generated plan) replays this list verbatim from a frozen copy β€” do not change ordering, do not re-phrase steps between the before and after runs, or the comparison will be invalid.

### Grammar

- **One step per line**, imperative.
- Reference routes by **path** (e.g. `/goals`, `/goals/new`), not by human description.
- Form submissions include the data as an inline hash: `fill {field: "value", other_field: "value"}; submit`.
- Click targets use the visible button/link text in double quotes: `click "Edit"`.
- Chain actions on the same page with `;` within a single line.
- Use the resulting URL phrasing when an action produces a redirect: `On the resulting /goals/:id page, click "Edit"...`.
- Skip auth steps β€” the agent signs in first automatically.

### Example (replace with your own)

```
- Visit /dashboard
- Visit /goals
- Visit /goals/new; fill {title: "Perf test goal", target_date: "2026-12-31"}; submit
- On the resulting /goals/:id page, click "Edit"; change title to "Perf test goal edited"; submit
- Visit /debts
- Visit /market_lists
```

### Your scenarios

<!-- BEGIN user scenarios -->
- Visit /dashboard
<!-- END user scenarios -->

---
  • Step 2: Verify the Scenarios block exists with grammar + example + user-editable markers

Run:

grep -n "^## Scenarios\|BEGIN user scenarios\|END user scenarios\|### Grammar\|### Example" /code/life-management/docs/performance-enhancer/performance-enhancer.md

Expected: five matches in order (Scenarios heading, Grammar, Example, BEGIN marker, END marker).

  • Step 3: Commit
cd /code/life-management
git add docs/performance-enhancer/performance-enhancer.md
git commit -m "$(cat <<'EOF'
Add Scenarios section with grammar and example

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
EOF
)"

Task 4: Append Phase 1 (Setup) instructions

Exact commands the agent executes to kill any running server, clear logs, start bin/dev, wait for readiness, and sign in.

Files:

  • Modify: docs/performance-enhancer/performance-enhancer.md (append)

  • Step 1: Append Phase 1 instructions

Append exactly this content:

## Agent instructions

**Do not edit anything below this line.** The agent follows these steps in order. Each phase must complete successfully before the next begins (except where explicitly noted β€” e.g., a single failed scenario step does not abort Phase 2).

Before starting, the agent captures a timestamp `TS` in the format `YYYY-MM-DD-HHMMSS`. This single timestamp is reused for every artifact produced by this run: `<TS>-before.json`, `<TS>-findings.md`, `<TS>-plan.md`. Use `date "+%Y-%m-%d-%H%M%S"` to generate it and echo it for the record.

### Phase 1 β€” Setup (clean slate)

1. **Kill any existing dev servers.**
   ```bash
   pkill -TERM -f "foreman start -f Procfile.dev" 2>/dev/null || true
   pkill -TERM -f "rails server" 2>/dev/null || true
   sleep 2
   pkill -KILL -f "foreman start -f Procfile.dev" 2>/dev/null || true
   pkill -KILL -f "rails server" 2>/dev/null || true
   lsof -ti :3004 | xargs -r kill -9 2>/dev/null || true
   ```
   Verify port is free:
   ```bash
   lsof -ti :3004 && echo "STILL_HELD" || echo "FREE"
   ```
   Must print `FREE`. If it prints `STILL_HELD`, see the troubleshooting appendix before continuing.

2. **Truncate logs.**
   ```bash
   : > /code/life-management/log/development.log
   : > /code/life-management/log/bullet.log
   ```

3. **Ensure the runs directory exists.**
   ```bash
   mkdir -p /code/life-management/docs/performance-enhancer/runs
   ```

4. **Start `bin/dev` in the background.** Use the Bash tool's `run_in_background` option; do not block. Note: `Procfile.dev` already runs `bin/rails logs:clear` on boot, which is redundant with step 2 but harmless.
   ```bash
   cd /code/life-management && bin/dev
   ```

5. **Poll for readiness.** Retry up to 60 times with a 1s sleep.
   ```bash
   for i in $(seq 1 60); do
     code=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3004/users/sign_in || echo "000")
     if [ "$code" != "000" ] && [ "$code" != "500" ] && [ "$code" != "502" ] && [ "$code" != "503" ]; then
       echo "READY after ${i}s (HTTP $code)"
       break
     fi
     sleep 1
   done
   ```
   If after 60s no success line appears, abort and surface the last 50 lines of `log/development.log` to the user.

6. **Log in via MCP Playwright.**
   - `browser_navigate` to `http://localhost:3004/users/sign_in`.
   - `browser_fill_form` with `user[email]` = `[email protected]` and `user[password]` = `123456`.
   - Submit the form (click the submit button, or `browser_press_key` Enter on the password field).
   - Wait for navigation to complete. Confirm success by `browser_snapshot` and checking the resulting URL is no longer `/users/sign_in` (typically redirects to `/dashboard` or root).
   - If login fails: abort, report the error, and recommend the user run `bin/rails db:seed`.

---
  • Step 2: Verify Phase 1 content

Run:

grep -n "### Phase 1\|pkill\|lsof -ti :3004\|browser_navigate\|user\[email\]\|user\[password\]" /code/life-management/docs/performance-enhancer/performance-enhancer.md

Expected: matches for the Phase 1 heading, the kill commands, the port check, and the Playwright login references.

  • Step 3: Commit
cd /code/life-management
git add docs/performance-enhancer/performance-enhancer.md
git commit -m "$(cat <<'EOF'
Add Phase 1 (Setup) agent instructions

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
EOF
)"

Task 5: Append Phase 2 (Before run) instructions + JSON schema

The measurement loop: walk scenarios, harvest logs, emit before.json. Includes the shared JSON schema used by both before and after runs.

Files:

  • Modify: docs/performance-enhancer/performance-enhancer.md (append)

  • Step 1: Append Phase 2 instructions and schema

Append exactly this content:

### Phase 2 β€” "Before" run

For each step in the user's `## Scenarios` section, in order:

1. **Record step-start timestamp** (ms precision). Use `date +%s%3N` on Linux.
2. **Execute the step** via the appropriate MCP Playwright tool(s):
   - `Visit /path` β†’ `browser_navigate` to `http://localhost:3004/path`.
   - `fill {field: "value", ...}; submit` β†’ `browser_fill_form` with the hash, then submit (click submit button or press Enter).
   - `click "Button Text"` β†’ `browser_click` on the element with matching visible text.
   - `change title to "..."` β†’ `browser_fill_form` on the title field with the new value.
3. **Capture Playwright navigation time** (time from action issue to load event). If the MCP tool does not report it directly, use the elapsed wall-clock from step-start to when the snapshot stabilizes.
4. **Record step-end timestamp.**
5. **Harvest server-side metrics from `log/development.log`** for all requests whose Rails timestamp falls in `[step-start, step-end]`. Parse each `Completed` block. Example line format:
   ```
   Started GET "/goals" for 127.0.0.1 at 2026-04-22 14:30:12 -0300
   Processing by GoalsController#index as HTML
     Goal Load (4.2ms)  SELECT ...
   Completed 200 OK in 387ms (Views: 120.3ms | ActiveRecord: 245.1ms (42 queries, 12 cached) | Allocations: 45213)
   ```
   Extraction regex hints (Ruby-style, adapt as needed):
   - `Started (\w+) "([^"]+)"` β†’ method, path
   - `Completed (\d+) \S+ in (\d+)ms \(Views: ([\d.]+)ms \| ActiveRecord: ([\d.]+)ms \((\d+) queries` β†’ status, total_ms, view_ms, db_ms, query_count
   - `Allocations: (\d+)` β†’ allocations
6. **Harvest Bullet warnings from `log/bullet.log`** for lines whose timestamp falls in the same window (Bullet log timestamps are RFC-3339).
7. **Append the record to `runs/<TS>-before.json`.** The file is a single JSON object; build it in memory as you go and write it at the end of Phase 2.

#### Shared JSON schema for `before.json` and `after.json`

Both runs must produce this exact shape. The comparison depends on it.

```json
{
  "timestamp": "2026-04-22-143000",
  "run_kind": "before",
  "scenario_source": [
    "Visit /dashboard",
    "Visit /goals",
    "Visit /goals/new; fill {title: \"Perf test goal\", target_date: \"2026-12-31\"}; submit"
  ],
  "scenarios": [
    {
      "index": 1,
      "step": "Visit /goals",
      "playwright_ms": 412,
      "requests": [
        {
          "method": "GET",
          "path": "/goals",
          "status": 200,
          "total_ms": 387,
          "view_ms": 120,
          "db_ms": 245,
          "query_count": 42,
          "allocations": 45213
        }
      ],
      "bullet_warnings": [
        "USE eager loading detected: Goal => [:category]"
      ],
      "parse_note": null
    }
  ]
}
```

- `scenario_source` is a verbatim copy of the user's `## Scenarios` list (each step a string, in order). **This is the authoritative contract for the after run.** If `performance-enhancer.md` is edited between runs, the plan prompt reads `scenario_source` from this JSON, not the live `.md`.
- `run_kind` is `"before"` in this file, `"after"` in the post-fix file.
- If a request cannot be parsed for a step, use `requests: []` and set `parse_note` to a human string describing what went wrong. Do not invent numbers.
- If a scenario step itself fails (e.g., Playwright can't find the button), include the step with `requests: []`, `bullet_warnings: []`, and `parse_note: "scenario failure: <error>"`. Continue to the next step β€” do not abort the run.

Write the final JSON to `/code/life-management/docs/performance-enhancer/runs/<TS>-before.json` with 2-space indentation.

---
  • Step 2: Verify Phase 2 content

Run:

grep -n "### Phase 2\|scenario_source\|run_kind\|Completed\|Bullet" /code/life-management/docs/performance-enhancer/performance-enhancer.md

Expected: matches for the Phase 2 heading, the scenario_source field, run_kind, the Completed regex reference, and Bullet harvesting.

Run:

grep -n "\"before\"\|\"after\"" /code/life-management/docs/performance-enhancer/performance-enhancer.md

Expected: both strings appear in the schema discussion.

  • Step 3: Commit
cd /code/life-management
git add docs/performance-enhancer/performance-enhancer.md
git commit -m "$(cat <<'EOF'
Add Phase 2 (before run) instructions + shared JSON schema

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
EOF
)"

Task 6: Append Phase 3 (Analysis) instructions

Build the hotspot list, read code for root causes, emit findings.md.

Files:

  • Modify: docs/performance-enhancer/performance-enhancer.md (append)

  • Step 1: Append Phase 3 instructions

Append exactly this content:

### Phase 3 β€” Analysis & artifact generation

1. **Build the per-scenario summary table.** For each scenario in `before.json`, produce a row with: index, step, total playwright ms, sum of request total_ms, sum of db_ms, sum of query_count, count of Bullet warnings.

2. **Apply the hotspot rules from `## Hotspot thresholds`** to every request in `before.json`. A request is a hotspot if it matches any rule. Collect all hotspots with: scenario index, request method + path, which rule(s) fired, the offending numbers.

3. **Research root causes.** For each hotspot, read the responsible Rails code:
   - From the path, derive the controller + action via `bin/rails routes | grep <path>`.
   - Open the controller file, the action, and any instance-variable models it loads.
   - Open the matching view/partials under `app/views/...`.
   - Look for: missing `includes` / `preload`, `.count` on relations in views (use `.size` with preloaded associations or counter caches), unscoped loads (`Model.all` where a `current_user` scope is expected), N+1 on associated scopes, uncached expensive computations, repeated identical queries.
   - Note exact `file:line` references for each finding.

4. **Optionally consult context7** (`mcp__plugin_context7_context7__query-docs`) for framework-specific guidance on any non-obvious fix β€” e.g., Rails counter caches, `load_async`, Bullet configuration, fragment caching API. Only use it when the canonical fix is unclear from the code.

5. **Write `runs/<TS>-findings.md`** with this structure:

   ```markdown
   # Performance findings β€” <TS>

   ## Summary table

   | # | Step | Playwright ms | Request ms (sum) | DB ms (sum) | Queries (sum) | Bullet warnings |
   |---|------|--------------:|-----------------:|------------:|--------------:|----------------:|
   | 1 | ... | ... | ... | ... | ... | ... |

   ## Hotspots (ordered by impact)

   ### Hotspot 1: GET /goals β€” 42 queries, N+1 on Goal => [:category]
   **Rule(s) fired:** query_count > 30; Bullet warning
   **Numbers:** total_ms=387, db_ms=245, query_count=42
   **Root cause:** `app/controllers/goals_controller.rb:12` loads `Goal.where(user: current_user)`; the index view at `app/views/goals/index.html.erb:23` iterates and calls `goal.category.name` per row.
   **Proposed fix:** Add `.includes(:category)` in the controller query.

   ### Hotspot 2: ...

   ## Scenario failures (if any)
   <list any steps with parse_note starting "scenario failure"; include the error string>
   ```

6. **Write `runs/<TS>-plan.md`** using the template in the next section. Fill in every `<PLACEHOLDER:...>` with concrete content from the findings; leave the rest of the template byte-for-byte identical.

7. **Report back to the user** in chat: the count of scenarios executed, the count of hotspots flagged, and the three output paths (`before.json`, `findings.md`, `plan.md`). Do not summarize the findings inline β€” the files are the deliverable.

---
  • Step 2: Verify Phase 3 content

Run:

grep -n "### Phase 3\|Summary table\|Hotspots (ordered\|Root cause\|Proposed fix\|context7" /code/life-management/docs/performance-enhancer/performance-enhancer.md

Expected: matches for the Phase 3 heading, the findings.md structure labels, and the context7 reference.

  • Step 3: Commit
cd /code/life-management
git add docs/performance-enhancer/performance-enhancer.md
git commit -m "$(cat <<'EOF'
Add Phase 3 (analysis) instructions with findings.md structure

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
EOF
)"

Task 7: Append the plan-prompt template block

This is the template the agent fills in and writes to runs/<TS>-plan.md. Most of it is fixed boilerplate β€” only the <PLACEHOLDER:...> slots get substituted at generation time. The verification gate is the core of the template.

Files:

  • Modify: docs/performance-enhancer/performance-enhancer.md (append)

  • Step 1: Append the plan-prompt template

Append exactly this content. Note that the plan-prompt block is wrapped in a quadruple-backtick fence so the triple-backtick fences inside it render correctly.

## Plan-prompt template

When emitting `runs/<TS>-plan.md`, the agent writes exactly the content below. Placeholders are marked `<PLACEHOLDER:...>` β€” the agent substitutes each one with concrete content derived from `findings.md`. Every other byte (including the verification gate) is copied verbatim.

````markdown
# Performance fix plan β€” <PLACEHOLDER:TS>

> This is a self-contained Claude Code prompt. Open it in a fresh session and say: **"Execute this performance fix plan."** You do not need any other context.

## Context

<PLACEHOLDER: one-paragraph summary of the app area(s) touched by the hotspots β€” controllers, models, feature name. Written by the agent based on findings.>

## Before-run artifacts (do not modify)

- **Scenarios (contract):** `docs/performance-enhancer/runs/<PLACEHOLDER:TS>-before.json` β†’ `scenario_source` field (frozen copy; authoritative β€” do NOT read the live `performance-enhancer.md`, it may have been edited).
- **Raw metrics:** `docs/performance-enhancer/runs/<PLACEHOLDER:TS>-before.json`
- **Findings:** `docs/performance-enhancer/runs/<PLACEHOLDER:TS>-findings.md`

## Hotspots to fix (ordered by impact)

<PLACEHOLDER: ordered list. Each entry has this exact shape:

N. **<METHOD> <path>** β€” <metric that flagged it: e.g., 42 queries, N+1>
   - **Root cause:** <plain English, with file:line refs>
   - **Proposed fix:** <concrete code-level change>

Repeat for each hotspot in descending impact order.>

## Implementation rules

- Follow `AGENTS.md` patterns: no `app/services/`, user-owned models (`belongs_to :user`), Pundit policies receive `(user, record)`.
- Prefer eager-loading (`includes` / `preload`), counter caches, scopes, and fragment caching over new abstractions.
- **No behavior changes** β€” performance only. Existing tests must still pass.
- Run the project's test command (`bin/rails test`) before proceeding to the verification gate.
- Commit each hotspot fix as its own commit with a message naming the route and the fix.

## Verification gate (REQUIRED β€” do not claim completion without this)

After implementing every fix, you MUST complete this gate. Skipping any step means the plan is not done, regardless of how confident you feel about the fixes.

1. **Stop any running `bin/dev` process.**
   ```bash
   pkill -TERM -f "foreman start -f Procfile.dev" 2>/dev/null || true
   pkill -TERM -f "rails server" 2>/dev/null || true
   sleep 2
   lsof -ti :3004 | xargs -r kill -9 2>/dev/null || true
   ```

2. **Truncate logs.**
   ```bash
   : > /code/life-management/log/development.log
   : > /code/life-management/log/bullet.log
   ```

3. **Start `bin/dev`; poll `http://localhost:3004/users/sign_in` until it returns a non-5xx response** (60s timeout).

4. **Log in as `[email protected] / 123456`** via MCP Playwright (`browser_navigate` β†’ `/users/sign_in`, `browser_fill_form` with `user[email]` and `user[password]`, submit).

5. **Execute the EXACT scenarios from `runs/<PLACEHOLDER:TS>-before.json` β†’ `scenario_source`**, in order, verbatim. No substitutions, no skipping. Do NOT read the live `performance-enhancer.md` β€” it may have been edited since the before run.

6. **Capture metrics using the schema in `performance-enhancer.md`** (`## Agent instructions` β†’ Phase 2 β†’ shared JSON schema). Build a JSON object with `run_kind: "after"` and the same `scenario_source` array copied from the before file.

7. **Write `runs/<PLACEHOLDER:TS>-after.json`** with 2-space indentation.

8. **Write `runs/<PLACEHOLDER:TS>-comparison.md`** with this exact structure:

   ```markdown
   # Performance comparison β€” <TS>

   - **Before run:** <TS> (kind: before)
   - **After run:** <TS> (kind: after)
   - **Scenarios:** <count>

   ## Per-scenario comparison

   ### Scenario 1 β€” <step text>

   | metric | before | after | delta | delta % | regression? |
   |---|---:|---:|---:|---:|:---:|
   | total_ms | ... | ... | ... | ... | ... |
   | db_ms | ... | ... | ... | ... | ... |
   | query_count | ... | ... | ... | ... | ... |
   | view_ms | ... | ... | ... | ... | ... |
   | allocations | ... | ... | ... | ... | ... |
   | playwright_ms | ... | ... | ... | ... | ... |

   (repeat per scenario; for multi-request scenarios, sum across requests)

   ## Aggregate totals

   | metric | before | after | delta | delta % |
   |---|---:|---:|---:|---:|

   ## Regressions

   Flag any metric that got worse by **>5%**. List: scenario index, metric, before β†’ after, explanation if known.

   ## Per-hotspot status

   For each hotspot from the original `<TS>-findings.md`:
   - **Hotspot N: <METHOD> <path>** β€” **fixed / improved / unchanged / regressed** β€” <before numbers> β†’ <after numbers>

   ## Bullet warnings diff

   - **Resolved:** warnings present in before, absent in after (one per bullet)
   - **New:** warnings absent in before, present in after (one per bullet β€” these are regressions)
   - **Persisting:** warnings present in both

   ## Remaining issues

   Any hotspot not fixed (with one-sentence justification), plus any new issues surfaced by the after run.
   ```

9. **Do NOT report completion until `runs/<PLACEHOLDER:TS>-comparison.md` exists on disk and every table in it is populated with real numbers.** An empty table or a "TODO" inside the comparison is a failure β€” the plan is not done.
````

---
  • Step 2: Verify the template block

Run:

grep -n "## Plan-prompt template\|Verification gate\|run_kind.*after\|Per-hotspot status\|Bullet warnings diff" /code/life-management/docs/performance-enhancer/performance-enhancer.md

Expected: matches for the Plan-prompt template heading, Verification gate, the after run_kind reference, Per-hotspot status, and Bullet warnings diff.

Run:

grep -c "<PLACEHOLDER:" /code/life-management/docs/performance-enhancer/performance-enhancer.md

Expected: at least 4 (TS is referenced multiple times; context + hotspots also use placeholders).

  • Step 3: Commit
cd /code/life-management
git add docs/performance-enhancer/performance-enhancer.md
git commit -m "$(cat <<'EOF'
Add plan-prompt template with verification gate

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
EOF
)"

Task 8: Append Troubleshooting appendix

Covers the three realistic failure modes: port held, empty bullet.log, scenario step failing mid-run.

Files:

  • Modify: docs/performance-enhancer/performance-enhancer.md (append)

  • Step 1: Append the Troubleshooting appendix

Append exactly this content:

## Troubleshooting

### Port 3004 is held

If `lsof -ti :3004` keeps returning a PID after the Phase 1 kill commands, a non-foreman process is holding it (Docker, another Rails app, a zombie Puma). Force-kill:

```bash
lsof -ti :3004 | xargs -r kill -9
```

If that still doesn't free it, run `sudo lsof -i :3004` to identify the holder and stop it manually. Do not retry the agent until `lsof -ti :3004` returns empty.

### `log/bullet.log` is empty after the run

Bullet only writes to its log when it detects issues **and** is enabled for the environment. Confirm:

```bash
grep -n "Bullet" /code/life-management/config/environments/development.rb
```

You should see `Bullet.enable = true` and `Bullet.bullet_logger = true`. If those are absent or commented out, Bullet is off β€” re-enable, restart `bin/dev`, re-run the agent.

An empty `bullet.log` with Bullet enabled just means no N+1 / unused-eager-load issues were detected in the scenarios you ran β€” this is not an error.

### A scenario step fails mid-run

The agent does NOT abort on a single step failure. It records the step with `requests: []`, `bullet_warnings: []`, and `parse_note: "scenario failure: <error>"`, then moves on. `findings.md` surfaces all failures under a **Scenario failures** section.

If you see repeated failures, the most common causes are:
- Route doesn't exist (typo) β€” fix the step in `## Scenarios`.
- Button text changed β€” fix the step.
- Form field name changed β€” inspect the page with `browser_snapshot` manually and fix the fill hash.
- Step depends on prior-step state that didn't happen (e.g., an Edit click after a failed create) β€” reorder or add the missing prerequisite.

### The after run produces very different Playwright numbers from the before run

Playwright nav time has some run-to-run variance (usually <10%). The **server-side** numbers (total_ms, db_ms, query_count) are the trustworthy signal; they're derived from Rails's own log lines and are deterministic for a given code path + data shape. Focus the comparison on those. The >5% regression flag in `comparison.md` applies per-metric; a noisy Playwright value alone should not block acceptance if server metrics improved.

### "I want to re-run just the after phase" after already running a plan

Don't. The timestamp ties before to after. If you want a fresh measurement, run the agent again (new TS) β€” it's fast. If you just want to sanity-check a single page, run it manually with `curl -w` or the browser dev tools; don't reuse a stale plan.
  • Step 2: Verify the appendix

Run:

grep -n "## Troubleshooting\|Port 3004 is held\|bullet.log.*empty\|scenario step fails" /code/life-management/docs/performance-enhancer/performance-enhancer.md

Expected: matches for the Troubleshooting heading and each of the three main subsections.

  • Step 3: Final file check β€” full structure

Run:

grep -n "^## " /code/life-management/docs/performance-enhancer/performance-enhancer.md

Expected (in order):

## How to invoke
## Prerequisites
## Credentials (hardcoded β€” dev only)
## Hotspot thresholds (editable)
## Scenarios
## Agent instructions
## Plan-prompt template
## Troubleshooting

Run:

wc -l /code/life-management/docs/performance-enhancer/performance-enhancer.md

Expected: ~400-550 lines (exact count depends on whitespace; the shape is what matters).

  • Step 4: Commit
cd /code/life-management
git add docs/performance-enhancer/performance-enhancer.md
git commit -m "$(cat <<'EOF'
Add troubleshooting appendix for performance-enhancer agent

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
EOF
)"

Task 9: End-to-end smoke test

This task runs the agent for real against a single simple page to confirm all three artifact files land and their structure matches what the plan template expects. Do NOT skip this β€” it's the only real validation the prompt artifact works.

Files:

  • Temporarily modify: docs/performance-enhancer/performance-enhancer.md (edit ## Scenarios β†’ restore at end)
  • Generated: docs/performance-enhancer/runs/<TS>-before.json, <TS>-findings.md, <TS>-plan.md (gitignored; delete after smoke test)

  • Step 1: Edit the Scenarios section for the smoke test

Replace the content between <!-- BEGIN user scenarios --> and <!-- END user scenarios --> with a single-step scenario that doesn't mutate data:

- Visit /dashboard

Save the file. Do NOT commit this change β€” we restore it in Step 5.

  • Step 2: Invoke the agent in Claude Code

In Claude Code, say: "Run the performance enhancer against this scenario list." Let it execute Phases 1–3.

Expected: server starts, login succeeds, dashboard loads, three files appear under docs/performance-enhancer/runs/.

  • Step 3: Validate the generated artifacts
ls -la /code/life-management/docs/performance-enhancer/runs/

Expected: three files with matching <TS> prefix β€” <TS>-before.json, <TS>-findings.md, <TS>-plan.md.

Validate before.json shape:

python3 -c "
import json, sys
p = sorted(__import__('glob').glob('/code/life-management/docs/performance-enhancer/runs/*-before.json'))[-1]
d = json.load(open(p))
assert d['run_kind'] == 'before', 'run_kind wrong'
assert isinstance(d['scenario_source'], list) and len(d['scenario_source']) == 1, 'scenario_source wrong'
assert isinstance(d['scenarios'], list) and len(d['scenarios']) == 1, 'scenarios wrong'
s = d['scenarios'][0]
assert s['index'] == 1
assert 'step' in s and 'requests' in s and 'bullet_warnings' in s and 'playwright_ms' in s
print('before.json OK:', p)
"

Expected: before.json OK: <path>.

Validate findings.md:

grep -c "^## Summary table\|^## Hotspots\|^## Scenario failures" /code/life-management/docs/performance-enhancer/runs/*-findings.md

Expected: 3.

Validate plan.md has the verification gate (no remaining placeholders other than the optional Context paragraph):

grep -n "Verification gate\|run_kind.*after\|comparison.md" /code/life-management/docs/performance-enhancer/runs/*-plan.md

Expected: matches for all three.

grep -c "<PLACEHOLDER:" /code/life-management/docs/performance-enhancer/runs/*-plan.md

Expected: 0 β€” the agent must substitute every placeholder.

  • Step 4: Clean up generated artifacts
rm -rf /code/life-management/docs/performance-enhancer/runs/*

(The directory itself stays; .gitignore already excludes it so nothing to commit.)

  • Step 5: Restore the original Scenarios placeholder and commit nothing

Revert the Scenarios block to the single-line placeholder used in Task 3:

- Visit /dashboard

(This is already the committed content β€” if Step 1 matched, the diff is empty.) Verify:

cd /code/life-management
git diff docs/performance-enhancer/performance-enhancer.md

Expected: no output (no pending changes).

  • Step 6: Stop the dev server
pkill -TERM -f "foreman start -f Procfile.dev" 2>/dev/null || true
lsof -ti :3004 | xargs -r kill -9 2>/dev/null || true

No commit for this task β€” the smoke test validates behavior, it does not produce code changes.


Self-review checklist (already applied)

  • Spec coverage: header, scenarios, phases 1/2/3, JSON schema, plan-prompt template with verification gate, troubleshooting, file layout, .gitignore β€” all covered by Tasks 1–8. Smoke test covers the "testing strategy" section of the spec.
  • Placeholder scan: <PLACEHOLDER:...> appears only inside the plan-prompt template block, which is deliberate (those are fill-in slots the agent substitutes at runtime). No TBDs, TODOs, or "implement later" in task steps.
  • Type/name consistency: TS, scenario_source, run_kind, playwright_ms, query_count, db_ms, total_ms, view_ms, allocations, bullet_warnings, parse_note β€” checked consistent across Phase 2 schema, Phase 3 findings structure, plan-prompt verification gate, and smoke-test validation script.