railcall build only accepts an undocumented fixed schema — and the "BLOCKED" error throws away the one detail that would explain it
railcall build [path/to.csv] is described as "local CSV compile," but it only
accepts CSVs matching a specific, undocumented schema (the power-grid demo:metric_id, component, load_value, status). Point it at your own data and it
fails with ✗ BLOCKED: missing_required_headers — and it does not tell you
which headers it wanted, even though the compiler computed exactly that.
Reproduction (station-v0.97)
Any CSV whose columns aren't the demo schema — say orders.csv withdate,product,revenue:
railcall build orders.csv
Result:
✗ BLOCKED: missing_required_headers
No list of what's required or missing, and nothing anywhere says what schemabuild expects.
(Behavior traced from source below, not run live.)
Observed — 1. the schema is fixed and undocumented
The only user-facing description is the command list (railcall_cli.py L18):
railcall build [path/to.csv] local CSV compile + recursive socket audit + receipt
cmd_build itself has no docstring (railcall_cli.py L655 opens with a comment).
But every build compiles against a fixed contract — load_contract() (L582)
returns DEFAULT_CONTRACT (railcall_companion_daemon.py L131-135):
DEFAULT_CONTRACT = {
"required_headers": ["metric_id", "component", "load_value", "status"],
"enforce_strict_types": True,
"max_load_threshold": 120.0,
}
So build requires exactly metric_id, component, load_value, status and
rejects any load_value > 120. The success line even hardcodes the target:✓ compiled N rows → tables/power-grid (L742). None of this — the required
columns, the threshold, the "power-grid" target — appears in any user-facing
help or doc.
Observed — 2. the error that would explain it is discarded
On a schema mismatch, the compiler returns the exact missing headers
(railcall_companion_daemon.py L305):
return {"ok": False, "error": "missing_required_headers", "missing": missing, ...}
But cmd_build renders only error and violations, dropping missing
(railcall_cli.py L740):
lines.append(c(f"✗ BLOCKED: {result.get('error')} {result.get('violations') or ''}", "red"))
The information that would tell the user what to fix is computed and thrown away.
Why it matters
build [path/to.csv] reads as "compile your CSV," so pointing it at real data
is the obvious first thing to try. Every non-demo CSV gets ✗ BLOCKED, with no
doc describing the required schema and no detail in the error — even though the
compiler knows precisely which headers are missing. It's a dead end built from
two small gaps that reinforce each other.
Suggested fix
- Show the detail — append the missing headers to the error line, e.g.
✗ BLOCKED: missing_required_headers (need: metric_id, component, load_value, status; got: date, product, revenue). One line, using the missing the compiler already returns.
- Document the contract — give
cmd_builda docstring / help that states the
required schema and the max_load_threshold, or make the contract selectable
(--contract <file>) so build is honestly a general CSV compiler rather than
a fixed demo one.
---
Environment: station-v0.97. Verified by reading cmd_build / load_contract
in railcall_cli.py and compile_csv / DEFAULT_CONTRACT inrailcall_companion_daemon.py; the BLOCKED behavior is traced from those two
functions, not run live.