Successor to the 2026-08-01 review/decision record (fix-fable.md, now deleted). Every design
decision from that cycle is executed in the normative artifacts and traceable through the commit
history 0ca83bd..6165f47; this file carries only remaining work and standing records.
Working discipline: discuss → decide → sub-agent executes a closed task spec → coordinator checks
diffs → commit. Gate: uv run tools/validate.py stays FAIL 0 and warning-free.
- Machine-readable vector format per
spec/RL2_Compilation.md§7 interchange schemas:(module | universe, Request, WorldSnapshot, EvaluationConfiguration)→ expected(decision, determining SourceRefs, Duty/Promise statuses, causes, diagnostics). - Publish the four JSON Schema files (Request, WorldSnapshot, EvaluationConfiguration,
EvaluationResult) — field tables are normative in
RL2_Compilation.md§7; the schema files ship here. - Convert the four
conformance/vectors/*.mdfiles and ~15 highest-value use cases to vectors. - Negative compiler vectors: at least one per diagnostic code in
RL2_Compilation.md§6.1. - Missing positive vectors identified during review: complementary Privilege/Prohibition pair
under a missing operand; inapplicable prerequisite Duty (vacuous pass vs status-condition
False);permit + indeterminateresolution under each strategy; the four exception-pattern forms; profile version negotiation and digest mismatch; relative-window resolution incl. missing anchor; sentinel-population norm with missing population fact. - F-01 vectors (sentinel Duty binding,
rl2:consequentDuty): positive — a Privilege'sconsequentDutyfires on grant and the envelope carries the bound obligate atom (occurrenceOf(d, Env)), never the sentinel-bearing template; bound prerequisite-gating positive and negative — a sentinel-carryingprerequisiteDutytemplate is gated viadutyStatus(bind(d, requestAgent, requestAsset)), both fulfilled and unmet; a consequent Duty with anUnknowncondition of its own — confirms the decision outcome is unaffected by a consequent Duty's condition value (only the Privilege's ownaccessResult.truth = Truegates firing);UnboundStatusTargetnegative compile vector — anrl2:obligationStateOperandtargeting a Duty whoserl2:subject/rl2:objectis a sentinel. - R-01/R-02/R-03 vectors (status-map and occurrence model,
spec/RL2_Semantics.md§Duty Template Binding and Eval composition): a template Duty (prerequisite, consequent, or independent) reached via the widenedallDuties(P)receives no entry indutyStatusesunder its own identity — onlyconcrete(d)Duties do; a bound occurrence produced byoccurrenceOfat a consequent-duty or independent-clause site contributes adutyStatusesentry keyed by itsoccurrenceId, disjoint from the concrete-Duty key space (boundOccurrenceStatuses). TWO-IDENTICAL-READS — same agent, same asset, two distinctRequest.idvalues against the same template, with Evidence carryingdischargeOfnaming only the first occurrence'soccurrenceId— the first occurrence isKnown(Fulfilled)and the second staysKnown(Pending), confirming per-grant discharge does not leak acrossRequest.idvalues. Per-member fallback — two grants of the same template to the same agent/asset with noRequest.idon either request coalesce onto oneoccurrenceId, so evidence discharging the first grant also discharges the second. Consequent-duty trigger condition consumed — anoccurrenceOf-produced occurrence carriescondition := None(the Privilege's ownaccessResultalready decided applicability at grant time), so a laterdutyStatusre-evaluation of that recorded occurrence cannot re-decide firing eligibility, only fulfillment. Occurrence window resolved at grant time — an occurrence'swindowisresolveWindow's output computed once against the granting evaluation's own snapshot, so a later evaluation supplying a different snapshot does not re-anchor a relative window to the new snapshot's clock. - R-07 occurrence-identity interchange repair:
occurrenceIdbegins withsourceIdentity(d), which may be a module-scopedSourceReffor a valid blank-node Duty, whileEvidence.dischargeOfcurrently admits only an IRI in that position. Align the abstract type and canonical wire encoding (or require an authored IRI for a PER-GRANT template), then add a blank-node template plus correlated-evidence vector. - R-08 prerequisite status-map scope:
boundOccurrenceStatusesmust include a bound prerequisite only when its Privilege is in the request's prerequisite-gating domain (at leastmatchesRequest(n, Env)), or the result model must explicitly become an eager universe-wide report. Add an unmatched-Privilege vector that asserts no irrelevantBoundIdentityentry or diagnostic from its template. conformance/vectors/README.mdoverclaims: these documents are evaluator-independent boundary expectations that specify selected parts of the input tuple, not machine-readable vectors, and they do not by themselves establish evaluator conformance. Correct the framing when the executable suite lands and the two artifact kinds need distinguishing.- F-05 materialization vectors — action Promise with postCondition crystallizes into an
Achievement Duty carrying it (positive); state Promise with postCondition rejected by
PromiseShape(negative). - Translation runner comparing normalized ODRL fixtures to expected modules.
- Reference harness (
tools/evaluate.pyor similar); two independent evaluators before any interoperability claim. tools/validate.pyhardening: remove the...-means-placeholder fence skip; make the injected prefixPREAMBLEopt-in (or add a strict second parse pass); parse the SHACL report graph rather than counting rendered severity strings; validate the shapes graph itself.- F-10 regime fixtures — imports ignored, named graphs ignored, relative-IRI rejection, warning-does-not-reject, canonical report-projection comparison across two SHACL engines.
- Author the migration-profile document: the supported ODRL 2.2 fragment, term-by-term compatibility matrix, each row carrying its three-test verdict (demand / determinism / runtime-independence) and a fixture.
- Consequence mapping rule:
odrl:remedy/consequence→ Duty with applicability conditionobligationStateOperand(targetNorm) = Violated(profile-dependent disposition; the machinery already exists in core). - New migration fixtures:
odrl:xone;odrl:inheritFrom(multiple parents, missing parent, cycle);ConflictingCompactValue. conformance/migration/attached-duty.mdis stale against the committed mapping table: it is markednormalizedand asserts that an attachedodrl:dutybecomes a prerequisite Duty, whereasRL2_ODRL_Mapping.mdnow mapsodrl:dutytorl2:prerequisiteDutyorrl2:consequentDutywith dispositionclarified. Restate the fixture as recording one explicitly declared prerequisite interpretation, and say that it does not establish that interpretation as ODRL's default. Same correction inconformance/migration/README.md.- Reclassifications: ODRL
invalidconflict →clarified(whole-policy void vs request-scoped Indeterminate); Set-rule assigner preservation caveat;rightOperandReference→ pre-resolve into an attributed snapshot fact or reject. - Refinement compile-down rule in the transpiler (component-scoped refinements → rule-level conditions; not an RL2 authoring form).
- Crosswalk to the W3C ODRL Formal Semantics draft (Behaviour ↔
defaultDecision; state taxonomy; report model) and to XACML combining algorithms — expand mapping §8/§11.
- Remedies/Contracts — expression-only (decided): vocabulary + the §2 consequence shape;
document the assembler's violation-evidence obligation in
RL2_ExternalData.md. Violation-evidence vocabulary deferred until cross-party interop is live. - Privacy/Consent — align with pod/Solid practice (Esteves et al. as reference); rewrite
4–6 data-governance use cases onto
spec/profiles/rl2-privacy-profile.ttl(it currently has zero conformance coverage). - AI Vocabulary — adopt the W3C draft actions; align
no-ml-training.md/derived-data-restriction.mdaction names. - Commercial/Market-Data —
Quantity(Numeric, Unit)value type (same-unit comparison only, no conversion — decided); metering patterns viarequest.parameters; firstrl2:ProfileOperatorexercises.
- w3id.org registration PR for
https://w3id.org/rl2#(namespace already swapped in-repo). - Regenerate/re-check
docs/RL2_Vocabulary.mdafter each ontology change (standing sub-agent task-spec line: every batch touchingrl2.ttlupdates the Vocabulary doc).
- Go reference evaluator + ODRL importer (reconciled estimate: 6–10 person-months to a credible reference implementation with executable conformance).
- Lean 4 model of the kernel; differential random testing Go ↔ Lean (Cedar precedent,
arXiv:2407.01688). Proof order: compile-soundness theorem (
RL2_Compilation.md§8) → evaluator determinism/totality → verified RDF compiler last (largest trusted boundary). Mechanized proofs: +6–12 person-months, do not gate conformance. - Benchmarks before any scale claim: policy count, clauses/policy, condition depth, hierarchy size, snapshot facts, evidence, conflicting atoms; compare OVAL/OPA/Cedar on the shared authorization subset.
| Item | Trigger to reopen |
|---|---|
| Fact-vs-fact comparison (right operand is authored; dynamic-right needs widened RuntimeReference or stays assembler doctrine) | Commercial profile metering scenarios |
| Recurrence (bounded period+count, compile-time expansion to finite windows) | A real recurring-obligation contract |
| Maintenance boundary algorithm (lift the P8 fact/clock-only invariant restriction via recursive boundary collection) | An invariant genuinely needing status operands |
| Evidence-existence operand (event-triggered activation, Cimmino P4d) | Migration demand |
rl2:enforcementMode metadata (regimentation vs sanction, eval-ignored) |
PEP integration work |
| Policy containment / conflict detection tooling (SMT over the 3-valued condition language; authoring-time, no solver in Eval) | Negotiation or change-impact requirements |
| Assembly contract → normative | A deployment exercises assemble(), at which point the informative SelectedPolicyUniverse manifest (docs/RL2_ExternalData.md §6) also becomes normative |
| Port churn under a live agreement (product terms extending to later-created ports; amendment/lifecycle) | A deployed data product creates ports under an existing Agreement |
| RDFC-1.0 source-RDF digests | Cross-party identity over raw RDF re-ingestion |
| Violation-evidence vocabulary for the Remedies profile | Cross-party remedy interop |
| Declarative operator DSL with a normative interpreter (machine-checkable profile-operator semantics, replacing prose denotations) | A profile requires machine-verified operator semantics (e.g. the Lean-verified evaluator track) |
Full per-operand data contract (value schema, cardinality, owner, provenance fields beyond Attribution, freshness semantics beyond maxAge, trust-policy digest, completeness scope) plus a normative, versioned RequiredInputs companion (F-13) |
Cross-party snapshot exchange or a second independent assembler implementation |
Settled during the 2026-08-01 review cycle (rationale in commit messages and the spec):
rl2:prioritynever applied to Promises/Duties — its domain is Privilege ∪ Prohibition by design; crystallized Duties have nothing to inherit.- Set/Offer/Agreement stay distinct classes; operativity is structural (Promise ∉ Norm), not a mutable status property. Promise ≠ Duty deontically: directive-in-force vs commissive-contingent-on-acceptance, binding different parties.
- Atomic vs Logical constraint classes stay separate (structural arity checking).
neq/isNoneOfare authoring preference overnot(eq)/not(isAnyOf), not a projection rewrite: truth values agree, but causes differ (ComparisonSiteembeds the operator).- Independent-Duty
obligateatoms are action-local (derive only for requests matching the Duty's own action); an unmet prerequisite yieldsNotApplicable(no atom), not implicit Deny. - Never widen an
rdfs:range(toowl:unionOfor otherwise); domains widen only via the in-fileowl:unionOfprecedent; structure sharing across classes goes through SHACL. - Full Boolean canonicalization rejected (exponential; Salas 2026); the narrowed one-encoding- per-primitive-relation claim plus enumerated local rules is the standing position.
- The three-test rule (demand / determinism / runtime-independence) governs every future
vocabulary question;
odrl:impliesstays rejected;includedInstays core. - F-01 (sentinel Duty subject/object with no instance binding): resolved via new
rl2:consequentDuty(Privilege → Duty, non-gating, the post-use/companion counterpart torl2:prerequisiteDuty). The regression review (fix-codex.mdR-01..R-03) correctly called this cycle's original "resolved" claim premature: a singlebind(d, a, o)substitution at all four request-context sites left template Duties reachable bydutyStatusunbound (R-01), gave a bound Duty no per-grant occurrence identity (R-02), and gave Duty conditions incompatible request/status evaluation scopes (R-03). The completed model instead splits the four sites into two mechanisms: prerequisite gating and attached-duty reporting keep unchanged, purebind(d, a, o)substitution, with arequest.*path in these request-free contexts yielding an attributedMissing;consequentDutyemission and independent-clause atom emission useoccurrenceOf(d, Env)instead, which additionally consumes the Duty'scondition(evaluated once, in the request environment), resolveswindowtoAbsoluteorNoneviaresolveWindowat grant time (never an unresolvedRelativewindow — aresolveWindowfailure yields an indeterminate atom, not a broken occurrence), and assigns anoccurrenceId—(sourceIdentity(d), Request.id)when the request carries one (PER-GRANT), else(sourceIdentity(d), agent, asset)(PER-MEMBER fallback, coalescing repeated grants).dutyStatuses(RL2_Model.md§6) is restricted toconcrete(d)Duties under their own identity — a template contributes no entry there — plus a lazily-computed entry per bound occurrence, keyed byoccurrenceId(fromoccurrenceOf) or byBoundIdentity(from a bound prerequisite at gating); the two key spaces are disjoint by construction (RL2_Semantics.md'sboundOccurrenceStatuses).Evidence.dischargeOf(RL2_Model.md§4.3) correlates evidence to a specific PER-GRANT occurrence; PER-MEMBER occurrences and authored concrete Duties are unaffected. Envelope atoms anddutyStatusqueries are always concrete and sentinel-free; a bound occurrence recorded in anEvaluationResultis concrete and re-evaluable, and re-identifiable by its recorded key. Sentinels remain legal in Duty templates but areSentinelMisuseon a Promise (a Promise crystallizes at materialization with no binding source). ODRLodrl:dutyfrom a Permission isclarified, notnormalized: aTranslationConfigurationmust declare per profile/policy whether it meansprerequisiteDutyorconsequentDuty; an undeclared interpretation isMissingTranslationInterpretation. - F-03 (plug-in
rl2:ProfileOperatorsemantics unconstrained): operators stay (D8), quarantined — the declaring profile must ship a total-function denotation (same register as coreapply) plus positive/negative conformance vectors, or the profile is non-conforming; STRICT core conformance (spec/RL2_Compilation.md§8, §10) excludes profile operators entirely; a request-independent profile judgment should be precomputed as aWorldSnapshotfact rather than declared as an operator. - F-10 (validation regime pinned,
spec/RL2_Compilation.md§10.1): single merged default graph as phase (1)/projection input, named graphs ignored,owl:importsnot followed, no network/file fetching beyond supplied inputs; base IRI is an explicit caller-suppliedcompileinput, a remaining relative IRI rejects; NO RDFS/OWL entailment before or during validation; SHACL Core + SHACL-SPARQL only, Advanced Features (rules, functions) inactive;sh:Violationrejects,sh:Warningnever rejects but must be reported; canonical report projection is(source shape IRI, focus node, result path, severity), deduplicated and sorted, messages excluded. - F-06/F-07 (Offer bundling / output-port boundary): no
materializeBundleand no overloadingrl2:Offer/requiresProfileto carry a bundle of taggedSetpolicies —materialize(Offer, Acceptance)stays single-Offer. Concrete-at-acceptance data products (Level- are expressed with one Promise per named output port plus a product-wide prerequisite
attestation Duty (see
conformance/usecases/data-product-offer.md). Per-request policy-universe selection (e.g. tag-based, per port/asset) is the assembler's job, kept replayable via the informativeSelectedPolicyUniversemanifest (docs/RL2_ExternalData.md§6), not a kernel concept.
- are expressed with one Promise per named output port plus a product-wide prerequisite
attestation Duty (see
- F-13 (external-data admissibility language unpinned): trust verification (signatures, provenance
chains, trust anchors, connector authentication) is pre-
Eval, performed by the trusted assembler beforeWorldSnapshotexists;Evalnever does cryptographic or chain-of-trust verification.EvaluationConfigurationcarries admissibility as a closed, finite three-kind record (allowedSources,maxAgeper left-operand resolution path;evidenceSignersper Duty-evidence scope) — not a predicate language, not profile-extensible — with a canonical JSON form covered by the configuration echo/digest inEvaluationResult. A fact or evidence item failing the filter is treated exactly as absent, yielding the ordinary attributedMissing(never a distinct error kind, never a silent skip). The full per-operand data contract and a normativeRequiredInputscompanion stay deferred (§6). - R-04/R-05 (admissibility completion and closure complexity,
fix-codex.md):evidenceSignersis keyed by the owning Duty's own stable identity (sourceIdentity(d)), not byEvidenceSelectorshape, so a bound occurrence inherits its template's entry unchanged regardless of who requested it. A configured constraint (allowedSources,maxAge, orevidenceSigners) whose candidate lacks the attribution field it reads fails that filter, deterministically and conservatively in both directions. The "visible in causes" claim for admissibility filtering is qualified, not retracted: a filtered fact surfaces as an ordinary attributedMissing; filtered-to-empty evidence is a definite status (typicallyPending) with no distinct cause of its own; an over-restrictiveevidenceSignersentry is visible only by comparing two evaluations' configuration digests side by side, never by inspecting one evaluation's causes. Separately, the Complexity Analysis section's closure-construction claim (O(n_hier) space and O(1) membership for the same representation) was not simultaneously achievable and is corrected:n_closure(reachable pairs, up toV²) names the materialized closure's actual size, and the section now states the materialized-vs-compact space/lookup-cost trade-off explicitly instead of asserting both sides of it at once.