clawdbot-workspace/memory/lessons-learned.md

426 lines
27 KiB
Markdown

# Lessons Learned
## Upwork Proposals
- **#42 (2026-02-15):** Upwork blocks messages that reference off-platform communication channels (Discord, Telegram, Slack, WhatsApp, iMessage, email, phone) before a contract starts. NEVER mention specific messaging platforms, external contact methods, or "let's connect outside Upwork" language in proposals. Keep all communication on-platform until contract is signed.
## Cloudflare / Tunnels / DNS (2026-02-12)
- **nohup your tunnels**: cloudflared processes die when exec sessions close. Always use `nohup cloudflared tunnel ... &`
- **Verify before announcing**: Always curl the tunnel URL and confirm 200 before posting to Discord. Got burned 3 times in a row.
- **Workers need DNS**: Cloudflare Workers with routes need a proxied A record (use 192.0.2.1 RFC 5737 dummy IP)
- **http2 > quic**: `--protocol http2` works more reliably than default quic for cloudflared tunnels
- **CF Registrar is dashboard-only**: No API for new domain registration. Only management of existing domains.
- **Wrangler OAuth vs API Token**: The OAuth token (in wrangler config) and CLOUDFLARE_API_TOKEN have different scopes. Check both.
## Python / Veo (2026-02-12)
- **Unbuffered output**: Use `python3 -u` for scripts running in background — otherwise stdout is buffered and you see no output
- **Veo download workaround**: `client.files.download()` returns 404. Instead grab the URI from `video.video.uri` and download with `?key=API_KEY`
## Discord Etiquette (2026-02-12)
- **Don't spam debug messages**: Do work silently, announce clean results. Jake had to tell me to delete 45 messages of debug spam. — Buba's Self-Learning Log
> Every mistake is a lesson. Every lesson makes us mega beastly.
> This file is updated CONSTANTLY whenever I figure something out the hard way.
> Search this BEFORE attempting anything similar.
---
## Gateway & Infrastructure
### Gateway logs live at /tmp/clawdbot/ not ~/.clawdbot/logs/
- **Date:** 2026-02-11
- **Mistake:** Checked ~/.clawdbot/logs/ and said "nothing since Feb 5" — confused Jake
- **Reality:** Gateway switched to /tmp/clawdbot/clawdbot-YYYY-MM-DD.log. The old logs dir is stale.
- **Rule:** Always check `/tmp/clawdbot/` for current gateway logs.
### tmux death kills the auto-restart loop
- **Date:** 2026-02-11
- **Mistake:** Assumed compaction caused silence. Actually the entire tmux session died.
- **Reality:** `run-gateway.sh` has a `while true` loop that only works if tmux survives. If tmux itself dies, no recovery.
- **Rule:** When diagnosing downtime, check `tmux list-sessions` and session creation time with `tmux display-message -t clawdbot -p '#{session_created}'`. If the session is newer than expected, tmux died.
### Gateway freeze vs crash — different diagnostics
- **Date:** 2026-02-11
- **Mistake:** Initially thought it was an event loop freeze (alive but hung). Was actually a full crash.
- **Rule:** Check the log timeline for gaps. If there's a gap AND the tmux session is freshly created, it was a crash. If the tmux session is old but logs have a gap, THEN it's a freeze.
## Discord API
### channel-list needs guildId, not channel ID
- **Date:** 2026-02-10
- **Mistake:** Passed channel ID to channel-list, got "Unknown Guild"
- **Rule:** Guild ID ≠ channel ID. Jake's main guild is `1458233582404501547`. Channel IDs are different.
### Guild ID reference
- **Main server:** `1458233582404501547`
- **Config has all guilds listed** under channels.discord.guilds in clawdbot.json
### Deleting messages needs the channel as target
- **Date:** 2026-02-10
- **Rule:** `message delete` needs `target` set to the channel ID where the message lives.
## Cron Jobs
### Cron job parameter format
- **Date:** 2026-02-10
- **Mistake:** Tried multiple wrong formats before getting it right
- **Correct format:**
```json
{
"name": "job-name",
"schedule": {"kind": "cron", "expr": "0 9 * * 1,4"},
"sessionTarget": "main",
"payload": {"kind": "systemEvent", "text": "..."},
"enabled": true
}
```
- **Rule:** schedule needs `kind` + `expr`. Payload needs `kind: "systemEvent"` + `text`. NOT `label`, NOT `message`.
## File Operations
### Edit tool requires EXACT text match
- **Date:** 2026-02-11 (CREdispo sub-agent)
- **Mistake:** Multiple edit failures on CREdispo files because oldText didn't match exactly
- **Rule:** Always read the file first to get exact text before editing. Never guess at whitespace or content.
## iMessage / BlueBubbles
### Sending images to group chats via AppleScript is unreliable
- **Date:** 2026-02-10
- **Mistake:** Tried to send images to iMessage group chats via AppleScript — text sends but images may not deliver
- **Rule:** For image delivery to group chats, use BlueBubbles API directly or have Jake send manually from Discord.
### Group chat ID format
- **Date:** 2026-02-10
- **Rule:** iMessage group chat IDs look like `chat358249523368699090`. The send format is `any;+;chat358249523368699090`.
## Context & Memory
### ALWAYS save state to memory before heavy work
- **Date:** 2026-02-11
- **Mistake:** Was deep in CREdispo work, context got compacted, lost all working state
- **Rule:** Before starting any multi-step project, write current state to memory/YYYY-MM-DD.md. Update it at milestones. This survives compaction.
### Compaction ≠ crash — don't confuse them
- **Date:** 2026-02-11
- **Mistake:** Told Jake compaction caused the silence when it was actually a gateway crash
- **Rule:** Compaction just compresses context. It doesn't stop me from responding. If I went silent, something else happened.
## Image Generation
### Nano Banana Pro needs specific iterative prompting for character accuracy
- **Date:** 2026-02-10
- **Mistake:** Took 4 iterations to get Caleb's appearance right (white hair → brown, no beard → beard, etc.)
- **Rule:** When generating character images, be VERY specific about hair color, facial hair, build, and clothing in the first prompt. Don't assume defaults.
## Sub-agents
### Sub-agent results arrive as system messages after compaction
- **Date:** 2026-02-11
- **Mistake:** Didn't realize the CREdispo postgres migration had completed because context was compacted
- **Rule:** After spawning a sub-agent for heavy work, the result comes back as a user message. If context compacts before I process it, I need to check sessions_list for completed sub-agents.
## Security
### Cloudflare quick tunnels break HTML form POST (405 Method Not Allowed)
- **Date:** 2026-02-11
- **Mistake:** Signup/login forms used native HTML `<form method="POST">` which returns 405 through cloudflared quick tunnels
- **Reality:** Cloudflare quick tunnels can mangle POST form submissions. JSON API calls via `fetch()` work fine.
- **Rule:** When serving apps through cloudflared tunnels, use JavaScript fetch() for form submissions instead of native HTML form POSTs. Keep the old form routes for direct access but add `/api/` JSON endpoints.
### VPN breaks Cloudflare tunnels
- **Date:** 2026-02-11
- **Mistake:** Had Mullvad VPN connected to Mexico while trying to create new cloudflared tunnels — tunnels couldn't establish
- **Rule:** Disconnect VPN before creating new cloudflared tunnels. Existing tunnels may also break when VPN connects.
### API tokens must go in gateway config env.vars, not just .env files
- **Date:** 2026-02-11
- **Mistake:** Saved Cloudflare token to `.env.local` but not to gateway config. Gateway couldn't use it.
- **Reality:** The gateway reads env vars from `clawdbot.json``env.vars`. A `.env.local` file is for apps, not the gateway process.
- **Rule:** When Jake gives a new API token, save it via `gateway config.patch` to `env.vars` so the gateway has it. Also save to `.env.local` for local app use.
### NEVER save secrets/tokens in memory/*.md files
- **Date:** 2026-02-11
- **Rule:** Memory files are git-backed and could leak. Save tokens/keys to `.env.local` (which is in .gitignore). Reference them by name in memory, never by value.
### Delete messages containing tokens IMMEDIATELY
- **Date:** 2026-02-11
- **Rule:** If Jake or anyone pastes a secret in Discord, delete the message FIRST, then save the token. Every second it sits in a channel is a risk.
---
## Agent Coordination / Factory Builds
### 18. Parallel agents on shared filesystem = disaster
- **Date:** 2026-02-12
- **Mistake:** Spawned 5-10 sub-agents simultaneously, all writing to the same `mcpengine-repo/servers/` directory
- **What happened:** Agents deleted each other's files, overwrote each other's work, and left half-built servers everywhere
- **Rule:** For file-heavy work on a shared repo, go SEQUENTIAL (one agent at a time) or give each agent a SEPARATE directory, then merge. Never let multiple agents write to the same folder simultaneously.
### 19. "Delete everything and rebuild" agents are time bombs
- **Date:** 2026-02-12
- **Mistake:** Gave rebuild agents instructions to "DELETE everything, build from scratch"
- **What happened:** Agent deletes all files in minute 1, then times out at minute 10 with only 30% rebuilt. Now the server is WORSE than before.
- **Rule:** NEVER tell agents to delete first. Say "build new files alongside existing ones" or "write to a temp directory, then swap." Always keep the old code until the new code is verified.
### 20. Factory monitor cron + manual spawns = competing agents
- **Date:** 2026-02-12
- **Mistake:** Had a cron job (every 10min) spawning fix agents for incomplete servers, PLUS I was manually spawning rebuild agents
- **What happened:** 3-4 agents fighting over the same server simultaneously, each deleting what the others wrote
- **Rule:** Before spawning fix agents, DISABLE any cron monitors that might also spawn agents for the same servers. One coordinator, one set of workers. No freelancers.
### 21. 10-minute timeout is too short for full MCP builds
- **Date:** 2026-02-12
- **Mistake:** Set 600s (10min) timeout for agents building entire MCP servers (tools + apps + types + server + README)
- **What happened:** Agents got 60-80% done then died. "No output" completions burning 60-70k tokens each.
- **Rule:** Full MCP server builds need 900s (15min). App-only or tool-only jobs can use 600s. Always set `runTimeoutSeconds` based on scope.
### 22. Git checkout HEAD restores wiped files
- **Date:** 2026-02-12
- **Mistake:** Panicked when rebuild agents wiped committed files
- **What saved us:** `git checkout HEAD -- servers/{name}/` instantly restores all committed files
- **Rule:** Always commit after each server completes. Then if a rogue agent wipes files, one git command fixes it. Commit early, commit often.
### 23. Single-purpose agents > multi-purpose agents
- **Date:** 2026-02-12
- **Mistake:** Gave agents broad tasks like "build the complete MCP server" (tools + apps + types + infra + README)
- **What happened:** They'd run out of tokens/time trying to do everything, often failing at the apps stage
- **Rule:** Split into focused agents: "build tools only", "build apps only", "fix TSC errors only". Smaller scope = higher success rate. Each agent should have ONE clear deliverable.
### 24. Always verify sub-agent output — "success" doesn't mean complete
- **Date:** 2026-02-12
- **Mistake:** Trusted agent completion messages like "50+ tools built!" without checking
- **What happened:** Agent claimed 50 tools but only wrote 2 files. The "findings" text was aspirational, not factual.
- **Rule:** After EVERY sub-agent completion, run a file count check: `find src/tools -name "*.ts" | wc -l`. Never trust the narrative. Trust the filesystem.
### 25. Count apps correctly — multiple storage patterns exist
- **Date:** 2026-02-12
- **Mistake:** Kept miscounting apps because different servers store them differently
- **What happened:** Some use subdirectories, some use .tsx files, some use .ts files, some use .html files, some use src/apps/ instead of src/ui/react-app/
- **Rule:** Check ALL patterns: subdirs in react-app/, .tsx files, .ts files, .html files, AND src/apps/*.ts. Take the max. Use a consistent counting script.
## MCP Factory Quality Standards (2026-02-13)
### 26. ALWAYS start from the actual API spec — never hand-pick tools from vibes
- **Date:** 2026-02-13
- **Mistake:** For the 30 SMB MCP servers, I read API docs casually and hand-picked 7-8 "obvious" tools per server
- **What happened:** Ended up with surface-level CRUD (list/get/create/update) covering maybe 10-15% of each API, missing the tools people actually need
- **Rule:** ALWAYS pull the official OpenAPI/Swagger spec (or systematically crawl every endpoint). Build a complete endpoint inventory BEFORE deciding what becomes a tool. If Mailchimp has 127 endpoints, I need to know all 127 before picking which 50 become tools.
### 27. Prioritize tools by real user workflows, not alphabetical CRUD
- **Date:** 2026-02-13
- **Mistake:** Mechanically created `list_X / get_X / create_X / update_X` for each resource — zero workflow awareness
- **What happened:** A CRM MCP that can `list_leads` but can't `log_a_call` or `add_note_to_lead` — the things salespeople do 50x/day
- **Rule:** Research the platform's top use cases. Map workflow chains (create contact → add to list → send campaign → check results). Tier the tools:
- **Tier 1 (daily):** 10-15 things every user does daily
- **Tier 2 (power user):** 15-30 things power users need
- **Tier 3 (complete):** Everything else for full API coverage
- Ship Tier 1+2 minimum. Tier 3 = "best on market" differentiator.
### 28. Rich tool descriptions are NOT optional — they drive agent behavior
- **Date:** 2026-02-13
- **Mistake:** Wrote basic descriptions like "Lists contacts" with minimal parameter docs
- **What happened:** AI agents make tool selection decisions based on descriptions. Vague = wrong tool chosen = bad UX
- **Rule:** Every tool description must tell an AI agent WHEN to use it:
- BAD: "Lists contacts"
- GOOD: "Lists contacts with optional filtering by email, name, tag, or date range. Use when the user wants to find, search, or browse their contact database. Returns paginated results up to 100 per page."
- Every param needs: description, type+format constraints, defaults, required/optional, example values
- `_meta` labels from day one: category, access (read/write/destructive), complexity, rateLimit
### 29. Maintain a coverage manifest for every MCP server
- **Date:** 2026-02-13
- **Mistake:** No tracking of which endpoints were covered vs skipped. No way to measure quality.
- **Rule:** Every server gets a coverage manifest in its README:
```
Total API endpoints: 127
Tools implemented: 45
Intentionally skipped: 12 (deprecated/admin-only)
Not yet covered: 70 (backlog)
Coverage: 35% → target 80%+
```
Every skipped endpoint needs a REASON (deprecated, admin-only, OAuth-only, redundant). Set 80%+ as "production quality" threshold.
### 30. 7-8 tools per server is a demo, not a product
- **Date:** 2026-02-13
- **Mistake:** Treated 7-8 tools as "enough" for the initial 30 servers
- **What it actually is:** A toy. Nobody can do their real job with 7 tools for a platform that has 100+ API endpoints.
- **Rule:** Minimum viable tool count depends on API size:
- Small API (<30 endpoints): 15-20 tools
- Medium API (30-100 endpoints): 30-50 tools
- Large API (100+ endpoints): 50-80+ tools
- If customers install it and can't do their #1 use case, it's not a product.
### 31. Consistent naming conventions across ALL servers — no exceptions
- **Date:** 2026-02-13
- **Rule:** Factory-wide naming standard:
- `list_*` for paginated collections
- `get_*` for single resource by ID
- `create_*`, `update_*`, `delete_*` for mutations
- `search_*` for query-based lookups
- Domain verbs: `send_email`, `cancel_event`, `archive_card`, `assign_task`
- NEVER mix `fetch_*` / `get_*` / `retrieve_*` — pick ONE
- All snake_case, all lowercase
### 32. Handle pagination and rate limits properly in every server
- **Date:** 2026-02-13
- **Rule:** Every `list_*` tool must:
- Support cursor/page tokens
- Use reasonable default page sizes (25-100, never "all")
- Return `has_more` / `next_page` indicators
- Handle API rate limits (429) with retry + exponential backoff
- Document known rate limits in tool `_meta`
### 33. Stop asking Jake to do things I can do myself
- **Date:** 2026-02-15
- **Mistake:** Asked Jake to set up Chrome relay / attach tabs / help me log in when I have full access to his computer + 1Password CLI
- **What happened:** Jake rightfully called me out — I have Peekaboo (macOS UI), 1Password CLI (credentials), browser tool (DOM control), shell access (nodes), and AppleScript. Everything I need to be fully autonomous.
- **Rule:** NEVER ask Jake to do something I can do myself. The workflow is:
1. Need credentials? → `op item get "ServiceName" --fields username,password`
2. Need to browse a website? → browser tool (clawd profile) + log in with 1Password creds
3. Need macOS app interaction? → Peekaboo (`see --annotate` → `click --on elem_XX`)
4. Need shell commands? → nodes.run or exec
5. Need to type/click in native apps? → Peekaboo + AppleScript
6. **Only ask Jake when something genuinely requires his physical presence** (like granting macOS permissions or biometric auth)
### 34. Use the right tool for the right layer
- **Date:** 2026-02-15
- **Mistake:** Tried to use Peekaboo (macOS UI automation) to drive a web browser, when the browser tool gives direct DOM access
- **Rule:** Match tool to layer:
- **Web pages** → browser tool (DOM snapshots, ref-based clicking, no screenshots)
- **macOS native apps** → Peekaboo (element IDs, annotated maps)
- **System dialogs** → AppleScript / key codes (they don't expose accessibility elements)
- **File/CLI tasks** → shell commands (fastest, most reliable)
- **Credentials** → 1Password CLI (`op`)
- Don't use a higher-friction tool when a lower-friction one works
### 35. 1Password items may not be titled by service name
- **Date:** 2026-02-15
- **Mistake:** Searched for `op item list | grep "upwork"` and got nothing. Assumed creds weren't stored.
- **What happened:** The Upwork login was stored under a blank/untitled item with `www.upwork.com` in the URLs field, not in the title.
- **Rule:** When searching 1Password, ALWAYS search by URL too:
```bash
op item list --format json | jq -r '.[] | select(.urls != null) | select(.urls | map(.href) | any(test("SERVICENAME"))) | .id'
```
Then `op item get <ID> --fields username,password --format json`. Never assume title matches service name.
### 36. 1Password CLI requires re-auth per exec session
- **Date:** 2026-02-15
- **Mistake:** First `op` commands got SIGKILL'd because 1Password wasn't signed in.
- **Rule:** Always check `op whoami` first. If not signed in:
1. Run `op signin` in background
2. Wait 1-2 seconds for the 1Password authorization dialog
3. Approve with: `osascript -e 'tell application "1Password" to activate' && sleep 0.5 && osascript -e 'tell application "System Events" to key code 36'`
4. Every subsequent `op` call in a new exec session may also trigger the dialog — pre-emptively send the approve keystroke
### 37. Upwork proposal form requires "Schedule a rate increase" selection
- **Date:** 2026-02-15
- **Mistake:** Tried to submit proposal without filling in the rate-increase frequency/percent dropdowns. Got "Please fix the errors below."
- **Rule:** On Upwork proposal forms:
1. Set hourly rate (clear field with Cmd+A first, then type new amount)
2. Select rate-increase frequency → "Never" (click combobox, then use JS: `document.querySelectorAll('[role="option"]').forEach(el => { if(el.textContent.trim() === 'Never') el.click() })`)
3. Fill cover letter
4. Then submit
The rate-increase fields are REQUIRED even though they look optional.
### 38. Browser tool DOM snapshots > screenshots for web automation
- **Date:** 2026-02-15
- **Mistake:** Previously tried screenshot-based navigation which was slow and brittle.
- **What happened:** Used browser tool snapshots to read entire Upwork DOM, click refs, type into fields, and submit — all without a single screenshot. Completed full job search → login → proposal in minutes.
- **Rule:** For web automation:
1. `browser navigate` to URL
2. `browser snapshot` (compact=true) to read DOM
3. `browser act` with click/type/press by ref
4. Verify with another snapshot
5. Never fall back to screenshots unless DOM is inaccessible (canvas apps, etc.)
### 39. Upwork job application complete workflow
- **Date:** 2026-02-15
- **Complete autonomous workflow:**
1. `op whoami` → sign in if needed (with dialog approve)
2. Search 1Password by URL: `op item list --format json | jq ... test("upwork")`
3. Get creds: `op item get <ID> --fields username,password`
4. Browser tool → navigate to Upwork login
5. Type email → Continue → Type password → Log in
6. Search jobs: `/nx/search/jobs/?q=QUERY&sort=recency`
7. Read job details from DOM snapshot
8. Click "Apply now"
9. Set hourly rate (Cmd+A → type amount)
10. Set rate increase to "Never"
11. Type cover letter
12. Click "Send for X Connects"
13. Verify "Your proposal was submitted" alert
*Last updated: 2026-02-15 06:42 EST*
### 40. Compaction can produce EMPTY summaries — save context BEFORE it happens
- **Date:** 2026-02-15
- **Mistake:** Compaction fired with "Summary unavailable due to context limits." Lost ENTIRE conversation context — couldn't remember what Jake and I were discussing in #ai-tech-research. This is the SECOND time compaction has wiped my memory (first was Feb 11 CREdispo).
- **Root cause:** I rely on the compaction summary to preserve context. When the context window is too large, the summary itself fails, and everything is gone.
- **Rule — MANDATORY CONTEXT SAVES:**
1. **Every time Jake gives me a task or we make a decision:** Immediately append a 1-2 line note to today's daily log AND working-state.md
2. **Before ANY heavy work (research, building, multi-step):** Save full context of what we're doing, what was chosen, what options were on the table
3. **Every 10-15 messages in a conversation:** Do a quick memory flush to daily log
4. **When discussing options/choices:** Save ALL options AND the chosen one immediately when the choice is made
5. **Never assume compaction summary will work** — treat it as unreliable and save everything proactively
6. **If I realize I'm in a long conversation without saving:** STOP and save immediately, don't wait for a natural break
- **This is Jake's HIGHEST PRIORITY fix. No excuses.**
### 41. Read channel history when returning from compaction with lost context
- **Date:** 2026-02-15
- **Mistake:** Told Jake I lost context instead of first trying to read the channel's recent messages to reconstruct what we were discussing.
- **Rule:** After compaction with lost/empty summary:
1. FIRST: Try to read recent messages from the active channel
2. SECOND: Check working-state.md and today's daily log
3. THIRD: Search memory for related keywords
4. ONLY THEN: If still can't figure it out, ask Jake — but acknowledge you tried everything first
*Total lessons: 41*
### 17. Jake's Preferred Image Style
- **Mistake:** Used comic book/vibrant cartoon style when Jake asked for "the style I like"
- **What happened:** Jake corrected — his preferred style is **chibi kawaii anime**, NOT comic book
- **Rule:** Jake's go-to image style = chibi/kawaii anime (pastel colors, big eyes, oversized heads, tiny bodies, sparkles, hearts, stars). Same style as Buba's visual identity in IDENTITY.md. Always default to this unless he says otherwise.
### Peekaboo / 1Password CLI (2026-02-15)
- **Mistake:** Tried many complex approaches to click 1Password "Authorize" button (coordinate math, AppleScript, accessibility API)
- **What worked:** Simple `peekaboo press tab` + `peekaboo press tab` + `peekaboo press return` with `--app "1Password"`
- **Rule:** For 1Password CLI auth dialogs, use Tab+Tab+Enter. The "Authorize" button is the second focusable element.
- **Also:** Every `op` CLI command triggers a new auth dialog. Run the op command in background, then immediately approve.
- **Coordinate scaling:** Mac mini display is 2560x1080 but Peekaboo screenshots display at 2000x844. Scale factor = 1.28x.
- **Chrome windows location:** Chrome browser windows may be on external monitor, not the main display. Use `open -a "Google Chrome" URL` or the clawd browser instead of trying to control Chrome via Peekaboo on the wrong monitor.
### Don't Stream Narration (2026-02-15)
- **Mistake:** My step-by-step narration was streaming to Discord as I debugged, making it look chaotic
- **Rule:** For complex multi-step debugging, work silently and only send final results/questions to Discord
### 42. macOS TCC permissions can be granted via sudo sqlite3
- **Date:** 2026-02-15
- **Discovery:** When macOS won't let you add a CLI binary (like Node) to Screen Recording via System Settings UI, you can grant it directly:
```bash
sudo sqlite3 "/Library/Application Support/com.apple.TCC/TCC.db" \
"UPDATE access SET auth_value=2 WHERE service='kTCCServiceScreenCapture' AND client='/opt/homebrew/Cellar/node/25.2.1/bin/node';"
```
- auth_value: 0=denied, 2=allowed
- For new entries: INSERT OR REPLACE INTO access (service, client, client_type, auth_value, auth_reason, auth_version, flags) VALUES (...)
- client_type: 1=path-based
- Requires sudo (passwordless sudo was set up: `echo "jakeshore ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/jakeshore`)
### 43. Don't over-engineer computer use — act like a person
- **Date:** 2026-02-15
- **Mistake:** When trying to fill an Upwork form, I went through: screenshot → coordinate guessing → DevTools console → JavaScript injection → CDP protocol → restarting browser. Each approach added complexity and broke something else. Jake called it out: "you should be acting like a person"
- **Rule:** For computer use tasks:
1. **See:** Take a screenshot, look at it
2. **Click:** Click where you need to (use Peekaboo element IDs for native apps, browser refs for web)
3. **Type:** Use peekaboo paste for native apps, browser type/fill for web
4. **Verify:** Take another screenshot
5. **Don't reach for DevTools/CDP/JS injection** unless the simple approach genuinely fails
6. For WEB forms: the Clawdbot browser tool with DOM snapshots is ALWAYS better than screenshots
### 44. Brave requires restart for "Allow JavaScript from Apple Events"
- **Date:** 2026-02-15
- **Discovery:** Toggling View > Developer > Allow JavaScript from Apple Events in Brave doesn't take effect immediately. Needs browser restart. Also, the Dictation dialog (`defaults write com.apple.HIToolbox AppleDictationAutoEnable -int 0`) keeps intercepting keystrokes.
### 45. screencapture -x -R for cropped screenshots
- **Date:** 2026-02-15
- **Discovery:** `screencapture -x -R x,y,w,h /tmp/crop.png` captures just a region. Useful for getting close-up views of specific UI areas without resizing the full ultrawide.
*Total lessons: 45*