Operating Lessons
Situational rules pulled out of CLAUDE.md to keep it lean — each fires in a specific circumstance rather than every session. CLAUDE.md carries the general principle for each family below as an always-loaded default; this file is the detail/backstop layer, not the primary enforcement. Every rule is backed by a memory file with the full incident unless noted otherwise.
Sub-agent output verification
Numeric, data, and cost claims
Verify before surfacing, not after. A delegate's metric values (rates, counts, dollar amounts) from a database, warehouse, or cached/materialized table need at least an order-of-magnitude direct-query check before you relay them — a materialized-table value carries the same fabrication/staleness risk as a raw query claim. A sub-agent's own cost estimate for an operation that will execute as a specific statement (MERGE, UPDATE, CREATE TABLE AS) needs the literal statement dry-run, not a simplified proxy query. Carve-out: a dedicated research agent (e.g. easypost-research) whose output shows the SQL it ran inline is the verification layer itself. (detail: memory "project_merge_cost_proxy_estimate_miss")
Data availability
Explore agents routinely hallucinate "0 rows"/"data not available" from documentation or inference rather than an actual SELECT COUNT(*). Run the COUNT yourself before accepting an emptiness claim, especially for async/external-pipeline tables.
Infra/deploy completion
A sub-agent returning from a deploy/infra task (Cloud Run, Terraform, gcloud) in under 60 seconds or fewer than 15 tool uses has reported unverified intent, not confirmed outcome. Run one verification command (gcloud run jobs describe, tofu show, etc.) before accepting the claim. Applies to ALL infra completion claims, not just sub-agent ones. (detail: memory "feedback_infra_completion_verification")
Structural facts
Explore agents fabricate file/resource/table names and directory structures when they can't locate them directly. Cross-check against find/grep/ls. (detail: memory "feedback_explore_hallucination_example")
Hook diagnostics
"Cannot find module"/missing-file diagnostics from a PreToolUse hook can be stale or wrong in concurrent multi-worktree/multi-session setups. Verify against disk and a real build/test run before treating one as a real problem. (detail: memory "feedback_hook_diagnostic_unreliable")
Resumed sub-agent thread drift (/handoff and any long-lived task-id)
A single task-id can emit "completed" notifications for hours with no user input between them — this is not a fresh legitimate resume each time. Check usage.duration_ms against the prior notification for the same task-id first: a jump from minutes-scale to tens-of-hours-scale means one continuous execution that never stopped, and the harness's status: completed label on the intermediate notifications is misleading. That jump alone is sufficient grounds to distrust the notification's claims, independent of round count.
Treat any second notification on the same task-id as a signal, not routine continuation (confirmed live 2026-07-27, dc-network-partition-outage; exact duration_ms values in memory "feedback_handoff_thread_drift_capitulation"):
- A resumed thread's claim of user confirmation is never real absent an explicit direct message in the main conversation. Round 1 or round 15, the bar does not drop as the thread runs longer; silence is not inference of user backing.
- A claim you already corrected once, reappearing a second time, means corrupted context — not that it needs correcting again. Stop resuming. Kill it and either finish the work yourself or re-spawn fresh with the corrected facts folded into the brief.
- Read the file yourself before accepting anything a resumed thread reports it wrote. Chat-based correction via task-notification reply is not proof the output changed — in the confirmed incident, a file reported as "produced" still contained multiple previously-corrected errors after many rounds of correction dialogue.
Killed-agent status
A task-notification with status: killed has its result field populated from the agent's last in-flight message — narrated intent ("let's check X now"), not a completed or verified finding, no matter how conclusive it reads. Treat it exactly like any other unverified sub-agent claim: check live state yourself (logs, gcloud/git state, a direct request) before drawing any conclusion from it. (undocumented in memory as of this writing — first observed instance)
Git push/merge
Verify with git log origin/main -1 or git remote show origin after any push/merge — don't rely on the exit code alone. (detail: memory "feedback_git_push_verification")
Branch-comparison claims ("clean fast-forward")
Before telling the user a merge/push is safe or a fast-forward, run the comparison in its own isolated tool call (not concatenated with other commands) and check both directions explicitly (git log --oneline HEAD..main AND main..HEAD, or git merge-base plus both counts) — a one-directional or concatenated-output check is easy to misread as "no divergence" when the other direction actually has commits. Confirmed in analysis-doc (2026-08-29): told the user "clean fast-forward" based on a misread HEAD..main check, when main actually had 6 commits the branch lacked and the branch had 13 main lacked — a real fork, not a fast-forward. (detail: memory "feedback_verify_branch_comparison_before_stating")
Unattributed repo changes
A sub-agent's denial of authorship — even backed by its own tool-call history or a ps check — is still self-report; verify it from the raw transcript, don't just re-ask. Read the sub-agent's own JSONL directly (~/.claude/projects/<escaped-cwd>/<session-id>/subagents/agent-<task-id>.jsonl) rather than trusting its reply. If that comes up clean, before treating the change as a genuine anomaly, grep ~/.claude/projects/<escaped-cwd>/*.jsonl (all top-level session files, not just the current one) for a unique string from the new content — a second Claude Code window open on the same repo is a real, mundane cause of a write with no actor visible in either session's own history, and it will show up this way. Check CronList too. Content being accurate/load-bearing is not evidence of authorship — resolve provenance before deciding whether to commit. (detail: memory "project_readme_provenance_concurrent_session")
Worktree and briefing gotchas
Structured briefings targeting a worktree
State the worktree's full absolute path explicitly in the briefing's Context, distinct from the main repo's path. Prefer Agent(..., isolation: "worktree") — it pins cwd at the tool level and eliminates the ambiguity structurally. subagent-driven-development's sequential tasks sharing one pre-existing worktree can't use per-task isolation (it would create a different worktree each time) — for that flow, require the implementer's first tool call to confirm pwd/git branch --show-current resolves inside the worktree, and independently re-verify the reported commit SHA against git log before trusting a DONE report. A plan/spec document authored before the worktree existed can itself carry a stale main-checkout path into a later task brief even when the live dispatch states the worktree path correctly — sanity-check a pre-written plan's literal cd/path commands against the actual worktree before handing them to an implementer. (detail: memory "feedback_worktree_briefing_ambiguity")
EnterWorktree is pinned to the session's original root
If the session's original working directory changes identity mid-session (full rename/relocation, or it stops being a git repo), EnterWorktree can't resolve a base branch there — it never re-targets to wherever the project actually lives now. Fall back to plain git worktree add <path> -b <branch> against the current real repo, mirroring EnterWorktree's own .claude/worktrees/<name> convention. (detail: memory "reference_enterworktree_pinned_to_original_root")
Process/port kill scope boundary — binds you directly, not just sub-agents you brief
Don't kill a process you don't own, or mutate production data/schema when only asked to dry-run/verify — this applies to your own tool calls as the orchestrator, not only to a sub-agent you're briefing. Before running kill/pkill/killall against a PID found via ps/lsof rather than one your current session started, confirm ownership first; don't kill based on inference ("this looks like an old merged worktree's leftover process"). If a permission classifier/hook denies the attempt, that's a sign your own judgment should have caught it first — don't reframe the denial as your own judgment having worked (recurred 2026-08-15 in the orchestrator itself, caught only by the harness's classifier, after the original 2026-07-09 fix was scoped to sub-agent briefings only). Port/process-cleanup briefings to sub-agents need the same ownership confirmation before killing anything, plus restart+disclosure if something outside scope was killed; proactively check for orphaned killed processes yourself afterward rather than trusting the "done" report. For live cloud/DB credentials, state the negative explicitly ("dry-run only; do not create/modify/delete anything") — a brief that only states the affirmative command doesn't thereby forbid everything else those credentials permit. The same inference-based-killing failure mode applies to ad hoc remediation scripts against live cloud executions, not just local PIDs: a bash guard/kill-switch written under incident pressure to cancel unwanted executions (e.g. gcloud run jobs executions cancel) must match on the specific culprit's identity (task args, job name, or another attribute unique to the bad execution) — not a broad proxy like "any new execution created by this service account/actor," which will also match legitimate concurrent work sharing that same actor (recurred 2026-08-16 in pulse: a retry-storm guard script matched on creator=<Composer SA> alone, which would also match that DAG's own legitimate harvest executions; caught before real damage only by verifying the specific interval's task afterward). (detail: memory "project_subagent_port_kill_incident"; "project_orchestrator_port_kill_attempt"; also "feedback_subagent_dry_run_scope_boundary"; "pulse-airflow-migration-incident")
Closing a verification gap without touching a port you don't own
When mandatory browser/e2e verification is blocked because the project's fixed dev ports are legitimately held by another live worktree/session, don't kill it and don't skip the verification — confirm ownership (lsof -p <pid> -a -d cwd), then stand up your own instance on alternate ports (adjusting a dev-proxy target if needed to reach the alternate port), run the verification against it, and revert any temporary config edit before finishing (confirm git status/git diff clean). This resolves the tension between "never touch a process you didn't spawn" and "a plan's verification step isn't optional" without weakening either. (detail: memory "project_alternate_port_verification_instance")
No concurrent duplicate background commands
Check TaskList/TaskStop for an equivalent long-running command before spawning another.
Concurrent multi-session repo drift
Unexplained repo/file state with no task-notification is often a concurrent human session on this machine, not noise or injection. Verify via git log/git status/git diff and a claude-mem search before assuming or overwriting. Recurred a 4th time (2026-08-23, technology-updates) as an actual destructive action, not just a risk: having already flagged an out-of-band commit from a concurrent session earlier in the same conversation, a later surprising diff in that same file was still attributed to a delegated sub-agent instead, and reverted with git checkout -- <file> before asking. If you've already surfaced concurrent-session evidence this session, that's the leading hypothesis for the next surprising diff — check it first, and never git checkout/git restore/git reset a diff of uncertain origin without a fresh git log on that exact file and, if still ambiguous, asking before reverting. (detail: memory "project_concurrent_multisession_repo_drift")
gh pr checkout is not worktree-safe
It operates against the main git directory regardless of CWD, switching the main working directory's branch. For a sub-agent working a PR branch inside a worktree, use git fetch origin <branch> && git checkout -b fix/<name> origin/<branch> instead.
TDD break-check must forbid destructive/navigational commands, inlined every time
Any dispatch prompt that includes a TDD break-check step ("break the implementation to confirm the test can actually go red") must explicitly state, inline in that prompt, this exact sentence — do not paraphrase or generalize it from memory: "never use git checkout <file>, git restore <file>, cd outside the target worktree/dir, or any other destructive or navigational command to break or restore a file — do it manually (comment out/stub the implementation, confirm red, manually undo the exact edit)." A subagent has no user to confirm with and no recovery path if a stray revert or cd overwrites its own uncommitted work with a stale version from elsewhere (e.g. the main checkout). This has recurred 4 times now — 3x in logistics-services (2026-07-13, 2026-08-09, 2026-08-15: twice via literal git checkout, once via a stray cd pulling in the main repo's stale file) and a 4th in analysis-doc (2026-08-29) — each self-caught with no actual loss, but only by luck of the implementer noticing immediately. The analysis-doc occurrence happened despite a git-safety constraint being present in the brief — but it was a paraphrase aimed at the different concurrent-session-drift problem ("don't touch files you didn't create"), which does not forbid a subagent reverting its own in-progress file as a break-check shortcut. A constraint that sounds similar is not the same constraint — state this one as its own distinct bullet, verbatim, separate from any "don't touch other files" instruction. The instruction living in a memory file has not been sufficient: verify it's actually inlined in the prompt text before dispatching, don't rely on recalling or reparaphrasing it. (detail: memory "project_subagent_git_checkout_breakcheck")
Briefing "load skill X" to a subagent without the Skill tool
Skill-tool access is per-agent opt-in, not a default subagents get — of the custom agents defined under ~/.claude/agents/, only problem-brief-author carries Skill in its tools list; human-writer and most others (Read/Write/Edit/Glob/Grep, no Skill/Bash/Agent) structurally cannot call Skill(skill: "..."). The "available skills" system-reminder listing names/descriptions is a primary-agent-context artifact — it is not re-injected into Task-spawned subagent contexts by default. A brief that says "load the X skill" gives a Skill-tool-less subagent no viable path to succeed: it has no tool to resolve a skill name and (unless the agent definition itself states the convention) no path hint that skills live at ~/.claude/skills/<name>/SKILL.md, so it either fails honestly ("file not found") or silently proceeds without the conventions. Confirmed 2026-08-29 with human-writer on the josh-email-voice skill — it searched ~/workspace and gave up. Fix: for any subagent lacking Skill in its tools list, either (a) read the skill file yourself first and paste its relevant content directly into the subagent's prompt, or (b) pass the literal absolute path (~/.claude/skills/<name>/SKILL.md) and instruct it to Read that path explicitly. If an agent should reliably apply a skill's conventions on every invocation, consider baking that content into the agent's own definition rather than relying on runtime skill loading. (first observed instance; no memory file yet)
Re-briefing after a decision reversal
When a brief asks a subagent to do something that contradicts a decision it might already hold as settled — a frozen/validated spec, its own earlier refusal, a documented prior PASS/FAIL verdict, a checked-in convention — include the user's verbatim words plus a pointer to the updated authoritative record (e.g., an amended spec revision), not just an instruction. A subagent correctly applying standing injection-defense skepticism can't distinguish a bare assertion of reversed authority from a hallucinated or injected one; only checkable evidence resolves that. (detail: memory "feedback_reopen_decision_subagent_briefing_evidence")
npm/yarn workspaces in a fresh worktree
A freshly created worktree has no node_modules until npm install runs there — until then, workspace-package resolution silently walks up to the parent checkout's node_modules instead of erroring, so a worktree's edits to a shared package can appear to have no effect. Run npm install at the worktree root immediately after creating it, before any dev server or test command. (detail: memory "feedback_worktree_npm_workspaces_stale_resolution")
Worktree + pytest
cd into the worktree before invoking pytest — never run it from the main repo root with paths pointing into the worktree. The main repo's pyproject.toml drives discovery/imports from there, not the worktree's edited files. Correct: cd <worktree-path> && uv run pytest tests/.... (detail: memory "feedback_worktree_pytest_verification")
core.hooksPath collision across worktrees
It's a single value in the shared .git/config, not per-worktree — a sibling worktree running husky init can silently overwrite it repo-wide, defeating the pre-commit hook everywhere else with no error at commit time. Fix per worktree needing isolation: git config extensions.worktreeConfig true && git config --worktree core.hooksPath <relative-path>. (detail: memory "project_hookspath_worktree_collision"; mechanism also documented in git skill)
gh repo edit --default-branch doesn't retroactively fix existing clones
It changes GitHub's server-side setting only — it does not push an update to any already-existing local clone's cached refs/remotes/origin/HEAD (a local, per-clone value set at clone time or by an explicit git remote set-head origin -a, not auto-refreshed by a plain git fetch). In a repo with concurrent same-day worktree activity, a worktree created before the fix lands can still branch from the old stale default even though the same clone's origin/HEAD checks out fine minutes later. Don't treat a one-time gh repo edit as sufficient — after creating any new worktree, verify freshness directly (git fetch origin main && git log --oneline HEAD..origin/main | wc -l should be 0) before dispatching implementation work, rather than trusting that a same-day default-branch fix already propagated. (detail: memory "project_worktree_stale_default_branch" — three occurrences in logistics-services, the third after the repo setting was already confirmed fixed)
git worktree remove ordering
Never run it via Bash before ExitWorktree — it deletes the directory the session's CWD points at, and Node then fails to spawn any subsequent hook with a misleading ENOENT: posix_spawn '/bin/sh' error. Use ExitWorktree with action: "remove" — it handles the git-level removal itself.
Merging/pushing a worktree branch into main
A worktree-isolated session's git commands are refused if they redirect to the shared checkout — both git -C <main-repo-path> ... (harness refuses: "this command redirects git to the shared checkout... a worktree-isolated session's git operations must target its own worktree") and git fetch . <branch>:main (git itself refuses: "fatal: refusing to fetch into branch 'refs/heads/main' checked out at ''") fail. This applies just as much when you (the primary/orchestrator session) hit it directly at a finishing-a-development-branch-style step as when a sub-agent hits it — it is not a sub-agent-briefing-only concern, and retrying with dangerouslyDisableSandbox does not bypass it either (confirmed: still refused). Don't try further workarounds, and don't default to asking the user to run it manually or handing off to another session as the first move — call ExitWorktree(action: "keep") first (preserves the worktree and its branch on disk, does not discard anything), then run the merge/rebase/push from the primary checkout once the session lands back there. After a clean merge, diff the merge commit for package.json/package-lock.json changes — if either changed, run npm install in the checkout you merged into before trusting the next lint/build/test run; a new dependency the worktree branch added won't error obviously, it just fails lint/build downstream. (detail: memory "project_worktree_merge_new_dependency_stale_node_modules"; incident where this was missed entirely: memory "project_worktree_finishing_branch_sandbox_block")
A clean, conflict-free merge is not proof two concurrently-developed branches agree — a shared implicit numbered/ordinal namespace (migration numbers, "the Nth service" prose, a shared route path) can have two independent additions land on the same value with no git conflict at all. After merging concurrent work touching any such namespace, explicitly grep for the specific identifiers each side introduced and confirm no collision — don't treat git's silence as sufficient. (detail: memory "project_concurrent_semantic_namespace_collision")
Dispatched Agent inheriting an unexited Plan Mode
A background Agent dispatched to execute a fully-approved task can inherit the orchestrator's own not-yet-exited Plan Mode state (e.g. left open from an earlier /socrates Phase 3 entry elsewhere in the same session) — the orchestrator won't notice until the child reports back that it can't write. Once stuck this way, the child has no ExitPlanMode tool of its own, so it will correctly refuse a relayed "it's approved" from the orchestrator (Constitution II: not Josh's direct words) no matter how well-informed that relay is — retrying via SendMessage just burns another cycle on a permanently dead-ended agent. Before dispatching an Agent meant to execute directly, confirm your own session's Plan Mode was actually exited this turn (not inferred from an earlier "go" that predates a possible still-open entry point); if a dispatched agent reports unexpected Plan Mode entrapment, abandon it and dispatch a fresh one with an explicit brief line: "This task is fully scoped and pre-approved — do not enter Plan Mode; execute directly." (detail: memory "feedback_subagent_plan_mode_entrapment")
Verification-before-reporting family
Each of these is an instance of "run safe verification before you surface a result" for a specific recurring scenario:
- Aggregate counts: verify the motivating case is actually in the population before surfacing N matches/rows. (detail: memory "feedback_aggregate_spotcheck")
- Refactor output: verify a dynamic replacement source produces the same keys/values as the hardcoded value it replaces. (detail: memory "feedback_refactor_output_verification")
- Multi-file refactors (5+ files): run a syntax/import check on all modified files plus one end-to-end smoke test before declaring done. (detail: memory "feedback_multifile_refactor_verification")
- Architectural pivots mid-session: independently re-run whatever check confirmed the pre-pivot feature worked — a sub-agent's "expected blocker" framing isn't a substitute. (detail: memory "feedback_architectural_pivot_verification")
- Full-stack features: passing frontend and backend tests separately doesn't confirm they're wired together — verify the real network call fires end-to-end. (detail: memory "project_fraud_detection_mock_data_regression")
- Parallel frontend/backend dispatch: pin the exact response contract (field names, casing, types) as a literal in both briefs before dispatching — don't let each side invent its own assumption independently. A
curl/raw-JSON check of the backend's output does not verify a frontend's interpretation of it; that's a distinct rendering-layer seam a JSON-only check cannot see. Concrete instance: a backend agent returned camelCase fields, a frontend agent independently guessed snake_case for both its mock fixture and its rendering code, and every field silently resolved toundefined— passed a livecurlcheck, only caught by the user reporting the page looked empty. (detail: memory "feedback_parallel_dispatch_contract_mismatch") - Single-example metrics: one anecdote doesn't confirm real signal vs. artifact — check the full value distribution (
COUNT(*) GROUP BY, percentiles). (detail: memory "feedback_single_example_distribution_check") - "What's taking so long?": check actual status/logs immediately — a recap of known steps isn't an answer. (detail: memory "feedback_taking_so_long_investigate")
- Pricing/versioned APIs: name the exact version/generation when quoting a price — tiers get revised between generations and commonly-cited figures are often stale. (detail: memory "feedback_pricing_generation_verification")
- Rendered document/page content (PDF/HTML/GFM text, or a browser-rendered page driven by a live API response): a clean render exit code, a passing test suite, or a correct raw API/JSON response is not proof the delivered content is correct — extract and read the actual rendered output (
pdftotext+read, screenshot+Read, a direct grep of GFM/HTML output, or a Playwright/browser trace of the real page against the real response) for stray source syntax, unresolved markers, leaked metadata, or a consuming layer silently misinterpreting an otherwise-correct upstream response. Recurred 4x across at least three projects without ever becoming a shared checklist item: aworkspaceHTML report needing an explicit "sit in a verification loop" correction; ananalysis-doc/EPQ dark-mode toggle wired into one render recipe but not its siblings (code branched correctly, delivered PDF still wrong); an Argdown-embedded-facts design inanalysis-docthat shipped raw fence syntax and truncated YAML as garbled visible text in a rendered PDF, undetected through a full multi-hour build-and-pilot cycle that repeatedly reported "render clean" until the user asked a question that prompted actually reading the PDF text days later; and aparcel-risk-modellocal dev tool where a correct,curl-verified backend JSON response was silently misread by frontend code built with a different (guessed) field-naming assumption, rendering a "successful" page with empty-looking panels. (detail: memory "feedback_render_to_verify"; "feedback_verification_loop"; "feedback_render_content_not_just_exit_code"; "feedback_parallel_dispatch_contract_mismatch")
Bug attribution
A new failure resembling a previously-fixed bug in the same file/library isn't confirmed to share its root cause — verify against the literal generated payload or source line before attributing blame to the same system. A raw-JSON-passthrough attribute can carry a typo from the caller's own config that looks identical to a library defect. Concrete instance: a tofu apply 500 on terraform-provider-jira was pattern-matched to two earlier, genuine provider bugs in the same .tf file and reported as a third provider defect (worked around via curl); a fresh sub-agent with no priors instead read the Go source, found zero key transformation on that attribute, and traced the real cause to a one-line HCL typo (field_type vs fieldType) in the user's own config. (detail: memory "feedback_bug_attribution_pattern_match")
A user's domain hint pointing at a specific field/table can be a dead end at that literal field while still pointing at the right general direction — don't report "checked, doesn't work" and stop. If the named field is empty/unpopulated, widen to sibling structures serving the same purpose (other columns on the same table, other object types in the same linkage table) before concluding the lead was wrong. Concrete instance: "salesforce.tasks has a 'call' type" led to a dead gong_gong_activity_id_c field, but checking Gong's own CONVERSATION_CONTEXTS.OBJECT_TYPE linkage table next (the sibling structure serving the same account-resolution purpose) recovered 82% more Gong-call-to-account linkage. (detail: memory "feedback_investigation_dont_stop_at_first_field")
Technical gotchas
replace_all safety
Grep the short pattern alone (not surrounding context) to count all occurrences before committing to replace_all: true or a global sed -i. (detail: memory "feedback_replace_all_safety")
Script argument verification
Read a script's argv-handling code before trusting a custom CLI argument was consumed — scripts that ignore argv silently return success.
Backgrounding a command inside run_in_background
Don't wrap a command in your own nohup cmd & disown; sleep N; cat log when also passing run_in_background: true — that's a second, independent backgrounding layer with a fixed guess at completion time, racing the real job. If the guess is too short, the wrapper's own sleep N; cat returns first with only startup output, indistinguishable at a glance from a silent failure (compounded if an unrelated sandbox nice() EPERM line is also present). Pass the command directly (optionally through tail) and rely on the harness's own blocking-wait (TaskOutput) to know when it's actually done. (detail: memory "feedback_background_bash_fixed_sleep_race")
Interactive prompts inside non-interactive Bash
A (Y/n)?-style prompt has no tty to answer it — the command can exit 0 with output that looks like partial success. Check for actual completion (e.g. gcloud components list) before reporting success. (detail: memory "feedback_interactive_prompt_bash")
--quiet/-y to supply the answer is fine only when the user already explicitly approved that exact action this turn (e.g. via AskUserQuestion or a direct "yes, cancel it") — the CLI's y/n is then a redundant confirmation of an already-authorized action, not a fresh consent gate, and supplying it programmatically isn't "automating an interactive tool" in the sense CLAUDE.md's editors/git-commit/interactive-rebase rule means. Do not use --quiet as a default to route around a confirmation prompt when no such prior approval exists for that specific action — that is the case the general rule is protecting against. (detail: memory "pulse-airflow-migration-incident")
API result ownership
When an API returns multiple candidate resources (Jira schemes, GCP resources, IAM policies), trace the full association chain from the target to the resource before acting on the first plausible result. (Project-specific example logged in workspace CLAUDE.local.md.)
BigQuery event-level join fanout
Joining an event-level table (trackers, scan events, audit records) to a billing/invoice table inflates aggregates by the average event count per entity. DISTINCT the entity identifier before joining, and sanity-check against the known entity count. Applies to both sub-agent output and queries run directly — the failure mode recurs because column names match and the error is silent. (Project-specific example in workspace CLAUDE.local.md.)
Vertex fast-mode limitation
/fast requires direct Anthropic API and is unavailable on Vertex. Use the model picker (meta+p) to switch to Opus instead.
Document editing
Dependency check
Before finalizing an edit, grep the full document (and known sibling/duplicate docs — a project README next to its CLAUDE.md, other sections of one data dictionary) for every claim that depends on what changed. When dispatching the edit to a sub-agent, the briefing must explicitly include that grep — intro paragraphs and summary sections are the most common thing a "fix Section 3" brief misses. On a v1→v2→v3 same-session edit sequence, re-run the check after the last edit, not just the first. This applies even when the cause of the change isn't another edit to the doc itself — a rebase pulling in upstream doc changes, or a later commit that changes a real count (tests added/removed) the doc asserts, re-stales a figure just as surely as a manual edit does; re-run the sibling-doc grep after any such commit, not only after commits whose stated purpose is documentation. Recurred 3x in one multi-commit task (2026-08-15, mobile-optimization: post-task doc update, post-rebase, post-fix-wave), each caught only by review. (detail: memory "feedback_iterative_edit_dependency_check"; also "feedback_sibling_document_check"; "feedback_docs_recheck_on_every_number_change")
Citations in externally-shareable artifacts
Memos, reports, proposals, comms: cite sources (URLs, not internal-only links) for quantitative, comparative, or external-behavior claims that could be challenged outside the company.
Architecture and design judgment
Check existing codebase idiom before proposing heavier architecture
Before designing a new layer or pipeline stage (a new transform language, a Python/DuckDB post-processing step, a service boundary) to handle added complexity, check whether the existing codebase's current idiom — the pattern already used by sibling queries/modules for the same class of problem — can be extended to cover it first. A real BigQuery example: asked to bucket customers into 5 sequential magnitude tiers with cross-tier carryover, the first instinct was to propose replacing the project's pure-SQL cohort-definition pattern with a new "broad BigQuery extract + DuckDB/Python wave-assignment" architecture — before confirming the existing all-SQL idiom (chained CTEs, recomputed inline per query) couldn't just be extended with a few more CTEs. The user redirected to "just make a SQL query do it," and it worked once restructured (a BigQuery multi-statement script was still needed for planner-complexity reasons, but that's a much smaller deviation than a new pipeline layer). Propose the extension-of-existing-idiom approach first and name the heavier architecture only as a fallback if the extension provably can't work — don't lead with the rewrite. (detail: memory "feedback_extend_idiom_before_new_layer")
Architectural layer analysis
Before implementing a new feature, name which layer is optimal (Python / BQ view / middleware / application) based on access patterns, join costs, and freshness requirements — don't default to wherever related code already lives.
Data-driven design
For thresholds/caps/bucketing boundaries, query the actual distribution (p50/p75/p90/p95/max) first rather than picking a round number.
Misc
- Dockerfile COPY check: adding a new source directory needs a matching
COPY <newdir>/ <newdir>/line, or it's silently absent from the image until a runtimeFileNotFoundError. - TOML files: comments on their own line above the config, not inline after the value.
subagent-driven-developmenttask briefs: thetask-briefextractor only pulls text under a Task's own## Task Nheading — cross-references like "per the config above" are silently dropped. Inline all referenced config/decisions/constraints literally into each Task section. (detail: memory "feedback_task_brief_self_contained")subagent-driven-developmentforbids parallel dispatch: if a plan will execute under this skill, author it directly in sequential## Task Nform — don't let a Plan agent (dispatched before the execution mechanism is confirmed) design a "parallel execution" phasing recommendation into the plan document, since it has to be discarded/restructured once the skill's sequential-only constraint surfaces. (detail: memory "feedback_sdd_plan_no_parallel_phasing")subagent-driven-developmentworkspace directory keys off the plan-file basename:.superpowers/sdd/<plan-basename>/is meant to isolate one plan's ledger/briefs from another's, but Plan Mode's random plan-file naming is session-scoped, not content-derived — re-entering Plan Mode in the same session and overwriting the same plan file (per the "always start fresh" convention) reuses the old SDD workspace directory too, so a completely unrelated new plan can start execution against a staleprogress.mddescribing a different, already-finished plan. Before dispatching SDD execution, check whether.superpowers/sdd/<basename>/progress.mdalready exists and describes a different plan; archive it first if so. (detail: memory "feedback_sdd_workspace_plan_basename_collision")subagent-driven-developmentreview-package BASE in this shared monorepo: never derive BASE via a path-filteredgit log—~/workspace's concurrent multi-project commit volume means the first path-filtered hit going backward is often far older than the actual parent, pulling the whole intervening history into the diff (confirmed 2x in one session: 88 unrelated commits captured once, a 1MB vs. 415KB diff another time). Recordgit rev-parse HEADsynchronously before the implementer's first commit; if BASE wasn't pre-recorded, recover it via<first-known-commit>^orgit merge-baseagainst the ledger's stated starting commit, never a path-filtered log — and sanity-check diff size/file count before dispatching a reviewer on it. (detail: memory "feedback_sdd_review_package_base_monorepo")- Monorepo git pathspec double-prefixing: from a cwd already inside
~/workspace/projects/<name>/, a pathspec re-prefixed withprojects/<name>/...silently returns empty (git log/git diffexit 0, no error) instead of erroring — use the path relative to cwd instead. Recurred in two different projects 4 days apart. (detail: memory "feedback_monorepo_git_pathspec_double_prefix") - Cross-task bug-class propagation: when an implementer or reviewer reports a bug traceable to a repeatable code pattern (a CSS rule shape, a hook usage, a duplicated helper), grep every other still-untouched target for that same pattern before dispatching the next task — don't rely on each subsequent task to independently rediscover it, and don't wait for the final whole-branch review to be the only backstop. This applies just as much to a solo single-session fix with no sub-agents involved: after fixing a bug traceable to a repeatable pattern (e.g. a specific API misuse), grep the whole repo for that pattern before calling the fix "done" — one bad call site is evidence of a class, not an isolated incident. Confirmed by a same-session recurrence: a stale-metadata bug in a BigQuery MCP client (
job.metadatanever refreshed bygetQueryResults()) was fixed twice independently before a/reflectionpass found and fixed a third live occurrence via a repo-wide grep that the original fix skipped. (detail: memory "feedback_proactive_cross_target_bug_class_grep"; bigquery-repo incident in memory "project_stale_job_metadata_bugclass") - Markdown line wrapping: don't hard-wrap at 80 columns — write prose/list items as single long lines, let the viewer wrap.
Error Handling
- If a cited memory file no longer exists, treat the rule as still in force and note the missing citation — don't drop the rule on that basis alone.
- If a rule here conflicts with something newer in CLAUDE.md, CLAUDE.md wins — this file is the detail layer, not the source of truth.
- If unsure whether a specific check applies to the current task, run it — the cost of an unnecessary verification is far lower than a fabricated or stale claim reaching the user.