codebase-doctor migration dossier

Session 3fd51510 · generated 2026-09-26 10:24:20 UTC

react ^17.0.2 → 18.3.1

codebase-doctor-sandbox/pantry-list · codebase-doctor/react-18.3.1-upgrade from main

Verification failed · 6 open items

  1. Analyse — done
  2. Rules — done
  3. Risk — done
  4. Plan — done
  5. Approve — done
  6. Implement — done
  7. Verify — blocked
  8. Pull request — not started
11
files use react
16 imports · 19 API usages
9
files need attention
4 high-risk
8
breaking changes apply
3 automated · 5 manual/review
failed
verification
iteration 1 · f8756bf

01Risk & priority

Which files matter most, scored from concrete API evidence.

  • High ≥70 4
  • Medium 40–69 0
  • Low 1–39 5
  • Stable 0 2
  1. 90 src/legacy/toast.jsx react-bc-1 +40, react-bc-8 +20, root bootstrap +30
  2. 90 src/legacy/toast.test.jsx react-bc-1 +40, react-bc-3 +20, react-bc-12 +10, test harness +20
  3. 80 src/index.jsx react-bc-1 +40, react-bc-6 +10, root bootstrap +30
  4. 70 src/ssr-entry.jsx react-bc-2 +40, root bootstrap +30
  5. 20 src/App.jsx react-bc-4 +20
  6. 20 src/components/AddItemForm.jsx react-bc-4 +20
  7. 20 src/components/ItemList.jsx react-bc-4 +20
  8. 20 src/components/SyncStatus.jsx react-bc-4 +20

02Breaking changes

The rules that apply to this repository — and why.

high react-bc-1 automated confirmed by docs Applied automatically

ReactDOM.render() is deprecated in React 18 (the app keeps running in legacy React 17 mode) — replace with createRoot().render()

3 files: src/index.jsx, src/legacy/toast.jsx, src/legacy/toast.test.jsx

Transformed 3 file(s); no remaining usages of the old API.

high react-bc-2 automated confirmed by docs Applied automatically

ReactDOM.hydrate() is deprecated in React 18 (legacy mode) — replace with hydrateRoot()

1 file: src/ssr-entry.jsx

Transformed 1 file(s); no remaining usages of the old API.

medium react-bc-3 automated Applied automatically

act() from react-dom/test-utils is deprecated — import act from 'react' (available from React 18.3)

1 file: src/legacy/toast.test.jsx

Transformed 1 file(s); no remaining usages of the old API.

medium react-bc-4 manual confirmed by docs Manual action required

Automatic batching: setState calls in timeouts/promises/native handlers are now batched — may affect code or tests relying on intermediate renders

How to fix Review any tests that assert on intermediate render states inside setTimeout, Promise.then, or native event handlers. Wrap those assertions in act() or use flushSync from 'react-dom' to opt out of automatic batching where needed.

4 files: src/App.jsx, src/components/AddItemForm.jsx, src/components/ItemList.jsx, src/components/SyncStatus.jsx

No automated change exists for this rule. Review any tests that assert on intermediate render states inside setTimeout, Promise.then, or native event handlers. Wrap those assertions in act() or use flushSync from 'react-dom' to opt out of automatic batching where needed.

low react-bc-6 manual confirmed by docs Manual action required

Strict Mode now double-invokes effects (mount → unmount → mount) in development to surface side effects

How to fix Audit useEffect, useLayoutEffect, and class lifecycle methods for non-idempotent side effects (network requests, analytics calls, subscriptions). Ensure cleanup functions are implemented so double-invocation does not cause observable problems in development.

1 file: src/index.jsx

No automated change exists for this rule. Audit useEffect, useLayoutEffect, and class lifecycle methods for non-idempotent side effects (network requests, analytics calls, subscriptions). Ensure cleanup functions are implemented so double-invocation does not cause observable problems in development.

low react-bc-7 manual Manual action required

ReactDOM.unstable_batchedUpdates is redundant under React 18 automatic batching

How to fix With React 18's automatic batching, explicit calls to unstable_batchedUpdates are no longer needed in most cases. Remove them and verify behaviour. If you need to force synchronous rendering, use flushSync from 'react-dom' instead.

1 file: src/legacy/store.js

No automated change exists for this rule. With React 18's automatic batching, explicit calls to unstable_batchedUpdates are no longer needed in most cases. Remove them and verify behaviour. If you need to force synchronous rendering, use flushSync from 'react-dom' instead.

medium react-bc-8 manual confirmed by docs Manual action required

ReactDOM.unmountComponentAtNode() is deprecated in React 18 — call root.unmount() on the root returned by createRoot()

How to fix Keep the root returned by createRoot(container) and call root.unmount() instead of unmountComponentAtNode(container). In tests, unmount the root the test created (React Testing Library unmounts automatically).

1 file: src/legacy/toast.jsx

No automated change exists for this rule. Keep the root returned by createRoot(container) and call root.unmount() instead of unmountComponentAtNode(container). In tests, unmount the root the test created (React Testing Library unmounts automatically).

low react-bc-12 manual confirmed by docs Manual action required

Tests that render with react-dom directly need globalThis.IS_REACT_ACT_ENVIRONMENT = true under React 18

How to fix In the test setup file (e.g. setupTests.js / jest.setup.js), set globalThis.IS_REACT_ACT_ENVIRONMENT = true; so React 18 knows it runs in an act-aware test environment. React Testing Library ≥ 13 sets this automatically for tests that use it.

1 file: src/legacy/toast.test.jsx

No automated change exists for this rule. In the test setup file (e.g. setupTests.js / jest.setup.js), set globalThis.IS_REACT_ACT_ENVIRONMENT = true; so React 18 knows it runs in an act-aware test environment. React Testing Library ≥ 13 sets this automatically for tests that use it.

03Compatibility

Declared packages whose peer ranges could block the install.

Checked 602 packages via package-lock.json (lockfileVersion 3).

PackageRequiresSourceResolution
@testing-library/react@12.1.5react <18.0.0lockfileauto upgraded to ^14.3.1 in the dependency step

04Migration plan

What will change, in order, and what has happened so far.

Plan b3b654d66018 approved 2026-09-26 10:19:06 UTC. Approval is bound to this exact plan — a changed plan needs a new approval.

Estimated effort HIGH · 10 steps · 4 applied automatically · 0 completed manually

  1. Update react, react-dom, @testing-library/react to 18.3.1 and install
    Applied automatically config automated 2 files commit 798ad60
    • react ^17.0.2 → ^18.3.1
    • react-dom ^17.0.2 → ^18.3.1 (companion)
    • @testing-library/react ^12.1.5 → ^14.3.1 (peer-compat)

    Ran npm install --no-audit --no-fund; installed react@18.3.1, react-dom@18.3.1.

  2. ReactDOM.render() is deprecated in React 18 (the app keeps running in legacy React 17 mode) — replace with createRoot().render()
    Applied automatically codemod automated 3 files commit 72cea40

    Transformed 3 file(s); no remaining usages of the old API.

  3. ReactDOM.hydrate() is deprecated in React 18 (legacy mode) — replace with hydrateRoot()
    Applied automatically codemod automated 1 file commit 148d1eb

    Transformed 1 file(s); no remaining usages of the old API.

  4. act() from react-dom/test-utils is deprecated — import act from 'react' (available from React 18.3)
    Applied automatically codemod automated 1 file commit f8756bf

    Transformed 1 file(s); no remaining usages of the old API.

  5. Automatic batching: setState calls in timeouts/promises/native handlers are now batched — may affect code or tests relying on intermediate renders
    Manual action required manual human 4 files

    No automated change exists for this rule. Review any tests that assert on intermediate render states inside setTimeout, Promise.then, or native event handlers. Wrap those assertions in act() or use flushSync from 'react-dom' to opt out of automatic batching where needed.

  6. ReactDOM.unmountComponentAtNode() is deprecated in React 18 — call root.unmount() on the root returned by createRoot()
    Manual action required manual human 1 file

    No automated change exists for this rule. Keep the root returned by createRoot(container) and call root.unmount() instead of unmountComponentAtNode(container). In tests, unmount the root the test created (React Testing Library unmounts automatically).

  7. Strict Mode now double-invokes effects (mount → unmount → mount) in development to surface side effects
    Manual action required manual human 1 file

    No automated change exists for this rule. Audit useEffect, useLayoutEffect, and class lifecycle methods for non-idempotent side effects (network requests, analytics calls, subscriptions). Ensure cleanup functions are implemented so double-invocation does not cause observable problems in development.

  8. ReactDOM.unstable_batchedUpdates is redundant under React 18 automatic batching
    Manual action required manual human 1 file

    No automated change exists for this rule. With React 18's automatic batching, explicit calls to unstable_batchedUpdates are no longer needed in most cases. Remove them and verify behaviour. If you need to force synchronous rendering, use flushSync from 'react-dom' instead.

  9. Tests that render with react-dom directly need globalThis.IS_REACT_ACT_ENVIRONMENT = true under React 18
    Manual action required manual human 1 file

    No automated change exists for this rule. In the test setup file (e.g. setupTests.js / jest.setup.js), set globalThis.IS_REACT_ACT_ENVIRONMENT = true; so React 18 knows it runs in an act-aware test environment. React Testing Library ≥ 13 sets this automatically for tests that use it.

  10. Update and verify tests
    Manual action required test human 3 files

    No automated test changes are made. Update tests if needed, run verify_migration, then record with markManualComplete.

05Affected files

Every file that uses the package family, with line-level evidence.

11 files

src/legacy/toast.jsx react-bc-1react-bc-8 90
LineUsageSourceRules
1import reactimport React from 'react';—
2import react-domimport ReactDOM from 'react-dom';—
17render · react-domReactDOM.render(<Toast message={message} />, host);react-bc-1
19unmountComponentAtNode · react-domReactDOM.unmountComponentAtNode(host);react-bc-8
src/legacy/toast.test.jsx test react-bc-3react-bc-12react-bc-1 90
LineUsageSourceRules
1import reactimport React from 'react';—
2import react-domimport ReactDOM from 'react-dom';—
3import react-dom/test-utilsimport { act } from 'react-dom/test-utils';—
3act · react-dom/test-utilsimport { act } from 'react-dom/test-utils';react-bc-3 react-bc-12
13act · react-dom/test-utilsact(() => {react-bc-3 react-bc-12
18act · react-dom/test-utilsact(() => {react-bc-3 react-bc-12
27act · react-dom/test-utilsact(() => {react-bc-3 react-bc-12
28render · react-domReactDOM.render(<p>host</p>, container);react-bc-1 react-bc-12
src/index.jsx react-bc-1react-bc-6 80
LineUsageSourceRules
1import reactimport React from 'react';—
2import react-domimport ReactDOM from 'react-dom';—
5render · react-domReactDOM.render(react-bc-1
6StrictMode · react<React.StrictMode>react-bc-6
src/ssr-entry.jsx react-bc-2 70
LineUsageSourceRules
1import reactimport React from 'react';—
2import react-domimport ReactDOM from 'react-dom';—
6hydrate · react-domReactDOM.hydrate(<App />, document.getElementById('root'));react-bc-2
src/App.jsx react-bc-4 20
LineUsageSourceRules
1import reactimport React, { useReducer } from 'react';—
1useReducer · reactimport React, { useReducer } from 'react';react-bc-4
23useReducer · reactconst [items, dispatch] = useReducer(itemsReducer, []);react-bc-4
src/components/AddItemForm.jsx react-bc-4 20
LineUsageSourceRules
1import reactimport React, { useState } from 'react';—
1useState · reactimport React, { useState } from 'react';react-bc-4
4useState · reactconst [name, setName] = useState('');react-bc-4
src/components/ItemList.jsx react-bc-4 20
LineUsageSourceRules
1import reactimport React from 'react';—
3Component · reactexport default class ItemList extends React.Component {react-bc-4
src/components/SyncStatus.jsx react-bc-4 20
LineUsageSourceRules
1import reactimport React, { useState } from 'react';—
1useState · reactimport React, { useState } from 'react';react-bc-4
7useState · reactconst [status, setStatus] = useState('idle');react-bc-4
8useState · reactconst [syncedAt, setSyncedAt] = useState(null);react-bc-4
src/legacy/store.js react-bc-7 10
LineUsageSourceRules
1import react-domimport ReactDOM from 'react-dom';—
13unstable_batchedUpdates · react-domReactDOM.unstable_batchedUpdates(() => {react-bc-7
src/App.test.jsx test 0
LineUsageSourceRules
1import reactimport React from 'react';—
src/components/SyncStatus.test.jsx test 0
LineUsageSourceRules
1import reactimport React from 'react';—

06Changes

Each step's commit on the migration branch.

6 files changed on codebase-doctor/react-18.3.1-upgrade since main.

Update react, react-dom, @testing-library/react to 18.3.1 and install798ad60
 package-lock.json | 83 ++++++++++++++++++++++++-------------------------------
 package.json      |  6 ++--
 2 files changed, 39 insertions(+), 50 deletions(-)
diff --git a/package-lock.json b/package-lock.json@@ lockfile diff omitted: +36 −47 lines @@diff --git a/package.json b/package.jsonindex 3712010..20c83d0 100644--- a/package.json+++ b/package.json@@ -10,14 +10,14 @@     "lint": "eslint src"   },   "dependencies": {-    "react": "^17.0.2",-    "react-dom": "^17.0.2"+    "react": "^18.3.1",+    "react-dom": "^18.3.1"   },   "devDependencies": {     "@babel/core": "^7.24.0",     "@babel/preset-env": "^7.24.0",     "@babel/preset-react": "^7.24.0",-    "@testing-library/react": "^12.1.5",+    "@testing-library/react": "^14.3.1",     "babel-jest": "^29.7.0",     "esbuild": "^0.23.0",     "eslint": "^9.9.0",
ReactDOM.render() is deprecated in React 18 (the app keeps running in legacy React 17 mode) — replace with createRoot().render()72cea40
 src/index.jsx             | 7 +++----
 src/legacy/toast.jsx      | 3 ++-
 src/legacy/toast.test.jsx | 4 ++--
 3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/index.jsx b/src/index.jsxindex c1f31c5..8e50ea1 100644--- a/src/index.jsx+++ b/src/index.jsx@@ -1,10 +1,9 @@ import React from 'react';-import ReactDOM from 'react-dom';+import { createRoot } from 'react-dom/client'; import App from './App'; -ReactDOM.render(+createRoot(document.getElementById('root')).render(   <React.StrictMode>     <App />-  </React.StrictMode>,-  document.getElementById('root')+  </React.StrictMode> );diff --git a/src/legacy/toast.jsx b/src/legacy/toast.jsxindex 6fc7919..ef8f0f5 100644--- a/src/legacy/toast.jsx+++ b/src/legacy/toast.jsx@@ -1,5 +1,6 @@ import React from 'react'; import ReactDOM from 'react-dom';+import { createRoot } from 'react-dom/client';  function Toast({ message }) {   return (@@ -14,7 +15,7 @@ function Toast({ message }) { export function showToast(message, ms = 3000) {   const host = document.createElement('div');   document.body.appendChild(host);-  ReactDOM.render(<Toast message={message} />, host);+  createRoot(host).render(<Toast message={message} />);   setTimeout(() => {     ReactDOM.unmountComponentAtNode(host);     host.remove();diff --git a/src/legacy/toast.test.jsx b/src/legacy/toast.test.jsxindex fe5b58c..6d09d4e 100644--- a/src/legacy/toast.test.jsx+++ b/src/legacy/toast.test.jsx@@ -1,5 +1,5 @@ import React from 'react';-import ReactDOM from 'react-dom';+import { createRoot } from 'react-dom/client'; import { act } from 'react-dom/test-utils'; import { showToast } from './toast'; @@ -25,7 +25,7 @@ test('toasts can render inside an existing tree', () => {   const container = document.createElement('div');   document.body.appendChild(container);   act(() => {-    ReactDOM.render(<p>host</p>, container);+    createRoot(container).render(<p>host</p>);   });   expect(container.textContent).toBe('host'); });
ReactDOM.hydrate() is deprecated in React 18 (legacy mode) — replace with hydrateRoot()148d1eb
 src/ssr-entry.jsx | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/ssr-entry.jsx b/src/ssr-entry.jsxindex d20be01..91718cd 100644--- a/src/ssr-entry.jsx+++ b/src/ssr-entry.jsx@@ -1,6 +1,6 @@ import React from 'react';-import ReactDOM from 'react-dom';+import { hydrateRoot } from 'react-dom/client'; import App from './App';  // Entry used when the page is server-rendered: attach to the existing markup.-ReactDOM.hydrate(<App />, document.getElementById('root'));+hydrateRoot(document.getElementById('root'), <App />);
act() from react-dom/test-utils is deprecated — import act from 'react' (available from React 18.3)f8756bf
 src/legacy/toast.test.jsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/legacy/toast.test.jsx b/src/legacy/toast.test.jsxindex 6d09d4e..225ce96 100644--- a/src/legacy/toast.test.jsx+++ b/src/legacy/toast.test.jsx@@ -1,6 +1,6 @@ import React from 'react'; import { createRoot } from 'react-dom/client';-import { act } from 'react-dom/test-utils';+import { act } from 'react'; import { showToast } from './toast';  afterEach(() => {

07Verification

Does the upgraded code pass the repository's own checks?

Verification failed iteration 1 · commit f8756bf · 2026-09-26 10:24:19 UTC
CheckResultCommandDetail
Installed versionsPASS—react@18.3.1, react-dom@18.3.1
LintFAILnpm run lint
TestFAILnpm run test1 failed: shows the sync time once the save resolves
BuildPASSnpm run build

Failure diagnosis

LINT FAILURES:
> pantry-list@1.0.0 lint
> eslint src
<repo>\src\legacy\toast.jsx
  20:5  error  ReactDOM.unmountComponentAtNode is deprecated since React 18.0.0, use root.unmount instead, see https://reactjs.org/link/switch-to-createroot  react/no-deprecated
✖ 1 problem (1 error, 0 warnings)

TEST FAILURES:
Tests: 1 failed, 3 passed, 4 total
Failed tests: shows the sync time once the save resolves
lint output (283 characters)

> pantry-list@1.0.0 lint
> eslint src


<repo>\src\legacy\toast.jsx
  20:5  error  ReactDOM.unmountComponentAtNode is deprecated since React 18.0.0, use root.unmount instead, see https://reactjs.org/link/switch-to-createroot  react/no-deprecated

✖ 1 problem (1 error, 0 warnings)

test output (4031 characters)

> pantry-list@1.0.0 test
> jest

PASS src/legacy/toast.test.jsx (22.174 s)
  ● Console

    console.error
      Warning: The current testing environment is not configured to support act(...)

      16 |   const host = document.createElement('div');
      17 |   document.body.appendChild(host);
    > 18 |   createRoot(host).render(<Toast message={message} />);
         |                    ^
      19 |   setTimeout(() => {
      20 |     ReactDOM.unmountComponentAtNode(host);
      21 |     host.remove();

      at printWarning (node_modules/react-dom/cjs/react-dom.development.js:86:30)
      at error (node_modules/react-dom/cjs/react-dom.development.js:60:7)
      at isConcurrentActEnvironment (node_modules/react-dom/cjs/react-dom.development.js:25299:7)
      at warnIfUpdatesNotWrappedWithActDEV (node_modules/react-dom/cjs/react-dom.development.js:27598:12)
      at scheduleUpdateOnFiber (node_modules/react-dom/cjs/react-dom.development.js:25547:5)
      at updateContainer (node_modules/react-dom/cjs/react-dom.development.js:28897:5)
      at ReactDOMRoot.Object.<anonymous>.ReactDOMHydrationRoot.render.ReactDOMRoot.render [as render] (node_modules/react-dom/cjs/react-dom.development.js:29353:3)
      at render (src/legacy/toast.jsx:18:20)
      at src/legacy/toast.test.jsx:14:14
      at act (node_modules/react/cjs/react.development.js:2512:16)
      at Object.<anonymous> (sr
… [4003 characters omitted] …
s-src.js:1401:20)
      at FakeTimers.advanceTimersByTime (node_modules/@jest/fake-timers/build/modernFakeTimers.js:94:19)
      at advanceTimersByTime (src/legacy/toast.test.jsx:19:10)
      at act (node_modules/react/cjs/react.development.js:2512:16)
      at Object.<anonymous> (src/legacy/toast.test.jsx:18:6)

    console.error
      Warning: The current testing environment is not configured to support act(...)

      26 |   document.body.appendChild(container);
      27 |   act(() => {
    > 28 |     createRoot(container).render(<p>host</p>);
         |                           ^
      29 |   });
      30 |   expect(container.textContent).toBe('host');
      31 | });

      at printWarning (node_modules/react-dom/cjs/react-dom.development.js:86:30)
      at error (node_modules/react-dom/cjs/react-dom.development.js:60:7)
      at isConcurrentActEnvironment (node_modules/react-dom/cjs/react-dom.development.js:25299:7)
      at warnIfUpdatesNotWrappedWithActDEV (node_modules/react-dom/cjs/react-dom.development.js:27598:12)
      at scheduleUpdateOnFiber (node_modules/react-dom/cjs/react-dom.development.js:25547:5)
      at updateContainer (node_modules/react-dom/cjs/react-dom.development.js:28897:5)
      at ReactDOMRoot.Object.<anonymous>.ReactDOMHydrationRoot.render.ReactDOMRoot.render [as render] (node_modules/react-dom/cjs/react-dom.development.js:29353:3)
      at render (src/legacy/toast.test.jsx:28:27)
      at act (node_modules/react/cjs/react.development.js:2512:16)
      at Object.<anonymous> (src/legacy/toast.test.jsx:27:6)

FAIL src/components/SyncStatus.test.jsx (26.309 s)
  ● shows the sync time once the save resolves

    expect(received).toEqual(expected) // deep equality

    - Expected  - 4
    + Received  + 0

    @@ -2,13 +2,9 @@
        Object {
          "status": "idle",
          "syncedAt": null,
        },
        Object {
    -     "status": "idle",
    -     "syncedAt": "12:00",
    -   },
    -   Object {
          "status": "done",
          "syncedAt": "12:00",
        },
      ]

      11 |
      12 |   // Every state the component rendered, in order.
    > 13 |   expect(renders).toEqual([
         |                   ^
      14 |     { status: 'idle', syncedAt: null },
      15 |     { status: 'idle', syncedAt: '12:00' },
      16 |     { status: 'done', syncedAt: '12:00' },

      at Object.toEqual (src/components/SyncStatus.test.jsx:13:19)

PASS src/App.test.jsx (27.44 s)

Test Suites: 1 failed, 2 passed, 3 total
Tests:       1 failed, 3 passed, 4 total
Snapshots:   0 total
Time:        36.287 s
Ran all test suites.

08Remaining work

What still needs a human before (or after) merging.

  • Manual action requiredstep-5-react-bc-4

    Automatic batching: setState calls in timeouts/promises/native handlers are now batched — may affect code or tests relying on intermediate renders

    Review any tests that assert on intermediate render states inside setTimeout, Promise.then, or native event handlers. Wrap those assertions in act() or use flushSync from 'react-dom' to opt out of automatic batching where needed.

  • Manual action requiredstep-6-react-bc-8

    ReactDOM.unmountComponentAtNode() is deprecated in React 18 — call root.unmount() on the root returned by createRoot()

    Keep the root returned by createRoot(container) and call root.unmount() instead of unmountComponentAtNode(container). In tests, unmount the root the test created (React Testing Library unmounts automatically).

  • Manual action requiredstep-7-react-bc-6

    Strict Mode now double-invokes effects (mount → unmount → mount) in development to surface side effects

    Audit useEffect, useLayoutEffect, and class lifecycle methods for non-idempotent side effects (network requests, analytics calls, subscriptions). Ensure cleanup functions are implemented so double-invocation does not cause observable problems in development.

  • Manual action requiredstep-8-react-bc-7

    ReactDOM.unstable_batchedUpdates is redundant under React 18 automatic batching

    With React 18's automatic batching, explicit calls to unstable_batchedUpdates are no longer needed in most cases. Remove them and verify behaviour. If you need to force synchronous rendering, use flushSync from 'react-dom' instead.

  • Manual action requiredstep-9-react-bc-12

    Tests that render with react-dom directly need globalThis.IS_REACT_ACT_ENVIRONMENT = true under React 18

    In the test setup file (e.g. setupTests.js / jest.setup.js), set globalThis.IS_REACT_ACT_ENVIRONMENT = true; so React 18 knows it runs in an act-aware test environment. React Testing Library ≥ 13 sets this automatically for tests that use it.

  • Manual action requiredstep-10-tests

    Update and verify tests

    Update any test utilities that changed in the new version, then confirm the full suite passes with verify_migration. This step makes no automated source changes.

09Metrics

Measured values, labelled estimates, and what is not measured.

measured

Files using react11
Files with breaking-change evidence9
Files changed on branch6
Steps applied automatically4 / 10
Steps completed manually0
Steps still open (manual / skipped / failed)6
Elapsed session time (wall-clock)5 min

estimate

Manual effort
~4.5 h9 affected files × 30 min/file (heuristic, not benchmarked)
Time saved
~4.4 hestimated manual effort − measured elapsed time

not measured

  • Bobcoin consumption — not measured (not exposed to the MCP server)
  • Accuracy — not measured (no ground-truth benchmark)

10Repository

How the repository was analysed.

Repositorycodebase-doctor-sandbox/pantry-list
Default branchmain
Migration branchcodebase-doctor/react-18.3.1-upgrade
Languagejavascript
Package managernpm
Test frameworkjest
Lint / test / buildnpm run lint · npm run test · npm run build
Source files scanned13
Rule setreact-17-to-18.json + your migration docs
Session3fd51510-5cd8-41f8-8271-285c542bb72f