# Changelog OpenBooks follows [Semantic Versioning](https://semver.org/) while its public API and deployment format stabilize. Alpha releases may contain breaking changes; each release documents required operator action. ## [Unreleased] - Replace the migration history with one verified release baseline, `baselines/alpha35.sql`. Fresh installations apply the baseline and the forward migrations above it, which are numbered again from `0001`. The retired migration identities and their digests remain recorded in `schema/migrations/baseline.json`. - Upgrade to drizzle-orm 1.0.0-rc.5. Query parameters are no longer included in database error messages. Existing installations must first complete v0.1.0-alpha.34.3, then verify and adopt the baseline with `scripts/adopt-migration-baseline.mts --check` and `--apply` during a maintenance window with a verified backup, following the [baseline cutover procedure](docs/operations/migration-baseline-cutover.md). Adoption refuses an incomplete previous-release ledger or any catalog difference and changes no tenant data. Older releases upgrade to v0.1.0-alpha.34.3 first. ## [0.1.0-alpha.31] - 2026-10-13 - Add multi-book accounting, account mappings and translation, intercompany settlement, period-end journals, account merges and segment dependencies. - Add prepaid amortization, compound assets, construction in progress, maintenance, insurance, recognition, renewal and rebate workflows. - Add governed journal instructions, statistical measurements, typed FX adjustments, accounting preferences and GL audit numbering. Preserve posted history and published migration identities during upgrades. - Add RBC STD152 payment and payroll artifacts with live authority checks, retained numbering and governed replacement. Improve payroll cheque design and preserve issued payee identity. - Add processor receipt funding, saved settlement account review and native posting-effects recovery for payments. Require complete evidence before period-reopen decisions and independent reconciliation sign-off reviews. - Keep till sales, reservations and drop cutoffs within the selected legal entity, including when several entities share a cash account. - Correct Canadian separate-payment CPP exemptions and separate periodic and bonus tax bases. Retain the committed regular-income reference used for standalone bonus calculations without altering historical payroll. - Preserve field-ticket revisions and exact crew hours. Review, correct and selectively submit pending time, including resubmission after withdrawal, without changing prior approvals or unselected entries. Use declared time classifications for billing adjustments and refuse missing pricing. - Consolidate payroll navigation, lists and review actions; improve shared drawers, audit context, Setup help and localized financial reports. Migrations 0568 through 0705 and 0707 apply forward through the native migration runner. Run the release's read-only upgrade check and retain a verified backup before applying them. Resolve exact schema or published-body drift refusals instead of editing migration files or existing ledger identities. Enable new capabilities through Company Settings and configure their native accounts and policies before use. This release does not reprice or reconcile historical payroll, issue incomplete separation records, or transmit payments automatically. ## [0.1.0-alpha.30] - 2026-10-05 - Add commerce channels: sales channels and channel orders, a Shopify connector with inventory sync, item families with price schedules, promotions and restocking, stored value, tenders, shipping, and order economics. - Add payment-service automation: PSP payout pull and reconciliation, autopay with setup tokens, payment-run approval flows and reservation guards, revenue recovery, and Stripe billing history import. - Extend revenue and billing: quote-to-cash, usage schedules, revenue contract scope, contract costs, payer hierarchies, SaaS entitlements and a customer portal. - Add cross-border tax with evidence, marketplace tax commits, registration filing-period starts, and FX rate age policies. - Extend payroll and workforce: benefit program identity and selected-hours components, entitlement bank drawdown, compensation packages and source cycles with approved compensation paid through native payroll under currency-aware caps, prior-provider period payments within annual payroll carry-in (native drawer, API and Data Import), optional governed rosters and device attendance, training delivery, business calendars, and component protection classes. - Rebuild Analytics dashboards on shared scoped aggregates, add demand forecasting, configurable aging bucket policies and explicit department billable expectations. - Expand global search: menu pages and workspace tabs, reports, settings, help articles, operational records (opportunities, activities, subscriptions, fixed assets, equipment, timesheets, custom records, files, dashboards, locations, warehouses), contact people, phone numbers, migrated-system ids, line descriptions, barcodes, and recently opened results re-checked against current permissions. - Fence the subscription drawer and its customer picker to the reader's subsidiaries. - Fix opening CRM opportunities, cash agent settlement statistics, compensation cycle access order, and report subsidiary policies for channel, contract cost and billing import entities. - Upgrade an install that has not adopted the alpha.29 release baseline by replaying exactly the migrations and tenant-policy environment the baseline was verified with, adopting it, then applying later migrations forward. Migrations 0482 through 0567 apply forward on the alpha.29 release baseline. Migration 0567 builds search indexes with `CREATE INDEX CONCURRENTLY` outside a transaction. Run the release's read-only upgrade check before deploying and resolve any preflight refusal it names. No new operator configuration is required. ## [0.1.0-alpha.29] - 2026-10-02 - Install new databases from one verified release baseline. Preserve historical SQL and migration identities for controlled upgrades of existing databases. Existing installations must complete historical migrations, verify schema equivalence and adopt the release baseline before deployment. See the [baseline cutover procedure](docs/operations/migration-baseline-cutover.md). - Consolidate native Benefits contributions and employer vacation policies. - Refine Talent recruiting, performance reviews and Conversations workspaces. - Use native line action menus with structural editing and distribution controls. ## [0.1.0-alpha.28.3] - 2026-10-01 - Verify production PDF generation through the application's pinned Puppeteer driver, close the browser explicitly and check PDF structure with qpdf. - Run native renderer verification before copying the application bundle so browser failures surface early in image builds. - Include the Benefits wording, table-footer and patched native browser changes prepared in alpha.28.1 and alpha.28.2. No additional migrations or operator configuration are required. ## [0.1.0-alpha.28.2] - 2026-10-01 - Pin the production PDF browser to the patched official native release on both AMD64 and ARM64. Verify archive integrity, browser version and actual PDF output during image builds, independently of distribution package lag. - Include the English Enrollment wording and Benefits table-footer corrections prepared in alpha.28.1. No additional migrations or operator configuration are required. ## [0.1.0-alpha.28.1] - 2026-10-01 - Use Enrollment consistently throughout English Benefits buttons, headings, empty states, enrollment-window drawers and report labels. - Render Benefits record counts in native table footer rows to preserve valid markup during server rendering and hydration. No additional migrations or operator configuration are required. ## [0.1.0-alpha.28] - 2026-10-01 - Consolidate Benefits into native Programs, Enrollments, Rewards, Incentives and Payouts workspaces. Manage insured plans inside Benefits and enrollment windows from the enrollment header, with one operational list per workspace. - Make approvals optional per program. No approvals required is the default; employers may instead configure conditional, delegated and multi-level native Flows decisions. Pin the setting and decision evidence on each reward. - Process cash rewards and noncash provider benefits through native payroll. Noncash valuation follows the earning component's statutory treatment without increasing cash pay, and posts to its configured prepaid or clearing account. Record provider fulfillment separately after payroll processing. - Preserve precise approval thresholds, serialize concurrent decisions across policies, and refuse incomplete decisions, zero-value obligations and stale payroll calculations with actionable remedies. - Edit recruiting availability, offer clauses and retention scope through native structured setup controls while preserving their stored evidence. - Preserve verified currencies during account imports, report unavailable source denominations, scope HR scheduler work to each tenant and correct Sales reversal journal dates. Apply migrations 0473 through 0475 using the native migration runner before starting the updated application and worker. Existing programs receive the audited No approvals required setting. Unprocessed pending rewards return to draft for resubmission; posted payroll and fulfilled rewards remain unchanged. Configure a noncash earning component and its posting account before issuing provider benefits. Active program rules change through closure and creation of a replacement program with a new code. ## [0.1.0-alpha.27.1] - 2026-10-01 - Correct the alpha27 worker's deferred startup: live authorization and shared validation schemas no longer initialize HTTP framework adapters in the standalone transfer worker. - Verify deferred transfer dependencies in the final production image and load them before the worker starts publishing readiness. - Require sustained worker readiness when verifying deployment completion; a live web process or a previous worker heartbeat cannot satisfy that gate. - Preserve independent web and worker image pins during recovery, then repin both atomically after migration verification. Refuse if deployment configuration changed during verification. This correction adds no migration. It preserves the immutable alpha27 tag and all staged transfer data. Update both web and worker to alpha27.1 before running background imports and exports. ## [0.1.0-alpha.27] - 2026-10-01 ### Durable import and export - Add organization-scoped background transfers with bounded upload parts, record batches, live progress, resumable checkpoints, cancellation and retry. - Bind approval to the immutable source, mapping, field definitions and legal-entity scope. Preserve native permissions, posting rules, precise amounts and atomic batch audit evidence; detect identities shared across worker batches before approval. - Stream CSV, JSON and XLSX exports from a consistent database snapshot, support spreadsheet sheet rollover and resumable completed downloads, and preserve financial decimals without spreadsheet rounding. - Keep import, export and history within the native Company Setup workspace, restore transfers after navigation, and correct Back navigation. Sample company provisioning remains a separate Company Setup action. ### Company and employee workspaces - Standardize operational lists, local navigation, filters and drawers on shared registered components, including payroll, inventory, banking, property management, construction and administration. - Add governed Benefits programs, eligibility and enrollment workflows, incentive settlements and payroll-linked awards with financial evidence and controlled reversals. - Add employee-backed Sales planning, quotas, customer territory maps and performance charts. Reorganize Recruiting and Performance workspaces, succession plans and native review forms. - Rework the HR organization directory and employee update policy; improve audit query indexes, asynchronous drawers, dashboard scrolling and module cockpit presentation. ### Upgrade requirements - Update Next.js and DOMPurify to patched releases. - Apply additive migrations 0467 through 0472 with the release's native migration runner before starting the updated application and worker. - Run the updated worker for background import/export progress. Previously committed batches remain committed after cancellation or a later refusal; correct financial history through native reversals or adjusting entries. - Plan database capacity for source files, staged rows, evidence and export artifacts, and temporary disk for spreadsheet decoding. No automatic transfer-evidence retention deletion is enabled. ## [0.1.0-alpha.26] - 2026-09-30 ### Accounting and operational controls - Add governed provision recognition and reassessment, including supported construction-contract loss forecasts and independent approval. - Add native prepaid-rights breakage assessments, sales-agency accounting, partial assembly disassembly and controlled reversals, and consolidated net-investment FX attribution with correction lineage. - Select registered Canadian goods taxes through native invoices and bind filing identity to the applicable registration. Add Canadian cumulative withholding with verified opening history and bonus pension deductions. - Preserve atomic document corrections, exact overhead allocation, business clock ownership, and durable evidence for uncertain email deliveries and failed background work. ### Workspaces and demonstration companies - Provide 11 verified industry demonstration masters, isolated demo copies, explicit capability coverage, idempotent native scenarios, and guarded provisioning that refuses an unverified source. - Consolidate operational lists, journal drawers, setup child collections, shared table rendering, and role-sensitive dashboard defaults. Essentials workspaces prioritize everyday bookkeeping and setup actions. - Compose Inbox search and filters in the standard list header. Navigation, Inbox tabs, and Home share unfiltered personal approvals and task totals, including signatures and unread notices. Failed task sources remain named. - Complete the shipped locale catalogs for the new accounting controls. ### Verification and upgrades - Require per-file test-registration receipts derived from actual registered test counts, including failed, skipped and pending tests. Empty or unloadable selected files fail verification. - Scope conformance setup to its tenant and distinguish caught-error serialization from safe validation messages. Update dependency protections for glob expansion, URI parsing and IP-address validation. - Align accounting, architecture and mutation documentation with measured evidence and declared capability limits. Passing conformance cases prove their stated scope; they do not certify every accounting standard. This release includes forward schema migrations. Back up the database and retain the matching data key before upgrading. Run the read-only migration preflight and resolve its named refusals. The reference release rehearses representative populated upgrades before publishing scanned, attested images; production deployment backs up first, applies migrations, then updates both web and worker services. An application rollback must remain compatible with the migrated schema; do not reverse schema changes by deleting tenant data. ## [0.1.0-alpha.25.3] - 2026-09-29 The corrected alpha.25 release preserves existing organization role configuration during bootstrap, including custom roles whose keys overlap the built-in catalogue. Explicit role seeding still refuses collisions and names the supported replacement-and-reassignment procedure. Order reads preserve eight-decimal unit prices without relaxing monetary amount precision. Non-posting orders retain a null open balance as not applicable, never as an invented zero monetary amount. Production image builds isolate Webpack compilation with a bounded JavaScript heap, collect page data with one worker, and generate one page at a time to reduce peak memory use on standard build runners. Routine updates run static checks; comprehensive test matrices remain available on demand. Release verification exercises real database-backed financial controls and representative upgrades, followed by scanned, attested images and backup-first, migrate-first deployment. Upgrade refusals retain the underlying command error in their reports. The deployment applies pending forward migrations automatically. Run the read-only migration preflight and resolve any named refusals before upgrading an installation outside the reference deployment. The maintenance corrections above add no new migration and do not change published migration fingerprints. ### Backups - **Operator action: none for new backups; old backups remain readable.** Organization backup archives are now encrypted: one JSON envelope line (format version 3, key id `v1`, nonce, content key wrapped by `OPENBOOKS_DATA_KEY`), AES-256-GCM ciphertext of the gzip stream, and the authentication tag as the trailer. A new backup object is opaque without the source data key and is not readable by `zcat`. Restore reads encrypted v3 natively and still reads unencrypted v1/v2 archives under the existing explicit `--allow-legacy-*` flags; the restore CLI prints a `backup-legacy-unencrypted` notice naming any file without an envelope. A backup refuses before writing anything when `OPENBOOKS_DATA_KEY` is unset or still the `.env.example` placeholder — set it from the secret manager; restore requires the same key. Keep the key with the same care as the archive. ### Bank feeds - **Operator action: SFTP import schedules created before the expected-account binding need a one-time bind.** The scheduled-import identity check binds each schedule to the one external bank account it accepts, and schedules predating that check have no binding — every identified OFX, BAI2, MT940, or CAMT.053 statement in their watch folders refuses, so an active feed stops importing after the upgrade with only a per-file error to show for it. Affected tenants: any organization with an active SFTP import schedule in an identifying format (including `auto`) whose expected account was never set. CSV schedules are unaffected (CSV carries no account identifier and imports on folder isolation). What the operator must do: open the named notice (Notifications/inbox, linking the exact schedule) or Company Settings → Bank Feeds, where each affected schedule reads "Paused: expected account not set", and bind the expected bank account in the schedule settings — identified statements import on the next run. The binding is never guessed: nothing imports unverified, before or after this change. ### Engine - **The engine namespace is bounded.** The 204 files that lived side by side at `engine/src` root now live in module directories (`engine/src/ledger/`, `engine/src/payroll/`, `engine/src/tax/`, …), each declared in `engine/src/modules.json` with the modules it may import. `npm run check:engine-boundaries` refuses a file at root, an undeclared cross-module import, a declared edge nothing uses, engine code importing the web app, and any growth of the pinned dependency cycles. Pure relocation: no file's behaviour changes. Every deep import path (`@openbooks/engine/src/.ts`) changed; `scripts/engine-modules/moves.json` records every move. ### Permissions - **Operator action: existing organizations must grant the new fund permissions explicitly.** Existing organizations retain their built-in role permissions byte-for-byte and receive no automatic `funds.read`/`funds.manage` grant, so the new nonprofit funds surfaces stay hidden until access is granted. An administrator must explicitly grant the needed permissions in Admin → Users & Roles before users can open the nonprofit module home, funds, releases, or setup. New organizations receive the current defaults. ### Sealed secrets - **Operator action: no key change is required, but read this before rotating keys.** Stored secrets (connections, bank feeds, provider credentials, payroll identifiers, MFA factors, email credentials) now seal as `enc:v2::::`, bound to their tenant and purpose, so a blob copied into another row refuses instead of decrypting. The existing `OPENBOOKS_DATA_KEY` keeps working as key id `k1`. To rotate, list every live key as `OPENBOOKS_DATA_KEYS=k1=,k2=` with `OPENBOOKS_DATA_KEY_ACTIVE=k2`, run `npx tsx scripts/rotate-data-key.ts` (dry run, read-only) and then with `--apply`, and remove the retired id once every blob reports current. `node --import tsx scripts/bootstrap.ts --check` refuses a missing, placeholder, or wrong-length data key and names the fix (`openssl rand -hex 32`). Pre-`v2` blobs still open until rotated; email credentials sealed before the move keep reading through the same window. ## [0.1.0-alpha.17] - 2026-09-19 Three forward migrations (0180-0182). The swarm release script migrates before it swaps, so no manual step is required. **Operator action, multi-country payroll only.** An organization running BOTH the United Kingdom and Ireland packs shared ONE `PAYE` component between them (see below). Ireland now seeds its own `IEPAYE`, which starts unmapped, and a pay run refuses by name until someone maps the Irish PAYE slot in Payroll setup → Accounts & posting. The refusal is loud and names the remedy; the remedy is to map the slot and recalculate. An organization running only one of the two packs is unaffected. ### Payroll - **No Dutch employee could be paid at all.** The pipeline hands packs money in the canonical 4-decimal ledger form — even `"0.0000"` for an empty base — and the Netherlands and Singapore packs each hand-rolled a parser accepting one or two decimals, so every NL calculation died on its own money-parse refusal. The money contract is now written on the shared statutory compute context and both packs converge on it. The four per-employee Dutch inputs (age class, AWf low, AOf high, and the employer's own Whk beschikking percentage) also had no entry surface anywhere, so no NL employee could be configured even once the parse defect was gone; they are pack-declared certificates on the profile, and the German and French row-backed forms light up on the same surface. - **France over-withheld from every employee, twice over.** The prélèvement à la source was applied to gross rather than to the net imposable — the assiette is gross less deductible social contributions plus the non-deductible 2,4 points of CSG (CGI art. 204 A–M; BOFiP BOI-IR-PAS-20-10-10) — which over-withheld roughly 30,58 € a month on a 3 400 € salary. And allocations familiales were charged at the flat 5,25 % with no taux réduit: 3,45 % applies at or below 3,5 SMIC (CSS art. L241-6-1, D241-3-1). - **Ireland had no PAYE component of its own.** `pay_components` is unique on (org, code) and on (org, system_key, kind), so installing Ireland after the United Kingdom hit `on conflict do nothing` and Irish runs pushed onto the British component — each country's account mapping erasing the other's. Ireland seeds `IEPAYE`/`ie_paye`; withheld payroll tax is wired through a new `payrollDeductions` chart ROLE rather than any hardcoded account number. - **A statutory rate could save successfully and resolve to nothing.** A rate written with a scope key the slot does not declare stored cleanly, returned `{ok}`, and then resolved to `null` forever — so the engine priced the levy as unconfigured, refusing or accruing zero, with a rate visibly on file. The write now refuses a scope point the declaration does not admit, and an update matching zero rows throws instead of reporting success. - **Remittance bills posted to the wrong legal entity, in the wrong currency.** The bill stamped the root subsidiary unconditionally and took that root's base currency, while the accruals it clears were credited on whichever entity ran the payroll — so the liability never cleared per entity. Remittance groups now carry per-entity slices: the consolidated totals remain the worksheet reading, the slices are the write path, and a group spanning two entities raises one bill per entity. A filing account registered to a different entity than the accruals refuses, naming both. - **A partially-refused pay run committed and posted silently.** Calculation refusals are persisted per employee, and a commit is gated on them unless the refusals are explicitly acknowledged against the run's digest. - **Canadian employees were unpayable in any period containing a statutory holiday**, and the attestations that answer the eligibility question are now persisted rather than re-demanded on every recalculation. - **Two currencies were added together and labelled with one symbol.** A mixed currency sum is now refused or translated, never added as raw units. - Live-but-unconfigured employer levies refuse BY NAME rather than accruing 0.00 in silence, and Québec-only rate slots no longer demand a Revenu Québec account from an Ontario-only employer. - **Pay runs could be scoped to the wrong subsidiary.** The wizard asks, drafts are discardable, and re-scoping re-resolves rather than inheriting. - **Trace factors rendered as raw codes.** One flat 49-entry label map, keyed by bare short codes and shared by every country, covered 543 codes the packs actually trace; the rest fell through to the raw key beside a UI that already prints it, so an unlabelled factor rendered the code TWICE — a Maryland admin auditing a run read `MD_PA_LOCAL_EXEMPT MD_PA_LOCAL_EXEMPT`. The flat namespace was itself the defect: `CA_TAX` is California while `CA` is Canada's country code, and `DE_WITHHELD` is Delaware while `DE` is Germany's. Labels are now a pack-declared channel resolved through the registry, with a guard that derives the traced set rather than restating it. - **Saving a payroll profile on a freshly created employee returned "employee or pay schedule is not available."** Creating a person leaves the party inactive until the record is saved, and one message covered four distinct causes — no such party, inactive party, inactive employee role, and no such schedule — so the refusal was undiagnosable from the outside. Each cause now names itself, and the draft case says what to do. - Employee identifiers are pack-declared rather than a hardcoded nine-digit SIN/SSN shape, which had made ten countries' employees unsaveable. - Per-item payroll expense routing: an earning line posts to the service item's own expense account where one is set, so direct labour can reach cost of sales, and item accounts join the staleness digest so an account edited after calculation refuses the commit rather than posting a stale mapping. - German Konfession help text no longer tells administrators church tax is not withheld, and the payroll setup write path accepts every country its own picker offers. ### Changed - One account surface across the relationship lifecycle. Leads, prospects and customers are three stages of one record, but they had three list pages and two record flyouts — and `/entities/customers` inner-joined `customer_roles`, a row written only on promotion, so a real prospect could never appear there and its "potential customer" status could only ever mean a DEMOTED customer. `/crm/leads` and `/crm/prospects` are removed; the customer list now outer- joins both the customer role and the relationship profile and segments on lifecycle stage, carrying the retired lists' sub-status, owner, territory, qualification and last-activity columns. The CRM account drawer is gone: its fields are a Relationship tab of the party flyout, so a company has exactly one record. A customer with no profile can start one from that tab. With the CRM switch off — or without `crm.accounts.read` — the list collapses to the AR customer roll exactly as before. - The Customers workspace gets the payroll-style route-tab strip: Overview, Accounts, Opportunities, Activities, Forecasts and Receivables, permission- and feature-filtered in one place rather than per page. - The party flyout has ONE tab rail. The shared shell's Details / Attachments / Audit trail strip no longer sits above a second, record-specific strip: Overview, Pulse, Relationship, Invoicing, Pricing, Transactions, Activities, Contacts, Addresses, Attachments and Audit trail are peers. - "Customer 360" is now **Pulse**, and it lives only on the customer record. The standalone `/crm/customer-360` page — which picked an arbitrary customer — is deleted. The panel was also entirely hardcoded English and formatted money as `en-US` USD regardless of locale or org currency; it now runs through the catalogs and the money formatter like every other surface. ### Added - An Add button on the customer flyout's Activities tab, matching Add contact and Add address. It mints the activity already linked to the account and hands off to the activity editor with a return path, so closing lands back on the customer. ### Fixed - Saved list views no longer sort by a direction chosen for a column they are not sorting by. Orgs are seeded with a snapshot of the system default view, so a snapshot naming a column that has since left the registry fell back to a different column while keeping the stored direction — which listed customers Z→A. Column and direction now move together, and the party-family record types declare `name ascending` explicitly. ### Fixed - Insight charts over a time bin render chronologically. A card that stores no explicit sort fell through to the compiler's default, which ranks by the first measure descending — so "Revenue by month" drew twelve months in descending revenue order, a line that always slopes down. A binned temporal dimension now orders by that dimension ascending; an unbinned dimension still ranks by its measure. - Chart category labels name the period rather than the instant it begins on (Sep 2026, Q3 2026), and survive the JSON round trip. The formatter tested `instanceof Date`, but every card fetches over HTTP and parses with `res.json()`, which delivers a string — so axes printed a raw `2026-09-01T00:00:00.000Z`. ### Payroll and tax - Thailand's 7% VAT band renewed for 2026-10-01..2027-09-30 (Revenue Department release 18/2569, rate unchanged). - Canada and the United States become country packs like the other eight, rather than object literals inside the registry. Behaviour-preserving: the canonical dump of both packs is byte-identical across 10,241 lines. ## [0.1.0-alpha.16] - 2026-09-18 Nine forward migrations (0171-0179). The swarm release script migrates before it swaps, so no manual step is required; operators running Compose get the same ordering from the bootstrap one-shot. ### Payroll - Ten installable countries: CA, US, GB, DE, FR, IE, AU, IT, NL, ES, each with agency-published goldens, hand-worked cases independent of the engine, and band-boundary sweeps. DE reproduces both official BMF Pruftabellen; AU reproduces the legislative instrument's own tables. - A generic `credit` pay-component kind threaded through net math, GL, the deduction-protection pool, remittance netting and parallel-run attribution, with no country branches (0179). - Employer-aggregate levies with their own opening balances (0174, 0177). ### Tax - 41 country packs. Every source URL is now required to sit on an authority host or a named, reasoned exception. ### Product - Notifications inbox replaces the header bell, as a first-class page. - CRM phase 1 and 2: pipeline kanban, cost margins, and a customer-360 cockpit (0178). - Dashboard gains money and operational widgets, each permission-gated so a tile a caller cannot see never runs its query. - Approvals worklist pages server-side with honest select-all. - Expense lines carry an explicit settlement: out-of-pocket reimburses the employee, company-paid settles with the card issuer, personal is a receivable (0171). ### Correctness - Posting effects claim exactly one due row per drain call; the previous `UPDATE..FROM` re-evaluated `SKIP LOCKED LIMIT 1` per outer row and could mark every due effect running while reporting one. - Signing and encryption secrets are read live rather than from a snapshot taken at module evaluation, which could be taken before the value was set. - Tenant isolation is enforced across the integration suite under FORCE row-level security as the constrained runtime role, not the bootstrap superuser. - Lint warnings and explicit `any` are both zero, and both ceilings are pinned at zero rather than tracking the real count upward. ## [0.1.0-alpha.15] - 2026-09-17 Defect fixes on top of alpha.14. No new migration. ### Payroll, parties, and close - Backdated wage rates persist, capped before their successor. - Parallel-run net is attributed against stated gross; pay-run funding reads ledger balances including reversals. - Party PATCH accepts stored customer/vendor/employee kinds. - Close reporting-package slugs resolve to names and links. ### Gates, documents, and send - Disabled features and refused permissions explain themselves instead of silent 404s and bounces; Scripts and REST API docs name the Features-switch prerequisite. - Unset subsidiary stays unset; save and convert refusals pin as record alerts. Incomplete email config is a typed refusal; failed sends stay visible in the composer. - Inventory Post refusals pin on the record; line-less budget submits refuse with a typed message. ### Inventory, fulfilment, assets, and approvals - Standalone invoices refuse to post when stock-relief issue effects fail, so revenue cannot land without its matching COGS. - Fulfilment is a sales-order conversion target; an approved order is no longer a dead end. - Orders approved before line warehouse pickers existed name the warehouseless line and offer assignment instead of a generic conversion failure. - Depreciation runs that post nothing name the next due line. Equipment activation names the missing charge item. - Directly submitted budgets reach the approvals inbox. ### Banking, retainage, and CAM - A first bank reconciliation counts the proven statement opening toward cleared, so it can reach zero and be signed off. - Retainage Receivable has a Company Settings home; pay-application invoices are no longer blocked by an invisible control-account requirement. - CAM finalize refusals pin on the pool card instead of vanishing with a toast. ### Inventory chain, billing provenance, and hygiene - Order and invoice drawers have line-level warehouse pickers with save APIs, so stock relief can follow the line that will post COGS. - Draft line replacement keeps billed-time/cost provenance, so a billing-request invoice can be saved. - Project merge previews key by group and direction; refusals pin on the duplicates screen. - Hygiene flags persistently credit-normal unreconciled bank accounts. - Asset-category setup labels the default depreciation formula in every locale. ### Reports, analytics, purchasing, and tax - Vendor drills and compliance open-payables read the shared open-items reader, never reversed entries. Purchasing recovers through the lenient scope and groups the hero off that same set. - Registers keep capped-out parties with exact closings. The trial balance keeps zero-balance accounts that had activity. Statements print gross account balances so lines foot to section totals. - The OpEx-to-revenue ratio shares one P&L operating-expense reader. - Waiver forms are required while enforcement applies. Positive NOL and provision inputs measure as DTA, never DTL. - PSP settlement drafts are postable, with typed import refusals. - Dashboard money and AR tiles label the as-of cut-off. - Empty field-ticket drafts can be discarded instead of orphaned. ### Aging, budgets, and bank setup - Aging derives opens from stored base amounts and folds documentless control balances into a per-party residual, so the subledger ties to its control account. - Budget variance is direction-aware: a revenue shortfall reads Under. Self-approval is refused with a typed reason. - Bank-feed targets use the unified reconcilable-bank reader. Setup drawer edits write the keys the controls read, so a bank profile can be deactivated. - True-cost explains an empty Assign picker when no burden categories exist. ### Pay runs, journals, and locale catalogs - Pay-run posting failures persist per instruction and render in the activity feed. - Closed-period Post refusals pin on the record. Journals warn when control legs post with no party. - Journal counts read one scope across the setup guide, header, and list. - Send-for-signature refusals pin as a dialog alert. - All six non-English locales now carry the full product. The remaining keys are reviewed cognates, not gaps. Each namespace is pinned with placeholder-parity so English cannot be pasted back. - All seven aging and statement precision suites now seed and read under the scratch org. That was test drift, not money: they had been asserting against zero rows because an imported web reader denied unscoped queries. Assertions are unchanged. - Fifteen drifted integration suites now seed and read under the scratch org — MFA and reset, documents, orders, payroll, report drill and currency basis, account register, currency chain, partner statements, and draft columns. That was the same resolver drift, not product arithmetic. Assertions are unchanged. - Lien-waiver project-required refusals pin on the record. - The setup rail stacks below phone width. Readiness points at the chart of accounts. A blank tax-code percent falls through with a spoken refusal. Rate-card lapse uses the shared invoicing label. The book-policy custom-formula picker is labelled. Setup column, field, and filter keys must resolve to English labels; four orphaned keys are now labelled in every locale, and a dead allocations mode column is gone. - Project list cells and filters resolve custom type names. ## [0.1.0-alpha.14] - 2026-09-17 Defect fixes on top of alpha.13. Requires migration 0170. ### Locale remediation - Admin and analytics catalogs are fully translated in fr, es, de, ja, zh, and pt-BR, each namespace pinned so English cannot be pasted back. - Payroll catalogs land in all six non-English locales (848 keys each). ### CRM, banking, drawers, and PDF - Lead stage changes persist; new prospects start at the right stage. - Forecast snapshots file at the displayed scope, including the whole organization (0170 relaxes the target CHECK). - Banking overview uses one reconcilable-account reader even when consolidated rates are underived. - Dirty document drawers keep a header Save and confirm before close. - PDF template duplication no longer collides on default names; a taken name is a typed 409. - Currency-mismatched postings refuse with a typed message, never raw SQL. Unapplied credits net against party open balance. AR customer search gives an honest empty state. Close review tasks offer start, evidence, and complete. Customer status facets count without group-by when CRM is off. Catalog placeholder-parity pins no longer match a literal backslash-brace. ## [0.1.0-alpha.13] - 2026-09-17 Defect fixes on top of alpha.12. No new migration. ### i18n, shell, and allocations - `ar.collections` and `agents` catalogs backfill de, ja, zh, and pt-BR. - The platform workspace switcher lives in the account menu. - Allocation-rule wizard destination menus stay above the modal and match on any dimension, with codes in the option labels. - Pre-flow vendor bank accounts can be submitted into the current approval flow; party payloads publish the canonical OCC token so Edit-then-Save no longer 409s. - French and Spanish property type/status labels agree with the masculine building noun. ## [0.1.0-alpha.12] - 2026-09-16 Fixes across setup, banking, assistant, payroll, CRM, reports, dashboard, documents, allocations, construction/AR, rates, assets, payroll, i18n, drawers/search/mobile, expenses, compliance, invites, agents/AI, and a11y UX on top of alpha.11. Requires migration 0169. ### Setup and shell - Typed validation and duplicate conflicts with user-language messages; required fields persist errors and reset busy on transport failure; blank `keepDefault` fields are legal; time-classification option labels resolve under `options`. - Shell empty-state no longer repeats the boundary copy in the body. ### Banking, approvals, CRM - Account KPI reads posted+reversed lines; the 7-day work-queue badge counts only posted documents. - Approvals render an empty state instead of a blank surface. - Lead/prospect first save sends the party revision token; the lead owner picker uses the translated unassigned label. ### Payroll, assistant, allocations - Stub register buckets and the overview setup checklist come from pack declarations; FICA carry-in splits into W-2 boxes 4 and 6. - Assistant runs persist on the server across navigation and stick to the bottom with jump-to-latest. - Period-sweep preview runs inline on the rule Test tab; empty dimension options guide the user and bad values speak user language. - Deleted assistant threads stay deleted across chat switches; local blue/green rebuilds can override `NEXT_DIST_DIR`. - Posted pay-run journal entries are visible on the journal; FX test warns when no currencies exist; an already-running agent scan explains the 409. - Schedule editor mutations surface the server error; a blocked account save stays on a form-level alert; standalone checks require payee and funding bank. - AR/AP dashboard tiles use the shared open-item reader; remittance pickers list org-wide vendors; lease add-charge is satisfiable; ownership create refuses a full-method write with a typed error. - Data-io re-imports exported party kinds; PDF responses keep a strict CSP; pack-driven stubs re-resolve the open stub against live rows. - Approvals center tabs read the same union worklist as the dashboard tile; API-key fingerprints stay stable across revoke; users accept last_sign_in sort; agent finding hrefs resolve through the nav registry; period lists show locked modules and book; journal Origin=All includes migration entries; document drawer post fails closed. ### Construction, AR, rates, and consolidation (batches 23–29) - Rates-blocked banner on aging, registers, GL, journal, and workspaces; aging export uses the same as-of rule as the screen; consolidation refusals carry typed reason codes. - AR never renders a sync source handle as the document number; CRM lead/prospect first save completes the draft; owner picker resolves the nested unassigned label. - Allocations uncovered kinds answer empty instead of 404; new-rule setup is a guided wizard. ### Assets, payroll, i18n, drawers, search, expenses - Depreciation extends a stale schedule before posting and surfaces run problems instead of failing silent; payroll posts one labeled burden debit per employer component and lists org-wide vendors. - Inventory locations, BOM tabs, run funding, and the property workspace translate in every locale. - Document title row wraps so the status pill never clips; AR-pulse hero amounts wrap on narrow screens; mobile search sits in the topbar; exact numbers bypass the search candidate cap and journal entries are indexed. - Expense reports are editable via recall and correct; the vendor drawer Compliance tab assigns the class; funding-bank override persists on save. - Approvals center binds assignee ids as a PostgreSQL array literal so a single user-assigned gate no longer 500s the page. ### Invites, close, agents, AI, and a11y (batches 30–35) - Users can be invited with a set-password link and a pending state; the period drawer shows pending reopen requests. - Agent tiles and status filters no longer depend on the current query; the shortcut legend documents a/d; team roles and banner dates translate; evidence kinds are labeled in en/fr/es. - AI Test connection verifies the typed key; load-models errors stay structured. Saved list views and bank-feed disconnects confirm first; discard-draft on an app always confirms. - Open-aging built-ins count application-aware open lines; statement tables stay readable on a phone; cash cockpit, bank feeds, project billing, setup sidebar, and report filter presets translate. - Drawer banners wrap at 390px instead of squeezing. ### Invites, remittance, banking, and tax (batches 36–39) - Pending invites expose a one-time set-password link and resend; reset-request failures no longer claim the link is on its way; a blank company display name pins an inline required error. - Remittance vendor listbox reads parties+vendor_roles; card-liability is offered when no card instruments exist; reconcile never swallows an unreadable error; KPI titles wrap and trend ticks stay thin. - Failed flow runs can be retried; mark-as-filed refusals are typed in the filing drawer; delete-payment confirm resolves through the drawer scope. ### Invites, i18n, and cockpit (batches 40–41) - CSRF treats localhost / 127.0.0.1 / [::1] as one origin so invite activation works across loopback addresses. - Feature-toggle 409s map to localized copy; recent-entry status and line counts translate; opportunity status cells and filters use the drawer catalog; mobile tabs prefer short AR/AP labels. - Stat tiles wrap instead of truncating; a retried flow parks the subject in pending_approval so the engine-enforced release can land. ### Budgets, reports, drawers, and search (batches 42–44) - Draft budgets can be submitted, approved, and rejected with revision-guarded provenance so Pending approval / Approved are reachable. - Report-schedule DELETE accepts a bodiless request; add-journal refusals stay as a dialog alert; the related-party overlay receives Compliance tab inputs. - Exact journal-entry numbers resolve outside list origin/link scope; recurring cadence options translate in fr/es; setup wizard, go-live guide, api-keys, and the property buildings table copy backfill. ## [0.1.0-alpha.11] - 2026-09-16 UI and session-gate fixes on top of the self-deploy release. The per-process database pool size is now configurable. ### UI - Open receivables read the as-of book; the AR overdue subtitle takes a bare percent; recent journal widgets link through the posted-entry route. - Approvals show loading/pending/empty states; document lines derive amount from quantity × unit price; project create activates the placeholder on a completing save. - Customization form edits PATCH the member route, remount per session, confirm before deleting the org-default form, and expose visibility toggle pressed state. - Banking fails closed at the root route error boundary; Business Central invoice numbers land as `documentNumber`; US state/local income-tax statutory slots are labeled. ### Platform - The session gate fails closed per request with a request id. - The per-process connection pool size is configurable. ## [0.1.0-alpha.10] - 2026-09-16 Integrity fixes on top of the allocations release, and production now deploys itself from a version tag. ### Production release - Pushing a `v*` tag publishes the attested image, then `deploy-production.yml` runs on the LAN runner: it executes `deploy/swarm-release.sh` over a dedicated forced-options ssh key (migrate first, then swap both service pins) and stays red until `/api/v1/health` reports the tag. Manual edge publishes never deploy. ### Integrity - Period allocation runs refuse to post after the rule is deactivated or its published version is retired. - Report-backed allocation drivers resolve features through the canonical feature path. - A bank return supersedes a pending manual payment void and keeps its own evidence. - Sync identity writes qualify the stored row on party-role conflicts, and evidence refresh matches lines by the stored `sourceLineRef`. - Statement-format arithmetic goes through the engine money helpers; boolean custom-field list values extract as real booleans; workspace package edges that were imported undeclared are now declared. - Posted journal lines cannot be re-homed onto another entry (0165). - Optimistic-concurrency tokens advance a `revision_seq` counter on every update, independent of the editable `updated_at` display timestamp (0167). - Retainage draws and releases settle in whole currency minor units and refuse a release that would invert draw/reversal posting order. - Overhead published rates and preview share one exact decimal contract. ### Operator action - Two additive forward migrations: `0165_jl_guard_original_parent_immutability` (replaces `jl_guard()`; no data rewrite) and `0167_document_revision_counter` (`revision_seq` column and bump trigger on revisioned tables; existing rows backfill 0). No other data changes. ## [0.1.0-alpha.9] - 2026-09-16 Setup polish for the allocations release, from testing alpha.8 in production. ### Allocations setup workspace - Rules, Drivers, and Runs tabs now use the same composition as every other setup list: description plus one primary New/Preview action, the shared search toolbar with the show-inactive pill, and a table with a single "Nothing here yet." row when empty. The New rule and New driver actions are visible on an empty tenant. - Drawers carry their primary action in the header like the shared setup drawer. The driver drawer gets its own Description field, per-source configuration (account scope, unit, measure, report), and the manual values grid inside the drawer after the driver is saved; loading states use the house skeleton. - Rule drawer dimension filters render a proper none-state when a dimension has no values; all copy resolves from the catalog. ### Features page - Sub-features nest under their parent (indented, secondary type, no icon) and stay hidden until the parent is on, with a quiet "N options once enabled" hint; category counts cover visible rows only. Applies to Projects, Flows, and Allocations children. ### Operator action - No migrations. No data changes. ## [0.1.0-alpha.8] - 2026-09-16 Allocations release. One allocation kernel replaces three requests that used to be separate products elsewhere (allocation schedules, distribution keys at transaction entry, and a posting-time GL plug-in): a versioned, effective-dated rule model bound at three moments, a driver registry, one exact apportionment engine, and a lineage table that traces every allocated cent to its source line, rule version, and driver value. ### Allocation kernel - Rules are versioned and effective-dated; a published version is frozen and hashed, and every run and lineage row carries that hash. Applicability is per GL account (or account group) and per department, location, class, project, subsidiary, party, item, or custom segment, including untagged pools. Bases are fixed percentages, stepped tiers, or a driver; targets are explicit or dynamic (every active dimension value with weight). Impacts are reclass, net-zero pair (never changes an account total), or report-only. - Drivers resolve from statistical journals (`journal_lines.quantity`), GL activity or balance, seven native measures (headcount, labor hours, billed hours, labor cost, revenue, direct cost, rentable area), effective-dated manual tables, or any saved report definition, run under the actor's own permissions; unavailable measures fail loudly instead of returning zeros. - Entry mode: a bill, expense, or journal line that matches an automatic rule explodes on save into a stamped group of real lines (amounts exact, quantities proportional, tax recomputed per child); suggest rules offer a chip; a split dialog, group headers, un-split, and lock live in the line grid; imports and the records API accept `distributionKey`. - Post mode: contributions land on the transaction's own journal entry, stamped with their contributor (`journal_lines.contributor_kind/ref`), balanced per contributor and per subsidiary, mirrored on void, written to secondary posting books as their own entries, and grouped in the GL impact drawer. A `custom_gl_lines` user-script trigger contributes lines the same way, with the kernel lines frozen. - Period mode: preview, post, reverse, and re-run per rule, period, and book with one posted run per occurrence, an explain payload (sources, driver vector, shares, residual), re-run idempotency by fingerprint, scheduled occurrences through the durable outbox, a close automation action (`run_allocation`), and approval flows that hold a run pending until the flow approves. - The overhead net-zero-pair writer is now a system-owned kernel rule derived from the overhead settings and rate card, proven byte-identical to the previous journal lines, with per-time-entry lineage. - Setup → Allocations workspace (Rules, Drivers, Runs tabs with a four-tab rule drawer, driver values grid, run detail with lineage drill), Allocation summary and Allocation lineage reports, an in-app help article, six locale catalogs, seven read-only assistant/MCP tools, and an internal-controls evidence set (seven cases) published beside the trust corpus. ### Assistant, agents, and setup - Chat threads appear in the sidebar instantly, page long histories, get short generated titles, and forward finding handoffs; page headers wrap actions under the title on phones. - Agents overview, library, policy pages, activity, inbox, proposals, and briefings are rebuilt on the shared setup and table components. - Analytics sentences are localizable; source-evidenced bank sign-offs count as reconciled for close readiness; JPY/KWD are pinned end to end. ### Operator action - Five additive migrations (0160–0164) run automatically before the new code serves. No posted history is reinterpreted; the dormant planning-era allocation tables are replaced only if empty (they are, on every install). - Re-run `engine/src/seed-roles.ts` after upgrading: built-in roles gain the `allocations.read/manage/run/approve` grants (roles are snapshots). - The `allocations` feature defaults off. Enable it, and the entry and posting sub-features, under Company Settings → Features. - Posting a document under a rule whose driver is a saved report requires the posting actor to hold `reports.read` (fail-closed by design). ## [0.1.0-alpha.7] - 2026-09-16 Assistant and agents release. A live benchmark of the in-app assistant on a real ledger (three models, graded against tool output) showed nearly every lost point was a tool-surface gap; this release closed those gaps, made the model's context proportional to the question, generalised the background-agent runtime, and gave agent work a home in the product. Everything the assistant and MCP clients can do reuses the same services, permission gates, subsidiary scoping, and feature switches as the screens. ### Assistant and MCP - One capability catalog for chat and `/mcp`: read tools across ledger, reporting, banking, payroll, tax, projects (portfolio ranking), inventory, orders, fixed assets, equipment, subcontracts and WIP, CRM, subscriptions, property, time, expenses, close and periods, FX, budgets, files, data-io, sync, environments, PDF templates, reporting deliveries, and admin reads (secrets never leave the server). Governed commands with a review card and idempotency key: settings, feature switches, setup records, journal post, bank reconciliation actions, FX revaluation, budget cells, file upload. - Tools of disabled modules are hidden from the model, MCP, and the close agents; the system prompt states the org's module switchboard; every tool declares its feature key, and parity tests pin feature, permission, subsidiary scoping, and chat/MCP visibility. - Context proportional to the question: a 24-tool core is always on, `find_tools` activates a module's tools mid-turn, a pre-router pre-activates modules from the message, tool results are compacted for the model (full results still stream and persist), older turns become summaries, a rolling conversation summary with resolved entities persists per conversation (migration 0152), pronouns resolve from pinned entities, and step budgets adapt. A typical turn now sends roughly 6k–13k tokens of tool definitions instead of 45k. - Turns can no longer end silently: the last permitted step must answer, and a turn that produced no prose gets a visible fallback. Engine domain errors reach the model as actionable messages instead of `tool_failed`. - Strict provider compatibility: every tool schema is linted for RE2-safe patterns, described properties, and bounded sizes (an unescaped bracket had made one provider reject the whole catalog); a contract harness runs every read tool against a scratch org under a size budget. - App packages declare their own assistant/MCP tools (`tools[]` in the manifest with validated JSON-schema inputs and install-time contract checks); they run through the app runtime with governance evidence and use the same confirmation path as built-in mutations. - `describe_capabilities` answers "what can you do" from the live catalog. ### Agents - The continuous-close runtime is a registry of agent packs: accounting, finance, collections, payables, reconciliation, data hygiene, forensics, tax readiness, payroll compliance, project margin, and cash alerts (migrations 0151 and 0155 widen the agent-key constraints). Packs read and propose; nothing runs a write without a confirmed review card. - Agents are a first-party Setup category: overview with enable switches and run-now, a library, per-pack policy pages with detector controls and notification routing (migration 0154), and an activity log. `/admin/ai` is provider configuration only. - Agent Workbench at `/agents`: a ranked inbox with keyboard and bulk triage, a proposals lane rendering the same review cards as chat, a cached morning briefing with email send, ask-about-this handoff into chat, assignment with due dates and notes (migration 0153), and a dashboard tile. ### Connectors, banking, assets, currencies - Connectors mirror cleared markers and party merges/holds and carry source reconciliation evidence on every mirror run; a source-evidenced sign-off engine can close reconciliations from that evidence (migration 0158). - Fixed assets and lessee schedules continue from opening balances (migration 0156); the fixed-asset register import carries openings in. - The currency registry is the full active ISO 4217 list, backfilled for existing tenants (migration 0157), with localized picker labels. - Analytics sentences, sentinel forensics, ratio definitions, and health findings are localizable; sentinel duplicate groups and per-currency stats corrected; close readiness ignores non-posting order kinds. ### Operator action - Eight additive migrations (0151–0158) run automatically before the new code serves. No data is reinterpreted. - Agent packs beyond accounting and finance are off until enabled under Setup → Agents; the `continuousClose` feature still gates the runtime. - The MCP endpoint requires the `apiAccess` and `mcpAccess` features and an API key; provider model lists are cached for ten minutes. ## [0.1.0-alpha.6] - 2026-09-15 Defect-remediation release: ~300 atomic fixes from a third audit (persona attacks, real-data replay, mutation survivors, standards conformance, boundary sweeps), each with a red-then-green regression test and an independent review. Highlights below; every commit carries its own defect / root cause / fix / verification record. ### Integrations - Chargebee settlement imports now receipt what was actually collected. `amount_paid` is the booked receipt, `amount_adjusted` posts as its own adjustment line against the customer (carrying the provider's reason when the export surfaces one) instead of being silently dropped, and the import refuses to post when the provider total does not foot to amount paid plus adjustments plus applied credits — naming the invoice rather than booking an unreconciled receipt. Adjustments are tracked apart from refunds in batch totals and post as their own clearing line. Schema gains `psp_settlement_batches.adjustment_amount` and the `adjustment` settlement line kind (migration 0144, additive). ### Scheduler topology - Scheduled work now runs in the worker process only (`npm run worker`, the `worker` Compose service / worker Deployment). The web process no longer starts the scheduler unless `OPENBOOKS_RUN_SCHEDULER=1` is set explicitly (single-process installs), and never under `next dev` (`OPENBOOKS_RUN_SCHEDULER=force` is the only development override). Each process logs one `[scheduler]` line at boot stating which mode it is in. Concurrent worker replicas still run each tick once via the existing Postgres claim lock. No job semantics changed. ### Analytics - Analytics: True Cost dashboard restored as a hub dashboard; the report remains at /reports/true-cost. ### Operator action - Scheduler topology: multi-process deployments take no action — keep the worker service running (it already runs in `compose.yaml` and the HA reference). Single-process installs that run web without a worker: set `OPENBOOKS_RUN_SCHEDULER=1` on web. ### Ledger and close - Posted journal entries can no longer be flipped back to draft through the kernel guard (migration 0146, `je_guard` v3: posted exits only to reversed). Tax amounts without calculation evidence fail closed instead of silently dropping at posting. Account-parent cycles no longer hang every statement. Direct inventory postings stamp the GL location dimension. - Close readiness scopes the posting-period check to the run period, goes red on unrecognized deferred revenue, and re-publishing after a reopen versions the binder (original retained, restatement note required). Consolidation, elimination, lease, asset and FX-revaluation writes into closed periods refuse with named errors instead of raw storage failures. Depreciation catches up skipped months and allocates 4-4-5 calendars; secondary books revalue; asset disposal refuses while a begun stub period is unposted. - New admin remediation paths: bulk assign-posting-period from the readiness blocker, and duplicate-project detection with an audited merge. ### Cash, receivables and payables - Open items net unapplied credit memos (cash forecast, cash position, vitals and assistant tools now tie to aging to the cent); as-of agings and forecasts reconstruct settlement timing instead of reading live balances; day-90 items age into the 90+ bucket; quiet parties keep their balance on statements. Consolidated open items, bank balances, cockpit tiles and the analytics hubs translate every functional currency to the presentation currency at the correct spot and fail closed on missing rates. - Ad-hoc vendor payments honour subcontractor compliance blocks. Early-payment discounts round to the bill currency's minor units. Refund-first PSP webhooks park as pending clawbacks and settle exactly once (migration 0149). Void versus application races no longer deadlock. ### Tax, payroll and standards - GST34 packs installed by the pre-split seed are healed to the collected / paid / taxable bases (migration 0147; a real tenant filed net tax at double the true amount). Codeless source tax is refused upstream with the source reference. The payroll remittance summary survives a run with an unknown filing account and statutory payable remappings return a typed warning. - Conformance matrix republished at 75 passing cases and 15 declared gaps (partial disposal, intercompany asset transfer, lease early termination, 4-4-5 depreciation, partial disassembly, loss of control, …). ### Integrity, contracts and concurrency - Reference ownership is fenced on every write path (records API, documents and lines, journals, expenses, items, parties, assets, timesheets, rate cards, property, custom records, scripts, Flows actions and imports): foreign-org ids are refused with tenant-opaque 404s instead of storage errors. Dozens of routes refuse magnitudes wider than their numeric columns and dates that are not real calendar days with typed 4xx responses. - Revision tokens on prebill lines, AP-capture reviews, custom records and opportunities; compare-and-swap on percent-complete overrides; sorted advisory locks for inventory reversals; repeatable-read sandbox clones with per-sandbox refresh serialization; sandboxes holding posted documents can be created, refreshed and deleted (migration 0148). Audit rows now carry actor and before/after for the v1 writer, imports, Flows mutations, pay-run commits and mirror-posted documents. - The approvals worklist and the pending-approvals count unify Flows gates, document-status approvals and pay runs. KPI definitions are single-sourced (DSO across all six surfaces). Paginated lists carry a unique tiebreaker and malformed filters fail closed to empty. Budget vs actual and the dashboard cash tile bind to the resolved period. ### Imports and integrations - Every registered data resource round-trips export → import into a fresh org and re-imports idempotently (matrix test committed). Settlement links state their currency on all six connectors and the reconciler converts or refuses. Source subsidiary functional-currency changes are held for review. The QuickBooks Desktop Web Connector SOAP route is reachable through the edge and password guessing is bounded. ### Dashboard - Saving Quick Actions on a never-customized dashboard no longer wipes the widget grid; malformed or empty stored layouts fall back to the default and self-heal on the next view. The greeting follows the viewer's clock. - Performance: the indirect cash flow's outer scans are bounded by the window (138 s → ~10 s on a multi-million-line tenant); capped detail exports disclose the cap in every format. ### Operator action - Migrations 0146–0149 apply automatically on deploy (forward-only, idempotent). 0147 rewrites installed CA_GST34 pack rows for lines 101 / 103 / 106 on every org that installed the pre-split seed; re-run affected returns after upgrading. ## [0.1.0-alpha.5] - 2026-09-15 A hardening release. A multi-agent audit read the engine, API, and application layers end to end and shipped about 350 atomic defect fixes, each with a regression test that failed before the fix and passes after it. No feature work is included. Operator action: none beyond the normal migration step; the three payroll migrations below are additive. ### Financial integrity - Posting: source corrections can no longer over-apply live settlements; negative-total credit memos and vendor credits are refused at the posting boundary; FX spot lookups break direct/inverse ties deterministically; line accounts and parties are checked against the tenant and subsidiary before any write; the vendor-payment "total is the cash amount" contract is pinned. - Payments and receipts: allocation totals use exact money everywhere the UI or engine summed them; provider over-collection stays on account instead of being dropped; early-payment discounts are vendor-only; settlement evidence is scoped to the run's bank account; CPA-005, NACHA and SEPA writers refuse blank, oversized or checksum-invalid account and creditor identifiers. - Banking: Plaid pending authorizations are no longer imported as statement truth; GoCardless and CAMT.053 lines are keyed by provider-unique ids; inactive reconciliation rules cannot be applied; SFTP statements are parsed from exact bytes. - Payroll: signed stub lines net the deduction-protection base; WCB and employer QPIP caps consume committed stubs only; staleness watches pay schedules, statutory rates and union fringes; US state withholding engines receive YTD supplemental wages and current federal tax; RL-1 slips carry opening YTD; Quebec-only remittances use the Revenu Quebec calendar; SINs are Luhn-checked on T4/RL-1 transmission; bank-file generation re-verifies approval inside its transaction; opening balances now carry second-order YTD history and capped employer levies (migrations 0141 and 0143); retro readiness compares against a persisted quantification snapshot (0142). - Tax: taxable-base return boxes convert at the posted FX rate; MACRS pool runs refuse short-year factors; nexus rates compare at ledger scale; provision reconciliation percents round half away from zero; filing boxes keep exact decimals; 1099 thresholds and box rules follow the IRS instructions (fishing boat proceeds, OBBBA 2026 threshold, corporate boxes). - Consolidation and FX: proportionate interests are eliminated; source-to- elimination rate pairs are derived; test FX syncs no longer move the production schedule cursor. - Reports and analytics: quarter breakouts follow the organization's fiscal start month; cash-basis drill-downs tie to settlement-share recognition; utilization and true-cost bases count approved time only; project margins scale identically in tables and exports; CSV exports keep exact decimals; cockpit tiles are scoped to the primary book and visible subsidiaries and translate mixed-currency totals. ### Isolation, authorization, and audit - Subsidiary scope is enforced on budgets, journals, timesheets, approvals, flows, custom records, file-cabinet folders, parties, assistant and MCP tools, payroll run populations, and every module cockpit that read past it. - Feature gates for field tickets, WIP billing, work breakdown and project scheduling are enforced in services, not only in navigation. - Configuration saves (close policy, automation, calendars, reporting packages, sandbox schedules), timesheet decisions, and approval gate decisions write actor-attributed before/after audit evidence. - Sealed tax identifiers are omitted from directory payloads; sandbox clones scrub masked custom data and users and rebuild derived aggregates. ### API contracts - Malformed ids answer 404 on every verb instead of surfacing database errors; PATCH and DELETE validate what POST validates (dates, booleans, enums, referenced ids, custom fields on partial updates); document edits and voids carry the exact revision token so stale writes are refused. ### Sync - Open-item verification compares expense reports alongside invoices, bills and credits, so a clean ledger no longer fails the financial gate; QuickBooks report dates are normalized at the adapter boundary; realized-FX groups get their own entry numbers; master-data upserts type their JSON parameters; tax-rate mirrors touch only the open current window. ### Test infrastructure - Test fixtures refuse databases that do not carry the ephemeral marker, so a stray test run cannot write to a real database. ### Payroll - Remittance bills for Revenu Québec destinations are now due on Revenu Québec's own timetable. The Canada pack declares the RQ schedule (quarterly, monthly, or twice-monthly by average monthly remittance, per Guide TP-1015.G and form TPZ-1015.R): Québec income tax, QPP, and QPIP bills carry RQ due dates and rules instead of inheriting the filing account's CRA remitter type, which previously stamped CRA accelerated-threshold dates on RQ bills. Set the frequency from your Revenu Québec notice under Setup → Payroll (new employers remit monthly, the default); Setup readiness warns while it is unconfirmed and when last year's average points at another band. ## [0.1.0-alpha.4] - 2026-08-20 ### Time and timesheets - Timesheet weeks have a lifecycle. Approved time stays read-only, but a week can be reopened while nothing downstream has consumed its hours; the reasons it cannot be (invoiced, paid, costed) are named rather than implied. Weeks carry their own record, with rejection reasons and amendment links. - Whether hours require approval before they can be billed or paid is now organization policy rather than an assumption, and approval routing runs through flows, so approver, order, and quorum are configuration. ### Payments - Fixed: the NACHA and SEPA direct-debit writers accepted a half-configured originator. They checked only that each required field was non-empty, while the credit rails additionally reject unfinished `FILL-ME` placeholders and an ODFI routing number that is not exactly nine digits. Because the writer slices that number to eight characters, an over-long value produced a well-formed file addressed to the wrong originating institution. Both debit rails now parse their originator to the same standard as the credit rails. ### Integrations - NetSuite accounts can map their own job billing types instead of inheriting a single tenant-wide convention. ### Internal - Raw SQL now carries its row type as a type argument (`db.execute(…)`) instead of asserting the shape back afterwards. 2,381 laundering casts across 504 files were removed, along with a dozen ad-hoc "anything with `.execute`" parameter types that discarded the row type at the boundary; they share one `SqlExecutor` seam. Type-level only — no runtime behaviour changed. ### Operator action - None. The schema gains `timesheet_weeks` plus two nullable columns on time entries; existing weeks derive their state on first use. ## [0.1.0-alpha.3] - 2026-08-04 The first public community preview of OpenBooks. ### Accounting and operations - PostgreSQL-enforced double-entry ledger, exact decimal money arithmetic, periods and close controls, audit evidence, receivables, payables, payments, banking, reconciliation, budgets, tax workpapers, and income-tax provisions - Multi-currency, multi-book, multi-subsidiary, intercompany, consolidation, eliminations, non-controlling interest, and goodwill configuration - Inventory costing and reconciliation, fixed assets and tax pools, project accounting, time and job costing, construction progress billing, retainage, change orders, and project revenue recognition - Configurable transaction approvals and workflows, reports and analytics, saved searches, exports, custom fields and records, scripts, apps, API keys, backups, sandboxes, and optional AI assistance ### First-run experience - Adaptive company setup that uses industry, size, entity structure, currency, operational complexity, and control requirements to shape the workspace - Three progressive operating profiles—Essentials, Growing, and Advanced—with authoritative feature gates that remain fully adjustable in Company Settings - Go-live readiness guidance from company identity and fiscal calendar through opening balances, controls, and first month-end close - RLS-isolated industry sample-company imports for evaluation and training - Maintained country tax packs for Canada, the United States, Australia, New Zealand, the United Kingdom, Germany, France, Spain, Italy, the Netherlands, Ireland, Singapore, India, South Africa, the UAE, and Japan ### Data, integrations, and platform - Generic migration and mirror framework with adapter-scoped source identities for NetSuite, QuickBooks, Xero, ERPNext, Odoo, and Microsoft Dynamics - Governed tenant-scoped query console with schema browsing, contextual table actions, and access only through reviewed reporting views - Platform controls for apps, scripts, API documentation and keys, MCP, query tools, workflows, sandboxes, and AI features - English, French, Spanish, German, Brazilian Portuguese, Chinese, and Japanese locale catalogs ### Deployment, security, and integrity - One-command Docker Compose installation using separate database-owner and constrained runtime roles, generated secrets, health checks, MinIO, Redis, web, worker, and migration-first startup - Multi-platform GHCR image built once, scanned at the exact digest, retagged without rebuilding, and published with provenance attestation - One audited canonical PostgreSQL baseline for clean installations, plus release tests for row-level security, source identity, setup provisioning, accounting invariants, and documented claims - Production startup rejection of database roles that can bypass tenant RLS; tenant context is enforced across APIs, integrations, scripts, query tools, and background jobs - Product-neutrality, history-hygiene, secret, dependency, container, and workflow checks in the release and security pipelines ### Known limitations - This is alpha software and has not completed an independent accounting audit, security audit, or broad production validation. Start with test or parallel books and reconcile all opening balances, tax treatment, permissions, reports, backups, and jurisdiction-specific requirements. - Country packs provide maintained configuration, workpapers, and exports; they are not universal electronic-filing certification or professional tax advice. - The included Compose deployment is a single-host installation. A separate HA application-tier reference is provided, but operators remain responsible for production-grade database, cache, object-storage, ingress, monitoring, backup, and recovery infrastructure. ### Operator action - Fresh installations bootstrap directly from the canonical baseline. - Keep the generated `.env.compose` file secure and back it up; it contains the first administrator login and deployment secrets. - Before using OpenBooks as a system of record, complete a restore rehearsal and the validation steps in the upgrade and backup runbooks. [0.1.0-alpha.4]: https://github.com/braedonsaunders/openbooks/releases/tag/v0.1.0-alpha.4 [0.1.0-alpha.3]: https://github.com/braedonsaunders/openbooks/releases/tag/v0.1.0-alpha.3