Canonical source: docs/claude/bug-scoping-template.md. This page is generated by docs/scripts/sync-handbook.mjs. Edit the source file in the repo; changes appear here on the next build.
Bug Scoping Template
The canonical shape of a well-scoped bug on the Ready Practice board. It exists so every bug — whether it comes from a human (George, a clinic, staff), an automated source (Sentry, cost-watch, ops-watch, the QA fleet), or the Atlas Intake Router (#222) — carries the same fields, so an agent (or engineer) can go from "report" to "fix" without a second round of questions.
Two audiences, two halves:
- Report — filled by whoever/whatever raises the bug. The intake router maps its source payload into these fields; a human uses the GitHub
Bug Reportform (.github/ISSUE_TEMPLATE/bug-report.yml). - Triage / Agent scoping — filled by the first agent (or engineer) that picks it up, before any
agent:build. A bug isn't "scoped" until this half is complete. Post it as a comment or fold it into the body.
Golden rule: a bug is scopable when a competent agent could reproduce it and locate the fault from the issue alone. A bug is fixable when the triage half names the root cause (file:line), the blast radius, and how to verify the fix. If either is missing, the missing part is the next action — name it in Open Questions.
Half 1 — REPORT (reporter / intake source fills)
| Field | Why it matters | Example |
|---|---|---|
| Title | One line: symptom + surface. Not the guessed cause. | "Group class shows 'Unknown Client' on schedule after refresh" |
| Severity | P0 prod-down/data-loss/security · P1 major-broken-no-workaround · P2 broken-workaround-exists · P3 cosmetic | P2 |
| Surface | Which app and screen. basisflow-web (staff) / basisweb (client portal) / basishybrid (mobile) / basis-functions (backend) / Atlas. | basisflow-web → Schedule, week view |
| Tenant context | Multi-tenant: which clinic (ID), and is it one clinic or all? Most prod bugs are tenant-specific. | clinic xdoz-pjao-zxvm; seen at this clinic only |
| Actor | The role it happens for (Admin/Manager/Medical/CS/Coach/client) + affected user/member UID if known. Test with Admin, not System Admin (System Admin bypasses perm checks). | Manager role; member UID abc… |
| Expected vs Actual | The two-line core. Expected = what should happen; Actual = what happens. | Expected: full class is not bookable. Actual: full class still bookable. |
| Steps to reproduce | Exact, numbered, from a known start state. Include the data used. | 1. Open Schedule … 2. … 3. See … |
| When & how often | Timestamp with timezone (reporter is UTC+3, clinics are US Pacific, logs are UTC — always label). Always / intermittent / %. | 2026-07-11 ~3:09pm PT; every time |
| Regression? | Did it work before? When did it break / last-known-good? Any recent deploy near that time? | Worked last week; "possibly solved" then regressed |
| Evidence / identifiers | The concrete artifacts an agent will open: screenshot/recording, exact error text, console/log lines, Sentry link, request/trace ID, and specific doc IDs (event / membership / order / summary / claim). | Sentry …; event guzGyq…; screenshot attached |
| Environment | Build/version, browser/OS or device. | Chrome 126, macOS; app build 1.7.65 |
Reporter-side smells that block scoping (fix at intake):
- No clinic ID on a prod bug → almost always un-actionable (can't inspect the data).
- "Doesn't work" with no expected/actual → ask what they concretely saw (esp. payments/PaymentModal — verify the gap first, don't infer from source).
- A timestamp with no timezone → convert & label PT before it reaches an agent.
- A guessed cause in the title → keep the title to the symptom; guesses go in triage.
Half 2 — TRIAGE / AGENT SCOPING (first agent/engineer fills, before agent:build)
Post this block on the issue. Every field is required to call a bug "scoped."
### 🔎 Triage
**Reproduced:** yes / no / partial — <how; tool/test account/data used>
**Status:** confirmed root cause / strong hypothesis / needs-info
**Related / possibly duplicates:** #N — <why> (or "none")
**Root cause:** <one paragraph> — `path/to/file.py:LINE`
<the mechanism: what value/branch/contract is wrong and why the symptom follows>
**Blast radius:** <one clinic vs all tenants; which roles; how many users; PHI/payments/deploy-sensitive?>
**Suitability (feeds #217 gate / #224 self-healing):** taskType = deterministic-code | cross-repo | design-subjective | investigation · critical-path? yes/no (money/auth/PHI/clinical/prescribing/RCM/booking-money). Only deterministic-code + non-critical + checkable-criteria auto-builds; anything else → human.
**Fix direction (NOT the fix):** <the approach, and what it must NOT break — e.g. "gate on X; must keep wearable sync flowing (#241 lesson)">
**Confidence:** high / medium / low — <what would raise it>
**How to test / verify:** <exact observable that proves fixed>
- Repro/verify tool or script: <tools/… or "add one">
- Test as: Admin role (not System Admin), clinic <id>, <account/data>
- Expected after fix: <observable>
**Acceptance criteria:**
- [ ] <checkable done condition tied to the symptom>
- [ ] <no-regression guard for the blast radius>
- [ ] verified via the shipped UI (not only tests) for user-facing bugs
- [ ] backend fix DEPLOYED (`ship.sh`) + verified in prod — a merged PR is not a live fix
**Open questions / blockers (what's needed from a human):**
- <specific ask: repro data, an error string, a product decision, a screenshot — or "none">
Triage-side principles (how agents keep quality):
- Verify, don't guess. Confirm enums/paths/signatures/field-types against real code and real Firestore docs — TypeScript/Pydantic types lie at runtime. Never assume; read it. Concretely (our #1 bug class): Firestore fields are often missing or the wrong type —
genderas int1/2not"male", a date stored as a Timestamp instead of a"YYYY-MM-DD"string, an alias mismatch that drops a value to its default (see #271:attendeeRolewritten by field-name, read by alias → alwaysNONE). Rule this out on the real docs before assuming a logic bug. - Reproduce read-only first. Prefer a
basis-functions/tools/script that runs the real code path against prod data read-only (likeinspect_monarch_group_slots.py) over reasoning from source. Build a reusable one if none exists. - Name the blast radius before proposing a fix — multi-tenant impact, PHI, payments, deploy-sensitivity. A wrong guard can drop real data (see the mirror-loop and terra-for-else lessons).
- Root cause ≠ symptom. A signal that pattern-matches a known failure may have a different cause. Trace to the actual faulting line.
- Don't say "fixed" without runtime verification. Acceptance is an observable in the running product, confirmed by the reporter where possible.
- If blocked, the blocker is the deliverable — put the precise missing input in Open Questions rather than half-scoping.
Pipeline notes
- Intake Router (#222): dedups + routes all bug/feature/gap sources. It should populate Half 1 from the source (Sentry → error text + trace; clinic Slack → tenant + steps; QA fleet → repro + evidence) and, on a dedup match, append the new report to the existing issue instead of creating a duplicate (
board.shalready does append-on-match). - Labels: type label
bugis informational.agent:build(autonomous build → PR) andagent:triageare set only by George — never self-applied by agents.needs-humankeeps it manual. Addblocked+ "Blocked by: #N" when it can't start until another issue lands. - Board: move to In Progress when triage starts; close when the acceptance criteria are verified (the board workflow moves closed → Done). On close, sweep for dependents ("Blocked by: #
").
See also: .github/ISSUE_TEMPLATE/bug-report.yml (human form), .github/ISSUE_TEMPLATE/task.yml (features), and CLAUDE.md → Task board / Pre-Flight Checklist.