Claude applies partial fixes: changes the reported line but does not trace or verify the full data path

Resolved 💬 1 comment Opened Apr 27, 2026 by casriraj Closed May 29, 2026

Problem

When Claude fixes a reported bug, it changes the specific line mentioned in the finding but does not trace the fix through the full data path. This creates "partial fixes" where the reported symptom line is changed but the feature remains broken end-to-end. The user then needs multiple rounds of re-audit to find the remaining breakages.

In a 30-session production project with 93 tracked bugs, this pattern required 5 audit rounds to close 32 bugs that should have been fixed in 1 round. Each round, Claude claimed "all fixed" -- but independent verification found 4, then 2, then more still broken.

---

The Core Behavior

When Claude receives a bug report like "column sm.month should be sm.year_month in dashboard_service.py", it:

  1. Opens the file
  2. Finds sm.month
  3. Changes it to sm.year_month
  4. Reports "Fixed"

What it does NOT do:

  • Trace the data from model -> service SQL -> route -> API client -> frontend component to verify the full path works
  • Check if the same bug exists in other files (e.g., CTE outer SELECT, prod compose, lockfile)
  • Verify the fix actually makes the feature work end-to-end, not just changes the reported line
  • Check related files that consume the same data

---

Incident Timeline (all from one project, with KB tracking IDs)

Incident 1: KB-012 -- 32 bugs, 5 rounds, each round claimed "all fixed" (2026-04-23)

This is the defining incident. 32 bugs were found across 4 audit rounds. Statistics:

| Round | Bugs found | Actually fixed | Partial fixes | Still broken |
|---|---|---|---|---|
| Round 1 | 13 | 9 | 4 | 0 |
| Round 2 | 7 | 5 | 2 | 2 not fixed |
| Round 3 | 6 remaining | 4 | 2 | 0 |
| Round 4 | 2 partials | 2 | 0 | 0 |
| Round 5 | 12 new found | 11 | 0 | 1 architectural |

After every round, Claude reported "all issues fixed." The user had to independently verify each fix to discover the partials.

Specific partial fix examples from KB-012:

Example A: SQL CTE rename missed outer SELECT

  • Bug: _summary_monthly.month should be year_month
  • What Claude did: global replace sm.month -> sm.year_month in the main query
  • What Claude missed: the CTE outer SELECT still said month (different SQL string, not caught by the replace)
  • Result: query still fails at runtime because CTE alias and outer query disagree

Example B: Route added but pointed at wrong component

  • Bug: ledger statement page has no route
  • What Claude did: added route /ledger-statement in the router config
  • What Claude missed: pointed the route at TrialBalancePage instead of LedgerStatementPage (which didn't exist yet)
  • Result: clicking "Ledger Statement" shows the Trial Balance page -- wrong content, no error

Example C: API method added but never wired to UI

  • Bug: ledger statement data not loading
  • What Claude did: added getLedgerStatement() to the API client module
  • What Claude missed: the page component still has dataSource={[]} hard-coded, never calls useQuery with the new API method
  • Result: page renders with an empty table, no network request made

Example D: Dev compose fixed but not prod compose

  • Bug: Celery worker can't find Redis (missing env vars)
  • What Claude did: added CELERY_BROKER_URL and CELERY_BACKEND_URL to the worker service in docker-compose.yml
  • What Claude missed: docker-compose.prod.yml still has the old config -- production deployment will have the same bug
  • Result: works in dev, crashes in prod

Example E: Package removed but lockfile stale

  • Bug: unused package should be removed
  • What Claude did: removed the package from package.json
  • What Claude missed: package-lock.json still locks the old version; node_modules/ still has it
  • Result: the package is still installed and bundled, still taking space, potentially still imported

Incident 2: KB-011 -- Bulk generation with no verification (2026-04-23)

What happened: 20+ files created in a single session. No per-file verification.

Statistics: 13 bugs on first audit, 7 more on second audit, 4 of the 13 were partial fixes on first attempt.

From KB-011: "Speed of code generation is meaningless if the code doesn't work. Every 10 files generated without verification = ~3 bugs introduced."

The meta-pattern: Claude prioritized output volume. It generated service code, routes, and frontend pages in rapid succession without:

  • Reading target models/schemas before writing
  • Testing each endpoint after creation
  • Verifying frontend calls match backend contracts
  • Checking Docker compose env vars match config

Incident 3: KB-037 -- Patch-local thinking in lifecycle features (2026-04-25)

What happened: Branch-scoped agent activation feature (#044) required 6+ review passes with 20+ findings. Each pass found bugs the previous pass missed.

Three systematic partial-fix failure modes documented in KB-037:

Mode 1: Patch-local thinking
Claude fixes the visible line but does not trace the full lifecycle. Example: adding cleanup code in a finally block sounds correct until you trace that terminate=True kills the process before finally runs.

Mode 2: Happy-path tests
Tests cover "function returns expected result" but not timing, race conditions, cancellation, mid-operation-switch, or crash recovery. Celery termination, Redis queue state, and cross-schema consistency are blind spots.

Mode 3: Summary drift
Claude's fix summary says "fixed" in design language, but the code has incomplete wiring. Example: "force_password_change is now enforced" -- the field exists on the User model and is set to True on creation, but no middleware or route guard actually checks it. The summary describes the intent, not the reality.

Incident 4: Retry state bug fixed in one file, identical bug left in another (#046, #060)

What happened: Celery tasks persist FAILURE state to the database before calling self.retry(). If the retry succeeds, the database still shows FAILURE.

Partial fix pattern:

  • Bug found in tally_sync task -> Claude fixed it (changed to RETRYING state)
  • Same identical bug existed in intelligence_build task -> Claude did NOT fix it
  • Found in a separate audit round days later

From KB-012 rules: "When the same column name bug appears in 3+ files, do a project-wide grep before committing. One file fixed does not mean all are fixed."

This rule was written for column names but applies identically to any code pattern -- Claude fixes the reported instance but does not search for the same pattern elsewhere.

Incident 5: Docker env var fix applied to one service, not all (KB-014, 2026-04-23)

What happened: Celery environment variables (CELERY_BROKER_URL, CELERY_BACKEND_URL) were added to the worker service in docker-compose but not to the backend service.

Why both need it: The backend service enqueues Celery tasks (imports celery_app), and the worker service runs them (also imports celery_app). Both need the same Redis connection config.

From KB-014: "Fixed the worker (which runs tasks) but forgot the backend (which enqueues tasks). Both import celery_app, both need the same Redis connection config."

Incident 6: Frontend contract not updated when backend changes (KB-003, KB-008)

What happened: Backend endpoints were created or modified, but the frontend API client, interceptors, and TanStack Query hooks were not updated to match.

From KB-003: "Backend returns { success, data, message } but frontend expects data at response.data.data (Axios wraps in .data, then envelope has .data). Mismatch at any layer causes silent undefined propagation."

Result: Pages render with empty tables, no error messages displayed, components render blank. The API is working correctly; the frontend just doesn't parse the response correctly.

Incident 7: Test assertions not updated when code evolves (KB-065, 2026-04-26)

What happened: Intelligence builder tables and agent state format evolved (v1 -> v2), but test assertions were not updated.

Specific partial fixes:

  • Builder changed column name from month to year_month -> test still asserts month exists
  • Agent state changed from flat format to companies[] nested format -> tests still check flat keys
  • Tests pass because they skip on missing fixtures (desktop SQLite DB) or assert wrong column names that happen to exist in the old format

From KB-065: "Tests were written against desktop SQLite schema and v1 state format. When builders and state format evolved, tests were not updated because they run against optional fixtures (desktop DB) or were not in CI."

---

Why This Is a Model Behavioral Issue

  1. Claude does not trace data paths. When fixing a SQL column name, Claude should trace: model definition -> service query -> route response -> API client parsing -> frontend component rendering. Instead, it fixes only the reported layer.
  1. Claude does not search for pattern siblings. When fixing a bug in file A, Claude should grep for the same pattern in files B, C, D. Instead, it fixes only the reported file.
  1. Claude claims "fixed" prematurely. After changing the reported line, Claude says "Fixed" without verifying the feature works end-to-end. This forces the user into multiple audit rounds.
  1. This persists despite rules. KB-012 established a 6-step full-path verification checklist. Claude continued making partial fixes in subsequent sessions.
  1. The cost is multiplicative. Each partial fix requires a new audit round. 5 rounds x user verification time means the fix process takes 5x longer than it should.

---

Quantified Impact

| Metric | Value |
|---|---|
| Bugs requiring multiple fix rounds | 32 (KB-012 statistics) |
| Total audit rounds needed | 5 (should have been 1) |
| Partial fix rate (Round 1) | 31% (4 of 13 fixes were partial) |
| Cross-file pattern siblings missed | 100% -- Claude never proactively searches for the same bug in other files |
| "All fixed" claims that were false | 4 out of 5 rounds |
| Rules created to prevent | 2 (KB-012 full-path checklist, KB-011 incremental verification) |
| Rules effective at preventing | 0 (pattern continued after rules were added) |

---

The Full-Path Verification That Claude Should Do

From KB-012, the checklist Claude should run after EVERY fix:

1. Model -> service SQL         (column names match?)
2. Service -> route             (params/response match?)
3. Route -> API client          (body/response match?)
4. API client -> page component (useQuery wired? data passed to JSX?)
5. Dev compose -> prod compose  (same fix in both?)
6. package.json -> lockfile     (lockfile consistent?)

If ANY link in the chain is not verified, the fix is partial.

Additionally, after fixing any pattern-based bug:

7. grep project-wide for the same pattern in other files
8. Fix all instances, not just the reported one

---

Suggested Improvements

1. Full-path tracing after every fix

After Claude changes code in one layer, it should automatically read the adjacent layers in the call chain. For a backend service fix:

  • Read the route that calls the service (does the response shape still match?)
  • Read the frontend API method that calls the route (does the request shape still match?)
  • Read the component that uses the data (is it wired to useQuery? does it access the right fields?)

2. Pattern-sibling search

After fixing a bug that involves a wrong name, wrong pattern, or wrong config value, Claude should grep the project for:

  • The old (wrong) value -- are there more instances?
  • The pattern shape -- are similar bugs in similar files?

Example: after fixing sm.month -> sm.year_month in one service, grep for \.month[^_] across all Python files.

3. Environment parity check

After fixing any Docker Compose, environment variable, or infrastructure config:

  • Check if the same config exists in docker-compose.prod.yml, .env.example, or other environment-specific files
  • Check if all services that import the consuming module have the same env var

4. "Fixed" should mean "end-to-end verified"

Claude should not say "Fixed" after changing a line. It should say "Fixed -- verified by reading [list of files checked]" or "Changed the reported line -- please verify end-to-end as I cannot confirm the full path works."

5. Lock file consistency

After any package.json or requirements.txt change, Claude should note that the lockfile needs regeneration. Removing a package from package.json without running npm install leaves the lockfile stale.

6. Batch fix verification

When Claude fixes N bugs in one session, it should re-read each fix before claiming "all N fixed." The current pattern is: fix 1, fix 2, ..., fix N, claim "all fixed" -- without re-reading any of them.

---

Environment

  • Tool: Claude Code CLI + VS Code extension
  • Models observed: Claude Opus and Sonnet (pattern consistent across both)
  • Project scale: ~90K LOC, 31 ORM models, 68+ endpoints, fullstack (Python + TypeScript + Docker + Celery)
  • Duration: ~30 sessions, 93 documented bugs (KB-001 through KB-093)
  • Bug tracking quality: Every bug was tracked with KB-NNN IDs, root cause analysis, fix patterns, and rules. Claude had access to all of this. The pattern continued regardless.

Reproduction

  1. Create a fullstack project (backend + frontend + infra config)
  2. Introduce 10 bugs across different layers (wrong column name in SQL, wrong env var in compose, missing useQuery in frontend, route pointing at wrong component)
  3. Report all 10 to Claude in a single message
  4. Let Claude fix them and claim "all fixed"
  5. Manually verify each fix end-to-end
  6. You will find: ~30% are partial fixes (line changed but feature still broken), and 0 pattern-sibling searches were done

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗