Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
1508 commits
Select commit Hold shift + click to select a range
4341fb3
tree editor backend: an idle live socket checks its session and acces…
sjnarmstrong Oct 10, 2026
ced76c6
tree editor backend: a live message with a NUL character is rejected …
sjnarmstrong Oct 10, 2026
e3670e8
tree editor backend: a live message with a NUL character is refused w…
sjnarmstrong Oct 10, 2026
432d146
tree editor ui: failing tests for offline batches and missed heartbea…
sjnarmstrong Oct 10, 2026
aca26cd
tree editor ui: live validation that arrives before its version is sh…
sjnarmstrong Oct 10, 2026
c04b534
a zoned datetime in a naive declared field keeps its zone in a frame;…
sjnarmstrong Oct 10, 2026
a7d86a1
tree editor ui: live validation is held until the editor shows its ve…
sjnarmstrong Oct 10, 2026
41b7872
tree editor ui: live sync sends edits made while disconnected as one …
sjnarmstrong Oct 10, 2026
8beb6a1
tree editor ui: the flat-rule editor toasts a merged offline batch, k…
sjnarmstrong Oct 10, 2026
4423e01
e2e: the same-field live test releases the second edit after the firs…
sjnarmstrong Oct 10, 2026
0a6a493
docs: the numba engines (not only fused) fit an each's gathered list,…
sjnarmstrong Oct 10, 2026
cf2d22e
tree editor e2e: offline edits merge or are refused on reconnect (two…
sjnarmstrong Oct 10, 2026
c820ddd
e2e: the project paging test waits for the second page's cards, not o…
sjnarmstrong Oct 10, 2026
1d75454
live ops: failing cases: a path both sides left with the same value i…
sjnarmstrong Oct 10, 2026
86fbeb1
live ops: a path both sides left with the same value is no offline co…
sjnarmstrong Oct 10, 2026
7c21d2b
benchmarks: server cost of merging and refusing an offline batch of 5…
sjnarmstrong Oct 10, 2026
35a4b81
docs: the tree editor README describes offline edits, the merge rule …
sjnarmstrong Oct 10, 2026
33e5645
Merge step 1: live editing over a websocket per project, fanned out a…
sjnarmstrong Oct 10, 2026
b4f75d3
Merge the reviewed step 1 (spike/collab-review1) into step 2
sjnarmstrong Oct 10, 2026
5e9966a
tree editor: failing tests: an offline batch names the messages it co…
sjnarmstrong Oct 10, 2026
b150968
tree editor: an offline batch names the messages it contains and the …
sjnarmstrong Oct 10, 2026
c9d6e67
tree editor tests: the offline tests move to test_live_offline.py (ba…
sjnarmstrong Oct 11, 2026
1be973b
docs: the tree editor README: a message applies once; the offline e2e…
sjnarmstrong Oct 11, 2026
95c3bdd
Merge the live-sync payload benchmark (JSON, msgpack and protobuf mes…
sjnarmstrong Oct 11, 2026
e728a6b
tree editor live: failing case, an index edit equal to the item their…
sjnarmstrong Oct 11, 2026
456ae21
tree editor live: the same value is no conflict only where an index n…
sjnarmstrong Oct 11, 2026
75c27e5
tree editor live: failing tests, a message delivered late after the b…
sjnarmstrong Oct 11, 2026
0db51c3
tree editor live: a logged offline batch keeps the ids of the message…
sjnarmstrong Oct 11, 2026
6f38b6e
tree editor live: failing test, edits made in a second drop are based…
sjnarmstrong Oct 11, 2026
fdaea22
tree editor live: after a batch settles in a catch-up, the edits stil…
sjnarmstrong Oct 11, 2026
d8ac023
docs: the tree editor README: the same value on both sides merges; a …
sjnarmstrong Oct 11, 2026
602edd2
Merge step 2: offline edits merge on reconnect only when nothing over…
sjnarmstrong Oct 11, 2026
21c71e4
docs: experiment: live multi-session editing in the tree editor, its …
sjnarmstrong Oct 11, 2026
f53c086
Merge engine/wip-each-numba: every engine types an each's gathered li…
sjnarmstrong Oct 11, 2026
21a9aa4
docs: every example runs on every engine: the comparison's behaviour …
sjnarmstrong Oct 11, 2026
0807b83
Merge branch 'feature/python-api-redesign' into collab/step0
sjnarmstrong Oct 11, 2026
83fe48b
tree editor backend: a save that sends project_data must send base_ve…
sjnarmstrong Oct 11, 2026
eeef502
docs: the tree editor README says a document save must send base_vers…
sjnarmstrong Oct 11, 2026
cd9b9b0
tree editor backend: remove the dead edit-session manager (it importe…
sjnarmstrong Oct 11, 2026
fb0420b
tree editor backend: sign-in sessions and OIDC login state live in Po…
sjnarmstrong Oct 11, 2026
c5525b0
docs: tree editor README: running several backend nodes on one Postgr…
sjnarmstrong Oct 11, 2026
0e9cb58
Merge version-checked tree editor saves (a stale save gets 409; a doc…
sjnarmstrong Oct 11, 2026
4c2f832
tree editor backend: an authenticated request reads its session from …
sjnarmstrong Oct 11, 2026
6a92833
tree editor merge-core bench: shared vectors from the pure Python liv…
sjnarmstrong Oct 11, 2026
ec96b0a
tree editor merge-core bench: shared vectors from the pure Python liv…
sjnarmstrong Oct 11, 2026
2b60ee5
tree editor merge-core bench: interleaved benches against the referen…
sjnarmstrong Oct 11, 2026
27796fd
docs: the merge-core bench README: adapter contract, comparison rules…
sjnarmstrong Oct 11, 2026
f31eb6b
tree editor merge-core: Rust crate porting the live ops, with PyO3 an…
sjnarmstrong Oct 11, 2026
10c9518
Merge feature/python-api-redesign (version-checked tree editor saves)…
sjnarmstrong Oct 11, 2026
4008ff7
Merge spike/merge-core-harness
sjnarmstrong Oct 11, 2026
665bfee
tree editor merge-core: harness adapters for the Rust core, direct co…
sjnarmstrong Oct 11, 2026
d303346
tree editor merge-core: wasm-opt flags, boundary benchmarks for the J…
sjnarmstrong Oct 11, 2026
96e2034
tree editor backend: an expired session is logged without any part of…
sjnarmstrong Oct 11, 2026
0736644
tree editor merge-core: diff keeps one path stack, key lookups borrow…
sjnarmstrong Oct 11, 2026
d03b262
tree editor backend: a renewal that finds the session signed out on a…
sjnarmstrong Oct 11, 2026
5c8279d
tree editor merge-core: adapters for the direct conversion variants, …
sjnarmstrong Oct 11, 2026
4e85173
docs: the merge-core README: results of the custom Rust crate on the …
sjnarmstrong Oct 11, 2026
2f1e9b5
Merge tree editor sessions and OIDC login state in Postgres, so they …
sjnarmstrong Oct 11, 2026
222959c
tree editor merge-core: conflicts takes only ASCII digits as an index…
sjnarmstrong Oct 11, 2026
966983e
tree editor merge-core: drop the measurement-only direct conversion f…
sjnarmstrong Oct 11, 2026
56c39f8
tree editor backend: live_ops re-exports the merge-core extension; th…
sjnarmstrong Oct 11, 2026
cfa36bd
benchmarks: the live payload benchmark installs merge-core
sjnarmstrong Oct 11, 2026
4e7bfdd
tree editor UI: live-ops is a wrapper over the merge-core wasm, built…
sjnarmstrong Oct 11, 2026
df26e46
tree editor merge-core: browser cold load script for the built app
sjnarmstrong Oct 11, 2026
5811b9b
docs: the flexible engine comparison's final timings: single-record l…
sjnarmstrong Oct 11, 2026
872eaf5
Merge the flexible engine rework (engine registry, run absorbs score,…
sjnarmstrong Oct 11, 2026
9556dff
tree editor: the image builds merge-core in a Rust stage; the compose…
sjnarmstrong Oct 11, 2026
d2675ba
docs: the tree editor and merge-core READMEs for the wired core, the …
sjnarmstrong Oct 11, 2026
39ed3d5
tree editor merge-core bench: vectors for an integer past 2**53 again…
sjnarmstrong Oct 11, 2026
3229dcb
docs: the hidden-state spec README as built (status, done items, solv…
sjnarmstrong Oct 11, 2026
299848d
docs: decision bullets for the not-integral refusal, NotRequired keys…
sjnarmstrong Oct 11, 2026
b7c3fb4
tree editor merge-core: an integer and a float compare exactly, as in…
sjnarmstrong Oct 11, 2026
b6605b5
allow_fallback docstring: prefer a step that compiles
sjnarmstrong Oct 11, 2026
161b741
docs: the guide says @allow_fallback is discouraged outside an intent…
sjnarmstrong Oct 11, 2026
be96fa3
docs: known limitations for installed-package helpers and the polars …
sjnarmstrong Oct 11, 2026
771fbfb
serving: refuse a non-numba engine (the polars engine is batch-only) …
sjnarmstrong Oct 11, 2026
b0a81da
docs: engine.md polars host memory figures match the experiment
sjnarmstrong Oct 11, 2026
bc1f6af
docs: flexible engine spec 09, 02 and 10 mark the spike and emit-ever…
sjnarmstrong Oct 11, 2026
08ff041
CLAUDE.md: strict_compile belongs to the numba engines
sjnarmstrong Oct 11, 2026
1fdbf3d
docs: warm image build times in the merge-core README
sjnarmstrong Oct 11, 2026
5d140c6
examples: governance-replay self-diff and credit-limit-management par…
sjnarmstrong Oct 11, 2026
7ae8863
assert_equivalent: a declared optional key the record omits equals nu…
sjnarmstrong Oct 11, 2026
a0da530
docs: engine rework spec 05, run(record) equals the frame row except …
sjnarmstrong Oct 11, 2026
a3329c7
settings: APISettings refuses unknown fields, so a retired DECIKIT_AP…
sjnarmstrong Oct 11, 2026
9d40279
Merge branch 'spike/merge-core-wire' into spike/merge-core-review
sjnarmstrong Oct 11, 2026
b0cec89
Merge B-003: assert_equivalent treats a declared optional key the rec…
sjnarmstrong Oct 11, 2026
116a0c8
serving: a strict refusal of a kernel that splits at launch raises fr…
sjnarmstrong Oct 11, 2026
173b7d3
loan-granting-pricing README: a decline band on a rule without a decl…
sjnarmstrong Oct 11, 2026
0df3809
docs: serving takes a numba engine only, APISettings forbids the reti…
sjnarmstrong Oct 11, 2026
100a357
serving: name a refused engine by its public type
sjnarmstrong Oct 11, 2026
81628f4
Merge B-001, B-002, B-012: serving refuses a non-numba engine, APISet…
sjnarmstrong Oct 11, 2026
c3b29a5
docs: CLAUDE.md strict_compile line kept short; not-integral bullet n…
sjnarmstrong Oct 11, 2026
3c52405
Merge the post-review docs: hidden-state spec as built, missing decis…
sjnarmstrong Oct 11, 2026
1b84834
engine(): the docstring lists where a thread, executor, polars thread…
sjnarmstrong Oct 11, 2026
cbd6750
fingerprint: an object a project class holds as a public attribute is…
sjnarmstrong Oct 11, 2026
9a48776
docs: an object a class holds as a public attribute is hidden state; …
sjnarmstrong Oct 11, 2026
e15cf59
docs: concurrency.md covers the polars engine, the default-engine var…
sjnarmstrong Oct 11, 2026
91a3e35
Merge A-001: an object a project class holds as a public attribute is…
sjnarmstrong Oct 11, 2026
ab4d5b5
Merge the thread-safety audit docs: engine() context under threads, a…
sjnarmstrong Oct 11, 2026
aea40ae
Merge the post-review fixes (polars engine refused in serving, retire…
sjnarmstrong Oct 11, 2026
23e85c3
tree editor merge-core bench: a differential fuzz of the Rust core ag…
sjnarmstrong Oct 11, 2026
6e9cb05
docs: the merge-core READMEs: vector counts, the differential fuzz an…
sjnarmstrong Oct 11, 2026
a9164d2
Merge the reviewed Rust merge core (spike/merge-core-review): live op…
sjnarmstrong Oct 11, 2026
75c8917
docs: experiment: writing the live-sync merge core once (Automerge, Y…
sjnarmstrong Oct 11, 2026
772a78e
e2e: the serve tests build their in-process handler with engine=, the…
sjnarmstrong Oct 11, 2026
2d809ca
docs: Engine documents params_validation as an attribute, so the API …
sjnarmstrong Oct 11, 2026
c6e13a1
Merge spike/merge-core (live editing steps 1-2 with the Rust merge co…
sjnarmstrong Oct 11, 2026
4c775de
serving: RequestHandler.score is renamed run, one run everywhere; no …
sjnarmstrong Oct 11, 2026
eeda93c
docs: RequestHandler.run in the guide, serving docs and decisions; st…
sjnarmstrong Oct 11, 2026
9dc07fd
tree editor e2e: the REST-save live test sends base_version; backend …
sjnarmstrong Oct 11, 2026
9561520
compile: a step returning what no kernel stores is told the declared …
sjnarmstrong Oct 11, 2026
81ec5d4
docs: the fallback reason for an output no kernel stores names the fo…
sjnarmstrong Oct 11, 2026
16f1199
exceptions: StrictCompileError is exported from decikit; a strict ref…
sjnarmstrong Oct 11, 2026
7424e68
docs: StrictCompileError in the API reference, the guide and the stri…
sjnarmstrong Oct 11, 2026
3a8498e
docs: tree editor collaboration decisions (version-checked saves, liv…
sjnarmstrong Oct 11, 2026
0f0b851
docs: mark the engine rework decisions and hidden-state R-4 as decide…
sjnarmstrong Oct 11, 2026
c6edb50
tree editor: a checkout without Rust fails with a message naming what…
sjnarmstrong Oct 11, 2026
9e845ac
docs: tree editor README: what a contributor needs to build merge-cor…
sjnarmstrong Oct 11, 2026
8dd55f6
tree editor backend: failing tests: the op-log migrations on a databa…
sjnarmstrong Oct 11, 2026
58ce5b7
tree editor backend: failing tests: op-log snapshots, retention by co…
sjnarmstrong Oct 11, 2026
02013d5
tree editor: tests: a live edit near the session's expiry renews it a…
sjnarmstrong Oct 11, 2026
d149711
tree editor backend: tests: an ops message Postgres or the merge core…
sjnarmstrong Oct 11, 2026
6566f96
tree editor backend: tests: a live client that stops reading neither …
sjnarmstrong Oct 11, 2026
b761534
tree editor ui: test: the flat-rule store opens, shows and saves docu…
sjnarmstrong Oct 11, 2026
6bba05d
tree editor ui: the flat-rule store compares documents with a plain d…
sjnarmstrong Oct 11, 2026
8219f5d
tree editor e2e: test: a browser that cannot load the wasm says so an…
sjnarmstrong Oct 11, 2026
4dccaec
tree editor ui: a wasm load failure renders the app with a notice and…
sjnarmstrong Oct 11, 2026
df14408
tree editor ui: the node editor's document mapping (load and save) in…
sjnarmstrong Oct 11, 2026
75f9c46
tree editor: a live edit renews the session by the HTTP rule (shared)…
sjnarmstrong Oct 11, 2026
f8ac2e8
tree editor: tests: a live socket that is signed out or loses access …
sjnarmstrong Oct 11, 2026
91f3205
tree editor backend: live ops messages with lone surrogates, non-fini…
sjnarmstrong Oct 11, 2026
7faf14c
tree editor backend: the live fan-out sends to a project's sockets at…
sjnarmstrong Oct 11, 2026
55ff8e3
tree editor: the image builds merge-core as a portable abi3 manylinux…
sjnarmstrong Oct 11, 2026
b26e9fc
tree editor: a live socket whose session or access ends is closed say…
sjnarmstrong Oct 11, 2026
eb0b3fd
tree editor backend: tests: the live socket close checks receive once…
sjnarmstrong Oct 11, 2026
c4766d8
tree editor: the live editor gives up on edits after three drops in a…
sjnarmstrong Oct 11, 2026
431e939
tree editor backend: tests: the slow-client test waits for both the d…
sjnarmstrong Oct 11, 2026
84fd0b5
tree editor backend: the one-row-per-message migration renames the la…
sjnarmstrong Oct 11, 2026
fff93be
tree editor backend: the op log stores the whole document every 100 v…
sjnarmstrong Oct 11, 2026
4c56cd3
tree editor ui: test: the node editor store shows server documents ke…
sjnarmstrong Oct 11, 2026
7737da1
tree editor e2e: test: two browsers on two nodes edit one node tree l…
sjnarmstrong Oct 11, 2026
07d56b7
tree editor ui: the node editor edits live when the server does: push…
sjnarmstrong Oct 11, 2026
825f1c5
tree editor backend: the snapshot migration logs the current document…
sjnarmstrong Oct 11, 2026
06c1cf5
tree editor bench: an online editor's round trip while ten offline ba…
sjnarmstrong Oct 11, 2026
154675c
tree editor backend: a log row without a snapshot stores SQL NULL, no…
sjnarmstrong Oct 11, 2026
4e14d3d
tree editor backend: a base the log no longer keeps is refused with t…
sjnarmstrong Oct 11, 2026
c200fe4
tree editor backend: the migration tests read a new project's id with…
sjnarmstrong Oct 11, 2026
cebd27e
tree editor backend: the snapshot interval's comment says what it bounds
sjnarmstrong Oct 11, 2026
2197c51
docs: the tree editor's op log keeps the latest 5000 versions and 30 …
sjnarmstrong Oct 11, 2026
7db94ca
docs: tree editor README: the node editor edits live, and a browser w…
sjnarmstrong Oct 11, 2026
411ed70
compile: a container whose named values are not float, int or bool (a…
sjnarmstrong Oct 11, 2026
160db37
tree editor ui: tests: after signing in again the live editor connect…
sjnarmstrong Oct 11, 2026
d828288
tree editor ui: signing in again through the session dialog resumes l…
sjnarmstrong Oct 11, 2026
ec7b91a
Merge the owner's engine decisions (RequestHandler.run, fallback advi…
sjnarmstrong Oct 11, 2026
b18d463
tree editor bench: the herd counts every online round trip it overlap…
sjnarmstrong Oct 11, 2026
302038a
tree editor backend: failing test: an offline merge that repeats an e…
sjnarmstrong Oct 11, 2026
00ed23e
tree editor backend: an offline merge counts validation errors by (ty…
sjnarmstrong Oct 11, 2026
5d5933c
tree editor backend: failing tests: an offline merge validates withou…
sjnarmstrong Oct 11, 2026
98c8dbb
tree editor e2e: after the sign-out on node two, signing in again on …
sjnarmstrong Oct 11, 2026
b4d60f2
docs: tree editor README: live session renewal, sign-out, messages th…
sjnarmstrong Oct 11, 2026
c873fe7
tree editor backend: an offline merge validates from a snapshot witho…
sjnarmstrong Oct 11, 2026
9ee1d8e
tree editor backend: failing tests: a validator that never answers re…
sjnarmstrong Oct 11, 2026
e173ba6
tree editor backend: the validator answers within 30 s or is killed a…
sjnarmstrong Oct 11, 2026
f840c80
Merge feature/python-api-redesign (engine decisions: RequestHandler.r…
sjnarmstrong Oct 11, 2026
ba51f60
docs: tree editor offline merge validated off the row lock (before/af…
sjnarmstrong Oct 11, 2026
82497a6
tree editor backend: test: concurrent writes and offline merges while…
sjnarmstrong Oct 11, 2026
b2d854d
tree editor backend: the trim looks for its whole row below a version…
sjnarmstrong Oct 11, 2026
399690d
tree editor backend: test: a database from before project versions up…
sjnarmstrong Oct 11, 2026
5ea1788
docs: the op-log trim's cost on a log of REST saves, filtering every …
sjnarmstrong Oct 11, 2026
c3d2477
Merge ship/review-wb: op-log snapshots every 100 versions, retention …
sjnarmstrong Oct 11, 2026
8893df5
tree editor backend: tests: the slow-client test puts the stalled cli…
sjnarmstrong Oct 11, 2026
b1629b6
tree editor backend: failing test: a log that keeps no versions or no…
sjnarmstrong Oct 11, 2026
acf73af
tree editor backend: the op log keeps at least one version and one day
sjnarmstrong Oct 11, 2026
189b03c
tree editor backend: tests: an unexpected server error closes the liv…
sjnarmstrong Oct 11, 2026
5713d20
tree editor backend: an unexpected error closes the live socket as 10…
sjnarmstrong Oct 11, 2026
6cf3b01
tree editor backend: a database outage while a live socket opens clos…
sjnarmstrong Oct 11, 2026
63a475e
tree editor ui: tests: drops the server did not report as its failure…
sjnarmstrong Oct 11, 2026
9e28d9f
tree editor ui: the live editor counts only drops the server reports …
sjnarmstrong Oct 11, 2026
ff48d36
tree editor ui: signing in again resumes only a live editor the serve…
sjnarmstrong Oct 11, 2026
ccc56f2
tree editor e2e: the flat save interrupted by an expired session is s…
sjnarmstrong Oct 11, 2026
c1cece3
tree editor ui: test: opening a node tree live stores nothing until a…
sjnarmstrong Oct 11, 2026
9cfc86b
tree editor ui: test: a node another browser moved stays where they p…
sjnarmstrong Oct 11, 2026
0419801
docs: tree editor README: only the server's failure closes count agai…
sjnarmstrong Oct 11, 2026
1a952b8
tree editor ui: the node editor sends live only after an edit, so ope…
sjnarmstrong Oct 11, 2026
e39551c
tree editor ui: a server version that moves a node turns auto-arrange…
sjnarmstrong Oct 11, 2026
b03383f
tree editor backend: tests: an offline batch, or a message it contain…
sjnarmstrong Oct 11, 2026
81aa566
tree editor backend: failing test: the hub stops promptly while its v…
sjnarmstrong Oct 11, 2026
3372852
tree editor backend: the hub kills its validator on stop instead of w…
sjnarmstrong Oct 11, 2026
340517e
Merge ship/review-wc: websocket traffic renews the session, a signed-…
sjnarmstrong Oct 11, 2026
1f212c4
docs: tree editor README: opening a tree sends nothing, a remote move…
sjnarmstrong Oct 11, 2026
4b6beb3
tree editor backend: tests: the hung-validator tests wait for the val…
sjnarmstrong Oct 11, 2026
53f6112
docs: offline merge lock experiment: why a refusal decided on the sna…
sjnarmstrong Oct 11, 2026
959e40a
Merge ship/review-wd: the node editor edits live, and a browser that …
sjnarmstrong Oct 11, 2026
e229cf4
Merge ship/review-wa: offline merges validate off the row lock, valid…
sjnarmstrong Oct 11, 2026
9008f89
tree editor backend: the live fan-out reads only the log columns it s…
sjnarmstrong Oct 11, 2026
ec4cef1
docs: tree editor live editing: the experiment's open risks after col…
sjnarmstrong Oct 11, 2026
668ae8d
docs: tree editor collaboration decisions: offline merge validated of…
sjnarmstrong Oct 11, 2026
6db6716
Merge live multi-session editing for the tree editor (behind DSP_LIVE…
sjnarmstrong Oct 11, 2026
6393fde
tree editor ui: tests: the node editor stops live editing on a sign-o…
sjnarmstrong Oct 23, 2026
dda80c5
tree editor ui: one live editing hook for both editors: the node edit…
sjnarmstrong Oct 23, 2026
c9942c4
tree editor e2e: the save helper waits for a live Save, and REST-only…
sjnarmstrong Oct 23, 2026
9720bb5
docs: tree editor README: the smoke set and test_sessions.py pass wit…
sjnarmstrong Oct 23, 2026
c51577b
docs: tree editor live editing: the node editor's ended-socket handli…
sjnarmstrong Oct 23, 2026
b0fa935
docs: tree editor collaboration: the owner's calls recorded: log rete…
sjnarmstrong Oct 23, 2026
6d1c393
tests: the module-constant fingerprint test writes no .pyc, so a same…
sjnarmstrong Oct 23, 2026
9e769cb
release: the Docker image records the release version, not 0.0.0
sjnarmstrong Oct 23, 2026
6f274fc
tree editor e2e: the save helper fails when an editor edits live on a…
sjnarmstrong Oct 23, 2026
bfe88ff
tree editor ui: the Live Editing Stopped message closes when the edit…
sjnarmstrong Oct 23, 2026
4d2f7c3
Merge the live editing integration: one live hook for both editors (t…
sjnarmstrong Oct 23, 2026
8efb703
release: the docs restore keeps only the earlier versions' folders, s…
sjnarmstrong Oct 23, 2026
47451fd
benchmarks: tree editor op log size on disk for repeated REST saves o…
sjnarmstrong Oct 23, 2026
8de1c60
tree editor backend tests: a REST save is logged as a diff; the log s…
sjnarmstrong Oct 23, 2026
5906f10
tree editor backend: a REST save is logged as the diff from the store…
sjnarmstrong Oct 23, 2026
d812ec4
docs: experiment, what the tree editor op log costs on disk before an…
sjnarmstrong Oct 23, 2026
6d23b1d
docs: a tree editor REST save is logged as a diff (decision, known li…
sjnarmstrong Oct 23, 2026
ef9fe0d
tree editor backend tests: a REST save too deeply nested to diff is s…
sjnarmstrong Oct 23, 2026
f042a9d
tree editor backend: a REST save too deeply nested to diff is logged …
sjnarmstrong Oct 23, 2026
a2abe62
benchmarks: the tree editor op log size script also times the diff a …
sjnarmstrong Oct 23, 2026
c1b2ca3
docs: tree editor op log size: the diff timing comes from the script,…
sjnarmstrong Oct 23, 2026
eaf765f
tree editor ui: test: a live Save settles only once the server has va…
sjnarmstrong Oct 23, 2026
f607fb2
tree editor ui: a live Save waits for the validation of the version i…
sjnarmstrong Oct 23, 2026
f3193b4
tree editor backend tests: an online edit made before a REST save, on…
sjnarmstrong Oct 23, 2026
edc262f
tree editor backend: a REST save wins over a live edit made before it…
sjnarmstrong Oct 23, 2026
a02c9e3
docs: tree editor: a REST save wins over a live edit made before it o…
sjnarmstrong Oct 23, 2026
42a1590
tree editor e2e: test: a menu opened before live editing connects sta…
sjnarmstrong Oct 23, 2026
87875f5
tree editor e2e: on a live stack, opening the flat-rule editor waits …
sjnarmstrong Oct 23, 2026
6ede9db
tree editor e2e: the node editor's Save is disabled when clean only w…
sjnarmstrong Oct 23, 2026
ff2180e
tree editor e2e: REST-only save stories skipped when live, naming the…
sjnarmstrong Oct 23, 2026
a68f2b2
tree editor backend tests: a client cannot take the id REST saves or …
sjnarmstrong Oct 23, 2026
048d49f
tree editor backend: the REST-save check compares paths by prefix set…
sjnarmstrong Oct 23, 2026
3167666
Merge REST saves logged as diffs: about 1.1 KB per save instead of a …
sjnarmstrong Oct 23, 2026
4488e74
docs: tree editor README: the whole e2e suite runs with live editing …
sjnarmstrong Oct 23, 2026
3a9d6bb
docs: known limitations: the first live open of a flat-rule project w…
sjnarmstrong Oct 23, 2026
c505a73
tree editor ui: test: a live Save whose edits are stored but not vali…
sjnarmstrong Oct 23, 2026
13bb550
tree editor ui: a live Save whose edits the server stored but did not…
sjnarmstrong Oct 23, 2026
255fa67
docs: known limitations: a live Save that gets no validation of its v…
sjnarmstrong Oct 23, 2026
a8b344c
Merge the tree-editor e2e suite with live editing on: 616 passed on t…
sjnarmstrong Oct 23, 2026
6eff32c
tree editor backend: test: an unexpected data-source error's text is …
sjnarmstrong Oct 23, 2026
4a92907
tree editor backend: an unexpected data-source error is logged and re…
sjnarmstrong Oct 23, 2026
579dc08
release workflow: the token is read-only unless a job asks for more
sjnarmstrong Oct 23, 2026
58edaac
docs: known limitations: a data source's own error still carries the …
sjnarmstrong Oct 23, 2026
fe45073
tree editor backend: the streamed preview's error message parses on P…
sjnarmstrong Oct 23, 2026
7d19a37
Merge PR #50 audit fixes: an unexpected data-source error is logged, …
sjnarmstrong Oct 23, 2026
9126cd1
deps: drop the unused visualise (streamlit, graphviz) and eval (simpl…
sjnarmstrong Oct 23, 2026
6f9e98f
docs: the extras table and bridge capabilities without visualise and …
sjnarmstrong Oct 23, 2026
780dc4f
tree editor backend: drop typing-extensions, which nothing imports
sjnarmstrong Oct 23, 2026
cb4df93
tree editor ui: drop 21 unused packages (rete, axios, d3-shape, elkjs…
sjnarmstrong Oct 23, 2026
2237282
Merge the dependency cleanup: no streamlit (so no Pillow, urllib3, pa…
sjnarmstrong Oct 23, 2026
d7cc5d8
tree editor e2e: the leave and reload prompts after an unsaved edit h…
sjnarmstrong Oct 24, 2026
9d163a0
Merge the e2e leave-page fix: on a live stack the unsaved-edit prompt…
sjnarmstrong Oct 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
12 changes: 12 additions & 0 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "decikit",
"owner": {"name": "Capitec DSP"},
"description": "Claude Code plugin for building decision pipelines with the decider toolkit.",
"plugins": [
{
"name": "decikit",
"source": "./plugins/decikit",
"description": "Skill and MCP server for decider toolkit projects: pipelines, flow files, decision trees, decision tables, scorecards and params."
}
]
}
15 changes: 11 additions & 4 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
# Ignore everything then whitelist what the image actually needs
*

# Package source and build artefacts
!/decider
!/dist
# Package source (the builder stage builds the wheel from these)
!/decikit
!/pyproject.toml
!/uv.lock
!/README.md

# In-repo example projects (PROJECT=examples/<name>), without their tests and generators
!/examples
/examples/*/tests
/examples/*/scripts

# Docker helpers
!/docker/bake.sh

# Strip noise from allowed directories
**/__pycache__
Expand Down
2 changes: 1 addition & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
* text=auto

# Mark generated version file so it survives `git archive --export-subst`
decider/_version.py export-subst
decikit/_version.py export-subst
68 changes: 60 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ on:
tags:
- 'v[0-9]*'

# Jobs that need more ask for it themselves.
permissions:
contents: read

env:
DOCKER_IMAGE: sjnarmstrong/pydecider
TREE_EDITOR_IMAGE: sjnarmstrong/decider-tree-editor

jobs:
# ── 1. Resolve version from git tags (hatch-vcs) ──────────────────────────
Expand All @@ -31,7 +36,7 @@ jobs:
id: get
run: |
uv sync --group dev
VERSION=$(uv run python -c "from importlib.metadata import version; print(version('decider'))")
VERSION=$(uv run python -c "from importlib.metadata import version; print(version('decikit'))")
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"

# ── 2a. Build wheel → PyPI ─────────────────────────────────────────────────
Expand All @@ -40,7 +45,7 @@ jobs:
runs-on: ubuntu-latest
environment:
name: release
url: https://pypi.org/p/decider
url: https://pypi.org/p/decikit
permissions:
id-token: write # OIDC trusted publishing — no token secret needed
steps:
Expand Down Expand Up @@ -113,6 +118,7 @@ jobs:
context: .
file: docker/Dockerfile
push: true
build-args: VERSION=${{ needs.version.outputs.version }}
tags: ${{ env.DOCKER_IMAGE }}:${{ needs.version.outputs.version }}
cache-from: type=gha
cache-to: type=gha,mode=max
Expand All @@ -123,9 +129,48 @@ jobs:
context: .
file: docker/Dockerfile
push: true
build-args: VERSION=${{ needs.version.outputs.version }}
tags: ${{ env.DOCKER_IMAGE }}:latest
cache-from: type=gha

# ── 2b'. Build and push the tree-editor image ─────────────────────────────
publish-tree-editor:
needs: [version, publish-pypi]
runs-on: ubuntu-latest
environment:
name: release
permissions:
contents: read
steps:
- uses: actions/checkout@v4

- name: Download wheel artifact
uses: actions/download-artifact@v4
with:
name: dist
path: dist/

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Build and push tree-editor image
uses: docker/build-push-action@v6
with:
context: .
file: ui/tree-editor/Dockerfile
push: true
tags: |
${{ env.TREE_EDITOR_IMAGE }}:${{ needs.version.outputs.version }}
${{ env.TREE_EDITOR_IMAGE }}:latest
cache-from: type=gha,scope=tree-editor
cache-to: type=gha,mode=max,scope=tree-editor

# ── 2c. Build and deploy versioned docs to GitHub Pages ───────────────────
publish-docs:
needs: version
Expand All @@ -151,7 +196,7 @@ jobs:
python-version: "3.12"

- name: Install docs dependencies
run: uv sync --extra docs
run: uv sync --group docs

- name: Restore docs from previous release
env:
Expand All @@ -162,7 +207,13 @@ jobs:
if [ -n "$PREV" ]; then
gh release download "$PREV" --pattern "docs-site.tar.gz" --dir /tmp/prev-docs 2>/dev/null || true
if [ -f /tmp/prev-docs/docs-site.tar.gz ]; then
tar -xzf /tmp/prev-docs/docs-site.tar.gz -C _site
mkdir -p /tmp/prev-site
tar -xzf /tmp/prev-docs/docs-site.tar.gz -C /tmp/prev-site
# Only the earlier versions' folders: the root is this build, so pages it dropped don't linger there.
for v in $(jq -r '.[] | select(. != "latest")' /tmp/prev-site/versions.json); do
[ -d "/tmp/prev-site/$v" ] && cp -r "/tmp/prev-site/$v" _site/
done
cp /tmp/prev-site/versions.json _site/
echo "Restored docs history from $PREV"
fi
fi
Expand All @@ -171,10 +222,11 @@ jobs:
env:
VERSION: ${{ needs.version.outputs.version }}
run: |
uv run python -m sphinx docs docs/_build/html -b html
uv run jupyter nbconvert --to markdown docs/notebooks/*.ipynb --output-dir docs/tutorials
uv run zensical build --strict
mkdir -p _site/$VERSION
cp -r docs/_build/html/. _site/$VERSION/
cp -r docs/_build/html/. _site/
cp -r site/. _site/$VERSION/
cp -r site/. _site/

- name: Write versions.json
env:
Expand Down Expand Up @@ -211,7 +263,7 @@ jobs:

# ── 3. Tag and create GitHub Release (after all three deploys succeed) ─────
github-release:
needs: [version, publish-pypi, publish-docker, publish-docs]
needs: [version, publish-pypi, publish-docker, publish-tree-editor, publish-docs]
runs-on: ubuntu-latest
environment:
name: release
Expand Down
65 changes: 65 additions & 0 deletions .github/workflows/tree-editor-merge-core.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: Tree editor merge core

# Builds and tests ui/tree-editor/merge-core (the live-editing merge logic) as a Python wheel and as WebAssembly,
# and runs the shared vectors against both. Publishes nothing.
on:
pull_request:
branches:
- main
paths:
- ui/tree-editor/merge-core/**
- ui/tree-editor/merge-core-bench/**
- .github/workflows/tree-editor-merge-core.yml

permissions:
contents: read

jobs:
merge-core:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ui/tree-editor/merge-core
steps:
- uses: actions/checkout@v4

- name: Rust toolchain
run: |
rustup toolchain install 1.95.0 --profile minimal --target wasm32-unknown-unknown
rustup default 1.95.0

- uses: actions/cache@v4
with:
path: |
~/.cargo/bin/wasm-pack
~/.cargo/registry
~/.cargo/git
ui/tree-editor/merge-core/target
key: merge-core-${{ hashFiles('ui/tree-editor/merge-core/Cargo.lock') }}

- name: Install wasm-pack
run: command -v wasm-pack || cargo install --locked wasm-pack@0.15.0

- name: cargo test
run: cargo test

- name: Install uv
uses: astral-sh/setup-uv@v4

- name: Build the wheel (abi3, manylinux_2_28)
run: uvx --from 'maturin[zig]==1.15.0' maturin build --release --zig --compatibility manylinux_2_28 -o dist

- name: Vectors against the wheel (oldest supported Python)
working-directory: ui/tree-editor/merge-core-bench
run: uv run --no-project --python 3.10 --with ../merge-core/dist/*.whl python run_vectors.py adapters/rust_py.py --strict

- name: Build pkg/ (WebAssembly)
run: wasm-pack build --target web --release --no-pack -- --features wasm

- uses: actions/setup-node@v4
with:
node-version: "22"

- name: Vectors against the WebAssembly
working-directory: ui/tree-editor/merge-core-bench
run: node run_vectors.mjs adapters/rust_wasm.mjs --strict
18 changes: 15 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ downloads/
eggs/
.eggs/
lib/
!ui/lib/
!ui/tree-editor/ui/src/lib/
lib64/
parts/
sdist/
Expand All @@ -27,6 +29,7 @@ share/python-wheels/
*.egg
MANIFEST

.impeccable/
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
Expand Down Expand Up @@ -71,6 +74,7 @@ instance/

# Sphinx documentation
docs/_build/
docs/tutorials/
docs/_autosummary/

# PyBuilder
Expand Down Expand Up @@ -174,7 +178,15 @@ cython_debug/
.python-version

# hatch-vcs generated version file
decider/_version.py
decikit/_version.py

# project-level extension configs (generated by decider_extensions)
projects/*/configs/
# project-level extension configs (generated by decikit_extensions)
projects/*/configs/
.scratch/


# Benchmark output; the numbers that matter are in docs/design_decisions/experiments/.
benchmarks/*.csv

# decikit build output
examples/*/decikit-cpu-target.json
85 changes: 85 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# decikit

Use `uv` for everything: `uv run pytest`, `uv run python`. Python 3.10+.

To learn the API as a user: `uv run decikit guide` (`decikit/GUIDE.md`) and the
docstrings. Every Python example in `GUIDE.md` runs in `tests/test_guide.py`;
keep it that way.

`docs/` is the docs site (nav in `zensical.toml`; `uv run --group docs zensical
build`). `docs/design_decisions/` is not published: the decisions behind each
area, the experiments behind them and `known-limitations.md`.
Read an area's decision file before changing that area. `docs/design_specs/`
(also unpublished) holds proposals for work not yet built, one folder per rework.

## Comments and docstrings

- Docstrings only on public API, written as user docs: a one-line summary, the
arguments that aren't obvious, and a short example.
- Private helpers get no docstring unless the behaviour is non-obvious.
- Inline comments explain why, never what.
- Never reference docs, section numbers, stages, experiments, review findings or
"the agent" in code. No `doc 03`, `§4.2`, `EXPERIMENTS`.
- Design rationale ("we chose X over Y because Y failed") goes in
`docs/design_decisions/decisions/<topic>.md` (a measurement as an experiment in
`docs/design_decisions/experiments/`), not in code. Key points only; an open
gap goes in `docs/design_decisions/known-limitations.md`.

## Files

- One purpose per file, under 500 lines. When a file grows past that, split it
into a package (`foo/__init__.py` plus files) instead.
- A file-length exception goes in `ALLOWLIST` in `tests/test_conventions.py`;
import, `__all__` and layer exceptions go in `tests/conventions_allowlists.py`.
Each needs a reason, and stale entries fail the test.
- `uv run pytest tests/test_conventions.py -q` enforces the line limit and the
banned references.

## Writing code

- Invoke the `ponytail:ponytail` skill in `ultra` mode and apply it throughout:
the simplest code that passes the tests, no speculative abstractions, stdlib
first.
- If a simplification would change behaviour a test pins down, keep the
behaviour and leave a `# ponytail: <what could be simpler>` comment.
- Tests go in `tests/<area>/`. Test names describe behaviour
(`test_missing_required_param_is_invalid`, not `test_case_3`).

## Layout

- `decikit/` is the package. Its layout, the layer map and where a new feature
goes: `docs/architecture/package-layout.md`. The code is the truth where they
differ.
- `_engine/` imports nothing from `steps/trees|tables|scorecard`, `config/` or
`serving/`.
- Packages import only downward per the layer map in `package-layout.md`;
`tests/test_conventions.py` enforces it.
- `benchmarks/` holds plain scripts (`uv run python benchmarks/<name>.py`),
not tests. Each backs an experiment in `docs/design_decisions/experiments/`.

## Contract

- `docs/architecture/` plus the code describe the system (steps and IR: `steps-and-ir.md`,
`engine/ir.md`); `docs/design_decisions/` holds the why.
- If the docs and the code disagree, or the intended behaviour is ambiguous, stop
and report. Don't guess.

## Numeric and numba traps

- No generated source: fused kernels are built in-process from LLVM IR, and
user steps use numba's disk cache with the key extended by content
(`fingerprint` plus `SALT`). Key compiled code by content, never by path or
name.
- Serving kernels are `nogil=True`. `fastmath` is off by default.
- Kernels use CPython-equal `round(x, n)` and `x ** n`; int64 still wraps silently. Money
is `Currency` / `Decimal[p, s]`: an int64 mantissa with the scale in the type, half-even
operators, `decikit.ops.decimal.round` for other modes. Running totals accumulate in float64.
- Compile-time failures and steps compiled engines refuse fall back to Python per
step (with a warning; `FusedEngine(strict_compile=True)` raises). Runtime errors
always propagate.
- Nested polymorphic pydantic fields need `SerializeAsAny`, or subclass fields
are silently dropped on dump.
- Single-record calls take a dict, not keyword arguments. Cache converted params
bundles; conversion costs more than validation.
- Each git worktree has its own numba cache, so the first test run in a fresh
worktree is slow.
Loading
Loading