Surgical add of the two radar-authored planning docs onto architecture (both new
files, no conflict). Migration strategy: duplicate->cutover->diverge, strangler edge
+ layer-split shadow-diff, host-trio critical path. abstractions.md is the evidence
base the strategy cites (A1 done, W1/W4/W8 substrate-adoption findings).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Doc-only: records that the http-listen 'handler-mutex deadlock' is not a
mutex bug but an Erlang-scheduler-context issue (handler runs on a native
Thread.create outside any er-sched step, so gen_server:call->receive can
never complete). Pattern A inapplicable; correct fix is Pattern B in
er-bif-http-listen (lib/erlang, m2 scope). Full diagnosis + patch sketch in
plans/fed-sx-host-primitives.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Facade read-by-id was top-level only while content/edit's update/delete are
tree-wide — could not read back a nested block content/edit just modified.
Added generic ct-find-id (doc.sx) + doc-find-deep/doc-has-deep?; content/find
+ has? now descend into sections. content/find-top/has-top? keep top-level
lookup. Audit: remaining doc-find/ct-index-of callers are positional
insert/move (top-level by design). +6 api tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Investigated the http-listen "handler-mutex deadlock" per
plans/agent-briefings/fed-prims-mutex-fix.md. Reproduced deterministically
(single kernel-route request returns empty reply while a non-kernel route
returns 200; also reproduced with a 3-line minimal echo gen_server).
Root cause is in the Erlang substrate, not the OCaml mutex: native
http-listen runs each handler on a fresh Thread.create outside any Erlang
scheduler step, so gen_server:call -> receive (which raises er-suspend-marker
expecting an enclosing er-sched-step-alive! guard + er-sched-run-all! pump)
can never complete.
Pattern A is inapplicable: the failure reproduces on a single request with
zero contention, so it is not a mutex-contention deadlock; the mutex is in
fact required and must stay. Sx_runtime.sx_call is fully synchronous and no
OCaml symbol reaches the SX-level scheduler, so there is no OCaml-only fix.
The correct fix is Pattern B done entirely in er-bif-http-listen
(lib/erlang/runtime.sx) — spawn the handler as an er-process and
er-sched-run-all! to completion — which is m2 / loops/erlang scope.
Doc-only: full diagnosis + concrete patch sketch added to the Blockers and
Progress log of plans/fed-sx-host-primitives.md. No bin/sx_server.ml change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
content/diff + diff-versions enumerated ids top-level only (doc-ids/
doc-find), so diffs of documents with sections missed every nested add/
remove/change. Now via doc-tree-ids + doc-deep-find; sections excluded from
:changed (no own content), still reported in :added/:removed. Flat-doc
diffs unchanged. +9 store tests.
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>
tcl conformance.sh walks foreign lib/tcl/tests/programs/*.tcl files, reads each
first line's '# expected: VALUE' annotation, uses python3 to escape the Tcl
source into an SX helper, evaluates via (tcl-eval-string ...), and string-compares
got vs expected in bash. No SX test suites and no SX counter/dict scoreboard, so
the shared driver can't drive it (same category as lua/js/forth). Left
conformance.sh untouched; recorded the exclusion.
This completes the A1 worklist: 4 migrated onto the shared driver (common-lisp,
erlang, feed, go) and 5 excluded as foreign runners (forth, js, ocaml,
smalltalk, tcl).
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>
content/search-text + search-text-ids find every block whose (asText b)
contains a term — spanning all text-bearing fields by reusing the canonical
asText projection, so it can't drift from stats/find-replace. Section
wrappers excluded. +7 query tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
smalltalk conformance.sh catalogs foreign lib/smalltalk/tests/programs/*.st
programs, runs 'bash lib/smalltalk/test.sh -v', and scrapes its output (the
'OK 403/403' summary plus per-file pass counts via awk). It loads no SX test
suites directly and emits no SX counter/dict scoreboard. This is the briefing's
own classification example ('smalltalk runs *.st via test.sh') and the same
'scrapes a test.sh' exclusion as ocaml/lua. Left conformance.sh untouched;
recorded the exclusion.
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>
ocaml conformance.sh runs 'bash lib/ocaml/test.sh -v', scrapes its
human-readable ok/FAIL lines, and re-classifies each test into suites via bash
description-matching heuristics; it also scrapes lib/ocaml/baseline/run.sh
(foreign .ml programs). The underlying test.sh is a per-assertion epoch runner
(hundreds of individual (ocaml-test-...) evals, one epoch each) with no
suite-level counter variables or dict runners, so the driver's
counter/dict-scoreboard model has nothing to point at without rewriting the test
harness. 'Scrapes a test.sh' is the briefing's named exclusion criterion (test.sh
even notes it mirrors lib/lua/test.sh, the canonical excluded case). Left
conformance.sh untouched; recorded the exclusion.
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>
fr-rewrite dispatches per block type so image alt, list items, and table
headers/cells are renamed alongside text/heading/code/quote/callout —
matching exactly the set asText/stats/word-count fold into prose. Prior
find-replace skipped them, so a rename stayed visible in counts/exports.
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>
js conformance.sh walks lib/js/test262-slice/**/*.js (foreign test262
fixtures), escapes each with python3, evals via (js-eval), and compares output
to a sibling .expected file by substring match — counting pass/fail in bash
against a >=50% target. It loads no SX test suites and emits no SX counter/dict
scoreboard (no scoreboard.json). The shared driver only epoch-loads SX preloads
and evals SX test suites emitting a scoreboard — it cannot drive a
foreign-fixture-vs-expected comparison harness (same category as
lua/forth/smalltalk). Left conformance.sh untouched; recorded the exclusion.
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>