Skip to content

Architecture

Bird's-eye View

BXP is a single-binary ETL tool. All source-specific logic lives in a JSON5 config file - the binary is a generic engine. The diagram below shows the high-level relationship between components. It is a high-level topology (who talks to whom); the individual bxp-core modules and their internal dependencies are detailed in the bxp-core modules table and the Expression Evaluator — Call Stack diagram.

graph TD
    subgraph Actors["User / CI · AI agent"]
        CFG["bxp-cli.json<br/>(JSON5 templates)"]
        DATA["Input files<br/>(.csv / .xlsx / .json)"]
        AGENT["AI agent<br/>(MCP host)"]
    end

    subgraph GUI["bxp-gui (Flutter desktop)"]
        GUIMCP["gui-mcp (GuiMcpServer)<br/>drives the live store"]
        STORE["TraceStore + UI<br/>services / store / ui"]
    end

    subgraph MCP["bxp-mcp (JSON-RPC / stdio)"]
        MCPSRV["server + tools<br/>stateless tools + bxp_simulate"]
    end

    subgraph BRIDGE["bxp-gui-bridge (Zig FFI — single GUI backend)"]
        BEVAL["bridge_eval_* / bridge_inspect<br/>in-proc inspect / eval"]
        BRUN["bridge_run(_streaming)<br/>bxp-cli subprocess proxy"]
        BSIG["bridge_verify_minisign"]
    end

    subgraph CLI["bxp-cli (engine binary)"]
        MAIN["main.zig — args / dispatch"]
        PIPE["pipeline.zig — processBroker()"]
    end

    subgraph Core["bxp-core (library)"]
        INSPECT["inspect.zig<br/>stateless facade<br/>validate · eval · docs · templates"]
        ENGINE["engine modules<br/>csvstream · xlsx · json · json5 · expr<br/>datefmt · decimal · btrace · config · docs · diagnostics"]
    end

    OUT[".csvx output"]

    AGENT -->|JSON-RPC / stdio| MCPSRV
    AGENT -->|MCP / localhost HTTP| GUIMCP
    CFG -->|read| MAIN
    DATA -->|read| PIPE

    GUIMCP --> STORE
    STORE -->|dart:ffi| BRIDGE
    BEVAL -.links.-> INSPECT
    BRUN -->|spawns| CLI

    MCPSRV -.links.-> INSPECT
    MCPSRV -->|bxp_simulate spawns| CLI

    MAIN --> PIPE
    PIPE -->|parse · evaluate| ENGINE
    PIPE -->|write| OUT
    INSPECT --> ENGINE

bxp-gui-bridge is the FFI shim the GUI loads via dart:ffi at startup. It is the GUI's single backend on every platform — there is no bxp-fmt spawn and no Process.start route. Three roles in one shared library: (1) the subprocess proxy (bridge_run / bridge_run_streaming) wraps the bxp-cli runs the GUI needs (dry-run / full-run --trace=bin, --version) from native code, sidestepping dart-lang/sdk#1727 (~8 KB stdout cutoff that kills --trace); (2) the in-proc inspect / eval family (bridge_eval_expr / bridge_eval_expr_trace / bridge_inspect) links bxp-core/inspect directly, so the editor's live validation, the ExprPlayground, and the docs / config / template ops avoid the ~50 ms subprocess spawn cost; (3) bridge_verify_minisign checks the release SHA256SUMS signature for the auto-updater. Library probe failure at startup is fatal on all platforms — a missing library means a broken install.

For the per-call transport matrix (which GUI calls use which transport on each OS, plus the two-cause "why" behind the split), see internals's "Why the bridge exists" + "Per-call routing" section. The bridge's C-ABI surface and Debug→ReleaseSafe build rationale live in bxp-gui-bridge/CLAUDE.md.

The engine modules node groups two faces of bxp-core: the conversion engine (csvstream / xlsx / json / json5 / expr / datefmt / decimal / btrace, driven by bxp-cli's pipeline) and the support modules behind the inspect facade. datefmt, tz, encoding, json5, decimal, numparse, zipstream, csvstream and diagnostics are drawn here as engine modules because that is how the engine uses them, but none of them is in this tree any more — the whole primitive layer comes from the pinned zig_libs fetch dependency.

docs.zig aggregates the language/schema catalog — re-exporting expr.builtins (the FnDoc catalog) and flattening each config.zig struct's fields[] table — so adding a built-in or config field updates the docs automatically. Config validation (inspect.annotateRaw) runs json5.preprocessAnnotated to emit the $err_* / $warn_* / $info_* siblings the GUI renders; comments are stripped there as in the plain preprocessor, so keeping them is json5_ast's job on the Dart side.