Skip to content

Methodology

How the optimizer makes decisions you can audit.

Before you trust an AI with a client’s chart of accounts, you should know exactly what it checks, what it doesn’t, and where judgment gives way to rules. This page documents the ranges, criteria, and minimums the optimizer enforces — drawn live from the same source files that drive scoring in production.

Architecture

Two systems working together.

The optimizer pairs deterministic checks with AI judgment. Both run on every chart, and a deterministic enforcer scores the final result against the same rubric the optimizer was asked to satisfy.

Deterministic

Rules with a single right answer

Standard numbering range checks, account renumbering, hierarchy validation, fixed-asset pairing, duplicate detection. Pure functions in the codebase. Same input always produces the same output.

  • Numbering coverage and uniqueness
  • Number-range checks per account type
  • Parent / child type and number ordering
  • QBO import-blocking characters and length limits

AI judgment

Calls that need context

Account classification when names are ambiguous, naming clean-up, organizational grouping, and industry fit. The AI works inside the deterministic rules — it can never propose a change that violates them, because the enforcer rejects it and re-prompts.

  • Classifying ambiguous account names
  • Polishing names against firm conventions
  • Industry fit and depth-of-detail judgment
  • Suggesting subaccount structure

Numbering

The standard numbering ranges, in full.

Every numbered account must fall inside the canonical range for its type. The optimizer renumbers accounts that drift, preserves anchors that are already correct, and reserves the +5 offset on Fixed Assets for accumulated depreciation.

RangeAccount typeDescriptionExamples
1000–1099BankCash and bank accountsChecking Account, Savings Account, Petty Cash
1200–1299Accounts ReceivableMoney owed by customersAccounts Receivable, Allowance for Doubtful Accounts (typed Other Current Assets, detail type Allowance for Bad Debts)
1300–1399Other Current AssetsShort-term assets (excluding AR and Inventory)Prepaid Expenses, Short-term Investments, Deposits
1400–1499Inventory AssetGoods for sale or productionInventory Asset, Raw Materials, Finished Goods
1500–1799Fixed AssetsLong-term tangible assets (Accumulated Depreciation uses +5 pairing: 1505, 1515, etc.)Equipment, Vehicles, Building
1800–1999Other AssetsLong-term non-current assetsLong-term Investments, Intangible Assets, Goodwill
2000–2099Accounts PayableMoney owed to vendorsAccounts Payable, Trade Payables
2100–2199Credit CardCredit card liabilitiesBusiness Credit Card, Corporate Card
2200–2499Other Current LiabilitiesShort-term obligationsAccrued Expenses, Payroll Liabilities, Sales Tax Payable
2500–2999Long Term LiabilitiesLong-term debts and obligationsLong Term Debt, Equipment Loans, Mortgage Payable
3000–3999EquityOwner equity and retained earningsOwner Equity, Retained Earnings, Owner Draws
4000–4699IncomePrimary operating revenueSales Income, Service Revenue, Product Sales
4700–4999Other IncomeNon-operating income and gainsInterest Earned, Dividend Income, Gain on Sale of Assets
5000–5999Cost of Goods SoldDirect costs of producing goods/servicesCost of Goods Sold, Direct Materials, Direct Labor
6000–6999ExpensesOperating expenses (parents use 100-point: 6000, 6100; children use 10-point: 6010, 6020)Office Expenses, Marketing, Rent
7000–7499ExpensesOperating expenses numbered above 6999 (accepted, never assigned)Interest Expense, Bank Fees, Bad Debt Expense
7500–7999Other ExpenseNon-operating expenses and lossesLoss on Sale of Assets, Penalties & Settlements

Source: services/gaap-ranges.ts — the same module the analyzer, optimizer, and scorer all import.

Scoring

The 32-criterion rubric, grouped by step.

The optimizer runs through six analysis steps and scores against 32 machine-checkable criteria with a combined weight of 195 points. Each criterion has a pure-function validator; the deterministic enforcer normalizes the result to 0–100 and feeds failing criteria back into the optimizer’s next pass.

Step 1 · Foundational classification

5 criteria · weight 50

  • Valid QBO Account Type

    classification.typeValid

    weight 17

    Every account must carry a non-empty accountType that matches one of the 15 QBO-mandated types (singular aliases accepted).

    Optimizer guidance

    Every account must have an accountType that is one of the 15 official QBO types: Bank, Accounts Receivable, Other Current Assets, Fixed Assets, Other Assets, Accounts Payable, Credit Card, Other Current Liabilities, Long Term Liabilities, Equity, Income, Other Income, Cost of Goods Sold, Expenses, Other Expense. No empty values, no custom types.

  • Balance Sheet vs Income Statement

    classification.bsVsIs

    weight 8

    An account's type must align with the expected financial statement bucket. e.g., Bank/AR/Fixed Assets → balance sheet; Income/Expenses/COGS → income statement.

    Optimizer guidance

    Never mis-bucket statement placement. Bank, AR, Fixed Assets, Other Assets, Other Current Assets, AP, Credit Card, Liabilities, and Equity belong on the Balance Sheet. Income, Other Income, COGS, Expenses, and Other Expense belong on the Income Statement. A type that cannot be resolved to a canonical QBO type fails this criterion.

  • Detail Type Consistent with Account Type

    classification.detailTypeConsistent

    weight 7

    If detailType is populated, it must belong to the allowed set for the account's accountType (per QBO detail-type taxonomy).

    Optimizer guidance

    When you assign a detailType, it MUST be one of QBO's allowed detail types for that accountType. For example, a Bank account's detailType must be one of Checking, Savings, MoneyMarket, etc. See types/qbo-account-types.ts QBO_DETAIL_TYPES for the complete taxonomy. Missing detailType is not penalized by this criterion — only mismatches.

  • Known Name Patterns Match Expected Type

    classification.knownPatternTypes

    weight 8

    Accounts whose name matches a well-known pattern (e.g., "Cash", "Allowance for Doubtful Accounts", "Accumulated Depreciation - X") must carry the corresponding canonical accountType. Names that do not match any pattern are not penalised.

    Optimizer guidance

    Common account names dictate their type. The reference table below lists every name pattern the scorer checks — pulled directly from lib/account-name-patterns.ts so it is always in sync. Patterns are advisory: only matched-but-mistyped accounts cost points; novel names that match no row are not penalised.

  • Semantic Coherence (Contra / Prepaid / Returns)

    classification.semanticCoherence

    weight 10

    Accounts whose name implies a specific balance-sheet role (contra-asset, contra-revenue, prepaid) must carry the canonical accountType + detailType pair. Catches the dimensions the AI Analysis "Account Categories" step catches but the older rubric missed — preventing the rubric from reporting a high score while obvious semantic issues remain.

    Optimizer guidance

    When an account name clearly indicates a contra-account, a prepaid, or returns/allowances, its accountType AND detailType must match the canonical pairing. Examples: "Prepaid Insurance" → Other Current Assets / Prepaid Expenses (NOT Expense). "Allowance for Doubtful Accounts" → Other Current Assets / Allowance for Bad Debts. "Accumulated Depreciation - X" → Fixed Assets / Accumulated Depreciation. "Sales Returns & Allowances" → Income / Discounts/Refunds Given (NOT Other Income, NOT Expense). A "Depreciation - X" name is read from the chart: under an expense parent, when asset X already has its accumulated depreciation account, or when it carries the Depreciation detail type, it is a depreciation expense and stays an expense; under the fixed asset itself it is accumulated depreciation. When the chart does not show which, either coding passes. Names that do not match any semantic pattern are not penalized.

Step 2 · Account numbering

4 criteria · weight 30

  • Numbering Coverage

    numbering.coverage

    weight 12

    Every active account must have a non-empty account number.

    Optimizer guidance

    Ensure every active account has an account number assigned, inside its standard numbering range. Coverage is scored as (accounts_with_numbers / total_active_accounts) × 12. Missing even one number costs points.

  • Standard numbering ranges

    numbering.gaapRange

    weight 9

    Every numbered account must fall within the standard numbering range for its account type.

    Optimizer guidance

    Every numbered account must fall in the standard numbering range for its type. The exact ranges are listed in the reference table below and come directly from services/gaap-ranges.ts — they are authoritative. An account tagged with the WRONG TYPE will fail this check even if the number is "reasonable", so classify types correctly first, then assign numbers inside the type's range.

  • Numbering Uniqueness

    numbering.uniqueness

    weight 5

    No two accounts may share the same account number.

    Optimizer guidance

    Every account number must be unique across the entire chart. Duplicates cost 1 point each. The deterministic numberer prevents this — never override its output.

  • Systematic Numbering

    numbering.systematic

    weight 4

    Numbering should be orderly and leave room to grow. Each group of Expense accounts (a top-level account and its sub-accounts) owns the numbers from its first number up to the next group; its accounts sit inside that block in steps of 10 (steps of 5 only when the block cannot hold them in tens), and it keeps at least 3 free numbers. Other types use steps of 10 or 5.

    Optimizer guidance

    Use orderly numbering. Each Expense group (a top-level account and its sub-accounts) owns a block of numbers from its first number up to the next group: keep its sub-accounts inside that block in steps of 10, and leave at least 3 free numbers in every group. A group may start on any multiple of 10; a round start such as 6100 is a preference, not a requirement. For all other types, use 10-point steps from the range start (e.g., A/P: 2000, 2010, 2020). The deterministic numberer handles this — do not manually assign numbers.

Step 3 · Fixed assets & depreciation pairing

4 criteria · weight 25

  • Fixed Asset / Depreciation Pairing

    fixedAsset.pairing

    weight 10

    Every depreciable Fixed Asset named "X - Original Cost" must have a matching accumulated depreciation account under it, and every "X - Accumulated Depreciation" account must sit under its "X - Original Cost" account. An "Accumulated Depreciation - X" account under its fixed asset counts as paired.

    Optimizer guidance

    Keep each accumulated depreciation account under the fixed asset it belongs to (parentId pointing to the asset). The product names it "Accumulated Depreciation - <Asset>" and leaves the asset holding the cost under its own name; that pair passes. A chart that uses "X - Original Cost" bases must give each one a matching "X - Accumulated Depreciation" or "Accumulated Depreciation - X" child. Orphaned depreciation accounts or bases without their depreciation counterpart fail this check.

  • Accumulated Depreciation +5 Offset

    fixedAsset.numberOffset

    weight 5

    Accumulated Depreciation accounts should be numbered at parent + 5 (e.g., parent 1510 → accum dep 1515).

    Optimizer guidance

    Each accumulated depreciation account should be numbered at its parent fixed asset + 5. For example, if "Equipment" is 1510, "Accumulated Depreciation - Equipment" should be 1515. An account named "<Asset> - Accumulated Depreciation" is numbered the same way. Do not rename either account.

  • Accumulated Depreciation Contra Sign

    fixedAsset.contraSign

    weight 5

    Accumulated Depreciation accounts must carry a non-positive balance (≤ 0) since they are contra-asset accounts.

    Optimizer guidance

    Every "- Accumulated Depreciation" account must have a balance ≤ 0. A positive balance indicates either a sign error in posted entries or an account misclassified as Accum. Depr. when it is actually a regular asset. Skip this check for accounts with no balance recorded (treat as not-yet-posted).

  • Fixed Asset Naming Convention

    fixedAsset.namingConvention

    weight 5

    Every accumulated depreciation account must name its asset, as "Accumulated Depreciation - X" (the form the product uses) or "X - Accumulated Depreciation". A name in any other shape, such as a bare "Accumulated Depreciation", is flagged.

    Optimizer guidance

    Name each accumulated depreciation account after its asset. The product's form is "Accumulated Depreciation - <Asset Name>"; "<Asset Name> - Accumulated Depreciation" is also accepted. Do not rename an account from one accepted form to the other. The asset linker (lib/fixed-asset-linker.ts) and the numberer (lib/deterministic-gaap-numbering.ts) read both forms.

Step 4 · Hierarchical grouping

6 criteria · weight 30

  • Hierarchy Depth ≤ 3

    hierarchy.depthLimit

    weight 5

    No account should be nested more than 3 levels deep (parentId chain ≤ 3).

    Optimizer guidance

    QBO supports up to 5 levels of nesting but deep trees hurt readability. Keep all accounts within 3 levels of a root. Accounts deeper than 3 levels should be flattened or re-parented.

  • Child accountType Matches Parent

    hierarchy.parentTypeMatch

    weight 5

    A child account's accountType must equal its parent's accountType (QBO requirement).

    Optimizer guidance

    QBO enforces that a child account must share its parent's accountType. A Bank parent can only have Bank children; a Fixed Assets parent can only have Fixed Assets children. Mismatches will be rejected by QBO sync.

  • No Orphan parentId

    hierarchy.noOrphans

    weight 5

    Every account with a parentId must reference an existing account in the chart.

    Optimizer guidance

    Every populated parentId must point to an account that exists in the chart. Orphan references (parentId pointing to a missing/deleted account) will cause QBO sync failures and display bugs.

  • Balanced Fan-Out (≤12 children per parent)

    hierarchy.balancedFanOut

    weight 5

    No parent account should have more than 12 direct children. Wider fan-out becomes hard to scan and usually indicates the parent should be split into sub-categories.

    Optimizer guidance

    When a parent grows beyond ~12 direct children, split it into 2–3 logical sub-groups. Example: instead of "Operating Expenses" with 25 children, create "Operating Expenses → Office (10), Travel (8), Professional Services (7)". The 12-child threshold matches typical scan-list cognitive load.

  • Parent Number < Child Number

    hierarchy.parentChildNumberOrder

    weight 5

    For every numbered child with a numbered parent, the child's number must be greater than the parent's. This is a post-numberer guarantee — assignGaapNumbers() always satisfies this — so any violation indicates numbers were assigned outside the canonical pipeline.

    Optimizer guidance

    Never produce a chart where a child account has a lower number than its parent (e.g., parent 6100 with child 6050). The deterministic numberer at lib/deterministic-gaap-numbering.ts guarantees parent < child by DFS traversal. If a violation appears, the chart was numbered outside the canonical pipeline and must be re-numbered through assignGaapNumbers().

  • Chart Has Some Hierarchical Organization

    hierarchy.someOrganization

    weight 5

    A non-empty chart must show some organizational grouping — either ≥ 20% of non-header accounts linked to a parent, OR ≥ 1 header-type parent per top-level account type. Without this anti-flatness gate, a completely flat chart would score N/A on every other Step 4 criterion and get rewarded with a silently-inflated overall score.

    Optimizer guidance

    A good chart groups related accounts under type headers or parent accounts. Aim for at least 20% of non-header accounts to reference a parentId, or create header-type parents (isParent=true) for each top-level account type (Bank, A/R, Fixed Assets, Expenses, etc.). A completely flat chart with no hierarchy is a red flag — even a minimal "Operating Expenses" parent grouping its children adds clarity at zero cost.

Step 5 · Business optimization

1 criteria · weight 10

  • Required Accounts for Industry

    business.requiredAccountsForIndustry

    weight 10

    For each industry with a curated required-account list (12 today), the chart must contain accounts matching each industry-essential pattern (e.g., Construction → WIP + Job Costs + Subcontractors). For any other industry, or when no industry is set, the check is not run and not counted in the score.

    Optimizer guidance

    If the business has an industry that maps to a curated list (see lib/industry-required-accounts.ts), ensure the chart contains at least one account matching each required pattern. Example: Construction → "Work in Progress" + "Job Costs" + "Subcontractor Expenses" + "Costs in Excess of Billings" + "Materials". Missing any listed requirement flags that account type as absent.

Step 6 · Final validation

12 criteria · weight 50

  • No Empty Parent Accounts

    validation.noEmptyParents

    weight 4

    Every account marked isParent=true must have at least one child account.

    Optimizer guidance

    A parent account with no children is visual noise — flatten it or remove it. If isParent is set but no account has this parentId, it fails this check.

  • No Placeholder Names

    validation.noPlaceholders

    weight 3

    No account name should contain placeholder tokens like "todo", "tbd", "xxx", "temp", "test", or "unnamed".

    Optimizer guidance

    Account names must describe real categories. Never ship names containing "todo", "tbd", "xxx", "temp", "test", "unnamed", or similar placeholder tokens. These indicate incomplete categorization.

  • Non-Empty Statement Buckets

    validation.activeCount

    weight 3

    At least one active account must exist per major statement bucket (balance sheet and income statement).

    Optimizer guidance

    A complete chart has at least some active accounts on both the balance sheet and the income statement. An empty P&L or empty BS indicates the chart is incomplete and cannot produce financial statements.

  • No Duplicate Account Names

    validation.noDuplicateNames

    weight 4

    No two accounts share an identical name. Duplicate-name collisions usually indicate the LLM independently renamed multiple distinct accounts to the same string during a later optimizer step.

    Optimizer guidance

    Each account in the final output must have a unique name. Avoid renaming distinct accounts to identical strings during consolidation steps; if consolidation is intended, merge the accounts into a single row.

  • Cross-Dimensional Consistency

    validation.crossValidationClean

    weight 8

    Number, accountType, parentId, and detailType must be self-consistent. Catches: number outside type range, orphan parentId, parent/child type mismatch, child number ≤ parent number.

    Optimizer guidance

    Every account must satisfy 4 cross-dimensional invariants: (1) number falls in the standard numbering range for accountType; (2) parentId resolves to an existing account; (3) child accountType matches parent accountType; (4) child number > parent number. These invariants are automatically reconciled by services/gaap-ranges.ts crossValidateAndReconcile() in the production pipeline — the AI should not produce charts that fail them in the first place.

  • No Blank Account Names

    validation.noBlankNames

    weight 3

    Every active account must have a non-empty name.

    Optimizer guidance

    Every account must have a descriptive name. Accounts with empty, null, or whitespace-only names are data integrity bugs that will break import and reporting.

  • No Circular Parent-Child References

    validation.noCircularRefs

    weight 5

    Following parentId from any account must terminate; a cycle is a runtime bug.

    Optimizer guidance

    Parent-child relationships must form a forest (trees), not a graph with cycles. An account cannot be its own ancestor. Cycles break hierarchy traversal and reporting.

  • No Zero or Negative Account Numbers

    validation.noNonPositiveNumbers

    weight 3

    Any account number that exists must be a positive integer greater than zero.

    Optimizer guidance

    Account numbers must be positive. Zero or negative numbers indicate corrupt data or a typo that will fail QBO validation on import.

  • No Duplicate Account Numbers

    validation.noDuplicateNumbers

    weight 5

    Every numbered active account must have a unique account number within the chart.

    Optimizer guidance

    Account numbers must be unique. Two accounts sharing the same number will collide in QBO and break reporting.

  • Account Names Within QBO Length Limit

    validation.nameLengthWithinLimit

    weight 3

    Account names must be 100 characters or fewer to satisfy QBO import limits.

    Optimizer guidance

    QBO rejects account names longer than 100 characters at import. Keep names concise and specific.

  • No QBO-Breaking Characters in Names

    validation.noSpecialCharacters

    weight 3

    Account names must not contain characters that break QBO import: @ % * " ' < >.

    Optimizer guidance

    Account names should avoid characters that QBO rejects or that break CSV/JSON interchange. Disallowed: @, %, *, straight quotes (") and ('), angle brackets (<, >). Ampersand (&) and hash (#) are allowed by QBO and common in real names (e.g., "Payroll & Benefits"), so we do not flag them.

  • Critical GAAP (Generally Accepted Accounting Principles) Accounts Present

    validation.criticalGAAPAccountsPresent

    weight 6

    The chart must include at least one account serving each critical GAAP function: Cash/Bank, Accounts Receivable, Accounts Payable, Equity, Retained Earnings.

    Optimizer guidance

    Every production-ready chart of accounts must include accounts covering these critical GAAP functions: (1) Cash or Bank; (2) Accounts Receivable; (3) Accounts Payable; (4) Equity; (5) Retained Earnings. Matching is by canonical account type first, then by common name patterns — so a "Checking Account" typed as Bank covers Cash, and an "Owner Capital" account covers Equity.

Source: services/scoring-rubric.ts. One further judgment criterion is not shown. It has no automated check and does not count toward the score. The industry check that is scored, Required Accounts for Industry, is listed above.

Industry minimums

Twelve industries, with explicit required-account lists.

One rubric criterion checks for industry-specific accounts that are indispensable, not just typical. Below are the twelve industries we’ve curated. For any other industry, or when no industry is set, the check is not run and not counted in the score. Your analysis marks it as not checked, because we won’t pretend to know what we haven’t curated.

Construction

5 required

  • Work in Progress (WIP)
  • Job Costs (COGS)
  • Subcontractor Expenses
  • Costs in Excess of Billings (POC)
  • Construction Materials

Restaurant

3 required

  • Food Cost / Inventory
  • Beverage Cost
  • Restaurant Labor

Nonprofit

5 required

  • Grant Revenue
  • Contributions / Donations
  • Program Expenses
  • Management & General Expenses
  • Fundraising Expenses

E-commerce

4 required

  • Merchant Processing Fees
  • Shipping Income
  • Shipping/Fulfillment Cost
  • Sales Tax Payable

Professional services

3 required

  • Service / Consulting Revenue
  • Unbilled Revenue / Services WIP
  • Client Retainers / Deferred Revenue

IT services / MSP

4 required

  • MSP / Managed Services Revenue
  • Project Services Revenue
  • Software Subscriptions Expense
  • Technician Labor

Healthcare

4 required

  • Patient Service Revenue
  • Insurance Receivable
  • Medical Supplies
  • Malpractice Insurance

Legal

3 required

  • Client Retainers (Trust Liability)
  • Legal Fees Income
  • Case Costs / Client Advances

Manufacturing

5 required

  • Raw Materials Inventory
  • Work in Process Inventory
  • Finished Goods Inventory
  • Direct Labor
  • Manufacturing Overhead

Agriculture

4 required

  • Crop / Livestock / Timber / Catch Inventory
  • Growing Crops / Raised Livestock / Standing Timber
  • Seed, Feed & Farm Supplies
  • Farm Revenue (crop, livestock, timber or catch)

Retail

3 required

  • Merchandise Inventory
  • Cost of Goods Sold
  • Sales Tax Payable

Landscaping

3 required

  • Direct Crew Labor
  • Landscape Materials
  • Landscaping Equipment

Source: lib/industry-required-accounts.ts.

Scope

What the optimizer doesn't decide for you.

The optimizer restructures the chart of accounts. It does not touch transactions, balances, or anything that requires a professional’s sign-off. These calls stay with you.

Transaction history

We never read, modify, or reclassify ledger transactions. Only the chart structure is in scope.

Account balances

We read balances, for instance to flag a contra-asset with a positive balance. We never alter them.

Period close decisions

Closing entries, retained-earnings rollover, and period locks remain entirely in your hands and inside QuickBooks Online.

Tax treatment selections

Tax codes, tax tracking, and reporting elections are out of scope. Coordinate with your tax preparer.

Audit and review opinions

Independence, materiality, and audit conclusions are professional judgments that no automated tool should make.

Industry fit (judgment portion)

We surface the deterministic minimums and let an accountant decide whether granularity matches the engagement.

Audit trail

Every change is logged with the rule that drove it.

Every reclassification, rename, renumber, and merge the optimizer proposes is tagged with the rubric criterion that drove it. Charts can be exported with a Source column showing, per account, whether the value is Original, AI, AI Modified, or User-edited — so you can review the work the way you would review a junior’s.

Source column values

  • Original — pulled from QuickBooks Online, untouched.
  • AI — created by the optimizer to fill a deterministic gap.
  • AI Modified — an original account whose name, number, type, or parent was changed by the optimizer; original values stay queryable.
  • User — an edit made by the practitioner during review, after the optimizer ran.

Try it on a real chart

Run the rubric against your own books.

Upload your QuickBooks chart, run a full analysis, and review every flagged criterion. Free trial, no credit card.

Read, never write · Works with QBO Simple Start through Advanced