CX · Reference guideData tooling
PlaygroundDownloadsAboutv0.17.0

Data tooling

Every verb on this page runs against documents and needs nothing above Ring 0: no evaluator, no capabilities, no server. They are the daily drivers of the data-format adopter — convert, format, canonicalize, hash, compare, validate, query, and diff — and each is exit-code-honest so it slots straight into CI. The verbs that execute programs live with Ring 1 (code-tooling); the full subcommand catalog is in tooling.

Projection and conversion flags

Format projection is a top-level flag surface, not a subcommand. One flag picks the output projection of an input document; --from / --to drive explicit conversions:

      $ cx --ast config.cx                 # JSON AST
         $ cx --json config.cx                # JSON projection
         $ cx --yaml config.cx                # YAML projection
         $ cx --xml --compact config.cx       # XML, minimised
         $ cx --from=md --to=cx README.md     # Markdown in, CX out
         $ cx --from=cx --to=json --lossless order.cx
         $ echo '[hello world]' | cx --json -  # stdin
    
  • Projection flags: --ast --cx --xml --json --yaml --toml --md --csv --tsv --psv, each optionally with --compact.
  • --from=cx|xml|json|yaml|toml|md|csv|tsv|psv --to=cx|xml|json|yaml|toml|md|csv|tsv|psv — explicit conversion; --lossless makes XML carry per-value types (<cx:T>) for an exact round-trip.
  • --include-root=DIR — resolve [?cx include=…] against DIR before projecting (include resolution is opt-in; without a root the directive is preserved).

cx fmt

Pretty-prints in lossless canonical form. Comments, anchors, and authorial structure are preserved; whitespace and quoting are normalised. Reads the file argument (or stdin) and writes the formatted document to stdout.

      $ cx fmt config.cx                    # formatted, to stdout
         $ cx fmt config.cx > config.fmt.cx && mv config.fmt.cx config.cx
    

cx canonical / cx hash / cx eq

cx canonical emits the strict canonical text per spec/core/canonical.md: comments stripped, attributes sorted, anchors expanded, whitespace collapsed, quoting normalised. Two data-equivalent inputs produce identical output — that is the contract cx hash (SHA-256 of those bytes) and cx eq (exit 0 iff the strict-canonical forms match, 1 if they differ, 2 on error) build on.

      $ cx canonical config.cx | shasum -a 256   # equivalent to cx hash
         $ cx hash config.cx
         $ cx eq prod.cx proposed.cx && echo unchanged || echo changed
    

cx validate

Validates a document against a .cxs schema (spec/core/schema.md). Flags: --schema=SCHEMA.cxs (required), --fail-on=info|warn|error|none (default error), --mode=open|strict|closed (overrides the schema-mode directive), --apply-defaults (materialise schema-default attribute values). Exit 0 if no diagnostics at/above the threshold, 1 if any, 2 on I/O or schema failure.

      $ cx validate users.cx --schema=users.cxs
         $ cx validate users.cx --schema=users.cxs --mode=strict --fail-on=warn
    

cx select

CXPath query over one document — cx select 'PATH' [FILE] (stdin when FILE is - or absent). The document binds as $doc; PATH is a single CXPath value expression ($doc/…, /…, or //…, predicates included). Matches print one per line in canonical CX, in document order; attribute-axis matches materialize as [name value] fields. Exit 0 with at least one match, 1 on an empty match set (grep-style), 2 on error. A pure read — no capability grants accepted or needed. Contract: spec/misc/cli.md §3.8.

      $ cx select '//user[= $_@role admin]' users.cx
         [user name=Alice role=admin]
         $ cx select '$doc/user@name' users.cx
         [name 'Alice']
         [name 'Bob']
         $ cat users.cx | cx select '//user' && echo matched
    

cx table

The public Table API surface for CXCol / [table[…]] payloads: cx table info FILE (column/row counts, types, byte size), cx table dump FILE --to=cx (round-trip via the Table API), cx table load FILE --to=cx (symmetric inverse). Parquet / Arrow output (--to=parquet|arrow) defers to the libcx_arrow bridge. See analytics.

      $ cx table info data.cxbin
         $ cx table dump data.cxbin --to=cx | head
    

cx diff

Semantic diff — walks the strict-canonical forms, so presentation-only edits produce no delta. See diff for the output contract. Flags: --format=unified|json|summary (default unified), --no-color. Exit codes match diff(1): 0 equal, 1 differs, 2 error.

      $ cx diff prod.cx proposed.cx
         $ cx diff --format=json prod.cx proposed.cx
         $ cx diff prod.cx proposed.cx && echo no-op   # CI gate
    

Diff semantics

cx diff produces a semantic diff — tree-structured, canonical-byte-aware, formatting-blind. Two data-equivalent inputs diff to nothing, even if they differ at the text level. This is what makes cx diff usable as a CI gate: it reports semantic deltas, not formatting noise.

What cx diff reports (and ignores)

cx diff reports differences in the strict canonical form (spec/core/canonical.md §1.2). Specifically it reports:

  • Element added / removed (by canonical path).
  • Element renamed (different canonical name at the same path).
  • Attribute added / removed / value-changed (by canonical name).
  • Body content changed (atomic-value or text-content differences).
  • Type annotation changed (e.g., :int vs :u32 — the annotation IS part of the data).
  • Element order changed when order is data-significant (per spec/core/canonical.md §2.1).

And ignores:

  • Comments (# line and [# block #]).
  • Attribute order on the same element.
  • Anchor / alias / merge details — expanded to the resolved form before diff.
  • Whitespace, indentation, line breaks.
  • Numeric formatting (1_000_000 vs 1000000).
  • Quoting style ('foo' vs "foo").
  • Element-name aliases that resolve to the same canonical name.

Output formats

Three output modes via --format:

  • unified (default) — element-rooted hunks, - / + / prefixes. Human-readable.
  • json — structured array of change records. Suitable for CI gates and editor integrations.
  • summary — one-line counts. Useful for shell prompts and PR bots.
        $ cx diff prod.cx proposed.cx
           --- prod.cx
           +++ proposed.cx
           @@ /config/database @@
            host='primary.db'
           -port=5432
           +port=5433
            replica='secondary.db'
      
        [
             {
               \"kind\": \"attribute-changed\",
               \"path\": \"/config/database/@port\",
               \"before\": {\"name\": \"port\", \"value\": 5432, \"type\": \"int\"},
               \"after\":  {\"name\": \"port\", \"value\": 5433, \"type\": \"int\"}
             }
           ]
      

JSON change-record shape

Each record has a stable shape:

  • kind — one of element-added, element-removed, element-renamed, attribute-added, attribute-removed, attribute-changed, body-changed, type-changed, order-changed.
  • path — CXPath expression locating the change. The same expression drops straight into a CX program ([?for [in $x PATH] …]).
  • before / after — relevant value for the kind. Absent when the side has no value (e.g. element-added has no before). The field is omitted, not null — per the four-way null/empty/missing distinction of spec/core/code.md §8.13 this is missing.

Git integration

cx diff plugs into git diff via the standard custom- diff-driver mechanism. Add this to .gitattributes:

        *.cx    diff=cx
           *.cxd   diff=cx
           *.cxs   diff=cx
      
        # One-time per repo
           git config diff.cx.command 'cx diff'
      

With this in place, git diff path/to/config.cx and git show HEAD path/to/config.cx use the semantic diff automatically. PR review UIs that respect .gitattributes (GitHub, GitLab, Gerrit) pick it up too.

Exit codes

Match diff(1):

  • 0 — inputs are data-equivalent (no differences).
  • 1 — inputs differ (at least one semantic delta).
  • 2 — error (parse failure, file not found, invalid arguments).
        # Use directly as a CI gate
           cx diff prod.cx proposed.cx && echo no-change || review-changes