Skip to content

Latest commit

 

History

History
653 lines (475 loc) · 87.8 KB

File metadata and controls

653 lines (475 loc) · 87.8 KB

Changelog

All notable changes to the ERPClaw foundation skill.

[4.15.5] — 2026-10-08 — company-safe workflows and stronger reporting

Added

  • Consolidation translation previews, benefit-liability calculations and contract-balance reporting.
  • Balanced reciprocal fund-transfer drafts and commitment worksheets.
  • Company-scoped supplier quotation drafts with bounded snapshots and opaque audit references.
  • Item barcodes for inventory records.

Fixed

  • Ordinary audit-log reads are scoped to the selected company and legacy or non-company rows fail closed outside the administrator-only system path.
  • Party-ledger behavior is corrected for the accepted accounting flows.
  • Action discovery keeps exact all-word matches and adds stable ranking plus a bounded multi-word fallback.

Database

  • Foundation migrations 059 and 060 add item barcodes and the company-scoped supplier quotation draft store. They are idempotent and preserve existing business data.

[4.15.4] — 2026-10-06 — correct trust and WIP reports, close readiness

Fixed — money

  • Legal three-way trust reconciliation: transfers between trust accounts and account-level interest now count correctly on both the transaction and client totals, so a valid transfer no longer makes an account look unreconciled. Deposits and disbursements recorded without a matter stay unassigned, so a client shortfall is still flagged. A transaction or matter from another company is refused.
  • Construction WIP schedule: earned revenue uses the exact cost-to-cost ratio (rounded once, at the end) and is capped at 100 percent of the contract. Approved and executed change orders are included in the revised contract, and only submitted and approved progress bills count as billed. The response adds the change-order and revised-contract amounts.

Added

  • A read-only continuous close readiness preview for one company and date: draft journals, unbalanced posted vouchers, unapplied payments and fiscal-year coverage.
  • A 24-month business simulation report. It is a projection only and never touches the books.
  • A read-only business-rule preview, and exact percent, duration, rating and clock custom-field types.
  • An audit-log checkpoint: a fingerprint of the stored audit log that can be compared with a trusted earlier value.
  • Schema drift and planning tools can inspect PostgreSQL schemas.

[4.15.3] — 2026-10-05 — first versions across the floor plan

First versions (basic tests) of many planned capabilities. Each is a starting point; deeper coverage follows in later releases.

Added — accounting and reporting

  • Nonprofit: classified financial statements, release of donor restrictions, classification of conditional contributions, endowment appropriation, and split-receipt donor substantiation.
  • Financial statements export to CSV with exact amounts and a company filter.
  • A plain-language explanation of an account's budget-to-actual (flux) variance, with exact amounts and no model call.
  • Draft employer tax return amounts (Forms 941 and 940) prepared from posted payroll.
  • A weekly business digest report.
  • Nonprofit: a Form 990 preparation worksheet.

Added — operations

  • Selling: a read-only follow-up list of customers whose oldest overdue invoice has passed a set number of days, with exact outstanding totals.
  • Buying: draft vendor bills from reviewed email fields.
  • Inventory: a first demand forecast and a standard-cost variance report. Logistics: an inventory traceability report. POS: a measure of recorded retail volume.
  • Construction: a cost-to-cost work-in-progress schedule.
  • Legal: a three-way trust account reconciliation.

Added — industry and compliance

  • Health: patient revenue posted to the ledger, approved charity care applied, a claim (X12 837) preview, and a 340B accumulator.
  • Higher education: student receivables posted to the ledger and an IPEDS completions preview.
  • Compliance: exclusion screening and an attestation readiness report.

Added — integrations and growth

  • QuickBooks import staging and offline bank-feed file import.
  • A local CRM account brief and insights over imported commerce data.
  • The module generator validates generated modules safely before use.

Fixed

  • The ERPClaw MCP server works with the current Python MCP SDK.

Database

  • Foundation migration 058 adds the follow-up threshold store; nonprofit migration 002 adds donor substantiation columns. Neither changes existing data.

[4.15.2] — 2026-10-03 — credit notes, Stripe reports and safer concurrent updates

Fixed — money

  • A credit note now reduces the invoice it was issued against. Submitting a credit note allocates it to its original sales invoice; any excess stays as open customer credit. The original invoice cannot be cancelled while a live credit note is applied to it, and a credit note copies its invoice's currency fields. Foundation migration 052 brings existing books into line: it allocates already-submitted credit notes to their originals and records every change in the audit log.
  • Stripe MRR is exact. MRR counts active subscriptions only; trialing ones are shown as a count, never as revenue. Annual plans divide exactly by 12, while day and week plans keep their existing approximation. Rounding happens once, after grouping. Each currency gets its own total; with more than one currency there is no single combined total. An unknown interval, a blank currency or a negative or non-finite amount is refused rather than guessed.
  • The Stripe fee report keeps unitemised fees. An account with a mix of itemised and unitemised fees now reports its full fee total, and each entry names its source.
  • India Form 16 and Form 24Q read the real salary slips: submitted and paid slips over the April to March fiscal year, TDS per quarter, gross salary and taxable income after the standard deduction.

Fixed — concurrent updates

  • Loan approve, reject, update, disbursement, repayment schedule, restructure and close; stock revaluation; and work-order cancellation now take the company ledger lock first, then re-read the record and re-check its status before they write. A request that read a record which another request then changed rolls back and asks you to re-read it, instead of writing from stale data. Work-order cancellation never leaves a partial reversal.

Changed

  • add-carrier-invoice (logistics) now requires --total-amount, a positive amount with at most two decimals, and refuses a carrier invoice from another company. It no longer stores a silent zero.

Database

  • Migration 052: credit-note allocation for existing books (changes data; audited).
  • Migration 053: adds the authority session and credential tables. They are groundwork for operator sign-in and are not active yet; existing approvals work as before.
  • Migration 054: repairs PostgreSQL installs on which foundation migrations 001 and 002 had been recorded as applied without creating their objects.

[4.15.1] — 2026-10-03 — company boundaries and ledger discipline

Added — company boundaries

  • Lists and reports that used to total every company now need one. Selling, buying, inventory, payments, journals, HR, payroll and advanced-accounting lists and reports use the installation's sole company automatically, or refuse "Multiple companies found. Please specify the company by name." when more than one exists; a new --company flag resolves a company by exact id or case-insensitive name.
  • Cross-company reads and writes are refused. get-account, get-account-balance, get-journal-entry, get-payment, get-customer, get-sales-invoice, get-supplier, get-purchase-invoice and get-stock-entry refuse a record of another company than the one given. add-quotation, add-sales-order, create-sales-invoice, add-purchase-order, create-purchase-invoice and the recurring templates refuse a customer or supplier of another company. A posting now needs an open fiscal year on the posting company itself. Full list: ../../BREAKING_CHANGES.md BC17-BC22.

Added — authority groundwork

  • Principals, company memberships and single-use authorization envelopes bound to one document, company and amount are in the schema and the administration actions (issue-authorization, revoke-authorization, get-authorization, grant-/deny-/revoke-company-membership, list-company-memberships). STAGED is the supported install phase, where nothing changes for the operator; this release has no authenticated production issuer, so the ACTIVE issuance, revocation and envelope-reading actions answer AUTHORIZATION_ISSUER_UNAVAILABLE. Payments, journals, selling and buying submit/cancel paths pass through the shared authority gate. See ../../BREAKING_CHANGES.md BC18.
  • Read-only storage and read-only MCP sessions. ERPCLAW_DB_READONLY=1 opens the database read-only, refuses a path other than the install's own, and skips module, onboarding, dangerous and credential actions with read_only_session. ERPCLAW_MCP_READONLY=1 lists, describes and runs only a pinned set of reads proven to write nothing.
  • Business audit entries name the record they describe (skill, action, table, record id), not the company, across selling, buying, inventory, HR, payroll, retail, hospitality, healthclaw and more; they also record the operating-system account and, where available, a claimed actor context and its status. That context is a claim, not an authenticated identity. Migration-written audit rows keep their earlier shape.

Added — new actions

  • update-recurring-bill-template edits a recurring bill template's schedule and items and moves it from draft to active; invalid input is refused before any write.
  • list-landed-cost-voucher-anomalies is a read-only report of posted landed-cost vouchers that priced another company's receipt or counted a receipt line twice, from before those were refused; it repairs nothing (reverse with cancel-landed-cost-voucher).
  • delete-sales-invoice removes a draft sales invoice with no ledger rows or references and needs confirmation, alongside the existing pos-abandon-posting.

Added — MCP containment

  • Model-supplied erpclaw_action arguments cannot carry a routing, confirmation or database-target key (action, action-name, user-confirmed, db-path, db-url, actor, actor-id, session-token). A duplicate or abbreviated --action/--force option on the underlying router call is refused before anything dispatches.
  • Identity-mutating actions are not dispatchable over MCP at all (add-user, update-user, add-role, assign-role, revoke-role, set-password, seed-permissions, link-telegram-user, unlink-telegram-user, both spellings of initialize-database); they stay available on the direct operator CLI under their existing gate.
  • A model-facing call can carry an operator-issued authorization in one explicit authorization_id field. A malformed id, or one shorter than 16 characters, is refused before anything runs, and an authorization id never appears in an MCP reply. See ../../BREAKING_CHANGES.md BC30.

Changed — confirmation gate

  • Six actions that pass a confirmation on to a gated child in another module now need --user-confirmed themselves: transfer-materials-to-subcontractor, cancel-subcontract-transfer, pos-submit-transaction, construction-approve-progress-bill, legal-generate-invoice, legal-send-invoice. The flag records the user's own confirmation; it is not a substitute for an authorization envelope. See ../../BREAKING_CHANGES.md BC29.

Changed — concurrency

  • Payment submit/cancel/allocate/reconcile, sales- and purchase-invoice cancel, journal-entry submit/cancel/amend, expense-claim approval, purchase-invoice submit, payroll-run submit/cancel, intercompany-invoice cancel, delivery-note submit/cancel, and stock-entry/stock-reconciliation actions now take their company's ledger lock and re-check their document under it. A concurrent or stale change is refused instead of posting twice, losing an update, or reversing twice; on a refusal, read the returned error and the document's current state before deciding whether to retry. A per-company ledger chain head records posting order (migration 038 reports any legacy legs it cannot sequence and rewrites none). See ../../BREAKING_CHANGES.md BC43.

Changed — dimensions and cost centers

  • Payments, journal entries, and the selling, buying and inventory documents accept --dimensions or repeated --dimension-key/--dimension-value and carry them from draft to every ledger leg they post; an unknown or inactive dimension is refused before any write.
  • A ledger leg that needs a default cost center now uses the company's own default, or the oldest non-group cost center of that company; an approved expense claim posts on the employee's payroll cost center, else their department's, else the company default, and refuses when that account is a group cost center or belongs to another company.

Fixed — payments

  • allocate-payment refuses a currency mismatch between the payment and the invoice ("currency mismatch: invoice in <INV>, payment in <PAY>; invoice currency must equal payment currency"); reconcile-payments only pairs same-currency documents.
  • add-payment refuses a party that does not exist, is of the wrong type, or belongs to another company; every allocation path refuses an unregistered voucher type.
  • A customer refund clears credit notes with the receivable sign and never touches invoices or advances; migration 032 heals existing installs and now skips customer refunds when it does (matters only when upgrading from a release before 032).

Fixed — expense claims

  • add-expense-claim refuses a line on an account that is not an operating expense; approve-expense-claim checks every account it would debit before the first ledger leg. An approved claim with no payable row of its own cannot be paid through an allocation ("has no payable in the payment ledger").
  • HR writers record their audit rows in the same transaction as the change they describe, so a failure after the change can no longer leave a change with no audit row.

Fixed — landed cost and buying

  • add-landed-cost-voucher refuses a purchase receipt that belongs to another company, and a receipt listed more than once in the same voucher, before any write; a charge account must be an operating expense account (type expense or untyped) or an untyped accrual liability.
  • A purchase-order line takes --discount-amount or --discount-percentage, carried through receipt, stock valuation, bill and landed cost.

Fixed — HR and payroll

  • add-leave-allocation refuses a fiscal year that belongs to another company or is closed.
  • submit-payroll-run refuses with a named list of missing statutory liability accounts, the amount each needs, and the name to give it, rather than folding the gap into net pay or posting an unbalanced total.
  • Bulk attendance refuses a future date.

Fixed — accounting and inventory

  • Revenue-contract, performance-obligation and variable-consideration amounts must be finite, non-negative and at most two decimal places; probability must be within 0-100.
  • generate-revenue-entries takes --as-of-date (default today in UTC) and refuses a malformed or future date, posting only schedule rows due on or before it.
  • Inventory list and report actions refuse non-finite or overflowing numbers and invalid paging parameters.

Fixed — selling

  • create-delivery-note refuses "Nothing left to deliver: invoice <name> already moved these goods out of stock" when a stock-moving invoice already shipped what the order has left; a sales order's status follows both its deliveries and its invoices.

Fixed — read models

  • get-user no longer returns password_hash in any mode.
  • The setup --effect flag shadows the --effe/--effec abbreviations of --effective-date; use the full flag name.

Fixed

  • reconcile-payments offers each invoice at what it still owes. A partly paid invoice used to be offered at its full amount, so the reconcile failed and matched nothing for that customer or supplier; a paid invoice or another company's invoice could also be picked. Candidates now come from each invoice's own outstanding balance, in the right company, oldest first.

Removed

  • update-invoice-outstanding and update-purchase-outstanding are retired. They moved an invoice's balance with no ledger posting, so the books and the customer or supplier ledger could disagree. Calling either now returns a steer: record cash with add-payment and submit-payment (or allocate-payment), reduce an invoice with a credit or debit note, or write off with write-off-invoice.

Upgrade notes

  • Foundation migrations 037-051 ship in this release. Only 037 (cancellation-marking symmetry) and 051 (expense-claim payment-ledger backfill) change existing data; both audit each document they change. A PostgreSQL install that already recorded migrations 001 and 002 as applied does not re-run them under this release; the repair migration for that case is not part of this release. See ../../BREAKING_CHANGES.md, "Upgrading from v4.15.0 to release candidate 1".
  • Today's installer uses AvanSaber's signed registry. Accepting a customer's own publisher key is planned, not shipped in this release.

[4.15.0] — 2026-08-15 — correctness floor

Release gate: nine end-to-end business scenarios, driven in plain business language and judged by a deterministic oracle against the resulting books (short-pay write-offs, recurring billing honesty, outside-invoice linking, order-to-cash with corrections, deposit forfeiture, and more), all pass at this tree. The books-integrity invariants held in every scenario.

Fixed — books integrity

  • Cancelling an invoice now releases the payment allocations it voided. Cash no longer stays "applied" to a document that left the books; allocations are marked delinked (kept for audit), the payment's unallocated balance is restored, all in the cancel's single transaction. Migration 031 back-heals existing installs.
  • What a customer owes is no longer double-counted. Payment submission wrote the same cash against the party twice in the ledger; readers now agree with the truth (a 1,000.00 invoice paid 300.00 reads 700.00). Corrected at the source with compensating ledger rows; migration 032 heals existing installs, refusing (and reporting) any payment whose stored residual disagrees with its detail rows.
  • One canonical reading of the party ledger. ar-aging, ap-aging and get-outstanding share a single definition of which ledger rows count and which document each row belongs to; get-outstanding attributes each amount to the invoice it belongs to. New always-on invariant INV-27: every party's payment-ledger net must equal open invoices minus unapplied cash, exact to the cent.
  • Loan money reaches the ledger. Loan disbursement and repayment posted no GL under an error swallow; both now post balanced entries or fail loudly.
  • Asset disposal posts to the right accounts. Sale proceeds and gain/loss stopped going to depreciation expense; a disposal gain can no longer be credited to plain Sales Revenue, and disposal account choices are gated by account type.
  • Intercompany eliminations are idempotent, and old damage is correctable. Re-running elimination generation eliminates only new activity (migration 036 links existing entries); the consolidated trial balance names any unlinked elimination rows an earlier duplicate run left behind, list-elimination-surplus shows exactly those rows, and remove-elimination-surplus removes them only with --confirm, auditing every removal.
  • A billing period reads invoiced only when a real, GL-posted invoice backs it. Draft creation no longer flips the status; sync-billing-period-status materializes the truth, a cancelled invoice auto-reverts the period, and migration 033 re-derives existing installs. generate-invoices now produces a real invoice end-to-end (company derived, a generic billing service item resolved through inventory), and flags — never blocks, never silently skips — a possible covering invoice.
  • An invoice raised outside the billing flow gets attached, not duplicated. link-billing-period-invoice attaches an already-posted invoice to its rated period (one link only; a second is rejected); unlink-billing-period-invoice resets deliberately, with --reason while the invoice is live.
  • Write-offs are real accounting. New write-off-invoice posts balanced bad-debt entries under the invoice's own voucher, dated by the decision (today) rather than the sale; short-pays can be written off in the same payment via the write_off deduction type (migration 034); the legal edition's invoice write-off now moves the actual books instead of flipping a status. Both write-off actions ask for confirmation.
  • Property security deposits post GL (receipt, deduction, return, and the new prop-forfeit-security-deposit), with the liability netting to zero across the lifecycle.
  • Education edition: notifications accept everything core writes regardless of module install order (educlaw migration 003); LMS document numbering carries the year (LMS-2026-00001) with a counter migration for live installs; course-prerequisite inserts are dialect-portable on PostgreSQL.

Changed — retired surface

  • Intercompany elimination is done in one place. The legacy four-action elimination pair — which posted "eliminations" into operating companies' own books and left them unbalanced — is retired. The actions still answer: each refuses with one message naming the consolidation flow that does the job correctly, which keeps group adjustments at group level. Existing posted entries are left in the books (immutable; removing them is a bookkeeping decision); their two tables are archived and dropped by a migration.
  • The two one-sided stock-ledger actions are retired the same way. They could move quantity with no general-ledger leg; both now refuse and name the document flows (stock entries, receipts, deliveries) that tie quantity to the books, and a constitutional gate pins that every stock movement path reaches the books.

Changed — database layer

  • Every schema, connection and catalog access flows through one seam (erpclaw_lib.seam, SQLAlchemy Core vendored, dialect-correct DDL). 38 of 39 module installers now provision through it, the module install path is verified against live PostgreSQL, and a ratcheted gate prevents new bypasses. MySQL is explicitly unsupported (the TEXT-keyed schema cannot be indexed there) rather than silently falling through.
  • Migrations declare whether they change data, and the ones that do write an audit-trail row per changed document inside the migration's own transaction.

Changed — agent guidance

  • Three guidance steers close the gaps the release-gate scenarios exposed, each re-verified end-to-end: the one-payment short-pay write-off route; the billing flow opener (plan → rate → real invoice, in plain words); and the record-once-then-link rule for outside invoices (an invoice exists in the books exactly once — never generate a second one for a period already covered).

Fixed — measurement integrity

  • An invariant check that examined nothing may no longer report a pass; the smoke tier verifies through the product-library checker on both backends; test lanes bind the tree under test rather than the deployed install, with a poisoned-install companion proving each pin does its job.

[4.14.0] — 2026-07-27 — Wave F sprint 1 (F-debt): billing foundation

  • Rating engine complete: all 7 rate plan types rate to exact Decimal — time_of_use, demand, prepaid_credit, hybrid join flat/tiered/volume_discount. TOU tiers validated for 24h coverage; demand plans price peak + optional energy component; prepaid deducts FIFO inside the billing transaction with explicit over_limit on insufficient balance; hybrid composes non-hybrid components.
  • Money write gates: garbage, non-finite (NaN/Infinity), and finite-but-unrepresentable magnitudes are rejected at every billing write entry point with a named validation error; any value that passes a gate survives currency rounding. Legacy stored garbage is contained as a per-meter/per-row named data error — one bad meter never hides, lies, or aborts a run.
  • No silent non-billing: run-billing reports per-meter rating failures loud; generate-invoices marks a period invoiced only with a real invoice id (failures stay rated for retry with the reason reported).
  • Crash-safe billing runs: new billing_run + billing_run_target registry (migration 030, both dialects). run-billing, generate-recurring-invoices, and process-recurring process one target per transaction; a crash mid-run resumes via the new resume-billing-run with zero duplicate documents; failed targets isolate, healthy targets proceed. New actions: list-billing-runs, get-billing-run, resume-billing-run. Delegated resumes carry the caller's database context.
  • Truthful period rows: run-billing now stamps billing_period.rate_plan_id with the plan that actually priced a re-rated open period.
  • INV-25 always-on invariant: for every open AR/AP invoice, outstanding_amount must equal its payment-ledger detail net — checked continuously, not only at paid. update-invoice-outstanding / update-purchase-outstanding co-post the matching ledger detail row in the same transaction; auto-submitted recurring bills post their ledger row.
  • Riders: pre-master-key-load environment sanity check in the crypto layer (warn by default, ERPCLAW_STRICT_ENV=1 to refuse); cleanup-backups now requires confirmation (dangerous-action gate); phantom table declarations removed from the dependency map; billing SKILL section counts and homes corrected.
  • erpclaw-growth 2.10.0 (separate module): Wave-F usage-anomaly detector — consumption_spike fires on default sweeps via a meter-local recency split; rate_plan_mismatch judges each billing period against the plan that priced it with impossible-charge attribution; garbage data skips rows, never company-wide sweeps (migration 006).

[4.13.0] — 2026-07-23 — foundation hardening (v4.13.0 stabilization milestone)

Every known books-integrity gap surfaced by the live-agent testing program, closed and machine-guarded. All items shipped behind independent adversarial QA.

Fixed — books integrity

  • Landed costs now reprice inventory valuation (and post GL at all). add-landed-cost-voucher had been silently broken since the 2026-05-31 voucher-type registry displacement (migration 004): its voucher_type was never registered, so every landed-cost GL post failed the registry gate — masked because the feature had no tests. Migration 029 registers the type, and the action now writes zero-quantity stock-ledger valuation rows and updates FIFO layer rates so the goods carry their true landed cost, not just the base receipt rate. list/get/cancel-landed-cost-voucher added (cancel = reverse, both GL and valuation).
  • New constitutional invariant INV-24: stock-account GL ≡ stock-ledger valuation. For each company, the sum over stock-type GL accounts must equal the sum of stock-ledger valuation movements (reversal-inclusive, exact TEXT-decimal). Any posting path that moves one side without the other now reddens the gate. Fills the long-standing stock-subledger blind spot (the AR/AP subledgers already had this check).
  • revalue-stock now updates FIFO layer rates. Previously it repriced only the moving-average valuation, leaving FIFO items to consume at stale layer rates after a revaluation.
  • Rate-less invoice/order lines no longer silently price at $0. Line rate now resolves in order: explicit rate, then the customer's default price list, then any enabled selling price list, then the item's standard rate. --default-price-list-id added to add-customer/update-customer (the previously-unused customer price-list link is now live).

Added — feature completion

  • Open-order receiving/issuing guards. A standalone material_receipt for an item with an open purchase order is refused with guidance to receive against the order (prevents the double-count where the later bill re-receives the goods); the symmetric guard blocks a standalone material_issue when an open sales order covers the item.
  • Material request → purchase order. create-po-from-material-request copies requested lines into a PO, updates ordered quantities, and rolls the request status; get-material-request returns the request with its lines.
  • Payment deductions. add-payment --deductions records withholding / early-payment-discount / commission legs; submit-payment posts them to their GL accounts under full validation; cancel reverses them. Previously the table and its UI existed but no action could ever write a row.
  • Retroactive pay flows into salary slips. calculate-retro-pay is now idempotent, and generated salary slips consume pending retro adjustments as an earning line (applied on submit, reverted on cancel) instead of leaving them permanently stuck.
  • HR government-ID protection. Free-text HR fields are scanned and a non-blocking caution is returned when a value looks like a government ID; those fields are masked to last-4 on read; add-employee/update-employee now accept --ssn (encrypted at rest, last-4 on read).

Fixed — infrastructure

  • install-module no longer writes into the OpenClaw workspace when installed under a non-default ERPCLAW_HOME (a Hermes-home install had been touching the other runtime's skill directory), and its response now reports the real created-table count instead of zero.
  • Credit-hold refusal message no longer claims a --user-confirmed override that the module layer never honored; it now names the real release path.

[4.12.3] — 2026-07-21 — SKILL guidance steers

Changed

  • Receiving guidance tightened. The procure-to-pay receiving note previously permitted a standalone material_receipt for purchased goods "unless you restate the unit cost" — but a cost-stated standalone receipt still leaves the purchase order un-received, so a later bill/receipt flow receives the goods again and double-counts stock (reproduced end-to-end via live agent testing: one 100-unit delivery became 200 units on hand). The steer now says NEVER for purchased goods, and instructs checking list-purchase-orders before any bare "receive stock" request. The stock-entries catalog row carries the same warning at the point of use. Guidance-only; no action, schema, or GL change. A product-level guard (warn/link when receiving an item with an open PO line) is tracked separately as a build decision.
  • Catalog-pricing guidance added. The pricing catalog row now states that an item's default selling price is add-item/update-item --standard-rate, and that add-item-price rows (named price lists) are NOT automatically consulted at invoice time — previously nothing steered between the two overlapping surfaces, and live agent testing showed "set the selling price to X" landing in the unconsulted item_price table. Guidance-only.

[4.12.2] — 2026-07-06 — documentation hygiene

Changed

  • Internal design-document references in migration docstrings, the table provenance manifest, and repository prose were replaced with stable opaque provenance keys and neutral descriptions. No functional change to any migration, action, or schema (verified by AST comparison); the provenance governance gate keeps full strength through a maintainer-side reference map.

[4.12.1] — 2026-07-05 — upgrade version-row bookkeeping

Fixed

  • update-foundation now heals the erpclaw_module.version bookkeeping row. Since 4.12.0, a confirmed update-foundation reconciled files and ran pending migrations but never updated the module-version row that list-modules reads, so an upgraded ClawHub install under-reported its version indefinitely (fresh installs were unaffected — install-module writes the current version). The confirmed-success paths — both the applied path and the in-sync early return — now bump the row to the registry's foundation version, extending the 4.12.0 rule "a run reporting ok means files AND schema converged" to include the observable version row. The bump is idempotent, so the in-sync path also HEALS rows left stale by pre-rider upgrades on the next reconcile. It never fires on a dry-run/preview (previews stay read-only) or a failed migration (the DB stays at the last good migration and the run exits 1 — never a half-converged ok), and is a clean no-op on the DB-less skip. No schema change, no migration, no new action.
  • erpclaw-meta SKILL_TABLES stale CRM entry corrected. The erpclaw-crm presence-check row referenced pre-rename table names (crm_lead/crm_opportunity/crm_campaign); the live foundation tables are lead/opportunity/campaign, so the check-skills tables_ok flag for CRM could never read true. The ratified hygiene rider named crm_lead → lead; the two adjacent names on the same line were the same staleness and are corrected together so the presence check is actually functional (map-only change; no action, schema, or GL impact).

[4.12.0] — 2026-07-05 — foundation-hygiene release

Fixed

  • 33 shipped actions are now actually reachable through the router. The meta-router ACTION_MAP gained every action defined in the per-domain sub-scripts that it previously could not dispatch — most importantly migrate (the documented upgrade path), the whole custom-fields cluster (add-custom-field / list-custom-fields / remove-custom-field / set-custom-field-value / get-custom-field-values), and credit control (place-customer-on-hold and the dunning cycle). Previously these returned "Unknown action" via the router despite being fully built and tested. hr-status / payroll-status now route via ALIASES to each domain's status. A new L0 dispatchability gate (defined ⇒ routable, all modules) makes this bug class structurally unable to ship again.
  • update-foundation now converges files AND schema. A confirmed apply-path reconcile applies pending foundation migrations after the file sync (idempotent; loud, per-migration-ledgered failure; explicit skip on uninitialized DBs) — an upgrade can no longer silently skip schema migrations, including on the in-sync retry path. rollback-foundation restores files only, never schema (migrations are forward-only). migrate joined DANGEROUS_ACTIONS beside its schema-* siblings. One-time bootstrap note (box-verified): the very first update-foundation FROM a pre-4.12.0 version still runs the old in-memory code, so schema converges on the second call (or a follow-up migrate); every upgrade from 4.12.0 onward converges in one call.

Schema

  • 3 dead tables dropped (migration 028_drop_dead_orphan_tables_2.py): communication, erpclaw_module_validation, erpclaw_table_ownership — all verified zero-writer/zero-reader/zero-FK (necessity audit, adversarially QA-confirmed); dialect-aware + idempotent; PostgreSQL-rehearsed at close. Fresh-install table count 776.
  • Table provenance manifest (scripts/table_provenance.json): every table now declares why it exists (doc ref / parent concept / explicit accepted-risk); a diff-aware L0 gate blocks any NEW provenance-less table — adding a table is now a governance event.

Added

  • Shared integration lib modules: erpclaw_lib/action_validators.py (the validators previously duplicated across integration addons), erpclaw_lib/integration_secrets.py (AES-256-GCM field crypto with transparent legacy-format read-back), erpclaw_lib/integration_actions.py (shared sync-job/GL-rule/reconciliation action bodies; table writes stay with owning modules).

[4.11.0] — 2026-07-01 — Wave 2 (inventory + warehouse depth)

Added

  • Putaway rules + pick lists + hard, persisted stock reservations. Stock promised to one order can no longer be silently drained by another. Reservations are now first-class, persisted, and binding: add-reservation / release-reservation hold a quantity against an (item, warehouse), and a stock issue (e.g. material_issue) that would breach the active reserved quantity is refused with the available-minus-reserved figure and rolls back in one transaction (no partial SLE). get-projected-qty nets active reservations against on-hand and is byte-for-byte back-compatible when there are zero reservations. Putaway rules (add-putaway-rule / list-putaway-rules / delete-putaway-rule, apply-putaway-on-receipt) route received goods to a target warehouse by item or item group. Pick lists run end to end: create-pick-list from a sales order -> add-pick-list-item -> mark-picked -> complete-pick-list (into a delivery note) -> cancel-pick-list (releases the reservation). Warehouse-level for V1; bin-level is deliberately deferred. New tables putaway_rule, pick_list, pick_list_item, stock_reservation_entry via foundation migration 025_putaway_pick_reservation.py (net-new, dialect-aware, idempotent; all quantities Decimal-as-text). PostgreSQL-rehearsed before release.
  • Item-global alternatives / substitutes (Wave 2 S7). Define substitute items once at the item level instead of repeating them on every bill of materials: add-item-alternative / list-item-alternatives / remove-item-alternative, and get-best-alternative-for-item which returns the highest-priority substitute that actually has enough stock at a given warehouse (ties broken by on-hand quantity). Each alternative is directional (A can substitute for B without B substituting for A), ranked by priority, and carries a Decimal conversion_factor. Manufacturing's BOM substitutes now fall back to these item-global alternatives when a BOM line defines none of its own, so the same substitute list is not maintained twice (cross-module read only; manufacturing gains no new writes). New table item_alternative via foundation migration 027_item_alternative_table.py (net-new, dialect-aware, idempotent, UNIQUE + directional CHECK). PostgreSQL-rehearsed on the box at wave close. (Wave 2 S7)
  • Stock-entry typed dispatch — 3 new entry types (Wave 2 S6). add-stock-entry --entry-type now handles repack, subcontract (send_to_subcontractor), and material_consumption (it previously errored on these three, even though the stock_entry_type CHECK already permitted all seven). repack consumes input lines and produces output lines within one warehouse and enforces a cost-balance invariant (total input value == total output value within $0.01, Decimal) — an unbalanced repack is refused and rolls back. send_to_subcontractor transfers stock out to a --supplier-warehouse-id that must be a transit or production warehouse (this is the dispatch path Wave 2 S5's subcontracting transfer emits). material_consumption issues raw materials against an active --work-order-id (not_started/in_process), recording the work-order link on the stock entry. Two convenience wrappers: add-repack-stock-entry (one-in/one-out repack) and add-material-consumption (single raw-material issue). SLE remains immutable and balanced; submit stays a single transaction. No schema change, no migration (the CHECK already had all seven values). (Wave 2 S6)
  • Inventory anomaly hooks AI1 (Wave 2). detect-anomalies (erpclaw-ai-engine) gains two internal heuristics, bringing VALID_ANOMALY_TYPES from 18 to 20: reservation_over_available flags an (item, warehouse) whose active stock_reservation_entry reserved qty exceeds the on-hand SLE balance (a stock-out is predicted — critical when nothing is on hand, otherwise warning), and subcontract_receipt_mismatch flags a subcontracting_order whose finished-goods received_qty diverges from materials_transferred beyond a 5% tolerance (over-receipt = more FG than the transferred materials can yield, critical; a completed order that returned materially less than was transferred = yield loss, warning). Both are internal detectors run by the existing detect-anomalies sweep (no new public action) and emit through the shared _insert_anomaly helper; they READ M5's stock_reservation_entry / the SLE and S5's subcontracting_order (cross-module reads) and WRITE only growth's anomaly table. All quantities are Decimal-as-text. The anomaly.anomaly_type CHECK enum is extended in lockstep (init_db for fresh installs; growth migration 005_wave2_anomaly_types.py for existing ones — dialect-aware, idempotent, no rebuild dance since anomaly has no FK edges). Additive; no new action or table. (Wave 2 AI1)
  • Subcontracting receipt lifecycle (Wave 2 S5). Completes the outsourced-production workflow on top of add-subcontracting-order (erpclaw-manufacturing addon): submit-subcontracting-order (validates the BOM lists the service item, draft -> submitted), transfer-materials-to-subcontractor (ships only the stock raw materials to the supplier sub-store via S6's send_to_subcontractor stock entry — the service line is never shipped; partial transfers accumulate), receive-subcontracted-items (receives finished goods: the FG cost rolls up raw-material cost + --subcontract-charge-rate x received qty, posts exactly ONE balanced FG stock-ledger-entry + GL, generates a draft subcontract-charge purchase invoice, advances partially_received -> completed and stamps final_received_at), plus cancel-subcontracting-order (only pre-transfer), cancel-subcontract-transfer (reverses the transfer SLE — cancel = reverse, allowed only pre-receipt), get-subcontracting-order, and list-subcontracting-orders. Buying's create-purchase-receipt --subcontracting-order-id DEFERS the finished-goods bookkeeping entirely to receive-subcontracted-items and posts nothing itself, so a subcontract receipt posts the FG SLE/GL exactly once (no double-post). All money is Decimal-as-text (ROUND_HALF_UP); GL is immutable (cancel = reverse); the FG receipt is a single transaction running the 12-step GL validation. (Wave 2 S5)

Schema

  • 4 net-new inventory tables (Wave 2 M5): putaway_rule, pick_list, pick_list_item, stock_reservation_entry via foundation migration 025_putaway_pick_reservation.py. Quantities (reserved_qty/expected_qty/picked_qty) are Decimal-as-text (no float); FKs to item/warehouse/sales_order/company intact. Net-new, no rebuild; SQLite + PostgreSQL (box-rehearsed) green.
  • item_alternative net-new table (Wave 2 S7): foundation migration 027_item_alternative_table.py; conversion_factor Decimal-as-text, priority integer, both FKs -> item, UNIQUE + directional CHECK. Net-new, no rebuild; SQLite + PostgreSQL (box-rehearsed) green.
  • anomaly.anomaly_type CHECK extended +2 values (Wave 2 AI1): reservation_over_available + subcontract_receipt_mismatch added to the growth-owned anomaly table CHECK. Fresh installs get it from init_db.py; existing installs from growth migration 005_wave2_anomaly_types.py (dialect-aware, idempotent — SQLite rebuild guarded by a stored-SQL probe, Postgres DROP CONSTRAINT IF EXISTS + ADD CONSTRAINT). No FK edges on anomaly, so no rebuild dance.
  • subcontracting_order gains 2 nullable TEXT columns (Wave 2 S5): subcontract_charge_rate (Decimal-as-text per-unit subcontracting fee) and final_received_at (completion timestamp). Additive ADD COLUMN via foundation migration 026_subcontracting_charge_columns.py (dialect-aware, idempotent — SQLite guarded by _sqlite_has_column, Postgres ADD COLUMN IF NOT EXISTS); also in init_schema.py for fresh installs. No rebuild.

[4.10.0] — 2026-06-16

Added

  • Hermes runtime port v1 (experimental). ERPClaw is now runtime-portable from a single source tree via the ERPCLAW_HOME defaulting env var (unset = ~/.openclaw/erpclaw, byte-identical to today — OpenClaw users unaffected). Runs on the Hermes Agent runtime as an experimental hedge via a GitHub tap (hermes skills tap add avansaber/erpclaw). OpenClaw stays the primary, supported, ClawHub-distributed runtime; Hermes is best-effort, no SLA, no feature-parity promise. No encrypted-credential actions on Hermes in v1. v1 acceptance: two business-user NL scenarios (company setup + order-to-cash customer payment) graded GREEN on Hermes by the same deterministic oracle as the OpenClaw baseline. Registry re-signed to registry_version 61. (Also in this window: a thin stdio MCP server over the db_query routers, built + QA-green but shipped as optional runtime-reach only — not a feature dependency.)

[4.9.0] — 2026-06-15

Added

  • Wave 1B — built-in Sales/CRM depth. Contacts + Companies, first-class Tasks, customizable Pipelines, Saved views (filterable), global CRM search, and CSV import/export — a "lead → opportunity → quote → order" Sales module (built in erpclaw-growth). Foundation gains nullable FK links to the addon's contact/pipeline entities via foundation migrations 023_crm_contact_fks.py + 024_displace_opportunity_stage_check.py; the shared table_exists is now PostgreSQL-correct via the CLI. All six pieces were validated on the live gateway + PostgreSQL.

[4.8.0] — 2026-06-11

Added

  • Bank statement import + matching (erpclaw-integrations v2.1.0). Import OFX / CAMT.053 / MT940 / BAI2 statement files with pure-stdlib parsers (no new dependencies), idempotent re-import (external_id unique), a rule-based auto-match engine against open invoices/payments, manual match/clear, and bank-reconciliation-summary. Tables bank_statement/bank_statement_line/bank_match_rule via foundation migration 020; writes routed through the foundation erpclaw_lib/bank_import.py (module write-ownership preserved). --bank-account-name resolves accounts by name. NL-validated on the live gateway (fin-m2 PASS).
  • P&L by dimension — natural routing (M6 follow-up). profit-and-loss now accepts an optional --group-by <dimension> that returns the statement broken down per accounting-dimension value — revenue / expenses / net per value, income/expense accounts only, with entries that lack the key folded into an explicit (untagged) bucket (never dropped). The grouped totals reconcile to the flat statement (income_total / expense_total / net_income retained). Without the flag, output is byte-identical to before. The grouping composes the shared dialect-aware json_get GROUP BY (no SQL duplicated from multi-dim-trial-balance, which keeps owning whole-trial-balance grouping); an unregistered or deactivated --group-by key errors cleanly pointing at list-dimensions, and a --dimension-key/--dimension-value filter composes as filter-then-group. This makes the agent's natural reach (profit-and-loss for "P&L by department") correct instead of routing it to hand-rolled cost-center SQL. Read-only report — no schema, migration, registry, or new-action change. (Wave 1 M6 routing fix)
  • Anomaly types AI1. detect-anomalies (erpclaw-ai-engine) gains two heuristics, bringing VALID_ANOMALY_TYPES from 16 to 18: asset_book_value_drift flags assets whose current_book_value deviates >5% from the gross_value − accumulated_depreciation invariant (critical >25%), and dimension_tag_drift flags an account_type whose gl_entry.dimensions_json tagging is partial for a given key (some entries carry the key, others omit it — read & grouped in Python, dialect-safe, no JSON GROUP BY). Both names verbatim for corpus traceability; the anomaly.anomaly_type CHECK enum is extended in lockstep. dimension_tag_drift consumes M6's dimensions_json. Additive; no new action, table, or migration.

[4.7.0] — 2026-06-10

Wave 1 Financials depth (partial bundle: P0 + M6 + M7 + S3). Additive only — no breaking changes. M2 and AI1 ship in a later bundle. No ClawHub upload this release (bundled into the next functional release; ClawHub foundation stays v4.1.6, propagated to the OpenClaw box via signed reconciliation).

Added

  • Multi-dimensional GL (M6). A dimension_registry (seeded project / department / cost_center) now drives optional per-entry GL dimensions. insert_gl_entries serializes a dimensions dict into the existing dimensions_json column (default '{}', so every existing GL caller is byte-unchanged), GL validation gains step 13 (required-dimension enforcement per account_type + uuid_fk referential-integrity check), and reverse_gl_entries preserves the original dimensions on the mirror entry. Four CRUD actions in erpclaw-gl (add-dimension, list-dimensions, update-dimension, deactivate-dimension; deactivation is blocked while recent live GL references the key) and two new reports in erpclaw-reports (multi-dim-trial-balance --group-by "project,department", dimension-balance-report --dimension K). general-ledger / profit-and-loss / balance-sheet / cash-flow accept repeated --dimension-key / --dimension-value filters. All dimension SQL routes through erpclaw_lib.query.json_get() for dialect-safe extraction (no raw json_extract literals). Migration 017.
  • Asset depth (M7). Full fixed-asset lifecycle beyond depreciation: impair-asset / reverse-impairment (write-down to recoverable amount with balanced GL; reverse mirrors the GL and restores book value — cancel-by-reverse, impairment rows are immutable), capitalize-asset (initial recognition from purchase cost), and revalue-asset (upward/downward revaluation against a revaluation reserve, with depreciation recompute). complete-maintenance gains an --is-capex branch that capitalizes the cost into the asset (DR Asset / CR Cash) and recomputes the depreciation schedule instead of expensing it. New voucher types asset_impairment / asset_capitalization / asset_repair_capex; is_capex column on asset_maintenance. Migrations 018 / 019. (erpclaw-ops/erpclaw-assets)
  • Construction-work-in-progress (S3). A cwip_cost_accumulation ledger plus five actions in erpclaw-assets: add-cwip (start an under_construction asset), accumulate-cwip-cost (DR the capital_work_in_progress account, per-project via M6's dimensions_json), transfer-cwip-to-asset (capitalize to in_use + start depreciation from the transfer date), cancel-cwip (reverse all accumulations; blocked if any cost arrived from a submitted document), and list-cwip-projects. create-purchase-invoice (erpclaw-buying) and add-journal-entry (erpclaw-journals) accept an optional --cwip-asset-id that routes the GL leg to the CWIP account and records a cwip_cost_accumulation row in the same transaction. The existing gl_posting.py guard that rejects a direct JE to a CWIP account is now reachable. New voucher type cwip_capitalization. Migration 021.

Fixed

  • erpclaw_lib.query.json_get() now emits dialect-correct SQL on PostgreSQL. The Postgres branch previously emitted the SQLite JSONPath form col->>'$.key', which is invalid: Postgres ->> takes a plain object key, and the JSON columns (dimensions_json et al.) are provisioned as text, so the operator needs a ::jsonb cast first. It now emits col::jsonb->>'key' (verified on PostgreSQL 16). All three branches also escape the key as a proper SQL string literal (doubling embedded single quotes) instead of raw f-string interpolation, so a key containing a quote can no longer break or inject the emitted SQL. Latent until Wave 1 M6's multi-dimensional reporting, which is the first production consumer.

[4.6.1] — 2026-06-08

Fixed

  • close-fiscal-year now refuses to close when the chosen closing (retained-earnings) account belongs to a different company than the fiscal year being closed: it hard-errors with an actionable message and rolls back before any GL is posted. Previously, in a multi-company database, a mismatched closing account would have posted one company's net income into another company's equity (a silent cross-tenant contamination). Pure pre-write validation, no schema or API change.

[4.6.0] — 2026-06-08

Added

  • NL company-by-name resolution: business users can address a company by name ("for Acme, invoice Bruce") instead of a UUID. The resolve_company_id chokepoint now accepts a --company "<name>" flag (exact, case-insensitive, dialect-neutral LOWER(name), never .ilike()) alongside the unchanged --company-id; the sole-company auto-detect path is byte-unchanged. Wired across the GL, payments, inventory, tax, journals, and reports actions plus the educlaw-k12 router (~70 call sites). A named-but-missing company fails loudly listing the available company names and never falls through to a different company, so one entity's books can never post to another. SKILL.md instructs the agent to pass the user's exact wording and never fuzzy-substitute or autocorrect a company name. Verified end-to-end on the live gateway (mc01 right-company invoice; mc02 missing-name hard-error with zero posting).

[4.5.0] — 2026-06-05

Added

  • resolve-item (erpclaw-inventory): cascade item resolver that maps a loose/plural user phrase ("20 Brake Pad Sets") to the stored item via a deterministic 4-tier cascade (exact → singularized → substring → token-AND, stop at first non-empty, shortest-name-first). Read-only, cross-DB (dialect-neutral LOWER(...) LIKE, never .ilike()), stdlib-only singularizer. Returns single_match / multiple_matches / matched:false so callers can branch deterministically. (Inventory action count 42 → 43.)

Fixed

  • Gateway replies no longer leak accounting internals to business users: SKILL.md ## Speaking to the user now instructs the agent to translate or omit double-entry GL narration, account names, internal status/field labels (status, posting date, outstanding, valuation rate, naming series, gl/sle counts) and raw UUIDs, surfacing the business outcome instead. Guidance-only — no response-shape or schema change.
  • Stock receipts now always post the perpetual-inventory GL leg (DR Stock-in-Hand / CR Stock Received Not Billed); a missing Stock-in-Hand or Stock Received Not Billed account now fails loudly with an actionable message instead of silently skipping the GL while still moving the subledger. The create_perpetual_inventory_gl lib helper raises a structured error (caught at all 7 caller sites across inventory/buying/selling/manufacturing, surfaced as a clean JSON error with full transaction rollback — no SLE-without-GL). tutorial (demo company) now seeds a Stock Adjustment account so demo-company reconciliations post correct GL.
  • Purchased stock is now valued from its source document end to end: receiving against a purchase order (GRN) or recording a bill with stock update carries the unit cost into the stock ledger and posts the inventory GL. A standalone, rate-less external stock receipt with no item standard cost now fails loudly with an actionable message instead of silently booking inventory at $0 (internal transfers and manufacturing legs are unaffected — they keep inheriting existing valuation). SKILL.md now guides the procure-to-pay receipt flow.

[4.4.0] — 2026-06-05

Added

  • --email / --phone on add-customer / update-customer / add-supplier / update-supplier; returned by the corresponding get-*.

Schema

  • customer.email TEXT, customer.phone TEXT, supplier.email TEXT, supplier.phone TEXT (nullable); foundation migration 016 (dialect-aware SQLite + PostgreSQL). import-customers / import-suppliers INSERTs now resolve against real columns.

Fixed

  • AR/AP payment application now clears the invoice/bill: the document's outstanding_amount / status is synced and a per-allocation payment-ledger entry with against_voucher is posted, so a recorded payment marks the invoice/bill paid (previously it stayed unpaid).
  • Gateway voucher_type labels are canonicalized system-wide (e.g. "Sales Invoice" -> sales_invoice) so the NL gateway and downstream GL/inventory/tax/reports/payments consumers agree on the voucher vocabulary.

[4.3.1] — 2026-05-11

Fixed

  • erpclaw-selling/db_query.py main(): re-add --description argparse flag that was incorrectly removed as a dupe during v4.3.0 (it's used by add-dunning-level). Server smoke caught: Unknown flags: --description on first invocation.

[4.3.0] — 2026-05-11

Customer credit limit + dunning levels (ROADMAP S1). B2B AR depth: enforce credit limits at invoice submit and run automated dunning escalation cycles.

Added

  • 5 new actions in erpclaw-selling:
    • check-credit-limit --customer-id X — returns credit_limit, outstanding_ar, available_credit, credit_status (read-only).
    • place-customer-on-hold --customer-id X --credit-status on_hold|suspended|active [--reason ...] — flip credit status. Audited.
    • add-dunning-level --company-id X --level N --days-overdue D --dunning-action email|hold|call|suspend — configure escalation policy.
    • run-dunning-cycle --company-id X [--run-date Y-M-D] — match overdue invoices to highest-applicable dunning level, take action (hold/suspend update credit_status; email/call record-only until M8 ships SMTP).
    • list-dunning-runs [--customer-id X] [--company-id Y] — history.
  • Invoice-submit credit policy hook in submit-sales-invoice: blocks suspended customers, blocks on-hold customers, enforces credit_limit when configured. Outstanding AR computed from sales_invoice.outstanding_amount of submitted, non-return invoices.

Schema

  • customer.credit_status TEXT NOT NULL DEFAULT 'active' CHECK(credit_status IN ('active','on_hold','suspended')).
  • dunning_level table: id, company_id, level (1-10), days_overdue, action, template_id, description, UNIQUE(company_id, level).
  • dunning_run table: id, company_id, run_date, customer_id, level, invoice_ids_json, action_taken, status, generated_email_id, notes.
  • Migration 002_credit_dunning.py brings existing installs up to date. Idempotent.
  • customer.credit_limit already existed (since pre-v4.0); no change there.

Tests

  • source/erpclaw/scripts/erpclaw-selling/tests/test_credit_dunning.py — 17 tests covering check-credit-limit, place-customer-on-hold, add-dunning-level, and the invoice-submit policy hook. All pass.

Notes

  • The email dunning action is record-only at this version; actual SMTP send ships with ROADMAP M8 (Email marketing real sender). When M8 lands, run-dunning-cycle will trigger real emails for level=email rows.
  • The call action is also record-only by design — it logs that a human-call escalation is due; no auto-dialer.
  • credit_status is a separate concept from customer.status: status governs "is this customer still ours" (active / inactive / blocked); credit_status governs "are we currently extending credit" (active / on_hold / suspended). Both must be active for a new invoice to submit.

[4.2.3] — 2026-05-10

Operational follow-up to the v4.2.x publish saga. Three small Tier-1 fixes from the pending-work backlog.

Changed

  • Shared skip filters. Extracted SKIP_DIRS, SKIP_SUFFIXES, SKIP_FILE_EXACT into source/erpclaw/scripts/erpclaw-setup/lib/erpclaw_lib/skip_filters.py. Both release/regen_module_manifests.py (manifest hashing) and source/erpclaw/scripts/module_manager.py (install-time integrity walk) now import from the canonical module. Prevents the v4.2.1-class "extra files" failure where the two walks drifted out of sync.

Fixed

  • Registry cache auto-refresh on foundation upgrade. Previously ~/.openclaw/erpclaw/registry_cache.json was not invalidated when clawhub install upgraded the foundation, so the first install-module after an upgrade ran integrity checks against the OLD manifest and surfaced false-positive "N mismatched" warnings. Now _load_registry() compares the bundled foundation version against the cached one and treats the cache as stale when the bundled version is newer, forcing a refresh.
  • Snapshot publish-surface alignment. release/scripts/regen_clawhub.py was carrying test files (scripts/<sub>/tests/), .gitkeep markers, and .github/ configs that ClawHub strips at upload time — dead weight in the snapshot (~50% of file count). Fixed the snapshot walk to actively remove these from clawhub/erpclaw/ so the local snapshot matches what users actually receive. Snapshot file count dropped from 187 to 90.

Notes

  • No new actions, no schema changes, no behavioural changes for end users; this release is internal hygiene only.
  • Verified end-to-end on the OpenClaw test server via the post-republish smoke plan after publish.

[4.2.2] — 2026-05-10

Companion fix to v4.2.1. The v4.2.1 manifest correctly excluded tests/, .github/, bin/, etc. — but module_manager.py's install-time integrity check kept its old skip set, so when install-module <vertical> git-cloned a public repo (which ships the full tree including those dirs), the walk treated tests/ files as "delivered" while the manifest didn't list them. Result: every install-module after a v4.2.1 install failed with "0 mismatched, 0 missing, N extra files".

Fixed

  • source/erpclaw/scripts/module_manager.py: aligned the install-time integrity walk's skip set with release/regen_module_manifests.py. SKIP_DIRS adds tests, .github, bin; SKIP_SUFFIXES adds .sig; new SKIP_FILE_EXACT covers .DS_Store, .gitkeep, .clawhubignore. The two skip sets must stay in lockstep — drift between them produces "extra"/"missing" false positives. A code comment in module_manager.py flags this invariant for future maintainers.

Notes

  • Foundation must be re-installed (clawhub update erpclaw --force or fresh install) to pick up the fixed module_manager.py. The bug only manifests when install-module is called against a v4.2.0/v4.2.1-installed foundation.
  • Smoke plan re-run on 2026-05-10 confirmed the cold-install path now passes end-to-end with this fix.

[4.2.1] — 2026-05-10

Fix-up release for v4.2.0. Two install-blocking issues caught during cold-install smoke testing on the OpenClaw server:

Fixed

  • Manifest / shipped-package alignment. release/regen_module_manifests.py was hashing files (tests/, .github/, bin/, *.sig, .gitkeep) that ClawHub strips during publish/install. Result: every install-module call against a v4.2.0 install failed integrity check with "100 mismatched/missing files" because the manifest claimed files that don't ship. Fixed by aligning the walk's exclude set to ClawHub's actual ship contract: SKIP_DIRS now includes tests, .github, bin; SKIP_FILE_SUFFIXES adds .sig; SKIP_FILE_EXACT adds .gitkeep, .clawhubignore. Foundation manifest dropped from 183 file entries to 84 entries that exactly match what users see post-install. Registry re-signed; signer fingerprint unchanged: d471:335b:0e4d:75ce.
  • Vertical parser fixes propagated to public avansaber/* repos. The four argparse duplicate-flag fixes that shipped in v4.2.0's foundation manifest were not pushed to the public repos that install-module clones from (avansaber/healthclaw, avansaber/constructclaw, avansaber/legalclaw, avansaber/retailclaw). Result: install-module healthclaw cloned the broken parser, hash-verified successfully (we'd hashed the broken file), and crashed at first invocation. Fixed by re-running the public-repo publish across all avansaber/* targets.

Notes

  • v4.2.0 was published to ClawHub Marketplace 2026-05-10 with these latent bugs. v4.2.1 supersedes; users should clawhub update erpclaw --force to receive the fixed package.
  • Cold-install smoke plan run on 2026-05-10 caught both bugs before any external user encountered them.
  • clawhub install erpclaw still requires --force due to a VirusTotal Code Insight flag that fires on our crypto/external-API patterns. This is a marketplace-side scanner over which we have no control; the flag does not indicate actual malware. Out-of-scope for v4.2.1.

[4.2.0] — 2026-05-05 (republished 2026-05-10)

License change plus four parser bug fixes in vertical modules. ERPClaw moves from MIT to GNU General Public License v3, retroactively, across the entire codebase (foundation + 48 modules + addons + integrations + website + tooling). Existing copies that were downloaded under MIT retain their MIT rights for that downloaded copy per US copyright law on implied license; new clones, forks, and downloads receive GPL v3 terms. Patent (filed under USPTO provisional) is unchanged and continues to apply.

Changed

  • LICENSE.txt at every level (48 files) replaced with the canonical GPL v3 text from FSF, prefixed by a short notice and license history.
  • package.json license field in JS sub-projects updated from "MIT" to "GPL-3.0-only".
  • README, STATUS, ARCHITECTURE, ROADMAP, PROJECT_RULES, CLAUDE.md, and module READMEs across source/ updated to describe ERPClaw as GPL v3 with a license-history line for v4.1.x and earlier.
  • Marketing prose on erpclaw.ai updated to say "open source" generically, with explicit "GPL v3" only on license-specific pages.
  • Comparison pages drop "MIT vs. GPL" wedge framing; positioning shifts to architecture (AI-native vs. AI-decorated).
  • release/scripts/security_audit.py check_licence now accepts either GPL v3 or MIT (the latter retained as legacy-compatible for v4.1.x).
  • Publish/packaging tooling LICENSE_TEMPLATE re-templated with a short GPL v3 notice + license-history line.
  • Internal open-source sourcing guidance rewritten: GPL v3 is now license-compatible with ERPNext, Odoo Community, and other GPL/LGPL upstreams for direct inclusion; AGPL v3 is the new forbidden inbound boundary.

Fixed

  • Removed duplicate argparse flag registrations that crashed the parser at load time across four vertical modules. None of the affected verticals were callable for any action until the dup was removed.
    • healthclaw/scripts/db_query.py: duplicate --verified-by (kept LAB-domain copy at L453, removed Provider-Credentialing duplicate at L530).
    • constructclaw/scripts/db_query.py: duplicate --spec-section (kept SUBMITTAL copy at L234, removed Drawing duplicate at L369).
    • legalclaw/scripts/db_query.py: duplicate --reminder-days (kept CALENDAR copy at L161, removed SOL-Calculator duplicate at L235).
    • retailclaw/scripts/db_query.py: duplicate --category-id (kept MERCHANDISING copy at L136, removed Procurement/Shrinkage duplicate at L215).
  • Cleanup sweep across all source/*/scripts/db_query.py confirms zero remaining duplicate argparse registrations.

Strategic

  • License re-anchor was driven by long-term enterprise positioning (Linux/Ubuntu/Red Hat playbook): copyleft prevents proprietary fork-and-close by Microsoft / Salesforce / Oracle, while GPL v3 Section 11 patent grant + defensive termination preserves enterprise adoption optics. Trade-off accepted: SaaS resellers must contribute back; some hyperscaler co-selling motions become harder. Net: better foundation for paid-services + commercial-license dual-track at scale.

Trust root + signing

  • Registry signing key unchanged. Fingerprint remains d471:335b:0e4d:75ce.
  • Manifests regenerated and re-signed (registry_version 8 → 18) to capture the new LICENSE.txt hash and the four fixed db_query.py files.

Notes

  • Tested on OpenClaw 2026.5.7 with ClawHub CLI v0.12.3. Foundation passes the full 6-gate pipeline (270 L0 + 3,088 L2 + 248 L3 tests + invariants + smoke).
  • NL routing validated end-to-end on the OpenClaw test server: foundation 5/5 scenarios (companies, customers, invoices, employees, items), three verticals 3/3 (healthclaw patient, educlaw student, constructclaw job).
  • The license re-anchor was backed by a lawyer-level internal analysis.

[4.1.6] — 2026-05-04

Adds an ed25519 signature on the foundation registry. Reconciliation verifies the signature against an embedded public key before trusting any file hash, refuses tampered or downgraded registries, and refuses unsigned registries entirely. Closes the v4.1.5 supply-chain finding by giving the integrity check a cryptographic trust root rather than a hash-only one.

Added

  • ed25519 signature on module_registry.json. Public key embedded in erpclaw_lib/signing.py::TRUSTED_KEYS with a NamedTuple-based key list that supports rotation. Initial signer fingerprint: d471:335b:0e4d:75ce (label erpclaw-foundation-signer-2026-05-04). Verify locally with erpclaw verify-trust-root.
  • New foundation action verify-trust-root that prints the embedded key fingerprint(s). Use this to compare against the published fingerprint on erpclaw.ai before trusting a reconciliation.
  • Strict-mode registry loader. update-foundation refuses to proceed unless the registry is freshly signed by a trusted key. The lenient loader used for read-only listings (available-modules, search-modules) emits a stderr warning when the signature cannot be verified, but does not block.
  • Monotonic registry_version field inside the signed payload. The verifier rejects any registry whose registry_version is lower than the locally tracked value, defending against replay/downgrade of older legitimately-signed registries.
  • Append-only signing log at scripts/signing_log.txt recording each signing event (timestamp, hash prefix, version, signer fingerprint). Allows after-the-fact detection of unauthorized signing.
  • Recovery path --unsafe-trust-bundled for update-foundation. When a published key has been revoked and no rotated key has yet reached an offline install, an operator can reconcile against the locally-bundled hashes only. The flag emits a stderr warning, writes an entry to the audit log, and is documented as a recovery-only operator action; ordinary reconciliation always requires a verified signature.
  • Atomic publish pipeline: new entry-point release/regen_and_sign.py runs manifest regeneration + signing in one invocation and verifies registry mtime ≤ signature mtime before completion.

Changed

  • Foundation action count: 477 → 478 (added verify-trust-root).
  • _load_registry now returns the registry with _signed_by (fingerprint) on successful verification, or _signature_warning (string) when the lenient path falls back to unsigned content. The strict variant _load_registry_strict raises on any verification failure with no fallback to unsigned content.

Trust root rotation

Rotation is one of the few legitimate triggers for a future ClawHub re-publish, because every install ships with the embedded key list. Rotation procedure: ship the new key alongside the existing one with a valid_until for the old key, allow a grace period, then ship the new key alone. Stale installs that never reconcile remain locked to their original key list. Out-of-band fingerprint verification via erpclaw verify-trust-root and the published fingerprint on erpclaw.ai is recommended before trusting any rotation.

Notes

  • v4.1.5 → v4.1.6 transition: the first reconcile that crosses this boundary establishes signing on the install. Subsequent reconciles verify.
  • Long-running processes hold imported modules in memory; foundation file changes take effect on next launch.

Plan + audit

  • Backed by an internal fix plan and audit (8 BLOCK + 7 SHOULD items adopted from external + internal audits).

[4.1.5] — 2026-05-04

Foundation manifest reconciliation. Bundles the v4.1.4 runtime gate extension with two new gated actions that let an administrator align installed foundation files with the published module_registry.json manifest. Future foundation updates apply via these actions on explicit user invocation.

Added

  • Foundation actions update-foundation and rollback-foundation (gated, require --user-confirmed). The first compares each installed file against the manifest's files_sha256 map, and for drifting files re-fetches from the published source and re-verifies the declared SHA256 before atomic replacement. A pre-flight verifies all replacements before any rename, so a hash failure leaves the install unchanged. Replaced files are preserved as .bak for one cycle.
  • A non-blocking convenience check in the foundation router that surfaces a reminder on stderr when manifest-version drift is present, no more than once per 24-hour window per install. The check does not modify files; the user invokes update-foundation to apply. Suppressed by the marker ~/.openclaw/erpclaw/.skip_reconcile or the per-invocation flag --no-reconcile-check. Recursion-guarded for foundation-touching actions (update-foundation, rollback-foundation, install-module, remove-module, update-modules, schema-apply, schema-rollback).
  • fcntl.flock on ~/.openclaw/erpclaw/.sync.lock serializes reconciliation so the one-cycle .bak is never corrupted by concurrent invocation.
  • A safety guard refuses reconciliation when running inside a git-tracked source tree (developer checkout); the mechanism targets ClawHub-installed deployments only.

Changed

  • Foundation action count: 475 → 477 (added the two reconciliation actions).
  • _strip_router_flags continues to strip --user-confirmed before forwarding to domain scripts; the foundation router gate is the single source of truth for confirmation.

Integrity model

Reconciliation verifies each file against the SHA256 declared in the published manifest before atomic replacement. Cryptographic signing of the manifest itself is roadmap for v4.2.0. Operators preferring not to use the reconciliation path can place the opt-out marker ~/.openclaw/erpclaw/.skip_reconcile.

Notes

  • Long-running processes (MCP servers, daemons) hold imported modules in memory; foundation file changes take effect on next launch.

[4.1.4] — 2026-05-04

Closes the v4.1.3 OpenClaw Tool Misuse Concern by extending the runtime gate to administrative actions beyond financial postings.

Changed

  • DANGEROUS_ACTIONS frozenset extended with 11 entries spanning RBAC + identity changes (add-role, assign-role, revoke-role, seed-permissions, update-user, set-password), credential lifecycle (set-credential, delete-credential, migrate-credentials, import-master-key-from-backup), and account-state (unfreeze-account). All require --user-confirmed on every invocation.
  • Foundation SKILL.md ## Runtime gate paragraph reworded to describe high-impact actions broadly without enumerating action names. Catalog and frozenset are the source of truth.
  • Gate-rejection error message generalized: now says "is a high-impact action" instead of "materially changes financial or system state".

Fixed

  • Stale comment in db_query.py referenced a removed environment-variable bypass; cleaned up.

[4.1.3] — 2026-05-04

Cross-machine backup restore + Tier A regression fix-ups discovered during v4.1.x test-plan execution.

Added

  • New foundation action import-master-key-from-backup. Required for cross-machine restore: a backup taken on Machine A is now restorable on Machine B with full encrypted-column readability. The backup's ECRYPT02 header carries a passphrase-wrapped copy of the column-encryption master key; this action unwraps it and installs at ~/.config/erpclaw/master.key. Refuses to overwrite an existing master key without --force. Passphrase via --passphrase, --passphrase-from-stdin, or --passphrase-from-env.

Changed

  • backup-database --encrypt: now wraps the current machine's column-encryption master key with the backup passphrase and embeds it in the ECRYPT02 header. Backups taken without a master key (no encrypted columns yet) work as before. Response now includes carries_master_key: bool to indicate whether cross-machine restore is supported for this backup.
  • Foundation action count: 474 → 475 (added import-master-key-from-backup).

Fixed

  • Foundation SKILL.md catalog now lists the 5 credential-management actions (set-credential, get-credential, list-credentials, delete-credential, migrate-credentials) added in v4.1.0, plus 2 module-discovery actions (list-articles, build-table-registry) that were previously implemented but undocumented. L0 test_skillmd_action_completeness was failing on this drift; now passes.
  • test_nacha_ach.py::TestAddEmployeeBankAccount::test_basic_add updated to decrypt encrypted columns before asserting plaintext (regression caused by v4.1.0 column encryption).

Notes

  • No code logic changes; only documentation alignment + one test fixture update.
  • 3 pre-existing erpclaw-os-engine constitution failures (Article 5 cross-module write violations + addon SKILL.md drift) deferred to the vertical addon cross-test tier.

[4.1.2] — 2026-05-04

Made the v4.1.0 runtime gate's enforcement visible in SKILL.md so static-analysis review correctly attributes write actions to a gated context.

Added

  • New ## Runtime gate section in foundation SKILL.md (8 lines, immediately before the action catalog) describing the per-invocation flag requirement and the router's pre-dispatch rejection of unflagged calls.

Notes

  • No code logic changes. The v4.1.0+ runtime gate is unchanged.
  • Phase 2 audit reviewed the proposed text; revised to drop verb-enumeration and env-var-bypass wording that would have re-summoned previous trigger phrases.

[4.1.1] — 2026-05-04

Tightened v4.1.0 security posture in response to OpenClaw rescan feedback.

Changed

  • Hardened the runtime confirmation gate to require explicit per-invocation flag; removed an environment-variable form that could globalize confirmation across processes.
  • Module install now verifies the full file tree against the foundation registry, not only the manifest entry-point.
  • Trimmed user-facing security claims to neutral wording matching implementation. Mechanism specifics live in code, not in marketing prose.

Removed

  • Environment-variable bypass for the runtime confirmation gate. Per-invocation flag is the only path.

Migration

  • Cron / CI users that relied on the removed env var: switch to per-invocation flag. The gate's error message indicates the required flag.

Roadmap

  • v4.2.0 will add cryptographic signature verification (sigstore/cosign) on top of the file-tree integrity manifest, plus an approve-pending queue for sanctioned automation.

[4.1.0] — 2026-05-04

Comprehensive security modernization. Real architectural changes: audited crypto, file-based credential management, column-level encryption, runtime confirmation gate for high-impact actions, supply-chain integrity verification, lib bootstrap removal. No legacy --api-key flag.

Added

  • Column-level encryption for selected sensitive fields (employee SSN, bank routing/account numbers). New helper erpclaw_lib.encrypted_columns.
  • Encrypted credential store with per-machine master key, accessed via foundation actions: set-credential, get-credential (returns redacted), list-credentials, delete-credential, migrate-credentials. Library: erpclaw_lib.credentials.
  • Runtime confirmation gate for high-impact financial-mutation actions. High-impact actions require explicit per-invocation confirmation; routed through the foundation gate before dispatch.
  • Module integrity verification in module_registry.json. module_manager.py install-module verifies content integrity against the foundation registry.
  • AES-256-GCM streaming backup format ECRYPT02 with 1 MiB plaintext frames, per-frame nonces, and an embedded passphrase-wrapped master key for cross-machine restore. Files of any size supported (no in-memory load).

Changed

  • erpclaw_lib/crypto.py rewritten to use the cryptography library (OpenSSL via cffi). PBKDF2-HMAC-SHA256 at 600,000 iterations (OWASP 2024). Field-level encryption uses raw AES-256-GCM with enc:v2: prefix. Legacy ECRYPT01 decrypt path retained for v4.0.x backups.
  • Stripe addon: hard-removed --api-key flag. erpclaw-integrations-stripe v2.0.1 reads credentials from the foundation credential store. Users migrate via erpclaw migrate-credentials (one-time read-from-DB → write-to-encrypted-store) or set fresh via erpclaw set-credential --integration stripe --from-stdin.
  • Lib bootstrap removed. _bootstrap.py deleted. ~/.openclaw/erpclaw/lib is now a symlink (created at initialize-database) to the skill-bundled location, not a self-healing copy. Eliminates the "self-modifying code at runtime" finding.
  • Foundation SKILL.md disclosure cleanup. Removed 4 v4.0.1 paragraphs (Credential handling, Data protection, Module installation safety, Library self-heal) that were reading as scanner trigger surfaces while describing addon behavior or implementation details. Kept only the factual one-line Security summary.

Removed

  • --api-key flag (Stripe addon). Use set-credential instead.
  • _bootstrap.py (erpclaw_lib._bootstrap). Lib symlink replaces self-heal.
  • install_shared_library foundation function. Symlink replaces.
  • Homemade HMAC-SHA256-CTR cipher in crypto.py. Replaced with cryptography library AES-256-GCM.

Fixed

  • Registry foundation entry was stale at version: 3.5.0 / has_init_db: false / action_count: 438. Now reflects current state: version: 4.1.0 / has_init_db: true / action_count: 467.

Migration notes

  • Existing v4.0.x backups remain decryptable (legacy ECRYPT01 path retained).
  • Existing plaintext column rows pass through decrypt_for_column unchanged; encrypted-on-write applies to new rows only. No mandatory data migration.
  • Stripe users who previously stored API keys via --api-key: run erpclaw migrate-credentials once after upgrade to move keys from DB plaintext to the encrypted credential store.
  • Cron/agent/CI users: append --user-confirmed to high-impact action invocations.

[4.0.2] — 2026-05-04

Eliminate the Rogue Agents (cron) concern from the ClawHub OpenClaw review by removing decorative cron: blocks from foundation and grouped-addons SKILL.md files.

Why

Phase 2 audit verification (B1) discovered that OpenClaw's runtime cron daemon does NOT auto-discover SKILL.md cron: blocks. Active scheduling requires explicit openclaw cron add CLI commands. The cron: block in foundation SKILL.md was therefore decorative metadata, not active scheduling — but the ClawHub static analyzer was reading it as scheduled financial mutation and flagging it as HIGH/Concern.

Removing the decorative blocks eliminates the trigger at the source without changing operational behavior (no user has ever had ERPClaw crons running automatically from clawhub install; users wanting daily jobs always had to run openclaw cron add manually).

Changed

  • Foundation SKILL.md: removed the entire cron: block (4 entries: process-recurring, generate-recurring-invoices, check-reorder, check-overdue). Replaced "Background automation" prose section with "Optional scheduling" pointer to openclaw cron add for users who want daily jobs.
  • erpclaw-growth SKILL.md (grouped addon): removed 1 cron entry (weekly anomaly detection sweep).
  • erpclaw-ops SKILL.md (grouped addon): removed 3 cron entries (monthly depreciation, daily overdue issues, weekly SLA compliance).
  • Library self-heal disclosure kept as standalone subsection (was bundled with cron prose in v4.0.1).
  • Foundation version: 4.0.1 → 4.0.2.
  • erpclaw_lib/__version__ = "4.0.2".
  • module_registry.json top-level version: "4.0.2".

Notes

  • No code paths read SKILL.md cron blocks. Verified via grep across the source tree and internal tooling — zero hits for cron-block consumption.
  • All 4 daily action targets (process-recurring, generate-recurring-invoices, check-reorder, check-overdue) remain in foundation as on-demand callable actions. No capability removed.
  • Users who want automatic daily runs use openclaw cron add --name <id> --cron "<expr>" --message "Using erpclaw, run the <action> action." — the same path that was always required for actual scheduling.

Migration arc

Version Cron in foundation SKILL.md Cron actually running on install
v3.5.1 4 entries, announce: false NO (decorative only)
v4.0.0 4 entries, announce: false NO
v4.0.1 4 entries, announce: true NO
v4.0.2 none NO (unchanged — same as all prior)

The "migration" is operationally a no-op. Only the SKILL.md text changed.

[4.0.1] — 2026-05-04

Security patch responding to ClawHub OpenClaw v4.0.0 review findings. Documentation, defaults, and disclosure changes; no schema changes, no new actions.

Changed

  • Cron jobs now announce. All four daily background jobs (process-recurring, generate-recurring-invoices, check-reorder, check-overdue) flipped from announce: false to announce: true. Each run is now visible in the OpenClaw activity feed. (Resolves OpenClaw v4.0.0 finding "Rogue Agents" HIGH/Concern.)
  • README scrub. Removed "Self-Extending ERP" section from foundation README. Module-generation prose lives in the optional erpclaw-os-engine addon README only. (Resolves OpenClaw v4.0.0 finding "Unexpected Code Execution" MEDIUM/Concern.)
  • Foundation SKILL.md actions table no longer advertises the ~30 module-authoring / variant-analysis / heartbeat / semantic-check actions that moved to erpclaw-os-engine in v4.0.0. Replaced with a single pointer to the optional addon. Removed generate-module and deploy-module from the "always confirm" list (now addon-only).
  • Addon SKILL.md description reworded to lead with developer-tooling framing and explicit sandbox-first / user-approval-before-deploy disclosure.

Added

  • Credential handling, Data protection, Module installation safety paragraphs in foundation SKILL.md security section. Mirror the OpenClaw recommendations for its Note-level findings.
  • Background automation section in foundation SKILL.md documenting the four cron jobs and the lib bootstrap self-heal behavior.
  • chmod 600 on data.sqlite, data.sqlite-wal, data.sqlite-shm — applied at initialize-database, after restore-database, after backup-database, AND on every foundation action invocation. Backup outputs are also chmod 600. New helper chmod_db_files() in erpclaw-setup/db_query.py.
  • Lib bootstrap self-heal (erpclaw_lib/_bootstrap.py). On every foundation action invocation, the router compares the bundled erpclaw_lib.__version__ to a marker file at ~/.openclaw/erpclaw/lib/.erpclaw_lib_version. On mismatch, it re-syncs the deployed erpclaw_lib/ from the bundled source, writes a new marker, and appends an entry to ~/.openclaw/erpclaw/logs/bootstrap.log. Eliminates the v3.5.1 → v4.0.0 upgrade gotcha where clawhub update skipped the foundation post-install hook and addon sandbox.py couldn't find the new gl_invariants.py. Honors ERPCLAW_DISABLE_BOOTSTRAP=1 env var as an explicit opt-out.
  • __version__ = "4.0.1" declared in erpclaw_lib/__init__.py as the canonical lib version.

Fixed

  • restore-database now explicitly chmods the restored DB file to 0o600 (previously inherited mode from the backup source via shutil.copy2).

Notes

  • The 3 Note-level OpenClaw findings (Agentic Supply Chain, Identity & Privilege Abuse, Memory and Context Poisoning) remain at Note status by design — they describe disclosed-and-accepted ERP behavior, and the recommendations are user-side practices (use scope-limited keys, rotate credentials, restrict file permissions, test imports against a separate DB). v4.0.1 mirrors those recommendations in the SKILL.md security section.
  • Keychain integration, SQLite encryption-at-rest, and module signature verification remain on the v4.1+ roadmap.

[4.0.0] — 2026-05-04

Architectural split. ClawHub static-analysis CRITs eliminated. clawhub install erpclaw works without --force.

Changed

  • Foundation / addon split. 21 dev-time files (generate_module.py, in_module_generator.py, sandbox.py, etc.) moved out of foundation to a new optional addon, erpclaw-os-engine v1.0.0, distributed via GitHub-only at avansaber/erpclaw-addons subdir erpclaw-os-engine. The addon trips the same scanner the foundation used to trip; isolating it keeps foundation users scan-clean.
  • os- prefix on 28 user-facing actions that moved to the addon (os-generate-module, os-deploy-module, etc.). Foundation router emits a structured migration error JSON for legacy bare-name calls.
  • gl_invariant_checker extracted to erpclaw_lib.gl_invariants for cross-package import. Foundation runtime keeps the GL invariant validation; addon's sandbox imports from erpclaw_lib.gl_invariants.
  • Foundation scripts/erpclaw-os/ retains 7 runtime actions only: validate-module, list-articles, build-table-registry, schema-{plan,apply,rollback,drift}.

Added

  • erpclaw-os-engine SKILL.md, db_query.py, web_dashboard.py, sandbox.py — addon entry points.
  • Foundation-locator pattern in addon's db_query.py (3-candidate sys.path resolution: env, prod, repo-relative).

Notes

  • ClawHub release id: k974yxfap664grmdnxfsstnhy5863wfa.