{
  "steward": "react-sp-practises-steward",
  "project": "Azure-AI-RAG-CSharp-Semantic-Kernel-Functions",
  "runDate": "2026-03-21",
  "runId": "2026-03-21T00-00-00",
  "findings": [
    {
      "id": "RSPA-EFFECT-001",
      "title": "useEffect fetch calls lack AbortController cleanup",
      "severity": "critical",
      "category": "EFFECT",
      "file": "src/web/src/SupportAgent/Agent.js",
      "line": 56,
      "description": "Both handleSession (invoked from useEffect) and handlePrompt fire fetch requests with no AbortController cleanup. If SupportAgent unmounts before a response resolves, the .then callbacks still execute and call setState on stale closures, causing potential memory leaks and stale state updates.",
      "recommendation": "Introduce an AbortController in handleSession and return a cleanup function from useEffect that calls controller.abort(). For handlePrompt, track an abort reference via useRef and cancel on component unmount.",
      "status": "open"
    },
    {
      "id": "RSPA-KEY-001",
      "title": "Array index used as key with in-JSX mutation (i++) in message list",
      "severity": "critical",
      "category": "KEY",
      "file": "src/web/src/SupportAgent/ChatLayout.js",
      "line": 9,
      "description": "The map callback uses (obj, i = 0) as parameters where i is the array index. Both the outer <div> and inner <Box> receive keys derived from i and i++ respectively, producing offset and unstable keys. Index-based keys prevent correct reconciliation when messages are added; the i++ mutation inside JSX also causes the inner Box key to always differ from the outer div key by one.",
      "recommendation": "Add a stable id field (e.g. UUID or incrementing counter) to each message object when it is created in Agent.js. Use that id as the key on the outermost mapped element only. Remove the duplicate key prop on the inner Box.",
      "status": "open"
    },
    {
      "id": "RSPA-EFFECT-002",
      "title": "useEffect dependency array missing session variable",
      "severity": "notable",
      "category": "EFFECT",
      "file": "src/web/src/SupportAgent/Agent.js",
      "line": 56,
      "description": "The useEffect reads the session state variable inside handleSession but declares an empty dependency array []. The react-hooks/exhaustive-deps rule would flag this. If session is ever reset to empty (e.g. session expiry), the effect will not re-fire because the dependency was omitted.",
      "recommendation": "Add session to the dependency array: }, [session]). The existing if (session === '') guard ensures the fetch only fires when the session is empty.",
      "status": "open"
    },
    {
      "id": "RSPA-STATE-001",
      "title": "Dead state declared in Main class component but never used",
      "severity": "notable",
      "category": "STATE",
      "file": "src/web/src/Main.js",
      "line": 13,
      "description": "Main.js declares this.state = { question: '', searchResults: [] } in the constructor but neither property is ever read, updated, or referenced in the render method. This dead state adds maintenance overhead and confusion.",
      "recommendation": "Remove the unused question and searchResults state properties from the constructor.",
      "status": "open"
    },
    {
      "id": "RSPA-COMP-001",
      "title": "Props object not destructured in ChatLayout — forces messages.messages double-access",
      "severity": "notable",
      "category": "COMP",
      "file": "src/web/src/SupportAgent/ChatLayout.js",
      "line": 6,
      "description": "The function signature is ChatLayout(messages) where messages is the entire props object. Accessing the messages prop requires messages.messages. If the prop name changes or the component is called incorrectly, the failure is silent (undefined). Idiomatic React destructures props in the parameter.",
      "recommendation": "Change the signature to function ChatLayout({ messages }) to destructure the prop directly and eliminate the double-access.",
      "status": "open"
    },
    {
      "id": "RSPA-EFFECT-003",
      "title": "No error handling on fetch calls — unhandled rejections and silent failures",
      "severity": "notable",
      "category": "EFFECT",
      "file": "src/web/src/SupportAgent/Agent.js",
      "line": 29,
      "description": "Both fetch calls use .then().then() chains with no .catch(). Network errors and JSON parse failures are silently swallowed. On chat failure, the user sees no feedback. On session failure, the session remains empty and subsequent chat requests will fail without explanation.",
      "recommendation": "Add .catch() handlers to both fetch chains. Introduce an error state variable in SupportAgent and render a user-facing error message when a request fails.",
      "status": "open"
    },
    {
      "id": "RSPA-COMP-002",
      "title": "Class component in hooks-based app with misleading exported name App",
      "severity": "minor",
      "category": "COMP",
      "file": "src/web/src/Main.js",
      "line": 8,
      "description": "Main.js defines class App extends Component but is imported as Main. The class name App collides conceptually with the App functional component in App.js. The class holds no non-trivial lifecycle logic that justifies keeping it as a class.",
      "recommendation": "Rename the class to Main to match its module name and import alias. Consider converting to a functional component for consistency with the rest of the codebase.",
      "status": "open"
    },
    {
      "id": "RSPA-COMP-003",
      "title": "console.log left in production code path",
      "severity": "minor",
      "category": "COMP",
      "file": "src/web/src/SupportAgent/Agent.js",
      "line": 38,
      "description": "console.log(res) on line 38 logs the full API chat response to the browser console in production. CRA does not strip console.log statements by default. This leaks potentially sensitive support-chat content.",
      "recommendation": "Remove the console.log call or gate it behind process.env.NODE_ENV === 'development'.",
      "status": "open"
    },
    {
      "id": "RSPA-TS-001",
      "title": "No TypeScript — all component props are untyped",
      "severity": "minor",
      "category": "TS",
      "file": "src/web",
      "description": "The project ships plain JavaScript with no TypeScript or PropTypes. All component props, API response shapes (res.resp, res.session_id), and message object structures are untyped. Type errors such as the ChatLayout props double-access (RSPA-COMP-001) would be caught at compile time with TypeScript.",
      "recommendation": "Migrate to TypeScript or add PropTypes declarations for all components as a lighter alternative.",
      "status": "open"
    },
    {
      "id": "RSPA-MEMO-001",
      "title": "ChatLayout not memoized — re-renders on every keystroke in parent",
      "severity": "info",
      "category": "MEMO",
      "file": "src/web/src/SupportAgent/ChatLayout.js",
      "line": 6,
      "description": "Every chatPrompt keystroke in SupportAgent updates state and triggers a re-render of ChatLayout even though the messages prop has not changed. At demo scale this is imperceptible, but it is an optimization opportunity for longer conversation histories.",
      "recommendation": "Wrap ChatLayout in React.memo to skip re-renders when the messages prop reference has not changed.",
      "status": "open"
    },
    {
      "id": "RSPA-STATE-002",
      "title": "Redundant defaultValue on controlled TextField",
      "severity": "info",
      "category": "STATE",
      "file": "src/web/src/SupportAgent/Agent.js",
      "line": 74,
      "description": "The MUI TextField has both value={chatPrompt} (making it controlled) and defaultValue=\"How do I fix Access Denied with Azure Blob Storage?\". React ignores defaultValue on controlled inputs; the initial display value is driven entirely by the useState initializer.",
      "recommendation": "Remove the defaultValue prop to avoid misleading future maintainers.",
      "status": "open"
    }
  ],
  "summary": {
    "critical": 2,
    "notable": 4,
    "minor": 3,
    "info": 2,
    "total": 11
  }
}
