1"use strict";(self.webpackChunkintellidocs=self.webpackChunkintellidocs||[]).push([[9144],{7899:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>a,default:()=>h,frontMatter:()=>r,metadata:()=>o,toc:()=>c});var i=n(4848),s=n(8453);const r={sidebar_position:4,title:"AI Code Review Quality Gate for CI in Node.js",sidebar_label:"Engineering quality gate",description:"Build a CI quality gate with IntelliNode for PR review, Jest test generation, coding agent fixes, commit messages, and release notes.",keywords:["node.js quality gate","intellinode ci workflow","ai pull request review","generate jest tests","coding agent test fixes","ai commit message generation"]},a="Engineering quality gate",o={id:"npm/use-cases/engineering-quality-gate",title:"AI Code Review Quality Gate for CI in Node.js",description:"Build a CI quality gate with IntelliNode for PR review, Jest test generation, coding agent fixes, commit messages, and release notes.",source:"@site/docs/npm/use-cases/engineering-quality-gate.md",sourceDirName:"npm/use-cases",slug:"/npm/use-cases/engineering-quality-gate",permalink:"/docs/npm/use-cases/engineering-quality-gate",draft:!1,unlisted:!1,editUrl:"https://github.com/intelligentnode/docs/edit/main/intellidocs/docs/npm/use-cases/engineering-quality-gate.md",tags:[],version:"current",sidebarPosition:4,frontMatter:{sidebar_position:4,title:"AI Code Review Quality Gate for CI in Node.js",sidebar_label:"Engineering quality gate",description:"Build a CI quality gate with IntelliNode for PR review, Jest test generation, coding agent fixes, commit messages, and release notes.",keywords:["node.js quality gate","intellinode ci workflow","ai pull request review","generate jest tests","coding agent test fixes","ai commit message generation"]},sidebar:"npmSidebar",previous:{title:"Support ticket triage",permalink:"/docs/npm/use-cases/support-triage"},next:{title:"Document data extraction",permalink:"/docs/npm/use-cases/document-extraction"}},l={},c=[{value:"1. Review every pull request",id:"1-review-every-pull-request",level:3},{value:"2. Generate the missing tests",id:"2-generate-the-missing-tests",level:3},{value:"3. Let the coding agent fix the failing branch",id:"3-let-the-coding-agent-fix-the-failing-branch",level:3},{value:"4. Release notes from the merged changes",id:"4-release-notes-from-the-merged-changes",level:3},{value:"5. The same tools inside every developer's assistant",id:"5-the-same-tools-inside-every-developers-assistant",level:3},{value:"Why it helps",id:"why-it-helps",level:3}];function d(e){const t={a:"a",code:"code",h1:"h1",h3:"h3",li:"li",p:"p",pre:"pre",ul:"ul",...(0,s.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(t.h1,{id:"engineering-quality-gate",children:"Engineering quality gate"}),"\n",(0,i.jsx)(t.p,{children:"Pull requests pile up, reviewers are busy, and the tests that would have caught the regression were never written. A quality gate in CI gives every pull request a first review, a set of unit tests and a clean commit message before a human looks at it, and hands the failing branches to a coding agent that fixes them until the tests pass."}),"\n",(0,i.jsx)("img",{src:"/img/use-cases/engineering-quality-gate.jpg",width:"100%",alt:"Code commits passing through a review gate"}),"\n",(0,i.jsxs)("svg",{viewBox:"0 0 760 200",width:"100%",role:"img","aria-label":"Quality gate pipeline",children:[(0,i.jsx)("defs",{children:(0,i.jsx)("marker",{id:"qgArrow",markerWidth:"10",markerHeight:"8",refX:"9",refY:"4",orient:"auto",children:(0,i.jsx)("path",{d:"M0,0 L10,4 L0,8 z",fill:"#64748b"})})}),(0,i.jsx)("rect",{x:"15",y:"55",width:"140",height:"80",rx:"10",fill:"#f8fafc",stroke:"#cbd5e1",strokeWidth:"1.5"}),(0,i.jsx)("text",{x:"85",y:"85",textAnchor:"middle",fontFamily:"system-ui, sans-serif",fontSize:"14",fontWeight:"600",fill:"#0f172a",children:"Pull request"}),(0,i.jsx)("text",{x:"85",y:"105",textAnchor:"middle",fontFamily:"system-ui, sans-serif",fontSize:"12",fill:"#64748b",children:"diff + changed files"}),(0,i.jsx)("line",{x1:"155",y1:"95",x2:"205",y2:"95",stroke:"#64748b",strokeWidth:"1.5",markerEnd:"url(#qgArrow)"}),(0,i.jsx)("rect",{x:"210",y:"55",width:"150",height:"80",rx:"10",fill:"#eff6ff",stroke:"#2563eb",strokeWidth:"1.5"}),(0,i.jsx)("text",{x:"285",y:"85",textAnchor:"middle",fontFamily:"system-ui, sans-serif",fontSize:"14",fontWeight:"600",fill:"#0f172a",children:"review_code"}),(0,i.jsx)("text",{x:"285",y:"105",textAnchor:"middle",fontFamily:"system-ui, sans-serif",fontSize:"12",fill:"#64748b",children:"score and issues"}),(0,i.jsx)("text",{x:"285",y:"122",textAnchor:"middle",fontFamily:"system-ui, sans-serif",fontSize:"12",fill:"#64748b",children:"generate_unit_tests"}),(0,i.jsx)("line",{x1:"360",y1:"95",x2:"410",y2:"95",stroke:"#64748b",strokeWidth:"1.5",markerEnd:"url(#qgArrow)"}),(0,i.jsx)("rect",{x:"415",y:"55",width:"150",height:"80",rx:"10",fill:"#fef3c7",stroke:"#d97706",strokeWidth:"1.5"}),(0,i.jsx)("text",{x:"490",y:"85",textAnchor:"middle",fontFamily:"system-ui, sans-serif",fontSize:"14",fontWeight:"600",fill:"#0f172a",children:"CodingAgent"}),(0,i.jsx)("text",{x:"490",y:"105",textAnchor:"middle",fontFamily:"system-ui, sans-serif",fontSize:"12",fill:"#92400e",children:"fixes failing tests"}),(0,i.jsx)("text",{x:"490",y:"122",textAnchor:"middle",fontFamily:"system-ui, sans-serif",fontSize:"12",fill:"#92400e",children:"on the branch"}),(0,i.jsx)("line",{x1:"565",y1:"95",x2:"620",y2:"95",stroke:"#059669",strokeWidth:"1.5",markerEnd:"url(#qgArrow)"}),(0,i.jsx)("rect",{x:"625",y:"55",width:"120",height:"80",rx:"10",fill:"#ecfdf5",stroke:"#059669",strokeWidth:"1.5"}),(0,i.jsx)("text",{x:"685",y:"85",textAnchor:"middle",fontFamily:"system-ui, sans-serif",fontSize:"14",fontWeight:"600",fill:"#065f46",children:"Human review"}),(0,i.jsx)("text",{x:"685",y:"105",textAnchor:"middle",fontFamily:"system-ui, sans-serif",fontSize:"12",fill:"#059669",children:"report + green tests"}),(0,i.jsx)("text",{x:"380",y:"175",textAnchor:"middle",fontFamily:"system-ui, sans-serif",fontSize:"11",fill:"#64748b",children:"a different model family reviews the code than the one that wrote it"})]}),"\n",(0,i.jsx)(t.h3,{id:"1-review-every-pull-request",children:"1. Review every pull request"}),"\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:"review_code"})," returns a structured report you can post as a comment or fail the check on. Use a different provider than your team's coding assistant to get an independent opinion."]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-javascript",children:"const { Gen } = require('intellinode');\nconst { execSync } = require('child_process');\n\nconst diff = execSync('git diff origin/main...HEAD').toString();\n\nconst review = await Gen.review_code(diff, process.env.ANTHROPIC_API_KEY, 'anthropic', { language: 'typescript' });\n// { summary, score, issues: [{ severity, title, description, suggestion }] }\n\nconst blocking = review.issues.filter((issue) => issue.severity === 'high');\nif (blocking.length) {\n console.log(`Blocking issues:\\n${blocking.map((issue) => `- ${issue.title}: ${issue.suggestion}`).join('\\n')}`);\n process.exitCode = 1;\n}\n"})}),"\n",(0,i.jsx)(t.h3,{id:"2-generate-the-missing-tests",children:"2. Generate the missing tests"}),"\n",(0,i.jsx)(t.p,{children:"For every new source file without a test, generate one in the framework the repository uses and run it in the same job."}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-javascript",children:"const fs = require('fs');\nconst path = require('path');\n\nconst changed = execSync('git diff --name-only origin/main...HEAD').toString().split('\\n').filter((file) => /^src\\/.*\\.js$/.test(file));\n\nfor (const file of changed) {\n const testFile = path.join('test', path.basename(file).replace(/\\.js$/, '.test.js'));\n if (fs.existsSync(testFile)) continue;\n\n const code = fs.readFileSync(file, 'utf8');\n const tests = await Gen.generate_unit_tests(code, process.env.OPENAI_API_KEY, 'openai', {\n framework: 'jest',\n modulePath: path.relative('test', file).replace(/\\.js$/, ''),\n });\n fs.writeFileSync(testFile, tests);\n}\n\nexecSync('npx jest --ci', { stdio: 'inherit' });\n"})}),"\n",(0,i.jsx)(t.h3,{id:"3-let-the-coding-agent-fix-the-failing-branch",children:"3. Let the coding agent fix the failing branch"}),"\n",(0,i.jsxs)(t.p,{children:["When the tests fail, the coding agent edits the branch until they pass and reports what it changed. Every file access stays inside the checkout, and the run stops at ",(0,i.jsx)(t.code,{children:"maxIterations"}),"."]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-javascript",children:"const { CodingAgent } = require('intellinode');\n\nconst agent = new CodingAgent({\n apiKey: process.env.ANTHROPIC_API_KEY,\n provider: 'anthropic',\n model: 'claude-sonnet-5',\n workspace: process.cwd(),\n maxIterations: 15,\n onAction: ({ iteration, tool, args }) => console.log(`#${iteration} ${tool}`, tool === 'write_file' ? args.path : ''),\n});\n\nconst result = await agent.run('The unit tests fail on this branch. Find the cause and fix it without changing the tests.', {\n testCommand: 'npx jest --ci',\n});
1\n\nif (result.success) {\n const message = await Gen.generate_commit_message(execSync('git diff').toString(), process.env.OPENAI_API_KEY);\n execSync(`git commit -am ${JSON.stringify(message)}`);\n console.log(result.summary);\n} else {\n console.log('Needs a human:', result.summary);\n}\n"})}),"\n",(0,i.jsx)(t.h3,{id:"4-release-notes-from-the-merged-changes",children:"4. Release notes from the merged changes"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-javascript",children:"const log = execSync('git log --oneline v2.4.0..HEAD').toString();\nconst notes = await Gen.generate_release_notes(log, process.env.OPENAI_API_KEY, 'openai', { version: '2.5.0' });\nfs.writeFileSync('CHANGELOG.next.md', notes);\n"})}),"\n",(0,i.jsx)(t.h3,{id:"5-the-same-tools-inside-every-developers-assistant",children:"5. The same tools inside every developer's assistant"}),"\n",(0,i.jsx)(t.p,{children:"Add the intellinode MCP server to the team's Claude Code, Cursor or VS Code setup and the review, test generation and fix tools are one prompt away, on any provider the company has keys for:"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-bash",children:"claude mcp add intellinode -e ANTHROPIC_API_KEY=sk-ant-... -e OPENAI_API_KEY=sk-... -- npx -y intellinode mcp\n"})}),"\n",(0,i.jsxs)(t.p,{children:["See the ",(0,i.jsx)(t.a,{href:"../mcp/server",children:"MCP server"})," page for the tool list."]}),"\n",(0,i.jsx)(t.h3,{id:"why-it-helps",children:"Why it helps"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsx)(t.li,{children:"Every pull request gets a review and tests before a reviewer spends time on it."}),"\n",(0,i.jsx)(t.li,{children:"Failing branches come back green with a summary of the fix, ready for the human decision."}),"\n",(0,i.jsx)(t.li,{children:"The review runs on a different model family than the code generator, which catches more."}),"\n"]})]})}function h(e={}){const{wrapper:t}={...(0,s.R)(),...e.components};return t?(0,i.jsx)(t,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},8453:(e,t,n)=>{n.d(t,{R:()=>a,x:()=>o});var i=n(6540);const s={},r=i.createContext(s);function a(e){const t=i.useContext(r);return i.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function o(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:a(e.components),i.createElement(r.Provider,{value:t},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.