Architecture Decision Records

Copy Markdown View Source
#DecisionStatus
0001Record architecture decisionsaccepted
0002Port the W3C SCXML algorithm literally (Appendix D)accepted (amended 2026-08-09: predicate naming)
0003Pure functional core returning effectsaccepted
0004Predicator is the datamodel; no ECMAScript, no Elixir evalaccepted (amended in part by 0026: <script>)
0005Full configuration; interned state indexesaccepted
0006Reuse conformance corpus and ratchet; commit a generatoraccepted
0007Beads for issue trackingaccepted
0008UXID for generated identifiersaccepted (amended 2026-08-15: invoke id format; invoke platformid is a session counter, not a UXID; amended 2026-08-15: the uxid dependency is dropped, the format decisions stand)
0009ex_quality is the quality gateaccepted
0010Worktree parallel development via beadsaccepted
0011Quality gate config is not agent-editableaccepted
0012Debuggability is designed into the coreaccepted
0013Archive the v1 statifier repo in placeaccepted
0014Expression-level spans are part of the retained-location constraintaccepted (amended 2026-08-15: item 4 stops at the predicator seam; engine policy checks are not expression failures; amended 2026-08-18: item 4's protected-root refusal is a policy check, not a predicator error)
0015Skill mechanics live in scripts, judgment lives in prosesuperseded by 0017 (amended in part by 0016)
0016The wurk skills live in their own repo; this repo gates its extensionsaccepted (amends 0015 in part; amended by 0017)
0017Judgment is not scriptable, scoped to the wurk extension surfaceaccepted (supersedes 0015; amends 0016 in part)
0018Process artifacts are not code commentsaccepted
0019A round budget bounds the macrostep foldaccepted (amended in part by 0020: round ordinal; amended in part by 0032: invoke re-entry)
0020A round ordinal joins the step countersaccepted (amends 0019 in part; amended in part by 0046: the core-effect exemption is withdrawn)
0021A failed donedata content expr yields no dataaccepted
0022A parallel is never the LCCA; SCION's contrary tests leave the corpusaccepted
0023Numeric-type gaps are fixed in predicator, never coerced at the boundaryaccepted
0024<data src> is never fetchedaccepted
0025Tracker authority follows the artifact; mirrors pull (adopts predicator ADR-0010)accepted
0026<script> bodies are predicator statement programsaccepted (amends 0004 in part)
0027Embedder-placed session runtime with a named registryaccepted
0028Executable-content blocks thread one context and bind/3 each writeaccepted
0029Session.interpret/2 stays public; replay records four inputsaccepted
0030In/1 becomes a provider; the built context still is not a MachineState fieldaccepted (amended 2026-08-15: predicator 8.0 memoization; hoist kept)
0031A failed invoke argument evaluation aborts the invocationaccepted
0032The round budget spans the invoke pass's re-entryaccepted (amends 0019 in part)
0033The validator has a warning tier; warnings ride on the Machineaccepted
0034Replay re-drives the core, not a live session; recording carries ordinal order, no clockaccepted
0035The send id is send_<n> off a new machine_state.send_counteraccepted (amended 2026-08-15: cross-session sendid collision recorded harmless)
0036A failed <send> argument discards the message (amends 0021 in part)accepted
0037Unbound is spelled :undefined at the writer; nil means nullaccepted
0038<invoke>'s source resolves at the session boundary, never inside the libraryaccepted
0039Session-detected send failures re-enter the core through deliver_internal/5accepted (amended in part by 0047: the rejected alternative is scoped to liveness; amended in part by 0048: the core may judge reachability against a caller-declared route snapshot)
0040Session telemetry event contract: the [:statifier, :session, ...] bridge, measurements-vs-metadata split, and trace-off policyaccepted (amended 2026-08-16: singleton location carve-out withdrawn; no trace event carries a location; amended 2026-08-16: :datamodel_change joins the core effect events; amended in part by 0046: round joins every core-effect event's measurements; amended in part by 0059: ordinal joins the :send_delayed and :cancel events' measurements; amended in part by 0063: caller_context joins the macrostep and durable-timer events' metadata)
0041<content> markup lowers to a source slice, compiled at invoke timeaccepted (amended 2026-08-16: namespace limitation corrected - the corpus child documents do not compile standalone; amended in part by 0042)
0042Invoke content markup compiles under the relaxed namespace ruleaccepted (amends 0041 in part)
0043Attribute values normalize per XML 1.0 3.3.3, guided by the raw sourceaccepted
0044Re-entry effects defer to the outer batch; subscriber delivery is monotone in (macrostep, round) and {:halted, _} is end-of-streamaccepted
0045Character data folds line breaks per XML 1.0 2.11, guided by the raw source (resolves 0043's open question)accepted
0046Every core effect carries round; the ADR-0020 exemption is withdrawn and round joins the core-effect event measurementsaccepted (amends 0020 in part; amends 0040 in part)
0047Static send target/type invalidity rejects in the core; reachability stays session-side, test496 deferred to its own recordaccepted (amends 0039 in part)
0048Send reachability is judged in the core against a caller-declared route snapshot; core-detected unreachability aborts the block (discharges 0047 decision 6's deferral)accepted (amends 0039 in part)
0049Late subscribers catch up by replaying the recording; no header effect joins the streamaccepted
0050Invoked children inherit the parent's observers by opt-in; the invocation table gets a public accessoraccepted
0051Invoke handlers are registered per session; the registered invoke-type set becomes core-visible deployment stateaccepted
0052Chart identity is a content hash of the SCXML source; a position blob carries identity and a format version, checked before useaccepted
0053The chart-author test helpers ship in lib/ under Statifier.Testingaccepted (amends 0006 in part)
0054Durable timers consume the effect vocabulary; the host owns keying and the 6.2 discardaccepted (amended 2026-08-19: decisions 2, 3, and 4 corrected; decision 2's recorded gap decided by 0055; decision 3's residual foreach collision decided by 0059)
0055Non-self delayed-send routes stay the library's: #_parent/#_invokeid/#_internal permanently, the external-session route deferred with a named trigger; no route field joins %SendDelayed{}accepted (decides 0054's recorded gap)
0056After a renumbering, pointer citations move (path fix plus an at-the-time note) and historical statements stand; cross-repo ADR citations must name the repoaccepted
0057A recording blob nests the chart blob in the compiled %Machine{}'s place; the codec lives on the @opaque owner; :invoke_handlers cross the boundary as strings, never as atoms or codeaccepted (answers 0052 decision 8's follow-up)
0058ADR number collisions fail the gate via a tree-local numbering invariant; the README table becomes machine-readaccepted (amended 2026-08-19: decision 2's bite point corrected - the base-ref half fires after the rebase, not merely after a fetch; its addition is the rename/renumber shape; renumbered from 0056 before merge)
0059%SendDelayed{} and %Cancel{} carry a per-execution ordinal off a new timer_counter; the durable dedup key gains it as its eighth component and the foreach author guidance retiresaccepted (amends 0054 in part; amends 0040 in part)
0060Session.start_link/2 gains a :resume option for a position blob or %MachineState{}; resume requires an identified chart, keeps the position's _sessionid, refuses a non-quiescent or terminated position, carries active_invocations verbatim, and anchors a resumed session's recording at the resumed positionaccepted (amends 0057 in part; amends 0049 in part)
0061Consumers pin main SHAs under a documented contract until 2.0.0: no pre-release is published, 2.0.0-dev stands on updated grounds, the fragment rule widens to cover breaks between pins, package/0 metadata lands without publishing, and the no-publish rule defers to a named triggeraccepted (contract ended with the 2.0.0 release per its own decision 5; re-decided by 0066)
0062The OpenTelemetry bridge is a separate package, opentelemetry_statifier: family-scoped, public-events-only, git-pinned and unpublished until statifier is on Hex; span topology and propagation in docs/opentelemetry.mdaccepted
0063An opaque caller_context :: term() (default nil, never read by the library) rides %Event{} via external/2, is stamped by the core onto %SendDelayed{}/%Cancel{}, surfaces in four telemetry events' metadata, and recordings carry it (format 2 -> 3); ADR-0054's keys untouchedaccepted (amends 0040 in part)
0064The position blob drops routes and invoke_types: to_binary/1 omits both alongside :machine, from_binary/2 blanks both on decode regardless of blob vintage, and the format version stays 2accepted (amends 0052 in part)
0065Statifier.Testing.HandlerCase: a use-injected conformance case any Statifier.Invoke.Handler implementation runs against itself - planning purity, perform/2 idempotency against a declared observation point (fail-not-skip), and the library-half pins via probe handlersaccepted
0066Publishes 2.0.0 to Hex, ending the SHA-pinning contract - ADR-0061's satellite trigger fired; known issues recorded; SemVer and the changelog take overaccepted
0067One telemetry contract across stepping drivers: [:statifier, :session, ...] names the logical session, the emitters generalize into Statifier.Telemetry, Statifier.Session.Telemetry stays as a driver: :session facade, and a driver key joins every event's metadataaccepted (amends 0040 in part)

New ADRs: next number, same three-section format (Context, Decision, Consequences), drafted or reviewed at the direction level per docs/workflow.md. Pick the number against a freshly fetched remote - git fetch origin && git ls-tree origin/main --name-only docs/adr/ - so a branch does not start behind a record that has already landed; mix quality's ADR guard catches a collision that materializes later, but only once the colliding file is in your tree.