oauth.sx routes the PKCE check through pkce_ok: an S256 challenge carried as
{s256, Hash} compares crypto:hash(sha256, Verifier) =:= Hash; a bare
challenge stays plain (§4.1), so both methods coexist with no change to
existing flows (the bare path is the old =:= behaviour). Raw sha256 digests
are compared (base64url is wire encoding, omitted). New tests/pkce.sx (6,
incl. S256 through PAR). Verified pkce 6/6; substrate fix is in the
preceding commit. 239 total.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
er-eval-binary-segment evaluated a string-valued segment (the parser
represents <<"abc">> as one integer segment whose value is the whole string
"abc") by calling er-emit-int! on the string, emitting a single bogus 0
byte. So every <<"...">> literal became {:tag "binary" :bytes (0)} — which
made binary =:= read as "always equal" and crypto:hash input-independent.
Fix: the integer branch now expands a string value to one byte per
character (Erlang semantics: <<"abc">> ≡ <<97,98,99>>). Verified:
byte_size(<<"abc">>)=3, <<"a">> =:= <<"b">> is false, crypto:hash distinct
per input.
(User-authorized cross-scope fix from the identity loop; loops/erlang
should adopt this as the owner of lib/erlang.)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ev/book-checked! prevents an attendee double-booking themselves across
different events by consulting their persist-derived availability for the
occurrence window (:time-conflict on overlap; same-occurrence re-book stays
idempotent).
ev/book-checked! prevents an attendee double-booking themselves across
different events: consults their persist-derived availability (ev/free-p?) for
the occurrence window, returns :time-conflict on overlap else the normal
ev/book-occ! result. Re-booking the same occurrence stays idempotent
(:already); other actors unaffected. ev/would-time-conflict? predicate.
311/311 green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
tests/integration.sx — one narrative across every module: catalog -> stock
check -> quote (promo+stack+tax) -> attribution -> order flow -> payment
envelope -> settle -> recon -> refund flow -> ledger mismatch, asserting the
seams tie together with consistent numbers. Proves the three-substrate
composition (minikanren pricing + flow lifecycle + persist ledger) end to end.
Total 297/297 across 18 suites.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
unrelate-node! retracts every local edge touching a node (all kinds, both
directions); leaves federated peer links alone. 147/147.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lib/relations/tree.sx over reach/ancestors/rnode — no new Datalog closures. 126/126.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
refund.sx — refund as a second flow-on-sx flow (request -> approve -> settle)
with two suspension points (approval = human/policy decision, settle =
provider). refund-begin! records :refund-requested and suspends at approval;
refund-approve! advances to settle; refund-settle! records :refunded
(idempotent) and completes; refund-reject! records :refund-rejected and cancels.
Only :refunded moves the books. Reuses order.sx flow helpers. Completes the
Phase 5 backlog. Total 278/278 across 17 suites.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Keep the Datalog ruleset minimal — every dl-query re-saturates, so shape
queries are SX BFS over erel, not extra closures. 110/110.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reference index (matching datalog/persist convention): canonical load order and
the full public surface across all 10 modules, plus artdag/version. Wired into the
conformance load list. Total 158/158 unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fault.sx run-safe: a node op may return (artdag/fail reason); failure is confined
to that node + downstream dependents while independent branches compute, and failed
results are never cached, so retry after a fix recomputes only the failed closure
and hits the good nodes. fault 14/14, total 158/158.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
stock.sx — reservation as a precondition the host checks before order-begin!
(validate -> begin), keeping the flow pure. available-stock reads catalog stock
facts; can-reserve?/reserve-check/reservation-shortfalls gate a cart;
effective-available nets out concurrent reservations so orders can't
over-reserve; sufficient-stocko is the multidirectional availability query.
Total 258/258 across 16 suites.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
stats.sx reports hit-ratio, cost-weighted work-recomputed/work-saved,
savings-ratio, and exec-summary over an execution record. Verifies cold (0
saved), warm (all saved), and incremental (saved = unchanged, ran = dirty
closure). stats 12/12, total 144/144.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
artdag/optimize entries outputs fusible? fuses the entry list then DCEs against
the output names — sinks survive fusion (never absorbed), so output-equivalent
with fewer nodes. optimize 22/22, total 132/132.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds the SX->Scheme delivery bridge (ev/deliver-messages): notification-
derivation modules (reminders/booking-lifecycle/reschedule) now flow through
the durable notify flow end to end, with an integration suite covering
delivery success, transient-failure, and empty-batch paths.
serialize.sx emits a topo-ordered (id op inputs params commutative) record list
that survives write/read (string-keyed node dicts do not; empty inputs read back
as nil and are normalized). wire->dag reconstructs a runnable dag by content-id;
wire-verify recomputes ids to reject tampering. dag->string/string->dag for text
transport. serialize 13/13, total 128/128.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
optimize.sx adds three result-preserving passes: dce (keep outputs + ancestors,
preserve ids), cse (==build; structural sharing is free from content addressing),
and fuse (collapse 1-to-1 fusible unary chains into an artdag/pipeline node fed by
the chain head's input; leaves/fan-out/non-fusible ops never fuse). fusing-runner
replays pipeline stages, output-equivalent to the unfused dag. optimize 18/18,
total 87/87.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
nettax.sx — alternative to quote.sx's gross-tax default: cart-quote-net taxes
the net (post-discount) base. allocate-discount spreads the basket discount
across lines by extended-price share with a deterministic largest-remainder
pass so per-line shares sum exactly to the discount; each line taxed on its net
at its class rate. Both policies reproducible; pick per jurisdiction.
Total 239/239 across 15 suites.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
execute.sx folds a plan, runs each node via an injected runner (perform in
prod, op-table in tests), and memoizes results in a lib/persist kv backend
keyed by content-id. Incremental recompute falls out of content addressing:
a leaf change reassigns ids across its dirty closure, so re-running hits the
unchanged nodes and recomputes only the closure (cold 5 -> rerun 0 -> change 3).
Cross-dag subgraph sharing verified. execute 15/15, total 69/69.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
plan.sx schedules a dag into Kahn-wave batches (parallel-safe), splits waves
wider than a cap into sub-batches, and plans incrementally over the dirty
closure only (out-of-set deps treated as satisfied cache hits). plan 18/18,
total 54/54.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
analyze.sx projects DAG edges to (edge in out) facts and runs recursive
reachable rules for deps-of/dependents-of/reachable-from/ancestors-of, plus
dirty-closure (dirty(Y):-edge(X,Y),dirty(X)) for incremental recompute. Keystone:
changing a mid node dirties only it + downstream. analyze 16/16, total 36/36.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ev/deliver-messages bridges SX notification messages to the Scheme notify
flow: each (id recipient body) is serialized to s-expr text, spliced as quoted
data into the digest-flow program, delivered over an injected transport, and
results unboxed. Integration suite drives all three derivations (reminders /
booking-notify / reschedule) through delivery end to end; empty batch guarded
(empty digest completes without suspending). 303/303 green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Go has the same structure as erlang: suites load into one session and each
exposes a pass counter plus a *count* (total) counter rather than a fail
counter. MODE=dict fits — each suite's runner is a dict literal
{:passed P :failed (- count P) :total count}. No driver change; conformance.conf
+ 3-line shim, historical scoreboard schema preserved.
Parity verified 609/609 (0 fail), every suite matching baseline.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
window.sx — a validity window kept separate from the promo tuple (promo.sx
untouched): windowed promo (promo from until), inclusive int timestamps, nil =
open bound. active-ruleset filters to promos live at `at` and feeds the existing
promo/stack/quote pipeline; active-codes is the backward "which codes live at
T?" query; windowed-quote is the datetime-aware, deterministic quote.
Total 228/228 across 14 suites.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
payment.sx — payment-request materialises {:order :amount :currency :return-url}
at the IO edge (amount from the ledger, currency/return-url host-supplied), so
lib/commerce stays vendor-agnostic; SumUp/Stripe adapters live in the orders
service and order-settle!(ref, amount) is the resume seam. pending-payments
enumerates suspended orders + envelopes (host poller seam). Gotcha handled: a
Scheme string flow-payload round-trips back wrapped as {:scm-string ...} —
unwrapped via scm->string. Total 209/209 across 13 suites.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Feed is the canonical MODE=counters shape: each suite runs in a fresh session
with shared preloads and a single feed-test-pass/feed-test-fail pair. Lifted the
old script's inline epoch-2 counter + feed-test helper defs into
lib/feed/test-harness.sx (preloaded last) so the driver can load them before
each suite. conformance.conf + 3-line shim; historical scoreboard schema
preserved. No driver change needed.
Parity verified 189/189 (0 fail), every suite matching baseline.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
attribution.sx — the briefing's marquee "which line item triggered this
discount?" backward query. promo-lines gives each promo's pure scope
(percent/member -> class lines, bundle -> sku lines, fixed -> order-level);
promo-toucheso relates (code, line) for applying promos, run forward
(lines-for-code) and backward (codes-for-line). Additive; promo amounts
unchanged. Total 201/201 across 12 suites.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Erlang's suites load into one session and each exposes a pass counter plus a
*count* (total) counter rather than a fail counter, so MODE=dict fits directly:
each suite's runner is a dict literal {:passed P :failed (- count P) :total count}.
No driver change needed (dict mode already supports arbitrary runner expressions).
conformance.conf + 3-line shim; historical scoreboard schema preserved.
Parity verified 761/761 (0 fail), every suite matching baseline.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extend the shared driver's MODE=counters with a backward-compatible SUITES
format: name:file[:pass-var:fail-var[:extra-preload ...]]. Optional per-suite
counter symbols (override the global COUNTERS_PASS/COUNTERS_FAIL) and per-suite
preload chains (loaded after the global PRELOADS). Plain name:file entries are
unchanged — verified against haskell (fib/sieve/quicksort 2/2/5, matches
committed scoreboard).
common-lisp has 8 distinct per-suite counter pairs and a different preload
chain per suite, so it could not fit the single-counter/fixed-preload model;
the extended format expresses it directly. conformance.conf keeps the historical
scoreboard schema; conformance.sh becomes the 3-line shim.
Result 487/487 (0 fail) vs the old 305/0 baseline — higher and explained: the
old per-suite 'timeout 30' was too tight for the slow eval suite (~15-25s under
contention), silently recording it as 0; the driver's 180s budget recovers its
true 182. geometry/mop-trace stay 0/0 (pre-existing refl-class-chain-depth-with
load error; counter vars defined as 0 -> clean gc-result, no fail-fallback).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
recon.sx — reconciliation as relational queries over the ledger: per-order
summary tuples + recon-statuso/neto/mismatcho miniKanren relations, so
overpaid/underpaid/settled and "settled to net N" are backward run* queries.
Tests cover double-charge guard, partial refund, webhook replay.
federation.sx (out-of-scope stub) — a federated catalog is the union of each
instance's product facts, so the same relations query cross-instance
(instances-with-sku, sku-offers, cheapest-offer). In-process mock, no network.
Completes the commerce-on-sx roadmap (Phases 1-4). Total 185/185 across 11 suites.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
timezone.sx: wall-clock LOCAL <-> absolute UTC. :fixed + :dst zones (std/dst
offsets + UTC transition rules, EU-style, no IANA DB) computed via calendar
helpers. ev-event-tz authors in local time; ev-expand expands tz events in
LOCAL time then converts each occurrence to UTC, so a 09:00 weekly meeting
stays 09:00 across a DST change (UTC instant shifts). Predefined utc/london/
paris. Plain events unaffected. 295/295 green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
order.sx — reserve -> await-payment -> fulfil as a flow-on-sx flow carrying
only the order-id; the SX driver services each request by appending to the
persist ledger. order-begin! creates+reserves and suspends at payment;
order-settle! (webhook) resumes -> fulfils, idempotent on replay
(:already-settled). order-flow-restart! simulates a process restart Scheme-side
and the suspended order resumes with the ledger intact. Composes all three
substrates: minikanren pricing -> flow lifecycle -> persist ledger.
Total 153/153 across 9 suites.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>