PageSourceSearch

https://0din.ai/docs/assets/js/c30813db.b0241b34.js

js 0din.ai collected 2026-10-04 00:15:22 UTC 11,476 bytes, 1 lines download raw bytes

1"use strict";(globalThis.webpackChunkscanner_docs=globalThis.webpackChunkscanner_docs||[]).push([[4283],{8015(e,n,s){s.r(n),s.d(n,{assets:()=>l,contentTitle:()=>d,default:()=>a,frontMatter:()=>c,metadata:()=>i,toc:()=>o});const i=JSON.parse('{"id":"development/conventions","title":"Conventions","description":"Commit Messages","source":"@site/docs/development/conventions.md","sourceDirName":"development","slug":"/development/conventions","permalink":"/docs/development/conventions","draft":false,"unlisted":false,"tags":[],"version":"current","sidebarPosition":7,"frontMatter":{"sidebar_position":7},"sidebar":"docsSidebar","previous":{"title":"Monitoring Integration","permalink":"/docs/development/monitoring"},"next":{"title":"Troubleshooting","permalink":"/docs/troubleshooting"}}');var r=s(4848),t=s(8453);const c={sidebar_position:7},d="Conventions",l={},o=[{value:"Commit Messages",id:"commit-messages",level:2},{value:"Types",id:"types",level:3},{value:"Scope",id:"scope",level:3},{value:"Breaking Changes",id:"breaking-changes",level:3},{value:"Examples",id:"examples",level:3},{value:"Branch Naming",id:"branch-naming",level:2},{value:"Code Style",id:"code-style",level:2},{value:"Ruby",id:"ruby",level:3},{value:"Views",id:"views",level:3},{value:"JavaScript / Stimulus",id:"javascript--stimulus",level:3},{value:"Security Conventions",id:"security-conventions",level:2},{value:"Database Conventions",id:"database-conventions",level:2},{value:"Pull Requests",id:"pull-requests",level:2}];function h(e){const n={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,t.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.header,{children:(0,r.jsx)(n.h1,{id:"conventions",children:"Conventions"})}),"\n",(0,r.jsx)(n.h2,{id:"commit-messages",children:"Commit Messages"}),"\n",(0,r.jsxs)(n.p,{children:["Follow ",(0,r.jsx)(n.a,{href:"https://www.conventionalcommits.org/",children:"Conventional Commits"}),": ",(0,r.jsx)(n.code,{children:"<type>(<scope>): <description>"})]}),"\n",(0,r.jsx)(n.h3,{id:"types",children:"Types"}),"\n",(0,r.jsxs)(n.table,{children:[(0,r.jsx)(n.thead,{children:(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.th,{children:"Type"}),(0,r.jsx)(n.th,{children:"Triggers Release?"}),(0,r.jsx)(n.th,{children:"When to Use"})]})}),(0,r.jsxs)(n.tbody,{children:[(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"feat"})}),(0,r.jsx)(n.td,{children:"Yes (minor)"}),(0,r.jsx)(n.td,{children:"New feature"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"fix"})}),(0,r.jsx)(n.td,{children:"Yes (patch)"}),(0,r.jsx)(n.td,{children:"Bug fix"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"security"})}),(0,r.jsx)(n.td,{children:"Yes (patch)"}),(0,r.jsx)(n.td,{children:"Security fix"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"perf"})}),(0,r.jsx)(n.td,{children:"Yes (patch)"}),(0,r.jsx)(n.td,{children:"Performance improvement"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"docs"})}),(0,r.jsx)(n.td,{children:"No"}),(0,r.jsx)(n.td,{children:"Documentation only"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"refactor"})}),(0,r.jsx)(n.td,{children:"No"}),(0,r.jsx)(n.td,{children:"Code restructuring without behavior change"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"test"})}),(0,r.jsx)(n.td,{children:"No"}),(0,r.jsx)(n.td,{children:"Adding or fixing tests"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"style"})}),(0,r.jsx)(n.td,{children:"No"}),(0,r.jsx)(n.td,{children:"Formatting, whitespace"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"chore"})}),(0,r.jsx)(n.td,{children:"No"}),(0,r.jsx)(n.td,{children:"Maintenance tasks"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"ci"})}),(0,r.jsx)(n.td,{children:"No"}),(0,r.jsx)(n.td,{children:"CI/CD changes"})]})]})]}),"\n",(0,r.jsx)(n.h3,{id:"scope",children:"Scope"}),"\n",(0,r.jsxs)(n.p,{children:["Use the issue key if available (e.g., ",(0,r.jsx)(n.code,{children:"feat(SCAN-123): add probe filtering"}),"), otherwise the component name (e.g., ",(0,r.jsx)(n.code,{children:"fix(reports): correct scan count"}),")."]}),"\n",(0,r.jsx)(n.h3,{id:"breaking-changes",children:"Breaking Changes"}),"\n",(0,r.jsxs)(n.p,{children:["Add ",(0,r.jsx)(n.code,{children:"BREAKING CHANGE:"})," to the commit footer for changes that require migration steps:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{children:"feat(targets): restructure JSON config format\n\nBREAKING CHANGE: target.json_config now uses snake_case keys.\nRun `rails db:migrate` and update any custom targets.\n"})}),"\n",(0,r.jsx)(n.h3,{id:"examples",children:"Examples"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{children:"feat(probes): add community probe filtering\nfix(reports): correct scan count in summary\ndocs: update quick start guide\nchore(deps): bump rails to 8.1\ntest(scans): add scheduled scan job specs\n"})}
1),"\n",(0,r.jsx)(n.h2,{id:"branch-naming",children:"Branch Naming"}),"\n",(0,r.jsxs)(n.table,{children:[(0,r.jsx)(n.thead,{children:(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.th,{children:"Pattern"}),(0,r.jsx)(n.th,{children:"When to Use"})]})}),(0,r.jsxs)(n.tbody,{children:[(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"feature/short-description"})}),(0,r.jsx)(n.td,{children:"New feature"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"fix/short-description"})}),(0,r.jsx)(n.td,{children:"Bug fix"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"chore/short-description"})}),(0,r.jsx)(n.td,{children:"Maintenance"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"docs/short-description"})}),(0,r.jsx)(n.td,{children:"Documentation"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"hotfix/short-description"})}),(0,r.jsx)(n.td,{children:"Urgent production fix"})]})]})]}),"\n",(0,r.jsxs)(n.p,{children:["Examples: ",(0,r.jsx)(n.code,{children:"feature/add-json-validator"}),", ",(0,r.jsx)(n.code,{children:"fix/escape-file-paths-mac"}),", ",(0,r.jsx)(n.code,{children:"docs/docusaurus-site"})]}),"\n",(0,r.jsx)(n.h2,{id:"code-style",children:"Code Style"}),"\n",(0,r.jsx)(n.h3,{id:"ruby",children:"Ruby"}),"\n",(0,r.jsx)(n.p,{children:"RuboCop is the enforcer. Run before committing:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"bundle exec rubocop -A  # auto-fix\nbundle exec rubocop      # check only\n"})}),"\n",(0,r.jsx)(n.p,{children:"Key style decisions:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"2-space indentation"}),"\n",(0,r.jsx)(n.li,{children:"Single quotes for strings (unless interpolation needed)"}),"\n",(0,r.jsx)(n.li,{children:"Frozen string literals enabled"}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"views",children:"Views"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["Use ",(0,r.jsx)(n.strong,{children:"semantic class names"})," in views, not raw Tailwind utilities directly"]}),"\n",(0,r.jsxs)(n.li,{children:["Define reusable styles in ",(0,r.jsx)(n.code,{children:"application.css"})," using ",(0,r.jsx)(n.code,{children:"@apply"})]}),"\n",(0,r.jsxs)(n.li,{children:["Icons use ",(0,r.jsx)(n.strong,{children:"heroicons"})," only, defined in ",(0,r.jsx)(n.code,{children:"icons.css"})," rather than inline SVGs"]}),"\n"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-erb",children:'<%# Good %>\n<button class="btn-primary">Run Scan</button>\n\n<%# Avoid %>\n<button class="bg-blue-600 hover:bg-blue-700 text-white font-medium px-4 py-2 rounded">Run Scan</button>\n'})}),"\n",(0,r.jsx)(n.h3,{id:"javascript--stimulus",children:"JavaScript / Stimulus"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["Chart configurations go in ",(0,r.jsx)(n.code,{children:"app/javascript/config/chartConfig.js"})]}),"\n",(0,r.jsxs)(n.li,{children:["Use shared config functions (e.g., ",(0,r.jsx)(n.code,{children:"getGaugeChartConfig()"}),") rather than duplicating chart code"]}),"\n",(0,r.jsxs)(n.li,{children:["ASR color scale must match between ",(0,r.jsx)(n.code,{children:"scores_helper.rb"})," and ",(0,r.jsx)(n.code,{children:"chartConfig.js"})]}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"security-conventions",children:"Security Conventions"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Never commit secrets"}),": use environment variables"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsxs)(n.strong,{children:["Always ",(0,r.jsx)(n.code,{children:"Shellwords.escape"})]})," for shell interpolation"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Encrypted fields"})," must be accessed within a tenant scope:","\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ruby",children:"ActsAsTenant.with_tenant(company) { target.json_config }\n"})}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsxs)(n.strong,{children:["Don't use ",(0,r.jsx)(n.code,{children:".pluck"})]})," on encrypted fields; use ",(0,r.jsx)(n.code,{children:".select(:field).map(&:field)"})," instead"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Non-deterministic encryption"})," means ",(0,r.jsx)(n.code,{children:"saved_change_to_<encrypted_field>?"})," always returns ",(0,r.jsx)(n.code,{children:"true"}),"; compare decrypted values instead"]}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"database-conventions",children:"Database Conventions"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Keep migrations small and reversible"}),"\n",(0,r.jsx)(n.li,{children:"Add a comment explaining non-obvious migrations"}),"\n",(0,r.jsxs)(n.li,{children:["Partial indexes are preferred for scoped queries (see ",(0,r.jsx)(n.code,{children:"add_partial_index_for_active_reports"}),")"]}
1),"\n",(0,r.jsxs)(n.li,{children:["Background jobs must explicitly scope tenant context with ",(0,r.jsx)(n.code,{children:"ActsAsTenant.with_tenant"})]}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"pull-requests",children:"Pull Requests"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"PR title must follow Conventional Commits format (enforced by CI)"}),"\n",(0,r.jsx)(n.li,{children:"Keep PRs focused, ideally under ~300 lines of diff"}),"\n",(0,r.jsxs)(n.li,{children:["Target ",(0,r.jsx)(n.code,{children:"main"})," branch"]}),"\n",(0,r.jsx)(n.li,{children:"Two approvals required for changes touching core services, background jobs, migrations, or security-sensitive code"}),"\n"]})]})}function a(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(h,{...e})}):h(e)}},8453(e,n,s){s.d(n,{R:()=>c,x:()=>d});var i=s(6540);const r={},t=i.createContext(r);function c(e){const n=i.useContext(t);return i.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function d(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:c(e.components),i.createElement(t.Provider,{value:n},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.