Columna Core (0.7.8-core)
v0.7.8-core — packaging hardening + the disclosure wire adapter. Declares the hard
pyarrowruntime dependency and resolvesCOLUMNA_BENCH_WAREHOUSEabsolutely (WP-0 audit finds); reconcilesbenchmark.cmlwith the code-built Manifold (addsregion_label); clears the v0.7.8 worklist (parserOptionalimport; unused-import / dead-local / f-string cruft). Addscolumna_core.disclosure_wire— the structured{code, materiality, …}wire adapter (ADR-032 D8 / WP-1.3), the one contract every surface serializes. SeeCHANGELOG.md.
v0.7.7-core —
ON UNIVERSEpin wiring (Option A). The population pin recorded byFrame.on_universe(u)is now threaded to the planner (run/plan→_infer), where it asserts the frame's intended population. A measure bound touserves; a measure bound to a different universe is out-of-domain for that population and refuses (out_of_universe); an unknownuis an error. This resolves the multi-universe and D5 co-anchoring ambiguity to the one chosen population — so a cross-universe ratio that clarifies unpinned becomes a serve or an honest refuse once pinned, and a multi-universe frame's coverage caveat is resolved. (Resolving a measure over a universe other than its declared one — true cross-universe confinement — is Option B, future.)coanchor_demo§D covers it. 124 checks across 11 suites.
v0.7.6-core — the no-result is a value, not an exception. The structured no-result is split into a plain
Outcomevalue (kind · discriminator · reason · alternatives) and a private internalRefusalsignal that merely carries it from deep in the recursive walk to the planner's single assembly point.ColumnResult.refusalnow holds anOutcome: a clarify/refuse/error is data every surface and agent reads, never anExceptiona caller could swallow withexcept Exception. (Refusalremains the internal control-flow goto; eliminating even that would mean return-threading the recursive type-inferencer — deliberately not done.)coanchor_demoasserts the clarify is anOutcome, not an exception. 116 checks across 11 suites.
v0.7.5-core — ratio/rate co-anchoring (ADR-032 D5). A ratio
N / Dis determinate only when numerator and denominator resolve over one shared population. The planner now checks this statically: a ratio whose operands span different universes is a clarify (co_anchor_ambiguous, discriminatorambiguous) naming the candidate populations — "which population is the rate over?" — never a silent number. It is not eager (a same-universe ratio, or a constant denominator, just serves) and is distinct from avg-of-averages (a B-anchor hazard). Scoped to the ratio: the same two measures as separate columns still serve with the multi-universe coverage caveat. Newcoanchor_demo; 115 checks across 11 suites.
v0.7.4-core — the two-level correctness contract (ADR-032). The column engine never judges: it attempts and returns either a result or a no-result carrying a discriminator —
ambiguous(no unique answer under the rules) orunsupported(the data can't support a result). The planner owns the four outcomes (serve · disclose · clarify · refuse), pluserrorfor vocabulary/capability failures, and classifies every no-result at one chokepoint (Refusal.classified). The multi-grain attribute case is now a clarify (ambiguous), not anunknownerror lumped with real failures; out-of-universe is a refuse; an unimplemented operator is an error.FrameResult.outcome / .clarifies / .refusals / .errorssurface the verdicts;confine_demoverifies the classification.
v0.7.3-core — attribute-anchor resolution hardened. When a universe predicate references an attribute
T.col, the engine must broadcast it at the levelTis keyed._attr_anchorno longer picks the first edgeTprovides: a single-grain table is unambiguous, a denormalized multi-grain table (onegeotable providingstore→regionandregion→country) is pinned by the delivery frame, and a genuinely ambiguous case is refused with the candidate levels named — never a silent wrong grain.confine_demoproves the pin and the refusal. 107 checks across 10 suites.
v0.7.2-core — universe-predicate evaluation hardened to typed predicates. The universe predicate is evaluated at the base grain by broadcast-and-filter, never a join: each referenced attribute is delivered single-table and broadcast onto the frame along its key (transport), the compared sides are coerced to a common dtype, and the frame is filtered. Fixes a real gap — a numeric (
qty >= 10) or real-Datepredicate previously raised a dtype error because predicate literals arrive as strings, and only the benchmark's ISO-date strings compared correctly by luck.confine_demonow proves numeric / Date / AND-ed predicates alongside out-of-domain exclusion. 101 checks across 10 suites.
v0.7.1-core — B-anchor crossing locus refinement + EXPLAIN-without-execution. Crossing detection moved from the engine (execute time) to the planner (compile time):
blocked_lineagesand the operator'sis_monoidare now surfaced on the shape projection, so a crossing is knowable from structure alone. The served contract is unchanged (still served-with-critical, never refused), butframe(...).plan()/explain(execute=False)returns the would-be annotation — the critical crossing plus spec-only provenance caveats — touching zero backend data. [Superseded in part, 2026-08-20 — ADR-036: the locus finding stands (detection is static, from shape, at the planner, andEXPLAINstill costs zero fetches), but the verdict does not: a crossing is now REFUSED there rather than served-with-critical.explainreturns the would-be refusal.]
v0.7.0-core — a custom type + three custom operators, planner untouched (HLL case study). The
distinctfamily is decomposed into a parametric typeHLLSketch(p)and three registered operators —hll_count(deliver),hll_merge(a monoid union, combine),hll_estimate(project) — that slot into the umbrella via the registry and engine only;planner.pyandprojection.pyhold zero sketch references. Precision is type identity (a mismatched merge is a type error; a raw sketch is opaque to arithmetic). A publish-time witness store makes sketches stored, not cached, sodistinct@regionmerges stored witnesses with zero base scans at query time.
v0.6.0-core — inform-and-serve reconciliation (Frame-QL Manual). [SUPERSEDED 2026-08-20 — ADR-036, the generated-family law: this release's central move is reversed. A structurally prohibited reduction REFUSES again (
blocked_reduction, no values), because Disclose exists inside the lawful region; it cannot legalize an operation the governed law does not possess. The refusal now holds in every spelling — written member, inline reducer generated above a lawful sibling (sum(stock.last@day)), and any unary/binary/scalar/scan/DERIVED carrier: family generation creates a new analytical family, not a new operator permission.b_anchor_crossingis tombstoned as a producer and retained, still wired, so archived wires resolve. The rest of this entry — refusals reserved for static planner cases, the severity lattice and its frame-level rollup — stands. The original text is kept below as the record of what 0.6.0 shipped.] A B-anchor crossing (e.g. summing a stock over time) is now served with a criticalb_anchor_crossingdisclosure that names the alternative reducer — no longer refused. Refusals are reserved for the planner's static clarify/inform cases (fan-out across an M:N edge — now naming all three remedies; out-of-universe; unknown operator; type error). The engine, once handed an executable atom, never withholds on analytical grounds. Disclosures now carry a severity lattice (none<info<caution<critical) with a frame-level rollup. 57 checks across 7 suites pass.
The column-foundation implementation specified in ADR-031 and the Manifold object model — multi-table, transport-based, validated against the real benchmark warehouse (299,934 transactions).
This is not the ADR-030 kernel. The kernel proved correctness techniques on a single denormalized table; Core implements the architecture: the backend delivers single-table column-atoms and functional relationship-columns; the engine transports and relates them; the backend never joins.
The discipline, in code
connector.py— single-table delivery only.deliver_measure(one-table group-by),deliver_edge(one-table key→key mapping),deliver_base_rows(one-table, for sketches). No SQL join is ever generated.model.py— the Manifold object model. Two layers: universes (populations) and the coordinate DAG (FunctionalEdges). A rollup (day→month) and a relationship (store→region) are the same object — a functional edge the engine transports along, tagged with a lineage. The B-anchor blocks per-lineage.engine.py— the center. For(measure, family-member) @ anchor: find the functional path, B-anchor-check every transport edge and every collapsed dimension, resolve cheapest-faithful (cache vs delivery), transport the measure along functional edges (in-engine; no join pushdown), co-compute disclosure, cache.planner.py— provenance-blind. Parses logicalfamily.member @ anchor, expands derived columns, typechecks addressability — fan-out and out-of-universe are refused HERE, statically — then assembles frames and folds disclosures.disclosure.py— the disclosure shadow-value and its sibling, the structured, dialog-capableRefusal(carries named alternatives).
What build_benchmark.py proves (10/10, real data)
- Transport replaces the join —
revenue@regioncomputed from a single-tablerevenue@storedelivery + thestore→regionmapping, related in the engine; exact vs. ground truth; zero joins pushed down. - Coordinate rollup is the same operation —
revenue@cal.monthvia theday→monthmapping. - ★ Fan-out is inexpressible ★ —
revenue@categoryis refused at the planner (transaction↔categoryis M:N); for contrast, the naive join silently inflates revenue 1.44× — a 44% overcount. The refusal names its alternatives (allocation = Pro; membership = rephrase). The flagship. - B-anchor blocks per-lineage —
level.sum@(region,day)works (additive over the store axis);level.sum@storeis refused (would sum a stock across days — non-reconciling over the calendar lineage). Same metric, opposite verdicts, by edge. (2026-08-20, ADR-036: this line has always saidrefused, and as of the generated-family law the shipped verdict agrees with it again — reasonblocked_reduction, and refused in every spelling, including asumgenerated inline abovelevel.last. The 0.6.0 banner above, which served the crossing with a critical caveat instead, is the entry that moved. Note the second half of the row is the ruling's other half: the bar is per operator × lineage, so summing the same stock across stores stays lawful — there is no stock/flow type.) - Pre/post-agg boundary —
aov = revenue/orderscomputed post-aggregation = correct AOV (not the avg-of-avgs the shippedmonthly_avg_order_valuewould give). - Sketch reaggregation —
visitors@cal.quarterby HLL-merging per-day sketches; ≈ true distinct, vs. a naive sum-of-daily-distincts that overcounts. - Out-of-universe —
level@productrefused as a distinct reason (out of domain — undefined, not missing).
engine: 5 single-table deliveries, 5 transports, 1 cache-hit, 10 backend fetches (all single-table)
Run
pip install --break-system-packages polars duckdb datasketches pyarrow
python3 build_benchmark.py # 10 checks against the benchmark warehouse
(The script reads the warehouse parquets from the benchmark instance path; adjust WAREHOUSE if needed.)
Ingest-first: the definition language
A written MANIFOLD definition (the .cf successor) parses to the Manifold object and is queryable end-to-end — no hand-construction. See benchmark.cml for the whole benchmark Manifold as text, and run:
python3 parse_benchmark.py # parse benchmark.cml, then the SAME 10 checks on the PARSED Manifold
The parser (parser.py) reproduces the hand-built object with full structural parity, the corrected semantics survive the parse (M_ANCHOR { } → MCAR; FAMILY { sum : additive BLOCKED { calendar } } → per-member B-anchor), and publish-time well-formedness checks reject malformed definitions (impure predicate referencing a measure, unknown level/universe/column, etc.).
Grammar (statement-oriented; # comments; { } blocks):
MANIFOLD <name> VERSION <n>
UNIVERSE <name> = <dim> * <dim> ... [WHERE <predicate>]
LEVEL <name> = <column> [BASE]
HIERARCHY <lineage> { <from> -> <to> VIA <table>(<from_col>, <to_col>) [-> <next> VIA <table>(...)] [; <path>] }
RELATE <a> <-> <b> VIA <table> [NOTE "<text>"]
MEASURE <name> ON <universe> FROM <table> AS <agg>(<expr>)
MEASURE <name> ON <universe> FROM <table> VALUE <expr>
[M_ANCHOR { <col>, ... }] [FAMILY { <agg> [: <tier>] [BLOCKED { <lineage>, ... }] ... }]
DERIVED <name> = <expr>
Scope (Core, per ADR-031)
Logical types, checked at compile time (types.py, types_demo.py): every measure declares a logical (Polars) dtype and every operator carries a type signature (sum: Numeric∪Duration→same, distinct: any→Int64, median: Numeric∪Temporal→same, last: T→T). The signature is vocabulary the planner holds; the mechanics (witness/combine/deliver_sql) stay engine-side. So "operator not supported" and "wrong type for this operator" are vocabulary errors caught at the planner, before the engine is ever asked — proven by zero backend fetches on a refused frame. A static type-inference pass runs as a compile step ahead of resolution (type_error is its refusal reason, a sibling of unknown-operator). The connector owns logical→physical: a measure declares logical Float64 over a raw column, and the connector supplies the TRY_CAST when the physical type is a dirty VARCHAR — so the author writes no casts, and a cast failure is a coverage fact at resolution, never a type error.
In scope and working: multi-table Manifolds, universes with runtime predicate confinement, the coordinate DAG, transport, measure-families with per-lineage B-anchors, the operator registry (reaggregation as a monoid property, plus type signatures), logical types with a compile-time planner typecheck, derived columns, structured refusals, EXPLAIN, the definition-language parser + well-formedness checks, the universe-support consistency check, the two projections as an enforced boundary. Deliberately out (Pro): multiple backends/federation, cloud, custom operators/types, sophisticated optimization, allocation (the M:N split — Core catches the case rather than computing it).
Two projections, enforced (projection.py, projection_demo.py): one authored Manifold has two projections. The planner holds a PlannerView — vocabulary/shape only: logical names, the DAG topology (frm→to + lineage, no physical columns), family member names, derived formulas, M:N pairs for fan-out. The engine holds the full Manifold — sources, realizations, the universe predicate, missingness, costs, the operator registry. This makes "the planner cannot see provenance" structural: home_table, pre_expr, realized_by, provider_table, and the predicate are simply absent from the planner's object — no source string is reachable from it — yet it still does real work (fan-out and out-of-universe are refused from shape alone). The handoff down is a logical request (measure, member, anchor); the return up is a frame + a Disclosure (caveats, not sources). Disclosures cross the boundary; provenance does not.
Reaggregation is a monoid property of the operator (operators.py — the registry, which is the Core/Pro extension point). An operator is reaggregable iff a possibly-enriched witness makes it a monoid; the engine reduces in witness-space and projects to the answer at the boundary: sum/count carry the value (combine +), min/max the value (combine min/max), distinct an HLL sketch (combine union), last/first the (value, order_key) pair (combine argmax/argmin — the order key is carried as the witness), and median/mode are holistic (no finite witness → recompute-from-base, never reduced). Two independent gates compose: monoid-ness (operator-level, possible) and the B-anchor (column-level, permitted). The headline (holistic_demo.py): level.last@(store, cal.month) works by carrying the day and reducing by argmax, while level.sum@(store, cal.month) refuses — same column, same target, the two gates disagree.
Universe-support consistency (universe_check_demo.py): a universe is one population, so every measure bound to it must reduce to the same base-point support (modulo declared coverage). The check reduces each measure to its universe and compares supports; a mismatch not explained by coverage flags a mis-declared universe. (Benchmark passes; a measure mis-sourced from a half-coverage table is flagged "50% short".) This is the count-reducer instance of a more general path-independence check — reduce a measure to the universe singleton along every anchoring and assert the reduced values agree — which becomes exercisable once a second coordinatization of an axis (e.g. a fiscal calendar forking from day) exists; with a single anchoring there is nothing to reconcile against.
Universe-predicate confinement (confine_demo.py): a measure bound to a predicated universe is confined to its valid points at the base grain, at delivery, before aggregation — logically prior to any query WHERE (domain before selection), inherited through cache and transport. A cross-table predicate like day >= stores.opened_date is evaluated in the engine: opened_date is delivered from stores (single table) and broadcast onto (store, day) by transport, then compared and filtered — never a backend join. The demo injects out-of-domain rows; the engine excludes them while an unconfined Manifold silently includes them.
Still thin in this build (next): the full cached-vs-stored cost search (resolution is correct but the cost model is minimal); hoisting the B-anchor permission refusal ( — done: the permission check is adjudicated at the planner, at compile time, from shape alone (0.7.1 moved detection; ADR-036, 2026-08-20, made the verdict a refusal and extended it to reducers generated inside an expression, not only to those written as declared members). The blocked_reaggregation) from the engine to the planner (it is structural and belongs there, but it currently sits in the engine to preserve behaviour — moving it means the PlannerView carries family-member shape)PlannerView does carry family-member shape, as predicted; automated Discovery (the parser consumes a written definition; generating one from a warehouse is the layer above).
Release files for columna-core 0.15.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| columna_core-0.15.0.tar.gz | 579.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| columna_core-0.15.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 747.3 kB
Release files / columna_core-0.15.0.tar.gz
| Download URL | columna_core-0.15.0.tar.gz |
|---|---|
| Size | 579.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
418d7441e78037b4951e1b8fb8cfc6da48bf3f9fbb104063d110c49f2e353d9c
|
|
BLAKE2b-256 checksum How to use checksums |
200836c9f1223d1af7e71a7a3a54bfe28fc08e05d853186bf661ca65fd68f916
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 21, 2026.
Transparency logRelease files / columna_core-0.15.0-py3-none-any.whl
| Download URL | columna_core-0.15.0-py3-none-any.whl |
|---|---|
| Size | 167.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b92eedef588ac6fc76e05c51c23e78296e57b682f1b8193fd63683080b84e61b
|
|
BLAKE2b-256 checksum How to use checksums |
a661dba64632e56b713914951f9dd681e5ef4bb7c0736cae1e32f77f9aa0662d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 21, 2026.
Transparency log