Releases: Fiyy/gridlang
v1.0.0 — Stability marker (v2.0 roadmap complete)
v1.0.0 — Stability marker
🎉 The v2.0 roadmap is complete. v1.0 doesn't add new features — it freezes the public Python and HTTP APIs as a stable contract through the v1.x line.
Stabilized surfaces
| Surface | Examples |
|---|---|
| Python API | parse, execute, render, apply_cell_edit, bundle_doc, CrdtDocument, CollabSession |
| HTTP API | /api/render, /api/save, /api/cell-edit, /api/collab/* |
| File format | section delimiter grammar, A1 refs, @source directives, chart: / format: / bind: DSL |
| CLI | run, render, validate, info, import, export, serve, js-bundle |
These surfaces follow Semantic Versioning — additive changes only until v2.0.
What's new in this release (housekeeping only)
CHANGELOG.md— full version history v0.2 → v1.0 in Keep a Changelog formatLICENSE— MIT, matching thepyproject.tomldeclaration.github/workflows/test.yml— CI runs the 442-test suite on Python 3.9–3.12 (Ubuntu) + 3.12 (macOS), plus a CLI smoke-test over every.gridexample and ajs-bundleround-trip via Node- README badges — CI / license / Python-version / latest-release
No code in gridlang/ was touched in this commit; the 442 tests still pass unchanged.
The v0.2 → v1.0 journey
| Version | Theme |
|---|---|
| v0.2.0 (May 2025) | Multi-sheet, 59 formulas, 9 charts, Excel I/O, live preview |
| v0.3.0 | Chart & Format DSL |
| v0.4.0 | Remote Data Sources (@source) |
| v0.5.0 | Reactive Bindings (cell(), bind:) |
| v0.6.0 | JavaScript Compute Engine |
| v0.7.0 | JS Bundles & Extended df API (~25 methods) |
| v0.8.0 | Collaborative editing (CRDT) |
| v1.0.0 | Stability marker |
By the numbers
- 442 tests, 0 failures
- 12 example
.gridfiles - 21 SPEC sections covering format + every feature
- 18 Python modules in
gridlang/ - ~250 lines of self-contained browser JS for collab
Get started:
pip install -e .
gridlang serve examples/12_collab.grid --collab --edit
# Open http://localhost:8080 in two browser tabs and edit cells live.🤖 Generated with Claude Code
v0.8.0 — Collaborative Editing (CRDT)
v0.8.0 — Collaborative editing (CRDT)
The final item in the v2.0 roadmap (§15). Multiple browser tabs (or peers on the same network) can now edit the same .grid file at the same time; operations converge via a per-cell LWW CRDT keyed by Hybrid Logical Clock timestamps.
Try it
gridlang serve dashboard.grid --collab --edit
# Open http://localhost:8080 in two browser tabs.
# Edit a cell in tab A → within ~700ms the value appears (and flashes blue) in tab B.
# Edit the same cell in both tabs at once → the higher-HLC write wins on every replica.Architecture (3 small modules, ~700 lines)
| Module | Role |
|---|---|
gridlang.crdt |
HLC clocks + LWW per-cell Document + version vectors |
gridlang.collab |
CollabSession — peers, persistence, sync |
gridlang.collab_client |
Self-contained ~250-line browser JS |
Wire protocol — JSON over HTTP
POST /api/collab/join { peer_id? } → { peer_id, site_id, ops, version }
POST /api/collab/leave { peer_id }
POST /api/collab/op { peer_id, cell, value, sheet? } → { op, version }
POST /api/collab/poll { peer_id, since: vv } → { ops, version, peer_count }
GET /api/collab/snapshot → { site_id, ops, version }
GET /api/collab/stats → { cells, journal, peers, version }
GET /api/collab/client.js → IIFE that joins + polls + applies
Version vector vv is {site_id: [wall_ms, logical]} — each peer summarizes "what I've seen from each replica" in O(sites) space.
Convergence guarantees (proven by tests/test_crdt.py)
- Commutativity —
apply(a) ∘ apply(b) ≡ apply(b) ∘ apply(a) - Idempotence — applying the same op twice is a no-op
- Random-permutation property — 10 random permutations of an op set yield byte-identical state on all replicas
What changed
- Added
gridlang/crdt.py,gridlang/collab.py,gridlang/collab_client.py - Extended
gridlang/server.pywith 7 collab endpoints +--collabflag - Added
examples/12_collab.grid(multi-peer demo) spec/SPEC.md§21 — full protocol, data model, convergence proof sketch, programmatic API- §15 marks the roadmap item DONE
README.md— collab section, updated CLI docs- 64 new tests; 442 total (was 378)
Backward compatibility
- When
--collabis off (the default), all/api/collab/*endpoints return 404. The single-user editor (gridlang serve --edit) is unchanged. - When on, the v0.5 contenteditable cells and
bind:widgets keep working — they just commit through the CRDT layer instead of/api/cell-edit. The on-disk.gridfile remains the source of truth, sogridlang run/render/ exports always see the merged values.
Out of scope for v0.8 (planned for v0.9+)
- Inserting / deleting rows or columns concurrently — would need an RGA layer beneath cell ops
- Federation between independent servers — current implementation is single-server
- Auth — protocol assumes peers on a trusted network
🎉 The v2.0 roadmap is now complete — multi-sheet (v0.2), Chart DSL (v0.3), Remote Data (v0.4), Reactive Bindings (v0.5), JavaScript Engine (v0.6), JS Bundles & Extended API (v0.7), and now Collaborative Editing (v0.8).
🤖 Generated with Claude Code
v0.7.0 — JS Bundles & Extended df API
Push the JS engine to its limit: standalone bundles + ~25 helper methods.
gridlang js-bundle — package a .grid file's data + compute layer into a single self-contained .js file:
gridlang js-bundle report.grid -o bundle.js # Node bundle
gridlang js-bundle report.grid --browser ... # Web Worker bundle
node bundle.js # prints JSONExpanded df API (8 → 25 methods):
- Aggregations: count, variance, std, median, quantile, describe
- Filtering: head, tail, slice, distinct, find, some, every, none
- Sorting: sortBy({desc?}), groupBy, countBy
- Reshaping: pluck, drop, rename, assign
- Joins: join, leftJoin, concat
- Conversion: toRecords, toCSV
- Metadata: columns, shape, empty
All chainable — df.where(p).sortBy(c, {desc:true}).head(3).col("x").
JS source extraction — gridlang/js/{df_helpers,bridge_node,runtime_pipeline}.js are now plain files (no inline Python strings); shared by the in-process bridge and bundles.
378 tests. See spec/SPEC.md §20.
v0.6.0 — JavaScript Compute Engine
Author the compute layer in JavaScript by setting engine: javascript.
--- compute ---
function transform(df) {
df.addColumn('Tax', r => r.Revenue * 0.2);
return df;
}
- Runs in a Node vm subprocess sandbox via JSON-over-stdio bridge
- Curated globals: Math, JSON, Number, String, Array, Object, Date, Map, Set, Promise, Error
- Blocked: require, process, fs, child_process, Buffer, setTimeout
- --max-old-space-size=256, per-stage VM timeout, total wall-clock cap
- df helpers: col, sum, mean, max, min, where, addColumn, columns, shape
- Falls back gracefully via JsRuntimeUnavailable when Node missing
- 48 new tests including sandbox bypass attempts
See spec/SPEC.md §19.
v0.5.0 — Reactive Bindings
Two-way binding between rendered preview and the .grid data section.
Inline cell binding via Jinja helper:
<td>{{ cell("B2") }}</td> <!-- editable -->
<td>{{ cell("B2@sales") }}</td> <!-- cross-sheet -->Form-style bind: blocks:
bind: input
cell: B2
type: number
bind: select
cell: A2
options: North, South, East, West
- POST /api/cell-edit accepts
{cell: "B2", value, sheet, save} - apply_edit() rewrites only the target row — preserves comments, blank lines, @Directives, formulas in other cells
- Header row (row 1) always read-only
- 73 new tests including HTTP roundtrip via stdlib http client
See spec/SPEC.md §18.
v0.4.0 — Remote Data Sources
@Directives at the head of any data section pull CSV/JSON/xlsx from URLs or files; inline rows act as fallback.
--- data ---
@source: https://api/sales.json
@format: json
@select: data.records
@cache: 1h
# Fallback used when remote fails
Region,Total
North,100
- file:// always allowed, http(s):// gated by --allow-remote
- Content-addressed TTL cache (SHA1 of url+headers+format+select)
- JSON @select supports
a.b.c[0]dot-path drilldown - 35 new tests covering fetch, cache, fallback semantics
See spec/SPEC.md §17.
v0.3.0 — Chart & Format DSL
Declarative chart blocks (chart: bar / format: color_scale) inside the present layer.
- 9 chart types accept references like
B2:D4,agg.foo,Q1,Q2,Q3(multi-series) - Cross-sheet refs:
sales!Revenue,B2:D4@sales - A1 cell qualifier required for single-cell refs to avoid shadowing column names
- 32 new tests, custom test runner (
tests/_run_no_pytest.py) for pytest-free environments
See spec/SPEC.md §16 for the full grammar.