You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The multi-axis TemporalContext runtime shipped, but the rule-version axis is write-only. tcRuleValidTime / tcRuleVersionTime / tcRuleEncodingTime / tcRuleCommit are stamped by EVAL UNDER RULES EFFECTIVE AT (and re-stamped per stepped day by the interval builtins), yet no reader consumes them, so EVAL UNDER RULES EFFECTIVE AT is pure bookkeeping — the evaluator still runs the single in-memory rule set regardless of the axis. tcRuleCommit is only ever Nothing.
This is the tracking issue for making the rule-version axis genuinely load-bearing, and for recording the design decision (which rejects the old git-backed vision).
EVAL AS OF SYSTEM TIME is load-bearing (TODAY/NOW read tcSystemTime).
Interval builtins EVER/ALWAYS BETWEEN, WHEN LAST/NEXT, VALUE AT (day-stepping; each day re-applies [UnderValidTime d, UnderRulesEffectiveAt d]).
The T6 WHNFWhen context-fingerprint cache (thunk-leak fix, Local declarations #58) and PR#80 exception-unwind context restore — these turn out to be exactly the two preconditions any axis reader needs, and they already ship.
Decision
Make the rule-validity axis load-bearing in-language; keep git out of the evaluator heap.
Phase 0 — RULES EFFECTIVE DATE, a nullary DATE builtin that reads tcRuleValidTime through an instrumented reader (new crRuleValidTime fingerprint axis). Rule versioning becomes ordinary L4: IF DATE_SERIAL RULES EFFECTIVE DATE AT LEAST DATE_SERIAL (Date 1 1 2024) THEN 9 ELSE 7. (spike done + verified — see linked issue.)
Phase 1 — semantics hardening: finalize the unset-axis fallback, delete the latent tcRuleEncodingTime coercion bug, add RETROACTIVE TO sugar, trace/provenance polish.
Phase 2 — authoring surface: @effective / @repealed / @label decorators on same-namedDECIDEs, desugared at check time into one visible dispatcher over RULES EFFECTIVE DATE (no runtime dispatch value, no EvalState table, no hidden name-mangling). Each arm keeps its own source range + amending-instrument label for citations, NLG/TNR anchors, and diffability.
Phase 3 — encoding-history counterfactuals as driver-level tooling (l4 diff-eval + a jl4-service deployment-version compare endpoint): two fully independent compile+eval passes diffed at the JSON boundary. No evaluator changes, nothing crosses a heap.
Permanently rejected: in-heap EVAL UNDER COMMIT
Splicing a separately-compiled module's References/constructors/entityInfo into a live evaluation is rejected on code-verified grounds:
imports resolve through project:/<name>.l4 (Import/Resolution.hs), so two snapshots collide on URIs and unique supplies → silent wrong-type dispatch;
the single caller-side entityInfo cannot describe historical constructors (cross-version values break both directions);
CONSIDER branches silently mis-select on sameResolved mismatch (worse than an error);
a published construct would mean different things per deployment config — disqualifying for an audit-grade legal SOR.
Any future revival must first solve URI namespacing and cross-version value marshalling — treat those as hard gates.
Why in-language wins
Convergent verdict of three independent adversarial design critiques (git-backed / in-language / hybrid).
Determinism/hermeticity: same source + same TemporalContext ⇒ same answer in every driver (CLI/LSP/WASM/service), offline, no repo convention, no resolver config.
Isomorphism for lawyers: a consolidated rule with dated arms keeps both regimes visible and citable in the reviewed artifact.
The interval builtins work for free: they already stamp UnderRulesEffectiveAt d per day, so VALUE AT / EVER BETWEEN compute the point-in-time reading (the canonical "rate changed mid-period" case).
opm2l4 can emit it; a code generator cannot emit a git-backed heap splice.
Design doc
Full reconciliation, per-approach kill-shots, phased plan with file:line insertion points, and the spike design live in specs/todo/TEMPORAL-RULE-VERSION-DESIGN.md (branch mengwong/temporal-rule-version, pending PR into unstable). The three older specs (TEMPORAL_EVAL_SPEC.md, TEMPORAL_PRELUDE_MACROS.md, TEMPORAL-MASTER.md) have been updated to mark the git-backed vision superseded.
Open questions (see design doc §6)
Unset-axis fallback: localized system-day vs tcValidTime-first vs hard error (leaning valid-time-first, for consistency with the interval builtins' per-day stamping).
Collapse tcRuleVersionTime into tcRuleValidTime vs give them distinct meanings.
Cache granularity under multi-decade day-stepping (regime-interval ReadObs refinement if a perf golden hurts).
Disposition of UNDER RULES ENCODED AT (warn "bookkeeping only" vs deprecate until Phase 3).
Summary
The multi-axis
TemporalContextruntime shipped, but the rule-version axis is write-only.tcRuleValidTime/tcRuleVersionTime/tcRuleEncodingTime/tcRuleCommitare stamped byEVAL UNDER RULES EFFECTIVE AT(and re-stamped per stepped day by the interval builtins), yet no reader consumes them, soEVAL UNDER RULES EFFECTIVE ATis pure bookkeeping — the evaluator still runs the single in-memory rule set regardless of the axis.tcRuleCommitis only everNothing.This is the tracking issue for making the rule-version axis genuinely load-bearing, and for recording the design decision (which rejects the old git-backed vision).
What already works (on
unstable)TemporalContext+ 4EvalClausevariants (UnderValidTime,AsOfSystemTime,UnderRulesEffectiveAt,UnderRulesEncodedAt);applyEvalClausespure.EVAL AS OF SYSTEM TIMEis load-bearing (TODAY/NOWreadtcSystemTime).EVER/ALWAYS BETWEEN,WHEN LAST/NEXT,VALUE AT(day-stepping; each day re-applies[UnderValidTime d, UnderRulesEffectiveAt d]).WHNFWhencontext-fingerprint cache (thunk-leak fix, Local declarations #58) and PR#80 exception-unwind context restore — these turn out to be exactly the two preconditions any axis reader needs, and they already ship.Decision
Make the rule-validity axis load-bearing in-language; keep git out of the evaluator heap.
RULES EFFECTIVE DATE, a nullaryDATEbuiltin that readstcRuleValidTimethrough an instrumented reader (newcrRuleValidTimefingerprint axis). Rule versioning becomes ordinary L4:IF DATE_SERIAL RULES EFFECTIVE DATE AT LEAST DATE_SERIAL (Date 1 1 2024) THEN 9 ELSE 7. (spike done + verified — see linked issue.)tcRuleEncodingTimecoercion bug, addRETROACTIVE TOsugar, trace/provenance polish.@effective/@repealed/@labeldecorators on same-namedDECIDEs, desugared at check time into one visible dispatcher overRULES EFFECTIVE DATE(no runtime dispatch value, noEvalStatetable, no hidden name-mangling). Each arm keeps its own source range + amending-instrument label for citations, NLG/TNR anchors, and diffability.l4 diff-eval+ a jl4-service deployment-version compare endpoint): two fully independent compile+eval passes diffed at the JSON boundary. No evaluator changes, nothing crosses a heap.Permanently rejected: in-heap
EVAL UNDER COMMITSplicing a separately-compiled module's
References/constructors/entityInfointo a live evaluation is rejected on code-verified grounds:project:/<name>.l4(Import/Resolution.hs), so two snapshots collide on URIs and unique supplies → silent wrong-type dispatch;entityInfocannot describe historical constructors (cross-version values break both directions);CONSIDERbranches silently mis-select onsameResolvedmismatch (worse than an error);Any future revival must first solve URI namespacing and cross-version value marshalling — treat those as hard gates.
Why in-language wins
TemporalContext⇒ same answer in every driver (CLI/LSP/WASM/service), offline, no repo convention, no resolver config.UnderRulesEffectiveAt dper day, soVALUE AT/EVER BETWEENcompute the point-in-time reading (the canonical "rate changed mid-period" case).Design doc
Full reconciliation, per-approach kill-shots, phased plan with
file:lineinsertion points, and the spike design live inspecs/todo/TEMPORAL-RULE-VERSION-DESIGN.md(branchmengwong/temporal-rule-version, pending PR intounstable). The three older specs (TEMPORAL_EVAL_SPEC.md,TEMPORAL_PRELUDE_MACROS.md,TEMPORAL-MASTER.md) have been updated to mark the git-backed vision superseded.Open questions (see design doc §6)
tcValidTime-first vs hard error (leaning valid-time-first, for consistency with the interval builtins' per-day stamping).tcRuleVersionTimeintotcRuleValidTimevs give them distinct meanings.ReadObsrefinement if a perf golden hurts).UNDER RULES ENCODED AT(warn "bookkeeping only" vs deprecate until Phase 3).