- [Workspace repos](workspace_repos.md) — three independent git repos under `/config/workspace/`: evolution (ERP), portal (PWA), apps (external API)
- [reportdb.inc deprecated](evolution_reportdb_deprecated.md) — live report menu is `reportsdashboard.inc`; don't edit reportdb.inc
- [Portal: no JS build, hand-maintained min/nomin](portal_minified_no_build.md) — runtime loads `.min.js`; edit both min+nomin or the fix ships dead
- [Evolution workspace mapping](evolution_workspace_mapping.md) — `/config/workspace/evolution/` shared by code-server + dev LAMP stack; prod syncs via git + external DB tool
- [Code-server setup scripts](codeserver_setup_scripts.md) — `codeserver-setup/{install,extensions}.sh` rehydrate PHP/MariaDB/lint + VS Code extensions after redeploy
- [Dev MySQL access](dev_mysql_access.md) — direct MariaDB creds (host/user/pw) for code-server; don't grep .env
- [Prod MySQL access](prod_mysql_access.md) — remote IP-granted user to the prod DB via `.env` `sqlClaude*` trio; **creds redacted from this bundle**; SELECT all + write to 5 whitelisted tables; don't reuse `sqlEvo*` (live EVO::/ESS_DB::); PTR-mismatch gotcha
- [Live-inject release notes](live_inject_release_notes.md) — notes now go straight into prod `releasenotes` as unapproved (status=0); field mapping + latin1 charset gotcha
- [Dev DB only](dev_db_only.md) — only query dev tenant `85-1384947162`; never enumerate other tenant DBs unless named
- [Reports: no second pageheader](feedback_reports_no_pageheader.md) — `report.inc` already renders the pageheader; use a styled title row instead
- [Summarise issues + fixes BEFORE editing](feedback_summary_before_changes.md) — lead with the diagnosis + proposed fix list and get approval; don't hand over a finished diff
- [Session token hygiene](feedback_session_token_hygiene.md) — suggest /clear per-ticket and /compact at ~40-50% context
- [Docs authoring](docs_authoring.md) — how to add a page to `evolution/docs` + hand-edit the sidebar nav
- [Evolution permission system](evolution_permission_system.md) — `mod_*` session vars (0/1/2); set in staffedit, loaded at login, enforced per-screen; changes need re-login
- [Evolution sort-injection validator](evolution_sort_injection_validator.md) — run sortby/sortdir through `safeOrderBy`/`validateSortby`; raw `ORDER BY $_REQUEST` is SQLi
- [Evolution BOM hierarchy](evolution_bom_hierarchy.md) — bom→quoteitems_bom→jobInventory_bom override chain; resolver `inventory.php::getChildren()`
- [Evolution AJAX *Save auth gate](evolution_ajax_save_auth_gate.md) — `checkSession()` is login-only; data-returning handlers need an explicit `mod_*` gate
- [timesheets.invoiced is an invoice id](evolution_timesheets_invoiced_column.md) — stores invoice id not a flag; test `> 0` / `= 0`, never `= 1`
- [Addressbook account history](evolution_addressbook_account_history.md) — contact employment spans in `addressbook_account_history`; `cmoveaccount` writes it; timeline in addressbookedit.inc
- [Evolution GL/P&L data model](evolution_gl_pl_data_model.md) — accountcats P&L type ids, gledger posting signs, branch-0 quirk, accrual/cash basis
- [Two accounting.php files](evolution_two_accounting_files.md) — web-root globals (live commit) vs library/ class (payments/xero); patch BOTH for GL changes
- [GST high-precision project](gst_precision_project.md) — per-rate header tax; Phase 1+2 verified, 3/3b/3c/4 code done unverified, Phase 0 run; OPEN project
- [importQuote tax drift + taxSell gotcha](evolution_importquote_tax_drift.md) — taxSell override can zero a line's GST when inventory.taxSell unset; now 4dp per-rate
- [Float money columns lose cents](evolution_float_money_columns.md) — invoiceitems.total/adjPrice are FLOAT; drop cents >~$8.4M; sum via ROUND(CAST(adjPrice AS DECIMAL(15,4))*qty,2)
- [Xero AP payment sync](evolution_xero_ap_payment_sync.md) — bills store raw GUID; use commitBillPayments (AP branch dead); AP control = getPref('accPay')
- [Xero payment externalRef](evolution_xero_payment_externalref.md) — payment.externalRef = Xero payment GUID; putPayment stamps on send; delete pre-flights Xero
- [Xero deleted-payment sweep + AP reversal sign](evolution_xero_deleted_payment_sweep.md) — high-water misses deletions (sweep fix); addPayment AP sign bug; AP balance is incremental
- [Ledger filter LEFT JOIN](evolution_ledger_filter_left_join.md) — GL register filter must LEFT JOIN accounts/branches; inner join hid AP payment rows
- [Deliveries audit open items](deliveries_audit_open_items.md) — runsheet page; CRITICALs+UX fixed 2026-06-15; OPEN: 2 High bugs (:159, :513) + write=admin option
- [Feature = release notes + docs (policy)](feature_release_docs_policy.md) — every feature needs customer release notes + a docs page
- [Session link hostname + repo path prefix](session_link_hostname.md) — browser links: host `shane.evolutionerp.com.au`, prefix `/evolution/`; app-relative paths omit the mount
- [Plugin security review (4/10)](plugin_security_review.md) — merged audit+roadmap at `evolution/audits/PLUGIN_SECURITY_REVIEW.md` (now single source of truth; plan file deleted); M1 shipped to prod; canonical phases M1–M6; OPEN: overdue drop-company migration, CSRF (R1), backfill (O3)
- [Invoice total reconciliation](evolution_invoice_total_reconciliation.md) — canonical subtotal = round(adjPrice*qty,2); adjPrice stays 4dp; invoiceitems.total is a different FLOAT formula, don't realign
- [Xero validation project](xero_validation_project.md) — pre-sync gate; reqs at `evolution/audits/XERO_VALIDATION_REQUIREMENTS.md`; fallback bank getSetting('defaultBankAccount'); wiring pending
- [selectFilter search UI + required validation](evolution_selectfilter_search_ui.md) — search-icon `input-group`+`filtered_list` markup; use `required_always` for hidden-field required validation
- [PO sent status](evolution_po_sent_status.md) — `purchases.sent` (≠ `emailed`); a sent PO is frozen from requisition appends
- [Picking→staging ledger model](evolution_picking_staging_ledger.md) — picks = SOH-neutral in/out pairs; pickingsave diffs by record id; in-place bin moves need relocate-in-place fix
- [PDF render throttle](evolution_pdf_render_throttle.md) — all wkhtmltopdf goes through `library/pdfRender.php` (slot pool + timeout + local header/footer); never `exec` it directly
- [master branch deprecated](evolution_master_branch_deprecated.md) — evolution `master` is long-diverged legacy; diagnose on current branch (staging)
- [Shopify inbound tax → invoice](evolution_shopify_inbound_tax.md) — Shopify order tax only in customextrameta; header GST is rate-driven from line taxId; fixed with FREE taxId + empty(0) guard
- [Apache PHP error-log feed](apache_error_log_feed.md) — Apache error_log mirrored to `/config/workspace/error.log`; scan daily for PHP warnings from our code, group by file:line, strip `/var/www/shane…/` prefix
- [invoices.staffid = masterid namespace](evolution_invoice_staffid_masterid.md) — staffid stores the evolution user id (= staff.masterid, not staff.id); masterid isn't unique → badge drift; fixed + dedupe/unique migration
- [getFieldArray deprecated](evolution_getfieldarray_deprecated.md) — don't use `getFieldArray()` in new code; use `DB::SELECT` bound params (FETCH_OBJ `$row->col`)
- [Plugin eval-hook include gap](evolution_plugin_hook_eval_include.md) — `var==""` eval hooks silently no-op unless plugin `include_once`d first; POST branch fixed, PRE branch (~L3710) still OPEN
- [Credit note commit + Xero](evolution_creditnote_commit_xero.md) — commit is JSON AJAX; xero.php raw echoes corrupt the body (buffer them); pushCreditNoteToXero unguarded → empty tenant, silent sync fail (OPEN)
- [Credit note party picker + allocations](evolution_creditnote_party_picker.md) — party picker now server-side selectFilter (all contacts, but is_supplier/is_client scoping lost); allocation-amount autofill + clickable target links
- [Credit note allocation lifecycle](evolution_creditnote_allocations.md) — creditallocations is the real link (sourcedoc_* = provenance only); auto-allocate-on-commit; commitBill now stores tax + multi-rate fix; allocations shown/click-through on invoiceadd/billadd; invoicePayment.inc modal now nets AR credits off outstanding (was the real gap, fixed)
- [Credit note type → restock](evolution_creditnote_type_restock.md) — restock checkbox replaced by required `credit_type` select; type DERIVES restock_check (6=Goods Returned→in, 8=Supplier Return→out); mapping spans creditnoteadd.inc/creditnotesave.php/creditnote.php; needs migration creditnotes_credit_type.sql
- [Invoice stock reversal asymmetry](evolution_invoice_stock_reversal_asymmetry.md) — uncommit reversal must mirror commit guards (assaccount>0 + jobid=0); missing asset-account check inflated SOH for service lines; fixed in invoiceaddsave.php unapprove
- [jobInventory item uploads](evolution_jobinventory_uploads.md) — new upload subtype `jobinventory` (sourceid=jobInventory.id); Files tab also inherits quote-item files via `referenceId`; register subtypes in functions.php userCanAccessSource + uploadSubtypesForScreen
- [BOM editor (itemModify) audit](itemmodify_bom_editor_audit.md) — living doc `evolution/audits/ITEMMODIFY_BOM_EDITOR_AUDIT.md`; critical auth/SQLi/extract/timestamp fixes DONE (sales-only, no mod_jc); high+structural OPEN
- [jobeditinv BOM inline render OOM](evolution_jobeditinv_bom_inline_oom.md) — showQuoteItemBom echoed per row; gate ONLY on $bomModified, never inventory.hasBom, or PHP 7.4 OOMs mid-render (blank pipe-wizard last step, no JS error)
- [invoiceitems.jobid is project source of truth](evolution_invoiceitems_jobid_source_of_truth.md) — group job-revenue reports on line-level invoiceitems.jobid not header invoices.jobid; manual lines inherit header job in invoiceaddsave.php ~L625; migrated tsProjectLabourMargin + projectClosedVariation to match projectWipSummary
- [Dispatch contact source of truth](evolution_dispatch_contact_source.md) — dispatch stores own contact_name/phone/email; docket must use it not site/entity contact; `buildDispatchContact()` + `@dispatchContact;` token
- [Invoice bill-to vs project owner](evolution_invoice_billto_vs_project_owner.md) — bill-to (clientid) may differ from job owner; link is jobid-only; Option-3 divergence shipped 2026-07-16 (job link preserved on client change, badges, committed lock); historical backfill customer-driven only
- [Tomago EXO invoice import](tomago_exo_import.md) — new tool syncing MYOB EXO invoices into Evolution for Tomago Industrial Supplies; scope, decisions, status
- [Tomago EXO data mapping](tomago_exo_data_mapping.md) — EXO CSV → Evolution field mapping + resolution keys (contacts.externalRef2+is_client, inventory.code, net-vs-gross gotcha)
- [Importers must not touch externalRef](evolution_exo_import_xero_ref_collision.md) — externalRef/externalTenant are Xero's; provenance goes in generic `invoices.sourceRef`; flagged 1,762 TIS invoices
- [TIS project location](tis_project_location.md) — TIS EXO-sync data/reconciliation lives in `projects/tis/` (not evolution); recon (309 CSV invoices missing in evo) + 17 missing-client debtors resolved from EXO DR_ACCS; open: CASH id-0 + Allied Cranes/Lucas Drilling blank-id backfill
- [Inventory Analytics suite](evolution_inventory_analytics_suite.md) — 3 legacy reports rebuilt in place (invmonthlysales/invitemhist/invstckmovement); kept lowercase filenames for deep-links, capital-S Save.php endpoints; docs + release notes 124–126 done
- [invbomrep rebuild](evolution_invbomrep_rebuild.md) — Bill of Material Detail rebuilt as read-only BOM cost audit (parent cost vs rolled-up component cost); driven off DISTINCT bom.parent; dropped broken Xero sync; RN 131; orphaned topselling shared the deleted invbomrepsave.php
- [Billing dashboard gotchas](evolution_billing_dashboard_gotchas.md) — supportdashboard/mybilling: `invoices.balance` is stale (gate outstanding on `paidstatus`, not balance); `closedate` never written (use `lastupdated` for resolved count); prod billing db = `85-1684121907`
- [Browser screenshot tooling](browser_screenshot_tooling.md) — `browser-tools/shot.js` (Playwright) captures rendered Evolution pages; two-stage login, dev creds (evoClaudeUser/Passwd), `--jpeg`/`--scale` for small files+tokens
- [Invoicing Advanced AR register](evolution_invoicing_advanced_ar_register.md) — invoicesFiltered rebuilt to secure AR register; aging=committed-only by duedate, balance=live total−paid, multi-job ex-GST expander; audit + docs + RN 129
- [invBomRestock rebuild](evolution_invbomrestock_rebuild.md) — BOM Restocking report rebuilt to 3-file modern pattern; preserved demand engine + added financials/MRP suggested-ordering/CSV; RN 132
- [timesheets.duration clamped](evolution_timesheets_duration_clamped.md) — `duration` float(5,4) caps at 9.9999 (long shifts undercounted → actual<billable); use `durations` seconds via CASE; fixed 8 reports + migration; RN 134; audit doc
- [Ticket triage routine](evolution_ticket_triage_routine.md) — daily bug triage over prod supportTickets; skill+scripts in `evolution/.claude/skills/ticket-triage/`; priority 1=Critical 2=Urgent 3=High; writes verdict to internalNotes; #1063 undelete dead-code = top fix
- [Portal timesheet sync audit](portal_timesheet_sync_audit.md) — fixes 1/3/4/7/9-15 shipped; design doc open for conflict-detection/idempotency/race fixes 2/5/6; #8 CSRF untouched
- [AI Quote Notes](evolution_ai_quote_notes.md) — quotedit.inc AI Notes icon; generate/preview/deploy scope notes via Claude; locked-quote silently no-ops sectionEdit() (use unlocked quote to test)
- [MultiSelect widget audit](evolution_multiselect_audit.md) — multiselect.js+selectFilter.php security review; XSS(S1)/eval-RCE(S2)/SQLi(S5) fixed uncommitted; audit `evolution/audits/MULTISELECT_AUDIT.md`; OPEN: eval cleanup + fragility
- [Headless Shopify Plus SOW](evolution_shopify_headless_sow.md) — new 2nd Shopify store for existing customer; SOW at evolution/audits/SHOPIFY_HEADLESS_STORE_SOW.md; reuse+extend plugin, not multi-store-ready today, open Qs unresolved
- [evoModal declarative deploy](evolution_evomodal_declarative.md) — Gen-2 dynamic modals deploy via `data-evo-type/-tgt/-id` attrs; evoModal.js global in index.php; registry+context+template+trigger; docs-dev/dynamicmodals; branch `quoteOptimizeV3Htmx` UNMERGED (34 ahead/13 behind, 5 files both-sides) — merge plan inside
- [Shoe customizer project](shoe_customizer_project.md) — in-house WooCommerce customizer replacing Kickflip; runtime multiply-tint of zone slices; own repo `projects/madalice` (moved out of evolution 2026-07-25); Phase 1b v0.4.1 shipped
- [Shoe customizer Kickflip admin](shoe_customizer_kickflip_admin.md) — Kickflip admin login (alison@) for full-res assets + real product builders; source for later customizer phases
- [GL control-account validation](evolution_gl_control_account_validation.md) — `insertQuery()` echoes PDO errors (silent lost GL legs + corrupt JSON); commitInvoice now validates accRec/collectedTax before posting; `glAccName()`; phantom-GST fix
- [claudeUsageTracker](claude_usage_tracker.md) — cross-tenant AI metering/hard-limit/top-up/audit; central `evolution` DB via EVO::; choke point `library/claudeUsage.php`; Phase 1 built (migration UNRUN); eWAY+Invoice top-up, per-tenant pool
- [COGS posted at sale value](evolution_cogs_at_sale_value.md) — sell-side cost journal used `invoiceitems.total` not cost; Option B shipped (journal removed, both copies + CN mirror); OPEN: perpetual-inventory project + historical GL restatement
- [PDF template authoring](evolution_pdf_template_authoring.md) — QtWebKit limits + the `td{vertical-align:text-top}` trap that kills `valign` and baseline-shifts image/text columns
- [AI Product Coding (#1308)](evolution_ai_product_coding.md) — rules-driven code/name generator on Add/Edit Item; AI extracts, PHP assembles; scope doc in audits/, no code yet
- [Job optional extras add/remove](evolution_job_optional_extras.md) — tick extras on/off a job post-import; jobs.price last written by getForecastedRevenue(), not setPrice()
- [HubSpot integration (Bioaction)](evolution_hubspot_integration.md) — doc `evolution/audits/HUBSPOT_INTEGRATION_PROJECT.md`; OAuth2 client + cron pull; 34h/$4,896; job+quote live in billing tenant `85-1684121907`; `oauth()` not `oAuth()`
- [Quote description scramble](evolution_quote_description_scramble.md) — kit-indent helpers detect by id-key but write by DOM position; duplication not shift; audit doc + F1–F7 unapplied
- [Pipe modifier cat scope](evolution_pipe_modifier_cat_scope.md) — getPipeFull filters by the SOURCE item's cat; full lengths only exist in cat 10/24, so post-cat sources return empty
- [Dispatch invoicing](evolution_dispatch_invoicing.md) — INVOICE button on a committed dispatch; documented + all 5 defects FIXED 2026-07-30 (`jobitemid` double-bill, re-invoice guard, dispatch contact + `contact_p`, LEFT JOIN ad-hoc lines, both-ways job-first guard via `billableLines()` + `invoice_flag` semantics)
- [Invoice Job quote import](evolution_invoice_job_quote_import.md) — target-invoice/invoice-type now honoured, `(Quote n)` line tagging defeats cross-quote combineLines merge, section-summary zero-GST fixed (multi-rate split path unverified live)
- [Deployment env validator (scoped)](deployment_env_validator.md) — unbuilt preflight checking a box has Evolution's exts/binaries/.env/paths before serving; scope doc in audits/; motivated by R2 fileinfo silent-break
