Skip to content

Releases: gulmezeren2-byte/erp-report-engine

v0.7.0 · The read-only agent layer — measured, not asserted

Choose a tag to compare

@gulmezeren2-byte gulmezeren2-byte released this 18 Jul 06:16

A verifiably-safe, read-only access layer for the SQL database behind an ERP — for AI agents as much as for weekly reports.

This release is a repositioning the tool had already grown into, plus the work to make its core claim checkable rather than merely asserted.

The headline: "read-only" is now a number, not an adjective

The trust benchmark runs 28 well-formed-SQL attacks (including the two most-cited MCP database failures by name — the Postgres COMMIT; DROP SCHEMA public CASCADE; transaction escape and the Supabase lethal-trifecta write leg) plus the legitimate reads that must still pass. New this release: it runs that same corpus through the shortcuts real tools ship, so you can see the gap instead of taking anyone's word:

Guard Attacks refused Legit reads allowed
starts-with-SELECT check 6 / 28 8 / 8
write-keyword blocklist 9 / 28 7 / 8 (blocks a read whose string contains "delete")
this guard 28 / 28 8 / 8

Every number is computed from a live guard run — reproduce it with erp-report-engine trust-benchmark, or paste your own SQL into the in-browser playground (the real guard.py via Pyodide, nothing sent anywhere).

Also in 0.7.0

  • The guard is fuzzed — hypothesis dresses every denylisted function up thousands of ways (case, whitespace, arguments, four dialects) and asserts it is still refused; a plain read with arbitrary identifiers still passes.
  • CodeQL scans the code itself on every push (it caught a real ReDoS in the benchmark's own code during this cycle — now fixed).
  • describe_model is a real semantic layer — every entity's grain, every column's type and meaning, and runnable example queries.
  • The guard moved to its own dependency-light module (guard.py) so the browser playground loads the genuine code.
  • On-time % is a p-chart (a proportion with limits that widen when a week is thin), not an XmR chart.
  • Automatic listing on the official MCP registry on release via GitHub OIDC — no stored token.
  • Security case-study, a fair five-way comparison of read-only DB access for agents, and a landing hub.

Links

Install

pipx install erp-report-engine        # or: pip install "erp-report-engine[mcp]"
erp-report-engine trust-benchmark     # reproduce the number above

v0.6.0 — Receivables, a narrative that can't lie, and a guard that keeps its word

Choose a tag to compare

@gulmezeren2-byte gulmezeren2-byte released this 17 Jul 12:25

First release on PyPI: pipx install erp-report-engine

The headline of this release is not the new features. A five-way audit of this repository found the honesty discipline holding in the Python core and not travelling to the newer surfaces — and for a project whose entire claim is measurement honesty, a place where it says more than it knows is not an ordinary bug. Everything under Fixed is one of those places.

Added

  • Receivables aging (cari yaşlandırma) as an optional canonical entity — current / 1-30 / 31-60 / 61-90 / 91+, the overdue share, and who owes the most. A profile that can't reach an AR ledger omits it and everything downstream degrades gracefully.
  • Real-ERP AR mappings for Logo Tiger (PAYTRANS), Netsis (TBLCAHAR) and Mikro (CARI_HESAP_HAREKETLERI) — each with its weak points flagged inline rather than smoothed over.
  • Mikro profile — the third Turkish ERP. With Logo Tiger and Netsis, the bundled profiles now cover most of the Turkish SME ERP market.
  • Optional LLM narrative (run --narrate) built only from audited aggregates, with the exact payload printed in the report as a "what the model saw" appendix. Works with any OpenAI-compatible endpoint, including a local keyless one.
  • Revenue concentration — top-3 share + Herfindahl index (HHI), across all three surfaces.
  • Agent skill pack + a sixth MCP tool (aging).
  • Power BI — a receivables Aging page, a dark theme validated against Microsoft's official theme schema (0 errors), DAX SVG micro-charts, and the SPC control band.
  • Packaging — PyPI Trusted Publishing (OIDC, no stored token) and a lean non-root Docker image.

Fixed — where this project said more than it knew

  • "Read-only by construction" did not hold at the guard level. The guard checked a statement's shape and never asked what it called, so pg_read_file, lo_export (which writes a file), dblink (which dials out), OPENROWSET, LOAD_FILE, load_extension, query_to_xml, SLEEP — and worst, set_config('default_transaction_read_only','off') — all passed. It was read-only by configuration: it held because the docs tell you to use a least-privilege login. Functions are now checked by AST and lexically (OPENROWSET is precisely what sqlglot cannot parse), the parser fails closed rather than waving through what it cannot read, and agent SQL runs in a strict mode that default-denies every function the guard cannot name. Pinned by name and per dialect in tests/test_guard.py.
  • Power BI plotted the current partial week — the exact thing the README promises never happens. The trend visuals now carry a locked filter built from the engine's own window constant, shipped as data.
  • The agent could read any table the login could reach. "The agent talks to orders, never LG_001_01_ORFICHE" was true of every tool except query, which passed raw SQL through — so the semantic layer, the entire product, was optional. It isn't now.
  • A duplicated item_code crashed the whole run. The three real profiles GROUP BY and hid it; generic.yaml doesn't — the "swap the profile, keep the report" path.
  • Stock cover was overstated on short history, suppressing the low-stock alert on exactly the first run, when a new deployment has the least history.
  • On-time % can rise as fulfilment collapses — a late, unshipped order is in neither the numerator nor the denominator. The engine now counts what the percentage cannot see.
  • Attribution could claim 999% of a move, exactly when attribution mattered most.
  • Every SPC signal was labelled provisional, forever — the limits promised to stabilise at a threshold the architecture forbade reaching.
  • Power BI had no SPC at all, and its alert layer flagged the ordinary variation the method exists to ignore.
  • The dashboard claimed a validated palette while using brightened approximations of it — two of its colours were below the normal-vision ΔE floor.
  • The narrative's "aggregates only" understated what left the building — an aggregate can still name a party. Names are pseudonymised by default now.
  • The 90+ aging bucket actually held 91+; MySQL had no read-only session or statement timeout; the credential check missed ?passwd= and ?sslpassword=; and SELECT 'please delete this note' was refused because the keyword scan read string literals as code.

Changed

  • report.lookback_weeks now defaults to 26 (was 13). The chart still shows 13; the extra history is what the control limits are computed from, and they only settle around n≥15. The demo already generated 26 weeks — half of it was being discarded.

91 → 155 tests. CI green on Linux and Windows (3.10–3.13), with a coverage floor.

Full detail: CHANGELOG.md · Security model: SECURITY.md

v0.5.0 - Signals, contracts, delivery, a plural profile library

Choose a tag to compare

@gulmezeren2-byte gulmezeren2-byte released this 16 Jul 08:27

erp-report-engine v0.5.0 — "Signals, contracts, delivery, a plural profile library"

Building on the v0.4.0 foundation, this release deepens the measurement-honesty moat and makes the profile library plural.

SPC / XmR anomaly layer — "signal vs noise, with receipts"

Weekly KPIs now get an individuals control chart. A flagged point is a genuine shift beyond week-to-week variation — and every signal shows its arithmetic so you can check it with a calculator:

Revenue signal: 148,291 is ABOVE the control limits (UCL 143,078 = mean 93,168 ± 2.66 × avg moving range 18,763) — a real shift beyond week-to-week noise.

Deterministic, no black box (the same statistics NHS England board reporting and Grafana use). Provisional limits are labelled while the baseline is short; a flat or steady series stays quiet.

Native delivery — the report delivers itself

run --send emails the report (SMTP), posts a summary to Slack or Teams (Power Automate Workflows), and pings a healthchecks.io dead-man's-switch on success or failure — so a silent cron is detectable. Every secret from an environment variable; a failed channel is logged, never fatal. The feature most BI tools charge for.

Declarative profile contracts

A profile can carry a contract: block — not_null, unique, accepted_values, relationships, min_rows — checked over the extracted data and reported in the quality gate. severity: fail trips run --strict. dbt-test-style, zero new dependencies.

The Netsis profile — the library is plural

profile: netsis maps Logo Netsis 3 (MSSQL, database-per-company) — sales orders from TBLSIPAMAS/TBLSIPATRA, customers from TBLCASABIT, stock from TBLSTOKPH — field-mapped from real production integrations, with the uncertain fields flagged inline to verify per install. With Logo Tiger, the bundled profiles now cover most of the Turkish SME ERP market (both MSSQL).

Also

A live sample report now renders via GitHub Pages: https://gulmezeren2-byte.github.io/erp-report-engine/

Full detail in CHANGELOG.md.

v0.4.0 - Installable, correct, and agent-ready

Choose a tag to compare

@gulmezeren2-byte gulmezeren2-byte released this 16 Jul 07:49

erp-report-engine v0.4.0 — "Installable, correct, and agent-ready"

A big step from polished demo to business-grade tool. Highlights:

The guarded ERP MCP server (new)

An AI agent can now query the ERP through the same three-layer read-only guard and audit trail as the report — talking to canonical entities (orders, never LG_001_01_ORFICHE), with every result framed as untrusted input. Five tools: describe_model, weekly_report, reconcile, check_query, query.

pipx install "erp-report-engine[mcp]"
erp-report-engine mcp -c config.yaml

As far as we can find, the first SQL-level-guarded ERP MCP server, and the first for Logo Tiger.

Now a proper package

pipx install erp-report-engine / uvx erp-report-engine — a real console command, profiles shipped inside the wheel, extras for [mssql] / [postgres] / [mcp].

Correctness & security fixes

  • Calendar anchor: "this week" is the last completed ISO week by the calendar (from the DB server's date), so a Monday-morning run no longer reports a stale week; empty weeks and W53 handled.
  • Stored XSS fixed: the report renders through Jinja2 autoescape.
  • Read-only guard hardened with a sqlglot AST layer (catches writes hidden in CTEs, lock hints) and read-only database sessions.
  • Data-leak & credential footguns closed: exports default to a gitignored folder; embedded credentials rejected in every URL shape.
  • Duplicate handling unified across the HTML and Power BI surfaces; on-time survivorship, driver attribution, stock-out flagging and the decline-streak all fixed and disclosed.

Robustness & DX

Exit-code taxonomy (a scheduler can branch on why a run failed), --strict, structured logging, retries, atomic writes. A runner facade shared by the CLI and MCP server. Property-based calendar tests; CI installs the package, runs ruff, and fails on PBIR generator drift, on Python 3.10–3.13.

Full detail in CHANGELOG.md.

v0.2.0 - The command center

Choose a tag to compare

@gulmezeren2-byte gulmezeren2-byte released this 14 Jul 13:02

The engine grows its second surface: an interactive Power BI Command Center, authored entirely as code (no .pbix binary in the repo).

  • export-powerbi: star-schema CSVs (facts, dimensions, and the honesty tables - reconciliation, data-quality findings, the full SQL audit trail) through the same guarded read-only path
  • TMDL semantic model: 20+ documented DAX measures whose alert thresholds mirror insights.py exactly - one definition, two surfaces; a Time Shift calculation group on a gapless week ordinal (year-boundary safe); a Selected KPI field parameter; DataFolder parameter so no absolute path is baked in
  • PBIR report: 4 pages / 24 visuals generated from compact specs by powerbi/tools/generate_report_pages.py; the signature Trust page renders the SQL receipts as visuals
  • Custom "Measurement Honesty" theme
  • Validation before Desktop ever opens it: official JSON schemas + field bindings checked against the loaded TMDL model (41 fields, 0 errors), visual overlap detection, theme resolution, 15/15 tests

Quickstart: python -m erp_report_engine export-powerbi -c config.demo.yaml, then open powerbi/ERP Command Center.pbip in Power BI Desktop, set the DataFolder parameter, refresh.

v0.1.0 - Read-only by construction

Choose a tag to compare

@gulmezeren2-byte gulmezeren2-byte released this 14 Jul 12:23

First public release.

erp-report-engine produces autonomous weekly HTML reports straight from the SQL database behind an ERP — with the security model as the headline feature:

  • Read-only by construction: one guarded code path to the database; single-statement SELECT/WITH only; comments, EXEC, SELECT INTO and 14 write/DDL keywords rejected before any connection. 8 injection attempts covered by tests.
  • Full SQL audit trail shipped inside every report (statement, parameters, rows, timing).
  • Semantic profiles: YAML contracts mapping ERP schemas to canonical entities — generic (demo) and logo_tiger (Logo Tiger / GO on MSSQL) included.
  • Self-auditing extraction: data-quality gate (duplicates, bad dates, negative totals, ship-before-order) + independent COUNT(*) source reconciliation.
  • Honest KPIs: ISO-week revenue / orders / on-time% / stock-cover vs an 8-week baseline; the current partial week is never plotted.
  • Driver-attributed findings and run-state memory ("3rd consecutive weekly decline").
  • Secrets via environment variables only — the loader refuses config files with embedded passwords.

Quickstart (no ERP needed): python -m erp_report_engine init-demo && python -m erp_report_engine run -c config.demo.yaml