rowspec
A specification and executable conformance suite for tabular data that must survive version control.
A .mdtbl file is a markdown table where a computed column carries its formula
in the header, rows have opaque ids, and nothing is addressed by position. The
point is that stock git merges it correctly, or refuses — never quietly
wrong, with none of this project's tooling installed.
| id | item | qty | unit | total = qty * unit |
| ------ | ------ | --: | ----: | -----------------: |
| r_0001 | widget | 10 | 12.00 | |
key := id
grand := sum(total)
Two branches insert a row far apart. Stock git merge — no driver, no
.gitattributes, nothing installed — merges cleanly, and grand is correct.
The same table with A1-style references merges just as cleanly and reports a
number that is 27% wrong, with no marker anywhere. That difference is the entire
project.
What it does not do, measured
If your table is a list of facts, you probably do not need this. We replayed
7,446 real commits from four public CSV-in-git registries, including 528 real
three-way merges. Neither .mdtbl nor plain CSV ever merged silently wrong.
For an ordinary sorted registry CSV, stock git already does what this format
promises, and none of those maintainers has wanted a computed column in four
years.
The format earns its keep when a table computes — when a merge can leave a total that is quietly wrong, which is the failure a CSV cannot even represent.
Two branches each adding a column conflict badly, across most of the file. That is inherent to one-row-per-line and it is exactly what happens to a CSV. Nothing here fixes it; a column addition rewrites every row either way.
It is not a data-quality tool. Whether a country code is in ISO 3166, whether a URL resolves, whether a date is plausible — none of that is here, and Frictionless does it well. rowspec asks a smaller question: will this table survive being edited by several people over years?
It runs on the CSV you already have
rowspec check data/
No migration. On an ordinary .csv that alone refuses a committed conflict
marker — which Python's own csv module parses as valid rows without
complaint — a duplicate column name, a ragged row, invalid UTF-8, and an
invisible character in a column name. A five-line
data/countries.csv.rowspec.json naming the key and the order column adds the
rest. See docs/csv.md, and copy
docs/ci/rowspec-check.yml into
.github/workflows/.
Run against 1,564 CSVs in three public data repositories it refused 16: two
grouped-header spreadsheet exports, thirteen truncated CDC snapshots in
owid/covid-19-data whose header declares 14 columns for a 6-field row, and
one deliberately broken fixture in iptv-org/database's own test suite. No
false positives.
What is actually shipped
The conformance suite is the deliverable. It checks out two branches, runs
stock git merge, evaluates the merged file, and asserts on the computed
number. No prior art does this. The spec, the validator and the reference
implementation exist so the suite has something to check.
410 conformance cases two implementations, both passing
76 mutants 74 killed, 0 survived, 0 stale
The second implementation is the point. reference/rowspec_alt/ was written
from SPEC.md alone by an author forbidden to read reference/rowspec/, and it
runs against the same fixture tree in CI on every push. It is the only evidence
that this document defines the format rather than describing one program, and
it has repeatedly been the half that was right: on the last three questions
where the two disagreed, the independent implementation was correct and the
reference was wrong.
The mutation gate is the other half. The suite is only worth its green tick if it can go red, so the implementation is deliberately broken in 76 specific ways and the suite must notice every one. A mutant that survives is reported as a failure, and so is a stale one whose pattern no longer matches the source — because a check that quietly stopped running is the failure this project keeps finding in itself.
Neither number is a claim about correctness in general. See docs/rationale.md for what has been measured, and what has been measured and found wanting.
Run it
just setup # uv sync
just check # fmt-check + lint
just test # conformance suite + mutation gate + corpus checks
just conform # the suite alone, against a stock git binary
just mutants # deliberately break the implementation; the suite must notice
just conform-alt # the SECOND implementation, against the same fixture tree
just run FILE # validate an artifact (rowspec check)
just eval FILE # print computed values and FAIL on any #REF!
A real git binary is required. The suite's central claim is about what stock
git does, so it uses stock git.
Two commands, and you need both
check applies the refusals in §9 — the structural ones, the things that make a
table survive version control. eval computes the table and fails on any
#REF!.
They catch different things and neither subsumes the other. A misspelled
column name, a pasted 1,299.00, or a non-breaking space in a number are all
#REF! under §8 and are correctly not §9 refusals — the file is well-formed,
its total is wrong. check alone reports 0 refused on a table whose total is
wrong, so a CI recipe built on check is green on a broken total.
Run both:
rowspec check . # will this table survive being edited by several people?
rowspec eval . # does it currently say anything false?
Licensing
Per directory, deliberately — see LICENSE. The fixtures are CC0 so they can be vendored into an implementation in any language under any licence.
Credit
Standing on Coopy/daff, ClassSheets, Object Spreadsheets, Lotus Improv and org-mode. See NOTICE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rowspec-0.1.0.tar.gz.
File metadata
- Download URL: rowspec-0.1.0.tar.gz
- Upload date:
- Size: 274.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a57eb35d9cf5b93f1580a74734ec245e024868fadc1ab8b70564df0c646aa499
|
|
| MD5 |
3db8dfe3bb8d267f9583d335825e64e6
|
|
| BLAKE2b-256 |
120e2bd3a5e5e12fe2e421a3a23692aaae4274587ed0b24ebe284bc83d70e2db
|
Provenance
The following attestation bundles were made for rowspec-0.1.0.tar.gz:
Publisher:
release.yml on kindspec/rowspec
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rowspec-0.1.0.tar.gz -
Subject digest:
a57eb35d9cf5b93f1580a74734ec245e024868fadc1ab8b70564df0c646aa499 - Sigstore transparency entry: 2657634186
- Sigstore integration time:
-
Permalink:
kindspec/rowspec@625ddb2bebdcb364e4849a8c9b34258f8244749a -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/kindspec
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@625ddb2bebdcb364e4849a8c9b34258f8244749a -
Trigger Event:
push
-
Statement type:
File details
Details for the file rowspec-0.1.0-py3-none-any.whl.
File metadata
- Download URL: rowspec-0.1.0-py3-none-any.whl
- Upload date:
- Size: 48.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cb4592562cd09dfafb6600d1a2fe3b5c1fdfc66c4203fdabdeddaba87d5dd9b0
|
|
| MD5 |
91732d02a63048d94d70b23a58852a71
|
|
| BLAKE2b-256 |
8ee81106535f60c05485be063ed52ba85117acfd07232ae391209684a2606a35
|
Provenance
The following attestation bundles were made for rowspec-0.1.0-py3-none-any.whl:
Publisher:
release.yml on kindspec/rowspec
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rowspec-0.1.0-py3-none-any.whl -
Subject digest:
cb4592562cd09dfafb6600d1a2fe3b5c1fdfc66c4203fdabdeddaba87d5dd9b0 - Sigstore transparency entry: 2657634217
- Sigstore integration time:
-
Permalink:
kindspec/rowspec@625ddb2bebdcb364e4849a8c9b34258f8244749a -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/kindspec
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@625ddb2bebdcb364e4849a8c9b34258f8244749a -
Trigger Event:
push
-
Statement type: