<?xml version='1.0' encoding='UTF-8'?>
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" category="std" docName="draft-srivastava-learnlog-03" ipr="trust200902" tocInclude="true" tocDepth="3" sortRefs="true" symRefs="true" version="3" xml:lang="en">
  <front>
    <title abbrev="learnlog">Learnlog Assistance Capture Profile and Optional Event File Representation</title>
    <seriesInfo name="Internet-Draft" value="draft-srivastava-learnlog-03"/>
    <author initials="K." surname="Srivastava" fullname="Kumar Srivastava">
      <organization>Quantum Learning Machines</organization>
      <address>
        <email>kumar@quantumlearningmachines.com</email>
      </address>
    </author>
    <date year="2026" month="October" day="20"/>
    <keyword>education</keyword>
    <keyword>learning</keyword>
    <keyword>logging</keyword>
    <keyword>integrity</keyword>
    <keyword>redaction</keyword>
    <abstract>
      <t>This document proposes Learnlog as an assistance capture profile for measuring AI scaffolding, with xAPI and Caliper bindings. Its motivation is Quantum Learning Machines' work on QLM Measure. The profile defines what assistance was requested, offered, generated, transmitted, displayed, or accepted; what an instrument captured; which facts remain unknown; and which assertions were observed, self-reported, or inferred. Existing logging ecosystems can carry these definitions. Their reviewed base specifications do not, by themselves, require this particular combined agreement on assistance, achieved capture, unavailable information, and mapping provenance. Existing AI and tutoring profiles already cover important parts of the domain. For discussion, the document also proposes event families beyond assistance, among them prediction, revision, recall after a delay, stated confidence, and agency.</t><t>The document retains the existing Learnlog event file representation, hash chains, optional signed heads, and verifiable redaction as an optional carrier. An implementation can use the assistance profile without producing that file representation. Experimental companion software constructs importer-observation records in xAPI and Caliper while preserving source assistance assertions separately. The implementation does not establish independent interoperability, truthful or complete capture, independent learning, or scientific equivalence across products. The core file format is unchanged in this revision.</t></abstract>
    <note removeInRFC="true">
      <name>Note to Readers</name>
      <t>This individual Internet-Draft is a working proposal for discussion, not an IETF-approved standard. Since revision -02, Learnlog is positioned primarily as an assistance capture profile with xAPI and Caliper bindings. The existing file representation is optional. The companion implementation is experimental, and its importer-observation mode is not a direct reconstruction of learner actions. All 20 core sourcecode blocks, the core hash algorithm, Appendix A test vectors, and the previously versioned companion definitions are preserved. Revision -03 adds an offered assistance state, rules for observed, self-reported, and inferred assertions, an informative account of three released research datasets, and proposed event families beyond assistance. It changes no core sourcecode block, hash computation, or test vector. This revision makes no first-profile, exhaustive-gap, scientific-equivalence, certification, or superiority claim. <xref target="changes-03"/>, <xref target="changes-02"/>, and <xref target="changes-01"/> record changes, <xref target="issues"/> lists open questions, and <xref target="impl"/> describes implementation limits.</t></note>
  </front>
  <middle>
    <section anchor="intro">
      <name>Introduction</name>
      <t>Learnlog is proposed as an assistance capture profile for measuring AI scaffolding, with xAPI and Caliper bindings. A request for a hint, an answer generated by software, and a hint displayed to a learner are different observations. Combining them under a single count of "help" can change the question an analysis answers. Interpreting a learner's work also requires knowing the instrument's observation boundary: a record from one application does not establish whether the learner used another tool. The profile makes these distinctions explicit so that producers and readers can examine them without reconstructing their meaning separately for every export.</t><t>The proposal originated in Quantum Learning Machines' internal work on measuring AI scaffolding while building QLM Measure. The company made the assistance contract an internal logging requirement for its products and decided to open the definitions and logging approach for wider review. This origin is the author's account of development motivation, not a community needs survey or evidence that every QLM product conforms. The working repository remains private; the decision to open the work does not establish a completed public release.</t><t>The profile has two layers: domain definitions and capture requirements (<xref target="assistance-contract"/>), and versioned bindings to existing logging ecosystems (<xref target="ecosystem-bindings"/>). xAPI and Caliper supply carrier models; Learnlog specifies the assistance agreement that the proposed bindings carry. The file representation in the remaining sections is an optional compatibility carrier for deployments that need its integrity and redaction behavior. An external product can retain its native log format; a supported transformer adapter can produce the profile representation.</t><t>The intended use is prospective instrumentation of learning and assessment activity, including AI assistance. Recording a state when the producing system observes it can preserve evidence that a later export cannot recreate. Retrospective transformation can retain source distinctions and disclose missing information, but it cannot manufacture absent display events, model versions, capture windows, or evidence about external AI. A log supports inspection of assisted performance; it does not establish independent ability, a learning effect, or the applicability of a prior study.</t><t>The existing Learnlog file representation has the following properties:</t>
      <ul>
        <li>Events are typed. An event schema defines the event types of one area, such as a prediction committed in a simulation (<xref target="event-names"/>).</li>
        <li>Every event can state who acted and toward whom. A request for help and a scaffold given in reply are events of their own, whether the helper is a person or a program (<xref target="parties"/>, <xref target="assist"/>).</li>
        <li>Entries are chained by hash, and a producer can sign the latest hash. A reader who trusts the signing key can then detect a change to the signed entries that was made without that key (<xref target="integrity"/>). <xref target="security"/> states the limits.</li>
        <li>An event can be redacted without breaking the chain (<xref target="redaction"/>).</li>
        <li>Records are kept in files. A record can be written, and its hashes checked, without a server.</li>
      </ul>
      <t>The structure of files, records, and event schemas follows qlog <xref target="QLOG"/>, which defines structured logging for network protocols. This document adds integrity rules and the members that a record of learning needs.</t>
      <section anchor="scope">
        <name>Scope</name>
        <t>The proposed assistance requirements are stated in <xref target="assistance-contract"/>. This working revision does not yet define a complete assistance-profile conformance target; unpublished companion files do not supply normative requirements. The file, event, integrity, serialization, and registration requirements elsewhere apply when the optional Learnlog file representation is used. A carrier binding also needs to satisfy its own ecosystem's requirements. A claim about one layer does not establish conformance to the others.</t><t>This document defines:</t>
        <ul>
          <li>an experimental assistance capture profile specifying assistance states, capture requirements, explicit unavailable information, the basis of each assertion, and versioned ecosystem bindings (<xref target="assistance-contract"/>);</li><li>proposed event families beyond assistance, for discussion and without new event types (<xref target="event-families"/>);</li><li>the structure of files, records, entries, and events (<xref target="files"/> through <xref target="events"/>);</li>
          <li>event schemas for consent, assistance, and dialog, which are not tied to one subject area (<xref target="generic"/>);</li>
          <li>the canonical form, the hashes, redaction, signed heads, and verification (<xref target="integrity"/>);</li>
          <li>two JSON serializations (<xref target="serialization"/>);</li>
          <li>registries for file schemas and event schemas (<xref target="iana"/>).</li>
        </ul>
        <t>The following are out of scope:</t>
        <ul>
          <li>Event schemas for subject areas. Other documents can define them.</li>
          <li>Transport and storage interfaces.</li>
          <li>Estimates of what a learner knows, such as scores or mastery values. A record states what the producer recorded. This document leaves conclusions drawn from a record to other formats.</li>
          <li>Distribution of the keys that verify signed heads.</li>
          <li>The legal basis for collecting data about learners.</li>
          <li>Decisions about whether an intervention or research finding applies in a new setting. Such decisions can cite records but are not event facts or core verification results.</li>
        </ul>
      </section>
      <section anchor="conventions">
        <name>Conventions and Terminology</name>
        <t>The key words "<bcp14>MUST</bcp14>", "<bcp14>MUST NOT</bcp14>", "<bcp14>REQUIRED</bcp14>", "<bcp14>SHALL</bcp14>", "<bcp14>SHALL NOT</bcp14>", "<bcp14>SHOULD</bcp14>", "<bcp14>SHOULD NOT</bcp14>", "<bcp14>RECOMMENDED</bcp14>", "<bcp14>NOT RECOMMENDED</bcp14>", "<bcp14>MAY</bcp14>", and "<bcp14>OPTIONAL</bcp14>" in this document are to be interpreted as described in BCP 14 <xref target="RFC2119"/> <xref target="RFC8174"/> when, and only when, they appear in all capitals, as shown here.</t>
        <t>Data structures are defined in the Concise Data Definition Language (CDDL) <xref target="RFC8610"/>. Examples are in JSON <xref target="RFC8259"/>. Some figures wrap long lines as described in <xref target="RFC8792"/>.</t>
        <t>This document uses the following terms:</t>
        <dl newline="false" spacing="normal">
          <dt>Learner:</dt>
          <dd>A person whose learning activity is recorded.</dd>
          <dt>Subject:</dt>
          <dd>The learner that a record is about.</dd>
          <dt>Producer:</dt>
          <dd>The software that writes a record.</dd>
          <dt>Holder:</dt>
          <dd>A party that stores a record or passes it on. The producer is the first holder.</dd>
          <dt>Verifier:</dt>
          <dd>Software that checks a record against this document.</dd>
          <dt>Record:</dt>
          <dd>A header and the entries about one subject, written by one producer. A record forms one hash chain.</dd>
          <dt>Entry:</dt>
          <dd>One element of a record. An entry carries one event and the hashes that bind it into the chain.</dd>
          <dt>Event:</dt>
          <dd>A typed description of one action.</dd>
          <dt>Event schema:</dt>
          <dd>A definition of a namespace of event types.</dd>
          <dt>Head:</dt>
          <dd>A statement that names the latest entry of a record at some moment (<xref target="heads"/>).</dd>
        </dl>
      </section>
    </section>
    <section anchor="assistance-contract">
<name>Assistance Capture Profile</name>
<t>This section specifies the proposed assistance agreement and its claim boundaries. It is a versioned companion contract, not new required members of the core CDDL. The experimental payload identified as <tt>urn:learnlog:proposal:assistance-capture:0.1.0</tt> uses <tt>schema_version</tt> <tt>0.1.0-draft</tt>. It is not an IETF-registered schema. Independent implementation and methodological validation remain pending. The older <tt>qlm:assistance-context:0.1.0</tt> companion profile remains a distinct, unchanged definition; this contract does not silently revise its meaning.</t>
<t>A producer implementing the proposed assistance requirements <bcp14>MUST</bcp14> preserve the assistance, capture, knowledge-state, and provenance distinctions defined in this section. It <bcp14>MUST</bcp14> identify the payload version and carrier binding. It <bcp14>MUST NOT</bcp14> replace unavailable information with a guessed observed value or silently discard required information during transformation. A binding that cannot preserve required information <bcp14>MUST</bcp14> reject the conversion or report the loss without claiming lossless preservation. Passing these checks establishes structural consistency under the stated rules, not that an asserted event occurred.</t>
<t>The supplied companion schema and consistency checker are informative reference-implementation artifacts. They are not unpublished normative dependencies. A future complete conformance target needs a fixed, independently retrievable specification of its schema, consistency rules, and bindings, with version and change control. Until then, implementations can report the particular checks performed but cannot claim complete assistance-profile conformance to this draft. The prepared review package includes the exact reference files and a SHA-256 manifest for inspection; this does not establish public availability.</t>
<section anchor="domain-gap"><name>The Specific Agreement Being Proposed</name>
<t>The xAPI Profiles specification already provides controlled vocabularies, statement templates, and patterns <xref target="XAPI-PROFILES"/>. Caliper defines event and entity models with extensibility <xref target="CALIPER"/>. Domain work also exists: Rustici Software has proposed an AI xAPI profile <xref target="RUSTICI-AI"/>, and GIFT documents xAPI statements for tutoring interactions, learner state, and environment adaptations <xref target="GIFT-XAPI"/>. Learnlog therefore makes no claim that assistance logging, domain profiling, or semantic interoperability is new, or that existing ecosystems cannot represent the necessary information.</t>
<t>The retrieved draft version 0.1.0 of the AI-Mediated Learning Interactions Profile from inXsol LLC / TLA Toolbox <xref target="TLA-AI"/> has direct conceptual overlap: it defines requested AI scaffolds, received AI responses, incorporated AI output, assisted and independent submission concepts, and model-version context. These are existing proposed domain definitions, not an empty vocabulary space for Learnlog to fill. Their presence does not establish that any particular submission was independent; that assertion still needs evidence about its observation boundary.</t>
<t>DataShop's Tutor Message format guide <xref target="DATASHOP-FORMAT"/> also defines paired help-request and tutor-response messages, with hint identifiers and ordering information. Separating a learner's request from a tutor's response therefore predates this proposal. Learnlog's candidate contribution concerns an explicit combined contract for assistance meaning, achieved capture, unavailable information, and mapping provenance; its value needs to be established against such existing work.</t>
<t>The bounded gap is that the reviewed base specifications do not by themselves require this particular combined agreement: distinct assistance lifecycle observations; a declared instrument and achieved capture boundary; explicit unavailable-information states; and provenance for mappings used in a proposed combined measurement. Profiles can supply such requirements. The proposal is to make them reusable and inspectable across products, then test their usefulness. A profile-by-profile comparison may reveal overlap, a better existing home, or requirements to remove. A separate file format is not necessary merely to express this agreement.</t>
</section>
<section anchor="assistance-definitions"><name>Separate the Observable Assistance States</name>
<t>A contract needs to define the subject of each assertion, its observation unit, the instrument that supplied it, and the evidence for that assertion. The prototype distinguishes these source-supported observations:</t>
<dl spacing="normal">
<dt>Requested:</dt><dd>A learner or other identified actor asked for assistance. The request does not establish that assistance was produced.</dd>
<dt>Offered:</dt><dd>The instrument observed a system or another identified actor initiate assistance, through a recorded initiation or trigger, for example step-by-step help given because of a wrong answer. An offer is established by that record, not by the absence of a request link (<xref target="assist"/>). An offer does not establish display. A declined offer is a separate observation; the absence of a recorded response does not show that an offer was ignored.</dd>
<dt>Generated:</dt><dd>A producing system generated assistance content. Internal generation does not establish transmission or display.</dd>
<dt>Transmitted:</dt><dd>The instrument observed assistance being sent to a specified destination. Sending does not establish display or use.</dd>
<dt>Displayed:</dt><dd>The instrument observed assistance being presented in a specified interface. Display does not establish attention, comprehension, or learning.</dd>
<dt>Accepted:</dt><dd>The instrument observed the interface action defined by the product as accepting assistance. Its product-specific meaning needs to remain explicit; it does not by itself establish incorporation, agreement, or learning.</dd>
</dl>
<t>For example, a learner requests a hint, the server generates text, and the interface displays it. There are three observed lifecycle actions. A count of requests can be one, a count of generated responses can be one, and a count of displays can be one; adding these counts does not establish three help episodes. A fourth event saying that the learner read or learned from the hint would require its own evidence. If the request and generated text are recorded but display is unobserved, observed display remains unavailable. A later analyst cannot fill that gap using the successful generation alone.</t>
<t>A count of requests leaves out assistance that a system started on its own. Two products with the same number of requests can differ in how much help learners received, so a comparison needs offered assistance to be visible as its own state. The third release in <xref target="three-releases"/> gives an example of probable system-started help that a request count misses.</t>
<t>These states are proposed companion-contract concepts, not additions to the core event registry. The existing <tt>assist:help_requested</tt> and <tt>assist:scaffold_provided</tt> names and CDDL are retained. The meaning of an absent request link was revised in this revision as explicitly described in <xref target="assist"/>; unchanged syntax does not imply unchanged semantics. A new lifecycle state needs an explicit versioned event definition or binding; it cannot be inserted as an undeclared core event or equated with scaffold provision without a justified mapping.</t>
</section>
<section anchor="capture-requirements"><name>Declare the Instrument and Observation Boundary</name>
<t>The proposed contract requires a capture instrument identifier, explicit information about which assistance states that instrument can observe, the systems and event classes inside its boundary, exclusions, an observation window, and achieved coverage. Relevant product and model identity or revision information also needs an explicit knowledge state. Capability and planned capture describe what an instrument could or intended to collect; achieved coverage describes what was captured within the stated boundary and window. One does not establish the other.</t>
<t>A claim of complete capture applies only within an explicitly stated boundary and observation window, with supporting evidence. It does not include an uninstrumented browser tab, another device, or external AI unless those are explicitly inside the demonstrated boundary. A source's completeness claim remains a source assertion. Independent checks against actual product behavior are needed to assess its accuracy. The prototype can check required fields and evidence references; it cannot establish that a producer's coverage claim is true.</t>
<t>The contract does not require raw conversations, prompts, or assistance content. A deployment should collect the minimum evidence needed for its declared purpose, use scoped identifiers, and control access and retention (<xref target="privacy"/>). Optional source references and context can support audit without making full learner text mandatory.</t>
</section>
<section anchor="explicit-unknowns"><name>Preserve What Is Unknown and Why</name>
<t>The prototype represents important assertions with an explicit knowledge state, an explanation, and evidence references. Its current states are <tt>observed</tt>, <tt>not_collected</tt>, <tt>not_reported</tt>, <tt>not_reviewed</tt>, <tt>unknown</tt>, and <tt>not_applicable</tt>. An observed value needs supporting evidence. An unavailable state must not carry a guessed observed value. These states remain candidates for review and are not asserted to be an exhaustive measurement taxonomy.</t>
<t><tt>not_reviewed</tt> means the relevant material has not yet been examined for this assertion. <tt>not_reported</tt> requires a documented review of specified material that did not report the information. <tt>not_collected</tt> needs evidence about the capture process; silence in an export does not establish that the original system failed to collect it. <tt>unknown</tt> records an unresolved fact when a narrower supported state is unavailable. <tt>not_applicable</tt> needs a reason tied to the assertion. Review scope and evidence need to accompany these distinctions.</t>
<t>A valid record with zero displayed events therefore supports a zero only for a question whose boundary, capture evidence, inclusion rules, and denominator justify that interpretation. Otherwise, the display count may be unavailable. A missing model version remains unavailable; a transformation must not invent one from a product name or assumed release date. Data availability, a researcher's review status, and whether a study applies to a new setting are separate questions.</t>
</section>
<section anchor="assertion-basis"><name>Mark Observed, Self-Reported, and Inferred Assertions</name>
<t>The basis of an assertion is separate from its knowledge state. This revision proposes three bases:</t>
<dl spacing="normal">
<dt>Observed:</dt><dd>The instrument recorded the action or presentation itself.</dd>
<dt>Self-reported:</dt><dd>The learner or another identified person stated it in answer to a named prompt or instrument. The act of reporting is observed; the content is the person's statement, not an observation of the state it describes.</dd>
<dt>Inferred:</dt><dd>A detector, model, rule, or human coder derived it from other evidence.</dd>
</dl>
<t>A producer implementing the proposed assistance requirements <bcp14>MUST</bcp14> record the basis of every assistance-profile assertion. An assertion without a recorded basis has an unknown basis; it is not thereby observed. An inferred assertion <bcp14>MUST</bcp14> identify the detector, model, rule, or coder; its version; the output and its scale; and the classes of input evidence it used. When the inputs are not known, they <bcp14>MUST</bcp14> carry an explicit knowledge state (<xref target="explicit-unknowns"/>), such as <tt>unknown</tt> or, after a documented review, <tt>not_reported</tt>, instead of being left out. An inferred assertion <bcp14>SHOULD</bcp14> also identify when the inference was made and any validation it rests on. An inferred or self-reported value <bcp14>MUST NOT</bcp14> be recorded as, or converted into, an observed value, and <bcp14>MUST NOT</bcp14> replace an observed value for the same assertion. A stated confidence and an inferred confidence for the same response are two assertions.</t>
<t>The input declaration matters when an inferred state is compared with the events it may have been computed from. If a detector's inputs include assistance events, comparing its output with assistance counts partly compares the detector with its own input. A reader can judge that only when the inputs are declared. A prediction that a learner needs help is inferred; an offer of help made because of that prediction is observed as offered (<xref target="assistance-definitions"/>). An emotional state estimated from interaction logs is inferred, while a learner's answer to an emotion prompt is self-reported.</t>
<t>A classification computed from several events, such as a display followed by another request within five minutes, is an analytical result, not a new observation. It is an inferred assertion made by a rule, so the requirements above apply: it needs a versioned rule, its time window and anchor, and references to the events it used, and it <bcp14>MUST NOT</bcp14> be recorded as observed.</t>
</section>
<section anchor="ecosystem-bindings"><name>Support Existing Logging Ecosystems</name>
<t>The experimental bindings carry the full assistance capture payload in documented xAPI and Caliper extension locations. The xAPI materials include a draft profile with a statement template. The Caliper materials identify the applicable event model and extension requirements. The binding identifiers use <tt>example.org</tt> as unpublished draft identifiers; they are not registered or deployed vocabulary endpoints. These bindings are implementation work within established ecosystems, not a new transport protocol.</t>
<t>The draft xAPI profile identifier is <tt>https://example.org/learnlog/proposal/assistance-capture-profile</tt>, with corrected binding version <tt>0.1.1-draft</tt>. The capture payload version remains <tt>0.1.0-draft</tt>. It reuses the Activity Streams <tt>create</tt> verb and defines a template for creating an observation record. The current xAPI encoding targets the legacy 1.0.3 statement shape <xref target="XAPI103"/>; it does not claim conformance to IEEE xAPI 2.0. The Caliper 1.2 encoding uses the General Profile's base <tt>Event</tt>, a <tt>SoftwareApplication</tt> actor, <tt>Created</tt> action, and <tt>DigitalResource</tt> object, with the profile payload in event extensions. It uses the standard transport envelope without adding custom envelope properties.</t>
<t>The xAPI caller supplies an explicit account-system homepage URL and account name separately from the importer software identifier. The bounded implementation supports HTTP(S) homepages; an arbitrary importer URN is not an account homepage. Account ownership is a caller assertion, not an authenticated fact. The constructor omits Statement <tt>version</tt>. Its reader accepts stable <tt>1.0.x</tt> Statement versions, including the <tt>1.0.0</tt> supplied by an LRS when absent. Statement version is distinct from the HTTP <tt>X-Experience-API-Version</tt> header. These rules follow the legacy Statement specification <xref target="XAPI103"/>.</t>
<t>The implemented mode records an importer's creation of an observation record for a source record. Its carrier actor identifies the importer, and its carrier time identifies creation of that observation record. The source assistance assertion, its source-scoped subject identifier when available, and its native time and unavailable-information states remain inside the payload. An importing application <bcp14>MUST NOT</bcp14> invent a learner identity or substitute creation time for an unknown source action time. A server generation record does not thereby become a learner's use of a tool. Example source observations are synthetic. Live executions need the actual importer identity and creation time; fixture values do not describe an actual product or learner.</t>
<t>An importer-observation binding is useful when a retrospective source lacks the actor or time required by a native-action carrier model. It does not reconstruct a native learner event. An analyst <bcp14>MUST</bcp14> use the source assistance assertions and their evidence when counting assistance; counting carrier import events instead answers a different question. A future native-action binding needs separately justified mappings and adequate source evidence.</t>
<t>The companion software constructs complete records and performs local validation, round-trip, and rejection checks for this limited mode. Its verification reports identify the exact records, rules, and tool versions checked. Those checks do not establish certified carrier conformance, live learning-record-store exchange, preservation of original integrity guarantees, or independent cross-product interoperability. The separate evaluation toolkit's experimental encodings concern their own frozen profile and do not establish conformance to this new contract.</t>
<t>A binding to the existing Learnlog representation must use declared extension points and versioned event definitions, while preserving the current core CDDL and integrity rules. Existing records keep their original commitments. Transcoding a record does not preserve its original signature by default; a transformation needs to retain the original artifact and state any lost guarantees. Common serialization and matching names do not establish scientific equivalence. Any pooled measurement still needs an operational definition, unit, denominator, capture requirements, and a supported comparability decision.</t>
</section>
<section anchor="three-releases"><name>Evidence From Three Released Research Datasets</name>
<t>This subsection is informative. The author applied the stage distinctions of <xref target="assistance-definitions"/> to three released research datasets <xref target="LEARNLOG-PREPRINT"/>: hint requests to an AI next-step hint tool in a programming course <xref target="HINTS-DATA"/>, chat queries in an instrumented programming environment <xref target="IDETRACE"/>, and problem records from a mathematics tutor <xref target="ASSISTMENTS"/>. The episode scripts and their aggregate outputs are public <xref target="LEARNLOG-EPISODES"/>. Links between stages come from the scripts' rules; the sources declare no such links. The results describe three releases, not products in general.</t>
<ul>
<li>Each release records a different subset of stages. The first records requests, system errors, and displays: of 1,050 requests, 738 were followed by a recorded display and 266 ended in an error. The second records 59 queries and their responses but no separate display; under the episode rule a response counts as a display, so its 59 requests are among the 797 below and none lacks a display. The third records hint requests and whether the learner reached the last hint, but no display events.</li>
<li>Pooling the two programming releases gives 1,109 requests against 797 requests followed by a recorded display, a count 39% higher. The pooled share of requests without a display also hides that the second release, under that rule, has none.</li>
<li>In the third release, the episode rule classifies 110,513 problems as steps given by the system after a wrong first answer, with no request on the main problem. A count of requests misses that help. The release records no initiation event, and only a student's first action is recorded, so under <xref target="assistance-definitions"/> these are candidates for the offered state, not established offers. The case shows why the state needs its own record.</li>
<li>The first release contains only request episodes, so going without help cannot be counted there; the third shows that 13,607 of its 46,544 students asked for no hint on any problem or step. A pooled table could read the first release's missing count as zero, which is the error the knowledge states of <xref target="explicit-unknowns"/> are meant to prevent.</li>
<li>Every record of the third release carries four affect detector confidences, which the source describes as predicted. The pages the author could read do not say whether hint use was among the detectors' inputs. This case motivates the input declaration of <xref target="assertion-basis"/>.</li>
</ul>
<t>The scripts also compute derived classes of the kind described in <xref target="assertion-basis"/>: help requested and not displayed; displayed and then requested again within five minutes (524 of 738 displays in the first release and 41 of 59 in the second); displayed with no recorded follow-up action; never requested; classified by rule as steps given by the system; and a paste with no copy inside the tool in the preceding ten minutes (291 of 1,826 pastes in the first release's activity file, which is not linked to its hint records). A paste without a prior copy can indicate outside material; it does not establish the use of external AI. Each class depends on a chosen window and anchor, which the scripts state.</t>
</section>
<section anchor="event-families"><name>Proposed Event Families Beyond Assistance</name>
<t>This subsection is informative. It proposes, for discussion, how the rules of this section could apply to other events in a learning record. It defines no event type, registers no namespace, and adds no member to the core CDDL. Each family needs a versioned event definition, a worked dataset, and review before it is specified. Only the assistance family has data behind it in this revision (<xref target="three-releases"/>). The inferred items below name what a separate estimate format would hold; consistent with <xref target="scope"/>, this document does not record estimates of what a learner knows. A score in the Test family is an observed scoring outcome, not such an estimate.</t>
<t>The same rules would apply to every family. An event names the stage it records; states its basis (<xref target="assertion-basis"/>); sits inside a declared capture boundary (<xref target="capture-requirements"/>); keeps unavailable information distinct from zero (<xref target="explicit-unknowns"/>); and identifies the assistance policy and the assistance observed before it in the same activity. A record is about one subject, so its entries are already linked. Linking events in different records to the same learner would need to be explicit and declared, use scoped identifiers, and rest on consent the deployment holds (<xref target="consent"/>, <xref target="privacy"/>); it would never be assumed from matching identifiers. A carrier binding would need an equivalent rule for statements or events that it does not already tie to one subject. The mechanism is an open issue (<xref target="issues"/>).</t>
<dl spacing="normal">
<dt>Predict:</dt><dd>Observed: the learner commits to a prediction before seeing an outcome. Inferred: whether the prediction reflects understanding. Linking: the prediction and the later outcome in the same episode. <xref target="ex-schema"/> shows a private event of this kind. Its <tt>confidence</tt> member predates <xref target="assertion-basis"/>; under this proposal it would carry a self-reported basis and its timing.</dd>
<dt>Test:</dt><dd>Observed: an attempt and its scored result, with the scoring rule. Inferred: mastery from the result. Linking: the item and scoring-rule versions.</dd>
<dt>Explain:</dt><dd>Observed: the explanation the learner produced, or its hash. Inferred: its quality, by a named judge or model. Linking: the judge's identity or the model version.</dd>
<dt>Revise:</dt><dd>Observed: a changed answer or artifact, and what prompted the change. Inferred: whether the revision fixed the misunderstanding. Linking: the earlier and later versions of the work.</dd>
<dt>Defend:</dt><dd>Observed: a response to a challenge or counterexample. Inferred: the strength of the defense. Linking: the challenge that was posed.</dd>
<dt>Discern:</dt><dd>Observed: a choice between similar cases, such as which problem a method fits, or a judgment of whether a supplied answer, including AI output, is correct. Inferred: the concept behind the choice. Linking: the set of options shown and, where known, the correct status of each.</dd>
<dt>Transfer:</dt><dd>Observed: success on a task unlike the practiced one. Inferred: that the skill, and not something else, transferred. Linking: a declared similarity between the two tasks, which is a producer assertion.</dd>
<dt>Recall:</dt><dd>Observed: retrieval without support in the same session. Inferred: that the knowledge is available without help. Linking: whether assistance was available at the time.</dd>
<dt>Delayed recall:</dt><dd>Observed: the same retrieval after a declared delay. Inferred: that learning lasted. Linking: the same learner across records, linked with consent and declared.</dd>
<dt>Emotion and need:</dt><dd>Self-reported: a learner's own report, when one is asked for. Inferred: the state, or a predicted need for help, from a named detector. Linking: the detector, its version, its confidence, and its inputs.</dd>
<dt>Confidence when answering:</dt><dd>Self-reported: the learner's stated confidence on a declared scale, the item it refers to, and when it was given: before answering, with the answer, after answering and before feedback, or after feedback. Inferred: confidence estimated from response time, hesitation, or a model. Linking: the scored correctness of the same response and the assistance in effect before it.</dd>
<dt>Agency at session start and end:</dt><dd>Self-reported: answers to a named instrument and version, with its items and scale, at the start and at the end of a session. Observed: acts of choice during the session, such as choosing a task, setting a goal, requesting help, declining offered help, or ending the session. Inferred: agency estimated by a named model. Linking: the start and end reports of the same session and learner.</dd>
</dl>
<t>Delayed recall shows why linking matters. A school cannot say that a misunderstanding was fixed and stayed fixed unless the later check is linked to the same learner, the delay is declared, and the help available at both times is known. Each of those is a privacy decision as well as a measurement decision, so the proposal would make linking explicit and consent-based.</t>
<t>Confidence and agency each need two measurements. Calibration, the agreement between stated confidence and correctness, is computed from a confidence report and a scored response; it is an analytical result, not an event. Confidence given after feedback answers a different question from confidence given before it, and the two are not pooled. A change in agency between the start and end of a session is computed from two reports; an end report that is missing because the learner left early is unavailable, not unchanged.</t>
<t>Together these families would let one record show what a learner predicted, tried, explained, changed, defended, chose, carried to a new task, and remembered, with the help in effect at each point and the learner's own confidence and sense of control alongside. That is a proposal. Whether one profile should hold all of these families, or whether existing xAPI or Caliper profiles are a better home for some of them, is an open issue (<xref target="issues"/>).</t>
</section>
</section><section anchor="overview">
      <name>Design Overview</name>
      <t>A log file holds one or more records. A record holds a header and a sequence of entries. Each entry holds one event.</t>
      <figure anchor="fig-structure">
        <name>Structure of a log file</name>
        <artwork type="ascii-art">log file
 +-- record
      +-- header          what the record is about
      +-- entry 1         one event and its hashes
      +-- entry 2
      +-- ...
      +-- signed heads    signed statements of the latest entry</artwork>
      </figure>
      <t>Each entry holds the hash of its event and the hash of the entry before it. The first entry holds the hash of the header, and every entry hash covers that hash as well. A change to the header or to an earlier entry therefore changes every later entry hash. A producer can sign a statement of the latest entry hash, which gives a verifier a fixed point to check against.</t>
      <figure anchor="fig-chain">
        <name>The hash chain of a record</name>
        <artwork type="ascii-art"> +--------+       +---------+       +---------+       +---------+
 | header |&lt;------| entry 1 |&lt;------| entry 2 |&lt;------| entry 3 |
 +--------+ prev  +---------+ prev  +---------+ prev  +---------+
            hash       |      hash       |      hash       |
                  event hash        event hash        event hash
                       |                 |                 |
                    event 1           event 2          (redacted)</artwork>
      </figure>
      <t>An entry commits to the hash of its event. The event itself can be removed later, and the chain still verifies (<xref target="redaction"/>).</t>
      <t>Event types live in namespaces that event schemas define. This document defines schemas for three namespaces that are not tied to one subject area. Other schemas are identified by URI.</t>
    </section>
    <section anchor="files">
      <name>Log Files</name>
      <t>A log file takes one of two forms. A contained file holds one or more records (<xref target="file-contained"/>). A sequential file holds one record and grows by appending (<xref target="file-seq"/>). Both forms hold the members of the log-file group, as in <xref target="QLOG" section="3" sectionFormat="of"/>.</t>
      <figure anchor="fig-logfile">
        <name>LearnlogFile and the members common to all log files</name>
        <sourcecode type="cddl" name="learnlog.cddl">LearnlogFile = {
    log-file,
    records: [+ Record]
}

log-file = (
    file_schema: text,
    serialization_format: text,
    ? title: text,
    ? description: text
)
</sourcecode>
      </figure>
      <dl newline="false" spacing="normal">
        <dt><tt>file_schema</tt>:</dt>
        <dd>A URI <xref target="RFC3986"/> that identifies the file schema (<xref target="file-uris"/>).</dd>
        <dt><tt>serialization_format</tt>:</dt>
        <dd>The media type of the serialization (<xref target="serialization"/>).</dd>
        <dt><tt>title</tt>, <tt>description</tt>:</dt>
        <dd>Free text about the file. These members <bcp14>SHOULD NOT</bcp14> hold information about a learner.</dd>
      </dl>
      <t>No hash covers these members. A producer <bcp14>SHOULD</bcp14> write <tt>file_schema</tt> and <tt>serialization_format</tt> within the first 256 octets of a file, as <xref target="QLOG" section="3" sectionFormat="of"/> also asks, so that a tool can identify the file from its start.</t>
      <t>The CDDL in this document has two root rules, LearnlogFile and LearnlogFileSeq. A third rule, SeqItem, describes each later element of a sequential file.</t>
      <section anchor="file-contained">
        <name>Contained Files</name>
        <t>A LearnlogFile is one JSON text that holds one or more records. It suits export and exchange. Its file schema URI is <tt>urn:ietf:params:learnlog:file:contained</tt>.</t>
        <t>The records in a file are verified one at a time. They can come from different producers and describe different subjects.</t>
      </section>
      <section anchor="file-seq">
        <name>Sequential Files</name>
        <t>A LearnlogFileSeq holds one record and grows by appending. It suits a log that is written while the learner works. Its file schema URI is <tt>urn:ietf:params:learnlog:file:sequential</tt>.</t>
        <figure anchor="fig-file-seq">
          <name>LearnlogFileSeq definition</name>
          <sourcecode type="cddl" name="learnlog.cddl">LearnlogFileSeq = {
    log-file,
    record: RecordStart
}

; every element after the first
SeqItem = Entry / SignedHead
</sourcecode>
        </figure>
        <t>The first element of a sequential file is the LearnlogFileSeq object. Every later element is a SeqItem. An element with a <tt>seq</tt> member is an Entry. Any other element with a <tt>head</tt> member is a SignedHead. Entries appear in order of <tt>seq</tt>. A producer writes a SignedHead after the entry that it names, and a reader accepts one at any position.</t>
        <t>The Entry elements, in file order, are the entries of the record, and the SignedHead elements are its signed heads. In a sequential file, <tt>event_schemas</tt> lists the schemas known when the file is started.</t>
      </section>
      <section anchor="file-uris">
        <name>File Schema URIs</name>
        <t>A registered file schema uses a URN of the form <tt>urn:ietf:params:learnlog:file:&lt;identifier&gt;</tt> (<xref target="iana-file"/>). Any other file schema uses a URI that its definer controls and <bcp14>MUST NOT</bcp14> use that URN form. Such a URI is not registered.</t>
      </section>
    </section>
    <section anchor="records">
      <name>Records</name>
      <t>A record holds a header and the entries about one subject, written by one producer.</t>
      <figure anchor="fig-record">
        <name>Record definition</name>
        <sourcecode type="cddl" name="learnlog.cddl">record-start = (
    header: RecordHeader,
    event_schemas: [+ text],
    ? truncated: Truncation
)

RecordStart = { record-start }

Record = {
    record-start,
    entries: [* Entry],
    ? signed_heads: [+ SignedHead]
}
</sourcecode>
      </figure>
      <dl newline="false" spacing="normal">
        <dt><tt>header</tt>:</dt>
        <dd>The record header (<xref target="header"/>).</dd>
        <dt><tt>event_schemas</tt>:</dt>
        <dd>The URIs of the event schemas used in the record (<xref target="event-names"/>). As in <xref target="QLOG" section="8" sectionFormat="of"/>, the list is a hint to tools about the event namespaces in the record. No hash covers it (<xref target="sec-names"/>), and it can grow with the record. A record <bcp14>MAY</bcp14> hold events from schemas that are not listed.</dd>
        <dt><tt>truncated</tt>:</dt>
        <dd>Present when the oldest entries were removed (<xref target="truncation"/>).</dd>
        <dt><tt>entries</tt>:</dt>
        <dd>The entries, in order of <tt>seq</tt> (<xref target="entries"/>).</dd>
        <dt><tt>signed_heads</tt>:</dt>
        <dd>Signatures by the producer over heads of this record (<xref target="heads"/>).</dd>
      </dl>
      <t>The CDDL in this document defines what a producer writes. A reader <bcp14>MUST</bcp14> ignore a member that it does not recognize, wherever the member appears. A hash or a signature covers such a member only in a header, an event, or a head. A producer <bcp14>SHOULD NOT</bcp14> add members anywhere else.</t>
      <section anchor="header">
        <name>Record Header</name>
        <t>The header states what a record is about and is fixed when the record is created.</t>
        <figure anchor="fig-header">
          <name>RecordHeader definition</name>
          <sourcecode type="cddl" name="learnlog.cddl">RecordHeader = {
    record_id: text,
    subject: text,
    hash_alg: HashAlg,
    created: Timestamp,
    ? producer: Producer,
    * text =&gt; any
}

Producer = {
    name: text,
    ? version: text,
    * text =&gt; any
}

HashAlg = "sha-256" / text

; lowercase hexadecimal
HashValue = text .regexp "([0-9a-f][0-9a-f])+"

; RFC 3339, UTC only
Timestamp = text .regexp
    "[0-9]{4}(-[0-9]{2}){2}T[0-9]{2}(:[0-9]{2}){2}([.][0-9]{1,9})?Z"
</sourcecode>
        </figure>
        <dl newline="false" spacing="normal">
          <dt><tt>record_id</tt>:</dt>
          <dd>An identifier for the record, unique among the records of the producer. A version 4 UUID <xref target="RFC9562"/> is <bcp14>RECOMMENDED</bcp14>.</dd>
          <dt><tt>subject</tt>:</dt>
          <dd>A pseudonymous identifier for the learner. <xref target="priv-ids"/> gives the requirements.</dd>
          <dt><tt>hash_alg</tt>:</dt>
          <dd>The hash algorithm used throughout the record. The value is a name from the IANA "Named Information Hash Algorithm Registry" <xref target="RFC6920"/>. That registry gives the output length of each algorithm, and the length <bcp14>MUST</bcp14> be at least 256 bits. Implementations <bcp14>MUST</bcp14> support <tt>sha-256</tt> <xref target="FIPS180"/>.</dd>
          <dt><tt>created</tt>:</dt>
          <dd>The time the record was created.</dd>
          <dt><tt>producer</tt>:</dt>
          <dd>The name and version of the software that writes the record.</dd>
        </dl>
        <t>A header <bcp14>MAY</bcp14> hold other members. The record hash (<xref target="hashes"/>) covers every member, so a change to the header changes the record hash and every entry hash.</t>
        <t>A Timestamp is a date and time in the format of <xref target="RFC3339"/>, in UTC, with an uppercase <tt>T</tt> and the suffix <tt>Z</tt>. The pattern in <xref target="fig-header"/> admits some impossible values, such as month 13. A Timestamp <bcp14>MUST</bcp14> also be a valid date and time. The second 60 is valid only in the time 23:59:60. A HashValue is the whole output of the hash named by <tt>hash_alg</tt>, written as two lowercase hexadecimal digits for each octet.</t>
      </section>
      <section anchor="entries">
        <name>Entries</name>
        <figure anchor="fig-entry">
          <name>Entry definition</name>
          <sourcecode type="cddl" name="learnlog.cddl">Entry = {
    seq: Seq,
    time: Timestamp,
    event_hash: HashValue,
    prev_hash: HashValue,
    entry_hash: HashValue,
    ( event: Event // redacted: true )
}

; a position in a record, from 1 to (2^53)-1
Seq = 1..9007199254740991
</sourcecode>
        </figure>
        <dl newline="false" spacing="normal">
          <dt><tt>seq</tt>:</dt>
          <dd>The position of the entry in the record. The first entry of a record has <tt>seq</tt> 1. Each later entry has the <tt>seq</tt> of its predecessor plus 1. A Seq is an integer of at least 1 and below 2^53.</dd>
          <dt><tt>time</tt>:</dt>
          <dd>The time the event occurred, as observed by the producer. Order within a record is given by <tt>seq</tt>. Values of <tt>time</tt> <bcp14>SHOULD NOT</bcp14> decrease from one entry to the next.</dd>
          <dt><tt>event_hash</tt>:</dt>
          <dd>The hash of the event (<xref target="hashes"/>).</dd>
          <dt><tt>prev_hash</tt>:</dt>
          <dd>The hash that links the entry to what precedes it (<xref target="hashes"/>).</dd>
          <dt><tt>entry_hash</tt>:</dt>
          <dd>The hash of the entry (<xref target="hashes"/>).</dd>
          <dt><tt>event</tt>:</dt>
          <dd>The event (<xref target="events"/>).</dd>
          <dt><tt>redacted</tt>:</dt>
          <dd>Present with the value true, in place of <tt>event</tt>, when the event was removed (<xref target="redaction"/>).</dd>
        </dl>
        <t>An entry holds exactly one of <tt>event</tt> and <tt>redacted</tt>.</t>
        <t>A producer appends an entry when the event occurs, or as soon after as it can. A written entry keeps its position and its hashes. This document permits two changes to the entries of a record. Redaction removes an event, and a party that kept the event can put it back (<xref target="redaction"/>). Truncation removes the oldest entries (<xref target="truncation"/>).</t>
      <t>A historical import needs to identify the action that the new event actually records. When a profile records an importer observing a source row, <tt>time</tt> is the time of that import observation, not an invented date for the original classroom action. The source timestamp, its precision, its clock basis, and any relative offset belong in the profile-defined provenance. An unavailable absolute source time remains unknown. The importer is the actor of an import-observation event; a quoted speaker is a separately identified source participant. A profile that instead represents the original action needs sufficient source evidence for the original actor and occurrence time and needs to state how that evidence was obtained. Neither construction turns a retrospective import into a contemporaneous observation by the original system.</t></section>
      <section anchor="truncation">
        <name>Truncation</name>
        <t>A holder can remove the oldest entries of a record, for example to meet a retention limit. The <tt>truncated</tt> member then holds the <tt>seq</tt> and <tt>entry_hash</tt> of the last entry that was removed.</t>
        <figure anchor="fig-truncation">
          <name>Truncation definition</name>
          <sourcecode type="cddl" name="learnlog.cddl">Truncation = {
    seq: Seq,
    entry_hash: HashValue
}
</sourcecode>
        </figure>
        <t>The first remaining entry has a <tt>seq</tt> of <tt>truncated.seq</tt> plus 1 and a <tt>prev_hash</tt> equal to <tt>truncated.entry_hash</tt>.</t>
        <t>A record can be truncated more than once. The <tt>truncated</tt> member then names the last entry removed overall. A holder can remove every entry, which leaves <tt>entries</tt> empty.</t>
        <t>A signed head that names an entry before <tt>truncated.seq</tt> can no longer be compared with an entry, and a holder <bcp14>MAY</bcp14> remove it. A holder <bcp14>SHOULD</bcp14> keep every other signed head.</t>
        <t>A verifier cannot check the entries that were removed. A change to the header still shows in the remaining entries, because every entry hash covers the record hash (<xref target="hashes"/>). <xref target="sec-shows"/> states what such a check establishes.</t>
      </section>
    </section>
    <section anchor="events">
      <name>Events</name>
      <t>An event describes one action: something the learner did, or something another party did that bears on the learner's work.</t>
      <figure anchor="fig-event">
        <name>Event definition</name>
        <sourcecode type="cddl" name="learnlog.cddl">Event = $Event

event-type&lt;Name, Data&gt; = {
    name: Name,
    salt: Salt,
    data: Data,
    ? actor: Party,
    ? to: Party,
    ? session: text,
    ? activity: Activity,
    ? instrument: Instrument,
    * text =&gt; any
}

; 22 or more characters of the base64url alphabet
Salt = text .regexp "[A-Za-z0-9_-]{22,}"
</sourcecode>
      </figure>
      <t>The <tt>event-type</tt> rule is a CDDL generic. An event type is an instance of the rule that fixes the name and the type of <tt>data</tt>. The <tt>$Event</tt> socket collects the instances (<xref target="event-extension"/>).</t>
      <dl newline="false" spacing="normal">
        <dt><tt>name</tt>:</dt>
        <dd>The event type, written as a namespace, a colon, and a type identifier (<xref target="event-names"/>).</dd>
        <dt><tt>salt</tt>:</dt>
        <dd>A random value that keeps a removed event from being guessed from its hash (<xref target="redaction"/>). For each event, a producer <bcp14>MUST</bcp14> generate at least 16 octets from a cryptographically secure source and encode them in base64url without padding <xref target="RFC4648"/>.</dd>
        <dt><tt>data</tt>:</dt>
        <dd>An object with the members that the event type defines.</dd>
        <dt><tt>actor</tt>:</dt>
        <dd>The party that performed the action (<xref target="parties"/>). When absent, the actor is the subject.</dd>
        <dt><tt>to</tt>:</dt>
        <dd>The party the action was directed at (<xref target="parties"/>). When absent, an action by another party was directed at the subject. An action by the subject then has no stated addressee.</dd>
        <dt><tt>session</tt>:</dt>
        <dd>An identifier that groups the events of one continuous period of use.</dd>
        <dt><tt>activity</tt>:</dt>
        <dd>The task in which the action took place (<xref target="activities"/>).</dd>
        <dt><tt>instrument</tt>:</dt>
        <dd>How the data of the event was produced (<xref target="instruments"/>).</dd>
      </dl>
      <t>An event <bcp14>MAY</bcp14> hold other members. The event hash covers every member of the event.</t>
      <section anchor="parties">
        <name>Parties</name>
        <t>A Party describes who acted or who was addressed.</t>
        <figure anchor="fig-party">
          <name>Party definition</name>
          <sourcecode type="cddl" name="learnlog.cddl">Party = {
    role: Role,
    ? kind: "human" / "software",
    ? id: text,
    * text =&gt; any
}

Role = "learner" / "instructor" / "assistant" / "peer" /
       "guardian" / "system" / text
</sourcecode>
        </figure>
        <t>The <tt>role</tt> member has one of these values:</t>
        <dl newline="false" spacing="normal">
          <dt><tt>learner</tt>:</dt>
          <dd>A person who is learning. A learner without an <tt>id</tt> is the subject of the record.</dd>
          <dt><tt>instructor</tt>:</dt>
          <dd>A party responsible for teaching the subject in this activity.</dd>
          <dt><tt>assistant</tt>:</dt>
          <dd>A party that responds to requests and has no teaching responsibility.</dd>
          <dt><tt>peer</tt>:</dt>
          <dd>Another learner.</dd>
          <dt><tt>guardian</tt>:</dt>
          <dd>A person who acts for the subject, such as a parent.</dd>
          <dt><tt>system</tt>:</dt>
          <dd>The producer acting in none of the roles above, for example a simulation that reveals an outcome.</dd>
        </dl>
        <t>Other values <bcp14>MAY</bcp14> be used.</t>
        <t>The <tt>kind</tt> member states whether the party is a person (<tt>human</tt>) or a program (<tt>software</tt>). When the member is absent, a learner, peer, or guardian is a person, and a system is a program. The member is <bcp14>REQUIRED</bcp14> with any other role. The CDDL in <xref target="fig-party"/> does not express this rule.</t>
        <t>The <tt>id</tt> member identifies a party other than the subject. For a person, the value is pseudonymous and follows <xref target="priv-ids"/>. For a program, the value names the program.</t>
        <t><xref target="tab-parties"/> shows how some common parties are described.</t>
        <table anchor="tab-parties">
          <name>Examples of parties</name>
          <thead>
            <tr>
              <th>Party</th>
              <th>role</th>
              <th>kind</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>A teacher working with the learner</td>
              <td>instructor</td>
              <td>human</td>
            </tr>
            <tr>
              <td>An intelligent tutoring system</td>
              <td>instructor</td>
              <td>software</td>
            </tr>
            <tr>
              <td>A general AI chat assistant</td>
              <td>assistant</td>
              <td>software</td>
            </tr>
            <tr>
              <td>A classmate</td>
              <td>peer</td>
              <td>human</td>
            </tr>
          </tbody>
        </table>
        <t>When the actor is a program, the <tt>instrument</tt> member (<xref target="instruments"/>) describes the model or the version that produced the data.</t>
      </section>
      <section anchor="activities">
        <name>Activities</name>
        <t>The meaning of an event can depend on the task it belongs to. A producer <bcp14>SHOULD</bcp14> include <tt>activity</tt> in every event that belongs to a task.</t>
        <figure anchor="fig-activity">
          <name>Activity definition</name>
          <sourcecode type="cddl" name="learnlog.cddl">Activity = {
    id: text,
    ? version: text,
    ? variant: text,
    * text =&gt; any
}
</sourcecode>
        </figure>
        <dl newline="false" spacing="normal">
          <dt><tt>id</tt>:</dt>
          <dd>An identifier for the task, such as a URI.</dd>
          <dt><tt>version</tt>:</dt>
          <dd>The version of the task content.</dd>
          <dt><tt>variant</tt>:</dt>
          <dd>The form of the task that this learner received, when forms differ. A random seed is one example.</dd>
        </dl>
      </section>
      <section anchor="instruments">
        <name>Instruments</name>
        <t>The <tt>instrument</tt> member states how the data of an event came to be. A reader needs it to weigh an event that a model wrote or judged.</t>
        <figure anchor="fig-instrument">
          <name>Instrument definition</name>
          <sourcecode type="cddl" name="learnlog.cddl">Instrument = {
    source: "deterministic" / "model" / "human" / text,
    ? model: text,
    ? config_hash: HashValue,
    ? parameters: { * text =&gt; any },
    ? validated: bool,
    * text =&gt; any
}
</sourcecode>
        </figure>
        <t>The <tt>source</tt> member has one of these values:</t>
        <dl newline="false" spacing="normal">
          <dt><tt>deterministic</tt>:</dt>
          <dd>Fixed program logic produced the data.</dd>
          <dt><tt>model</tt>:</dt>
          <dd>A statistical or generative model produced the data. A hint written by a language model and a transcript of speech are examples.</dd>
          <dt><tt>human</tt>:</dt>
          <dd>A person entered or judged the data.</dd>
        </dl>
        <t>Other values <bcp14>MAY</bcp14> be used. The remaining members are:</t>
        <dl newline="false" spacing="normal">
          <dt><tt>model</tt>:</dt>
          <dd>The identifier and version of the model or program.</dd>
          <dt><tt>config_hash</tt>:</dt>
          <dd>The hash of the configuration that governed the output, such as a prompt (<xref target="hashes"/>). The producer chooses the octets that are hashed and needs to keep them for the hash to be of use.</dd>
          <dt><tt>parameters</tt>:</dt>
          <dd>Settings that affect the output, such as a sampling temperature.</dd>
          <dt><tt>validated</tt>:</dt>
          <dd>True when a deterministic check accepted the output of a model before it was used.</dd>
        </dl>
        <t>An event without an <tt>instrument</tt> member makes no statement about how its data was produced.</t>
        <t>The <tt>producer.version</tt> member identifies recording software; <tt>instrument.model</tt> identifies the model or program that produced the event data. Keeping these identifiers distinct lets a reader inspect changes in either. A configuration hash identifies retained octets; it does not make those octets available, show that the named configuration was executed, or make a probabilistic output reproducible. The <tt>validated</tt> member reports a deterministic acceptance check, not empirical validation of educational effectiveness or a learner's knowledge.</t>
      </section>
      <section anchor="event-names">
        <name>Event Names and Event Schemas</name>
        <t>Event types are grouped in namespaces, as in <xref target="QLOG" section="8" sectionFormat="of"/>. An event schema is a document that defines a namespace or extends one. The name of an event is the namespace identifier, a colon, and the type identifier. Each identifier starts with a letter from a to z and continues with such letters, digits, or underscores.</t>
        <t>Each event schema has a URI. A registered schema uses the form <tt>urn:ietf:params:learnlog:events:&lt;namespace&gt;</tt> (<xref target="iana-event"/>). A schema that extends a namespace adds a fragment identifier to the URI of that namespace. Any other schema uses a URI that its definer controls and <bcp14>MUST NOT</bcp14> use that URN form. A URI that contains a domain name <bcp14>SHOULD</bcp14> include a month and year in the form mmyyyy. <xref target="QLOG" section="8.1" sectionFormat="of"/> gives the same rule, to avoid problems when a domain name changes ownership.</t>
        <t>A record can hold an event whose schema a tool does not know. Such an event does not match the CDDL that the tool has. Verification (<xref target="verification"/>) checks its hashes like any others and does not depend on its schema.</t>
      </section>
      <section anchor="event-extension">
        <name>Defining and Extending Event Types</name>
        <t>An event schema adds each of its event types to the <tt>$Event</tt> socket. The lines below, taken from the example schema in <xref target="ex-schema"/>, define the event type <tt>sim:prediction_committed</tt>.</t>
        <artwork>$Event /= event-type&lt;"sim:prediction_committed",
                     SimPredictionCommitted&gt;
</artwork>
        <t>Each data type in this document ends with a group socket named after the type, such as <tt>$$dialog-message-extension</tt>. A later schema can add members to the data type through that socket.</t>
      </section>
      <section anchor="event-guidance">
        <name>Guidance for Schema Designers</name>
        <t>The points below apply to any event schema.</t>
        <ul>
          <li>Define events for actions that have meaning in the subject area, such as a prediction committed before an experiment or a claim filed with its evidence. Interface activity, such as clicks and page views, does not belong in a record unless a schema gives it that kind of meaning.</li>
          <li>Record what happened and leave conclusions out. A detected misconception is a judgment about the learner. An event that holds one <bcp14>SHOULD</bcp14> carry an <tt>instrument</tt> member.</li>
          <li>Refer to an earlier entry of the same record by its <tt>seq</tt>.</li>
          <li>Allow text written by a learner to be left out. Free text can hold personal data. A hash can stand in for the text, within the limits that <xref target="sec-guess"/> describes.</li>
          <li>State which members can identify a person.</li>
          <li>Prefer integers and strings to fractional numbers where exact values matter (<xref target="canonical"/>).</li>
        </ul>
      </section>
      <section anchor="profile-guidance">
        <name>Companion Profiles for AI Use</name>
        <t>This subsection gives non-normative guidance. It adds no core fields or event types. A companion profile can use the extension points of the header and events to state context needed by a particular deployment. The profile needs its own identifier, version, and public specification. Event-schema URIs follow <xref target="event-names"/>. A profile identifier is not a registration in the registries requested by this document.</t>
        <t>A profile can distinguish practice, assessment, and other activity stages; identify the policy that permitted or prohibited specified assistance; and bind that policy to the relevant actor, task, stage, and period. Permission to use assistance in an activity is separate from consent to collect, use, or disclose data (<xref target="consent"/>). Neither can be inferred from the other. A policy assertion also does not establish compliance.</t>
        <t>A profile can state which channels, sessions, activities, and periods the producer planned to observe, which it asserts it captured, and which it excluded. Planned capture and achieved capture are different assertions. A missing assistance event does not demonstrate that no assistance occurred. Outside applications, other devices, between-session use, a capture interruption, and unavailable content can leave relevant exposure unknown. Describing a gap does not establish that AI was used in that gap.</t>
        <t>The header is fixed when a record is created (<xref target="header"/>). A plan stated there is not a later completion report. A change in stage, applicable policy, model, configuration, or observation coverage can be represented by starting another record with the new context, or by an explicitly defined extension carried in a hashed event. A profile using such an extension needs to define its effective scope and how events refer to the context. This document defines no runtime context-update or capture-completion event, and modifying an existing header changes the hash commitments.</t>
        <t>A profile can require the existing <tt>actor</tt>, <tt>activity</tt>, <tt>session</tt>, and <tt>instrument</tt> members in particular events. It can also identify where retained content or configuration is available to an authorized reviewer. A hash or content reference alone does not give that reviewer the semantic content needed to classify a scaffold, and access can be limited by <xref target="privacy"/>. Profiles need to distinguish missing information from information that has not been reviewed.</t>
        <t>Conditions such as whether AI changed practice, comparison-group exposure, or grading usually require evidence beyond one record. Condition assessments and judgments about the applicability of educational research belong in separately specified analytical records. Those records can refer to the relevant learnlog record and entries, state the reviewed scope and comparators, and preserve unresolved questions. The core verifier does not endorse those judgments or decide whether a study needs to be repeated.</t>
      <t>A concrete assistance or import profile needs to publish the following as versioned, machine-readable definitions with examples and validation rules:</t><ul>
<li>Assistance states, such as requested, generated, delivered, or displayed, each with its source evidence and unit. These are distinct observations; a profile cannot assume that a generated response was delivered or displayed.</li>
<li>The scope of a context assertion: actor, activity, session, stage, period, applicable assistance policy, producing instrument, planned capture, achieved capture, and gaps. Unknown, unavailable, and not yet reviewed information need distinct representations. Context changes and completion claims need effective-scope and precedence rules.</li>
<li>Import provenance: source identifier and release, source row or event identity, original time or relative offset, importer identity and version, import time, and the relation between source speakers and event actors.</li>
<li>Mapping definitions: the source concept and unit, target concept and unit, transformation rule, preserved distinctions, omitted or lost information, assumptions, reviewer and review status. Mapping identity and a digest show which mapping was used, not that it is substantively correct.</li>
<li>Annotation provenance: target source spans or entry references, coding scheme and version, annotating person or instrument, review status, and the distinction between an observed action and a later classification. Overlapping annotations need explicit counting rules.</li>
<li>For a proposed combined measurement, the operational definition, observation unit, denominator, missingness and coverage requirements, coding procedure, inclusion and exclusion rules, and review decision. Shared event names do not establish that measurements can be pooled.</li>
</ul><t>The separate experimental companion profile <tt>qlm:assistance-context:0.1.0</tt>, version 0.1.0, is distributed with the revision -02 preparation materials. Its machine-readable definition is <tt>assistance-context-v0.1.0.json</tt>, with event schemas <tt>research-import-v2.json</tt> and <tt>assistance-lifecycle-v1.json</tt>. It defines importer observations of source-declared assistance states, per-event context with unknown defaults, and explicit mapping limits. It does not implement a capture-completion event. It is not an IETF-registered schema, is not a normative dependency of this document, and has not received independent semantic validation. Its identifier and version must accompany data that claim to use it. A deployment should publish an immutable specification and schema location before asking unrelated implementations to interoperate. A local file path or unpublished identifier does not establish public availability.</t><t>The informative inventory below contains 16 candidate AI-related conditions for reviewing a study and a target setting. The labels come from the project's proposed condition crosswalk version 0.1.0; they are not core event types. It is optional analytical guidance, not a validated exhaustive taxonomy, a required set of event fields, or evidence that any condition occurred. A condition needs its own supporting evidence and explicit uncertainty. Research applicability assessments and recommendations to repeat a study remain outside core verification.</t><dl spacing="normal"><dt>C01:</dt><dd>Answer before retrieval</dd><dt>C02:</dt><dd>Explanation or reflection supplied</dd><dt>C03:</dt><dd>Feedback or scaffolding changed</dd><dt>C04:</dt><dd>Practice sequence or dose changed</dd><dt>C05:</dt><dd>Extra exposure between sessions</dd><dt>C06:</dt><dd>AI permitted on assessment</dd><dt>C07:</dt><dd>Prohibited assessment assistance</dd><dt>C08:</dt><dd>Comparison condition changed</dd><dt>C09:</dt><dd>Teacher or peer delivery changed</dd><dt>C10:</dt><dd>Baseline or preparation changed</dd><dt>C11:</dt><dd>Scoring or grading changed</dd><dt>C12:</dt><dd>Motivation or metacognitive signals</dd><dt>C13:</dt><dd>Task novelty or difficulty changed</dd><dt>C14:</dt><dd>Model or policy version changed</dd><dt>C15:</dt><dd>Access and use differ</dd><dt>C16:</dt><dd>Relevant exposure unobserved</dd></dl><t>This inventory reproduces candidate labels only. The earlier crosswalk targets an older draft, so its field selectors are not incorporated here as current requirements. Applying a condition requires a documented task, observation boundary, and supporting evidence; the label alone cannot establish occurrence, a learning effect, or a need to repeat a study.</t></section>
    </section>
    <section anchor="generic">
      <name>Generic Event Schemas</name>
      <t>This section defines event schemas for three namespaces that are not tied to one subject area: <tt>consent</tt>, <tt>assist</tt>, and <tt>dialog</tt>.</t>
      <section anchor="consent">
        <name>The consent Namespace</name>
        <t>The schema URI is <tt>urn:ietf:params:learnlog:events:consent</tt>. The namespace has two event types, <tt>consent:granted</tt> and <tt>consent:withdrawn</tt>. Both carry the same data.</t>
        <figure anchor="fig-consent">
          <name>Events of the consent namespace</name>
          <sourcecode type="cddl" name="learnlog.cddl">ConsentChange = {
    scopes: [+ ConsentScope],
    ? ref: text,
    * $$consent-change-extension
}

ConsentScope = "instruction" / "research" / "export" / text

$Event /= event-type&lt;"consent:granted", ConsentChange&gt;
$Event /= event-type&lt;"consent:withdrawn", ConsentChange&gt;
</sourcecode>
        </figure>
        <dl newline="false" spacing="normal">
          <dt><tt>scopes</tt>:</dt>
          <dd>The uses that the consent covers.</dd>
          <dt><tt>ref</tt>:</dt>
          <dd>A reference to the consent that the producer holds, such as the identifier of a signed form.</dd>
        </dl>
        <t>This document defines three scopes. Other values <bcp14>MAY</bcp14> be used.</t>
        <dl newline="false" spacing="normal">
          <dt><tt>instruction</tt>:</dt>
          <dd>Use of the record to support the instruction of the subject.</dd>
          <dt><tt>research</tt>:</dt>
          <dd>Use of the record in research.</dd>
          <dt><tt>export</tt>:</dt>
          <dd>Disclosure of the record outside the organization that operates the producer.</dd>
        </dl>
        <t>The actor of a consent event is the party that gave or withdrew consent, for example a guardian.</t>
        <t>A consent event records that consent was given or withdrawn, as the producer observed it. This document does not define how consent is obtained or how a producer acts on it.</t>
        <t>A scope is in effect from a <tt>consent:granted</tt> event that names it until a later <tt>consent:withdrawn</tt> event that names it. A reader cannot tell which scopes are in effect when a consent event may be among the entries that were redacted or removed. For that reason, a holder <bcp14>SHOULD NOT</bcp14> redact a consent event.</t>
      </section>
      <section anchor="assist">
        <name>The assist Namespace</name>
        <t>The schema URI is <tt>urn:ietf:params:learnlog:events:assist</tt>. The namespace records the help that the producer observed. A reader can treat an action by the learner that follows a scaffold in the same activity as possibly assisted.</t>
        <dl newline="false" spacing="normal">
          <dt><tt>assist:help_requested</tt>:</dt>
          <dd>The actor asked for help. The <tt>to</tt> member of the event names the party that was asked.</dd>
          <dt><tt>assist:scaffold_provided</tt>:</dt>
          <dd>The actor gave the learner a scaffold, which is support meant to help the learner make progress. The actor can be an instructor, an assistant, a peer, or the system. The event <bcp14>MUST</bcp14> hold the <tt>actor</tt> member, because an absent actor would mean the subject.</dd>
        </dl>
        <t>The core <tt>assist:scaffold_provided</tt> event asserts that the producer observed support being given to the learner; it does not establish that the learner read, heard, used, understood, or learned from it. Content generated internally but not given to the learner is not, on that fact alone, a provided scaffold. Profiles can define separate observations for generation, transmission, delivery, and display, and state which evidence supports each. Those stages cannot be inferred from one another or counted as separate help episodes without a measurement rule. A provider's source label such as "hint" remains a source assertion unless its meaning and mapping have been reviewed.</t><t>The change to the interpretation of an absent <tt>request</tt> is a semantic revision, even though the CDDL and hash computations are unchanged. Consumers of historical records need to consult the source's applicable schema or profile revision and retain any explicit original assertion that a scaffold was unsolicited. They must not treat a revision -01 producer's missing link as an independently established observation of request origin, nor silently rewrite old records to add a new commitment.</t><figure anchor="fig-assist">
          <name>Events of the assist namespace</name>
          <sourcecode type="cddl" name="learnlog.cddl">AssistHelpRequested = {
    ? type: ScaffoldType,
    * $$assist-help-requested-extension
}

AssistScaffoldProvided = {
    type: ScaffoldType,
    ? request: Seq,
    ? level: uint .ge 1,
    ? reveals_answer: bool,
    ? modality: "text" / "speech" / text,
    ? content: text,
    ? content_ref: text,
    ? content_hash: HashValue,
    * $$assist-scaffold-provided-extension
}

ScaffoldType = "socratic" / "probing" / "metacognitive" /
               "scaffolding" / "explain" / "hint" /
               "demonstrate" / text

$Event /= event-type&lt;"assist:help_requested", AssistHelpRequested&gt;
$Event /= event-type&lt;"assist:scaffold_provided",
                     AssistScaffoldProvided&gt;
</sourcecode>
        </figure>
        <t>In AssistHelpRequested, <tt>type</tt> is the kind of help that was asked for, when the interface distinguishes kinds. The members of AssistScaffoldProvided are:</t>
        <dl newline="false" spacing="normal">
          <dt><tt>type</tt>:</dt>
          <dd>The kind of scaffold (<xref target="tab-scaffold"/>).</dd>
          <dt><tt>request</tt>:</dt>
          <dd><t>The <tt>seq</tt> of the entry that this scaffold answers, such as an <tt>assist:help_requested</tt> entry or a <tt>dialog:message</tt> entry. An absent value means that this event contains no request link. It does not by itself distinguish unsolicited assistance from a request that was not observed, imported, or linked. A profile can record those cases explicitly. This clarification supersedes the inference from an absent link in revision -01.</t></dd><dt><tt>level</tt>:</dt>
          <dd><t>The delivery-order position of the scaffold among those given for the current activity, starting at 1. This is not automatically the source product's authored hint depth, pedagogical difficulty, or amount of support. A source-native hint index needs a separate profile-defined field unless a documented mapping establishes that the meanings coincide. If the delivery position is unknown, omit <tt>level</tt>.</t></dd><dt><tt>reveals_answer</tt>:</dt>
          <dd>True when the scaffold discloses the answer that the activity asks for.</dd>
          <dt><tt>modality</tt>:</dt>
          <dd>How the scaffold was expressed. The default is <tt>text</tt>.</dd>
          <dt><tt>content</tt>:</dt>
          <dd>The words of the scaffold.</dd>
          <dt><tt>content_ref</tt>:</dt>
          <dd>An identifier for authored scaffold content.</dd>
          <dt><tt>content_hash</tt>:</dt>
          <dd>The hash of the scaffold as shown to the learner (<xref target="hashes"/>), for use when <tt>content</tt> is left out. An event <bcp14>MUST NOT</bcp14> hold both <tt>content</tt> and <tt>content_hash</tt>.</dd>
        </dl>
        <table anchor="tab-scaffold">
          <name>Values of ScaffoldType</name>
          <thead>
            <tr>
              <th>Value</th>
              <th>Meaning</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>socratic</td>
              <td>An open question that leads the learner toward an idea</td>
            </tr>
            <tr>
              <td>probing</td>
              <td>A question about an assumption the learner made</td>
            </tr>
            <tr>
              <td>metacognitive</td>
              <td>A prompt about the learner's own approach</td>
            </tr>
            <tr>
              <td>scaffolding</td>
              <td>The problem broken into parts</td>
            </tr>
            <tr>
              <td>explain</td>
              <td>A direct explanation</td>
            </tr>
            <tr>
              <td>hint</td>
              <td>A hint</td>
            </tr>
            <tr>
              <td>demonstrate</td>
              <td>A worked example</td>
            </tr>
          </tbody>
        </table>
        <t>Other values <bcp14>MAY</bcp14> be used.</t>
        <t>A producer <bcp14>SHOULD</bcp14> record every scaffold given to the learner, requested or not. The <tt>actor</tt> member identifies the provider.</t>
        <t>Help can arrive as a turn in a conversation. A producer that knows a turn to be help records it as <tt>assist:scaffold_provided</tt>, with the words in <tt>content</tt> or their hash in <tt>content_hash</tt>, and writes no <tt>dialog:message</tt> for that turn. When a producer cannot tell whether a turn is help, it records the turn as <tt>dialog:message</tt> (<xref target="dialog"/>). The replies of a general chat assistant are an example.</t>
        <section anchor="assist-ai-example">
          <name>Hypothetical AI Assistance Example</name>
          <t>Consider a task that asks a learner to solve 2x + 3 = 11 and explain the approach. An AI assistant might say, "Before calculating, describe your plan and how you will check whether it worked." An <tt>assist:scaffold_provided</tt> event could describe this as <tt>type</tt> equal to <tt>metacognitive</tt>, with <tt>reveals_answer</tt> equal to false and the words in <tt>content</tt>. The <tt>actor</tt> identifies a software assistant, and the activity, session, and instrument identify the task and producing model.</t>
          <t>In a different hypothetical history, the assistant says, "Subtract 3 from both sides to get 2x = 8, then divide both sides by 2, so x = 4." The event could instead use <tt>type</tt> equal to <tt>explain</tt> and <tt>reveals_answer</tt> equal to true. These examples illustrate existing members; they are not measurements of learning and introduce no new event type.</t>
          <t>If a subject-area schema records a learner response after either scaffold, the sequence can distinguish the two recorded assistance histories. It cannot establish the learner's internal reasoning, when internal retrieval took place, whether another source was used, or which assistance caused a later outcome. A declaration of <tt>type</tt> or <tt>reveals_answer</tt> remains a producer assertion. A reviewer needs the relevant content and task to inspect it. An applicability assessment comparing this activity with a classroom intervention needs additional evidence and separately stated assumptions.</t>
        </section>
      </section>
      <section anchor="dialog">
        <name>The dialog Namespace</name>
        <t>The schema URI is <tt>urn:ietf:params:learnlog:events:dialog</tt>. The namespace has one event type, <tt>dialog:message</tt>, which is one turn in an exchange between the subject and another party. The actor is the party that produced the turn, and <tt>to</tt> names the party addressed.</t>
        <figure anchor="fig-dialog">
          <name>Events of the dialog namespace</name>
          <sourcecode type="cddl" name="learnlog.cddl">DialogMessage = {
    ? modality: "text" / "speech" / text,
    ? content: text,
    ? content_hash: HashValue,
    ? reply_to: Seq,
    * $$dialog-message-extension
}

$Event /= event-type&lt;"dialog:message", DialogMessage&gt;
</sourcecode>
        </figure>
        <dl newline="false" spacing="normal">
          <dt><tt>modality</tt>:</dt>
          <dd>How the turn was expressed. The default is <tt>text</tt>.</dd>
          <dt><tt>content</tt>:</dt>
          <dd>The words of the turn.</dd>
          <dt><tt>content_hash</tt>:</dt>
          <dd>The hash of the words (<xref target="hashes"/>), for use when <tt>content</tt> is left out. An event <bcp14>MUST NOT</bcp14> hold both <tt>content</tt> and <tt>content_hash</tt>.</dd>
          <dt><tt>reply_to</tt>:</dt>
          <dd>The <tt>seq</tt> of the entry that this turn answers.</dd>
        </dl>
        <t>A producer <bcp14>SHOULD</bcp14> include <tt>activity</tt> when the exchange takes place during a task. A reader can then treat a turn by another party as possible help.</t>
        <t>The words of a turn can hold personal data, and <xref target="privacy"/> applies. When software transcribes speech, the event <bcp14>SHOULD</bcp14> carry an <tt>instrument</tt> member that identifies the transcriber.</t>
      </section>
    </section>
    <section anchor="integrity">
      <name>Integrity</name>
      <section anchor="canonical">
        <name>Canonical Form</name>
        <t>Hashes are computed over a canonical form, so that two implementations produce the same octets for the same value. The canonical form of a JSON value is its serialization under the JSON Canonicalization Scheme (JCS) <xref target="RFC8785"/>, encoded in UTF-8.</t>
        <t>The following rules apply to a whole log file, including the parts that no hash covers:</t>
        <ul>
          <li>A log file <bcp14>MUST</bcp14> conform to I-JSON <xref target="RFC7493"/>. In particular, no object holds two members with the same name, and no string holds a noncharacter or a surrogate without its pair.</li>
          <li>A number is hashed as the IEEE 754 double nearest to its value, in the form that <xref target="RFC8785"/> gives that double. A file <bcp14>MUST NOT</bcp14> hold a number too large for a double. A number written without a fraction or an exponent <bcp14>MUST</bcp14> have an absolute value below 2^53. A larger integer is carried as a string.</li>
          <li>A string is hashed as the characters it denotes. An escape sequence and the character it stands for give the same hash. A holder <bcp14>MUST NOT</bcp14> apply Unicode normalization.</li>
          <li>A holder <bcp14>MUST</bcp14> preserve every member of a header, an event, and a head, including members it does not recognize.</li>
        </ul>
        <t>A serialization other than JSON (<xref target="ser-other"/>) is mapped to the JSON data model first. Hashes are always computed over the canonical JSON form.</t>
      </section>
      <section anchor="hashes">
        <name>Hashes</name>
        <t>In this section, H(x) is the hash named by <tt>hash_alg</tt>, applied to the octets x and written in lowercase hexadecimal. C(v) is the canonical form of the JSON value v.</t>
        <figure anchor="fig-hashes">
          <name>Hash computation</name>
          <artwork>record hash  = H(C(header))

event_hash   = H(C(event))

prev_hash    = record hash       in the entry with seq 1
             = entry_hash of the preceding entry, otherwise

entry_hash   = H(C({ "record_hash": record hash,
                     "seq": seq, "time": time,
                     "event_hash": event_hash,
                     "prev_hash": prev_hash }))</artwork>
        </figure>
        <ul>
          <li>The record hash is the hash of the <tt>header</tt> object.</li>
          <li><tt>event_hash</tt> is the hash of the <tt>event</tt> object with all its members.</li>
          <li><tt>prev_hash</tt> of the entry with <tt>seq</tt> 1 is the record hash. In every other entry, it is the <tt>entry_hash</tt> of the entry before it.</li>
          <li><tt>entry_hash</tt> is the hash of an object with exactly five members. The member <tt>record_hash</tt> holds the record hash. The other four are the <tt>seq</tt>, <tt>time</tt>, <tt>event_hash</tt>, and <tt>prev_hash</tt> of the entry.</li>
          <li>A member named <tt>content_hash</tt> or <tt>config_hash</tt> holds the hash of a string of octets. For text, the octets are its UTF-8 encoding. For other content, the producer chooses the octets.</li>
        </ul>
        <t>Every entry hash covers the record hash. An entry that is moved to a record with another header therefore fails step 4e of <xref target="verification"/>, even after the entries before it are removed (<xref target="truncation"/>).</t>
        <t>A header and an event each require members that the object hashed for an entry lacks. The canonical form of that object therefore differs from the canonical form of any header or event.</t>
      </section>
      <section anchor="redaction">
        <name>Redaction</name>
        <t>A holder redacts an entry by removing its <tt>event</tt> member and adding the member <tt>redacted</tt> with the value true. No other member changes.</t>
        <t>The entry hash is computed from <tt>event_hash</tt>, without the event itself. Every hash in the record therefore still verifies, and so does every signed head.</t>
        <t>The salt is removed with the event. A party that does not have the salt cannot confirm a guess at the event from <tt>event_hash</tt> (<xref target="sec-guess"/>).</t>
        <t>Redaction leaves <tt>seq</tt>, <tt>time</tt>, and the hashes of the entry in place. A holder that needs to remove those as well truncates the record (<xref target="truncation"/>) or deletes it.</t>
        <t>A verifier reports which entries are redacted. Redaction does not cause a check to fail.</t>
        <t>Only a party that kept the event can restore it. A restored event is checked against <tt>event_hash</tt> like any other.</t>
      </section>
      <section anchor="heads">
        <name>Signed Heads</name>
        <t>A head names the latest entry of a record at some moment. A producer signs a head so that a verifier can authenticate the record up to that entry.</t>
        <figure anchor="fig-head">
          <name>SignedHead definition</name>
          <sourcecode type="cddl" name="learnlog.cddl">SignedHead = {
    head: Head,
    signature: text
}

Head = {
    record_hash: HashValue,
    seq: Seq,
    entry_hash: HashValue,
    time: Timestamp,
    * text =&gt; any
}
</sourcecode>
        </figure>
        <dl newline="false" spacing="normal">
          <dt><tt>record_hash</tt>:</dt>
          <dd>The record hash (<xref target="hashes"/>).</dd>
          <dt><tt>seq</tt>, <tt>entry_hash</tt>:</dt>
          <dd>The <tt>seq</tt> and <tt>entry_hash</tt> of the entry that the head names.</dd>
          <dt><tt>time</tt>:</dt>
          <dd>The time of signing, as observed by the producer.</dd>
        </dl>
        <t>The <tt>signature</tt> member is a JSON Web Signature (JWS) <xref target="RFC7515"/> in the compact serialization with detached content (<xref target="RFC7515" section="F" sectionFormat="of"/>). The JWS payload is the canonical form of the <tt>head</tt> object. The value of <tt>signature</tt> is therefore the encoded protected header, two periods, and the encoded signature.</t>
        <ul>
          <li>The protected header <bcp14>MUST</bcp14> contain <tt>alg</tt> and <tt>kid</tt> and <bcp14>MUST NOT</bcp14> contain <tt>crit</tt>.</li>
          <li>The value of <tt>alg</tt> <bcp14>MUST</bcp14> name a digital signature algorithm that uses a public key. The value <tt>none</tt> and MAC algorithms such as <tt>HS256</tt> <bcp14>MUST NOT</bcp14> be used. Implementations <bcp14>MUST</bcp14> support <tt>ES256</tt> <xref target="RFC7518"/>.</li>
          <li>The value of <tt>kid</tt> <bcp14>MUST</bcp14> be the JWK Thumbprint <xref target="RFC7638"/> of the public key, computed with SHA-256 and encoded in base64url without padding.</li>
        </ul>
        <t>To check a signature, a verifier computes the canonical form of <tt>head</tt>, encodes it in base64url, places it between the two periods, and verifies the result as a JWS. A verifier uses each key it trusts with exactly one algorithm (<xref target="RFC8725" section="3.1" sectionFormat="of"/>).</t>
        <t>A producer <bcp14>SHOULD</bcp14> sign a head at the end of each session and whenever a record leaves its control.</t>
        <t>This document does not define how a verifier obtains the public key or decides to trust it. A key that travels in the file itself shows nothing about who signed.</t>
      </section>
      <section anchor="verification">
        <name>Verification</name>
        <t>A verifier is configured with the public keys it trusts and, for each key, the one signature algorithm it accepts. A verifier <bcp14>MUST</bcp14> report the result that the steps below define. Any procedure that gives the same result is acceptable.</t>
        <ol>
          <li>
            <t>Read the file.</t>
            <ol type="a">
              <li>Read a file that begins with the octet 0x1E as a JSON text sequence (<xref target="ser-seq"/>). Read any other file as one JSON text (<xref target="ser-json"/>).</li>
              <li>Check that each JSON text conforms to I-JSON and to the rules for numbers in <xref target="canonical"/>.</li>
              <li>Check that <tt>file_schema</tt> names a file schema that the verifier knows and that fits the serialization. Check that a contained file holds a <tt>records</tt> array with at least one element, and that the first element of a sequential file holds a <tt>record</tt> object.</li>
            </ol>
            <t>A verifier <bcp14>MUST</bcp14> reject a file when its JSON text fails this step, or when the first element of a sequential file does. None of its records is verified. A later element of a sequential file that fails step 1b is discarded (<xref target="ser-seq"/>). Steps 2 to 5 apply to each record of a file that passes.</t>
          </li>
          <li>
            <t>Check the header.</t>
            <ol type="a">
              <li>Check that <tt>header</tt> is an object, that <tt>record_id</tt>, <tt>subject</tt>, and <tt>hash_alg</tt> are strings, and that <tt>created</tt> is a Timestamp.</li>
              <li>If step 2a passed and the verifier does not support <tt>hash_alg</tt>, the outcome for the record is "unsupported" and no further step applies. A verifier <bcp14>MUST NOT</bcp14> support an algorithm whose output is shorter than 256 bits.</li>
              <li>Compute the record hash.</li>
            </ol>
          </li>
          <li>Set the expected <tt>seq</tt> to 1 and the expected link to the record hash. If <tt>truncated</tt> is present, check that <tt>truncated.seq</tt> is a Seq and that <tt>truncated.entry_hash</tt> is a HashValue. The expected <tt>seq</tt> is then <tt>truncated.seq</tt> plus 1, and the expected link is <tt>truncated.entry_hash</tt>.</li>
          <li>
            <t>Check that <tt>entries</tt> is an array. For each entry, in order:</t>
            <ol type="a">
              <li>Check that <tt>seq</tt> is a Seq, that <tt>time</tt> is a Timestamp, and that <tt>event_hash</tt>, <tt>prev_hash</tt>, and <tt>entry_hash</tt> are HashValues. Check that the entry holds exactly one of <tt>event</tt> and <tt>redacted</tt>.</li>
              <li>Check that <tt>seq</tt> equals the expected <tt>seq</tt>.</li>
              <li>Check that <tt>prev_hash</tt> equals the expected link.</li>
              <li>If <tt>event</tt> is present, check that it is an object, that its <tt>name</tt> has the form given in <xref target="event-names"/>, that its <tt>salt</tt> matches the Salt pattern of <xref target="fig-event"/>, and that its <tt>data</tt> is an object. Then check that the hash of the event equals <tt>event_hash</tt>. If <tt>redacted</tt> is present, check that its value is true.</li>
              <li>Compute the entry hash and check that it equals <tt>entry_hash</tt>.</li>
              <li>Set the expected link to <tt>entry_hash</tt> and add 1 to the expected <tt>seq</tt>.</li>
            </ol>
          </li>
          <li>
            <t>If <tt>signed_heads</tt> is present, check that it is an array. For each signed head:</t>
            <ol type="a">
              <li>Check that <tt>head</tt> is an object in which <tt>record_hash</tt> and <tt>entry_hash</tt> are HashValues, <tt>seq</tt> is a Seq, and <tt>time</tt> is a Timestamp. Check that <tt>signature</tt> consists of two base64url strings, neither of them empty, joined by two periods. Check that the first string decodes to a JSON object that conforms to I-JSON. Check that this object holds the strings <tt>alg</tt> and <tt>kid</tt>, that <tt>alg</tt> is not <tt>none</tt>, and that <tt>crit</tt> is absent.</li>
              <li>Check that <tt>head.record_hash</tt> equals the record hash.</li>
              <li>Check that <tt>head.seq</tt> is no greater than the <tt>seq</tt> of the last entry. A greater value shows that entries are missing from the end of the record. For a record without entries, the comparison uses <tt>truncated.seq</tt>, or 0 when the record is not truncated.</li>
              <li>If an entry has the <tt>seq</tt> that the head names, check that <tt>head.entry_hash</tt> equals its <tt>entry_hash</tt>. If <tt>head.seq</tt> equals <tt>truncated.seq</tt>, check that <tt>head.entry_hash</tt> equals <tt>truncated.entry_hash</tt>. A head with a lower <tt>seq</tt> names an entry that was removed, and this check does not apply to it.</li>
              <li>If the verifier trusts a key whose JWK Thumbprint equals <tt>kid</tt>, check that <tt>alg</tt> is the algorithm it accepts for that key, and verify the signature with that key as <xref target="heads"/> describes. If the verifier trusts no such key, the signature is not checked.</li>
            </ol>
          </li>
        </ol>
        <t><xref target="header"/> and <xref target="entries"/> define the terms Timestamp, HashValue, and Seq. A member that this document does not define is ignored in every step, apart from its part in a hash or a signature.</t>
        <t>Once a check in steps 2 to 5 fails, the outcome is settled, and a verifier <bcp14>MAY</bcp14> stop.</t>
        <t>The result for a record has these parts:</t>
        <dl newline="false" spacing="normal">
          <dt>Outcome:</dt>
          <dd>"consistent" when every check in steps 2 to 5 passed, "inconsistent" when a check failed, or "unsupported" as step 2 describes.</dd>
          <dt>Authenticated through:</dt>
          <dd>For a consistent record, the highest <tt>seq</tt> among the heads that had an entry hash to compare in step 5d and a signature that verified in step 5e, together with the <tt>kid</tt> of a head that names it. This part is absent when no head qualifies. Entries up to that <tt>seq</tt> were in the record when the holder of that key signed. Later entries are consistent and unauthenticated.</dd>
          <dt>Redacted:</dt>
          <dd>The <tt>seq</tt> of each redacted entry.</dd>
          <dt>Truncated:</dt>
          <dd>The value of <tt>truncated.seq</tt>, when the record is truncated. The entries up to that <tt>seq</tt> were not checked.</dd>
        </dl>
        <t>A verifier reports the following conditions, and they leave the outcome unchanged:</t>
        <ul>
          <li>a <tt>time</tt> that is earlier than the one before it, with the two compared as instants;</li>
          <li>a signed head whose signature was not checked;</li>
          <li>an element of a sequential file that was discarded (<xref target="ser-seq"/>).</li>
        </ul>
        <t>Verification does not use <tt>title</tt>, <tt>description</tt>, <tt>serialization_format</tt>, or <tt>event_schemas</tt>. The <tt>data</tt> of an event is not checked against an event schema, and an event of an unknown type is handled like any other. A tool <bcp14>MAY</bcp14> validate events against the schemas it knows and report the result separately.</t>
        <t>A consistent record agrees with itself. A verified head ties the entries up to its <tt>seq</tt> to a key. <xref target="sec-shows"/> states what these results establish and what they leave open.</t>
      </section>
    </section>
    <section anchor="serialization">
      <name>Serialization</name>
      <t>The structures in this document are defined in CDDL. <xref target="QLOG" section="11" sectionFormat="of"/> takes the same approach and maps its structures to JSON and to JSON text sequences. This document defines the same two serializations. Hashes always use the canonical JSON form (<xref target="canonical"/>).</t>
      <t>Where this document defines a member as an integer, a producer writes the number without a fraction or an exponent. A reader compares numbers by value.</t>
      <section anchor="ser-json">
        <name>JSON</name>
        <t>A LearnlogFile is serialized as a JSON text <xref target="RFC8259"/> in UTF-8. The media type is <tt>application/learnlog+json</tt>, which is also the value of <tt>serialization_format</tt>. The file extension is <tt>.learnlog</tt>.</t>
      </section>
      <section anchor="ser-seq">
        <name>JSON Text Sequences</name>
        <t>A LearnlogFileSeq is serialized as a JSON text sequence <xref target="RFC7464"/>. Each element is preceded by a record separator (0x1E) and followed by a line feed (0x0A). The media type is <tt>application/learnlog+json-seq</tt>, which is also the value of <tt>serialization_format</tt>. The file extension is <tt>.slearnlog</tt>.</t>
        <t>A producer can add an entry or a signed head to a sequential file without rewriting what came before. Redaction and truncation rewrite the file. In <xref target="fig-seq"/>, &lt;RS&gt; stands for the record separator and most values are left out.</t>
        <figure anchor="fig-seq">
          <name>Outline of a sequential file</name>
          <artwork>&lt;RS&gt;{"file_schema":"urn:ietf:params:learnlog:file:sequential",
     "serialization_format":"application/learnlog+json-seq",
     "record":{"header":{...},"event_schemas":[...]}}
&lt;RS&gt;{"seq":1,"time":"2026-10-07T14:00:05Z","event":{...},
     "event_hash":"...","prev_hash":"...","entry_hash":"..."}
&lt;RS&gt;{"seq":2,"time":"2026-10-07T14:01:10Z","event":{...},
     "event_hash":"...","prev_hash":"...","entry_hash":"..."}
&lt;RS&gt;{"head":{...},"signature":"..."}
</artwork>
        </figure>
        <t>A crash can leave the last element of a file cut off. A reader <bcp14>MUST</bcp14> discard an element that is cut off (<xref target="RFC7464" section="2.3" sectionFormat="of"/>) or that fails step 1b of <xref target="verification"/>. The same applies to a parsed element that has neither the <tt>seq</tt> nor the <tt>head</tt> discriminator. An object containing <tt>seq</tt> is recognized as an Entry; otherwise an object containing <tt>head</tt> is recognized as a SignedHead (<xref target="file-seq"/>). A recognized object that passes step 1b but fails the structural checks in step 4 or 5 <bcp14>MUST</bcp14> make the record inconsistent; it <bcp14>MUST NOT</bcp14> be discarded as an unrecognized element. When the first element is unusable, the file is rejected. When an entry is discarded, the entries after it fail verification.</t>
      </section>
      <section anchor="ser-other">
        <name>Other Serializations</name>
        <t>Other serializations <bcp14>MAY</bcp14> be defined, for example one in CBOR. A serialization <bcp14>MUST</bcp14> map every structure to the JSON data model without loss, because hashes are computed over the canonical JSON form (<xref target="canonical"/>).</t>
      </section>
    </section>
    <section anchor="related">
      <name>Relationship to Existing Work</name>
      <t>The file structure and the event schema model come from qlog <xref target="QLOG"/>. A learnlog record corresponds to a qlog trace (<xref target="QLOG" section="4.2" sectionFormat="of"/>). This document adds hashes, redaction, and signed heads.</t>
      <t>The Experience API (xAPI) <xref target="XAPI"/> defines a JSON data model and a web service for communication between activities and a Learning Record Store. The assistance capture profile uses that ecosystem through the experimental binding in <xref target="ecosystem-bindings"/>. The optional Learnlog file representation defines no web service. This document does not define a lossless generic conversion of every Learnlog file into xAPI.</t><t>xAPI Profiles <xref target="XAPI-PROFILES"/> already provides controlled vocabularies, statement templates, and patterns to address inconsistent representations and semantic interoperability. ISO/IEC TS 20748-3 <xref target="ISO20748"/> addresses syntactic and semantic mappings between learning-analytics data representations, using xAPI and Caliper as references. Learnlog does not originate this problem or supply a replacement for these approaches.</t><t>LearnLab's DataShop <xref target="DATASHOP"/> is established infrastructure for sharing and analyzing student interactions with educational software. Reusable interaction data and secondary analysis predate this proposal. Learnlog adds a candidate assistance and capture agreement and retains a file representation with the integrity and redaction behavior specified here. Whether either improves interoperability, measurement, or implementation effort remains an empirical question.</t><t>Caliper Analytics <xref target="CALIPER"/> defines events and entities, and an envelope that carries them to an endpoint. This document defines no transport.</t>
      <t>Existing ecosystems are the primary implementation paths for the assistance capture profile. <xref target="ecosystem-bindings"/> describes the implemented importer-observation mode and its limits. Neither a new format nor a shared profile eliminates the need to justify mappings and proposed combined measurements. Local software checks are not an independent interoperability result.</t><t>Redaction relies on salted hashes, an approach that Selective Disclosure for JSON Web Tokens also uses (<xref target="RFC9901" section="1" sectionFormat="of"/>). A signed head is similar to the Signed Tree Head of Certificate Transparency (<xref target="RFC9162" section="4.10" sectionFormat="of"/>), applied to a chain in place of a tree.</t>
    </section>
    <section anchor="security">
      <name>Security Considerations</name>
      <section anchor="sec-shows">
        <name>What Verification Shows</name>
        <t>A record that verifies as consistent has entries that agree with one another and with the header. Consistency alone does not show who wrote the record or that nothing was replaced. Anyone who holds a record without a signed head can change an event and recompute every later hash.</t>
        <t>A signed head limits this. Take a party without the signing key that changes the <tt>seq</tt>, <tt>time</tt>, or <tt>event</tt> of an entry at or before the head. The head stays valid only when the change is a redaction, the restoring of a redacted event, or a truncation (<xref target="sec-holder"/>). After any other such change, a verifier that trusts the key reports the record as inconsistent, or as unauthenticated when the head was removed as well.</t>
        <t>Neither property shows that events took place as recorded. A producer can write an event that is false. Verification establishes at most that the signed entries were not changed by a party without the key.</t>
        <t>No hash covers <tt>title</tt>, <tt>description</tt>, or <tt>event_schemas</tt>. The same holds for a member that this document does not define, unless it sits in a header, an event, or a head. A reader cannot rely on those values.</t>
      </section>
      <section anchor="sec-tail">
        <name>Removal of Recent Entries</name>
        <t>A holder can remove the latest entries of a record together with any signed head that covers them. What remains verifies. A verifier detects the removal only if it knows a later head from another source. This document defines no way to exchange heads.</t>
      </section>
      <section anchor="sec-holder">
        <name>Redaction and Truncation by a Holder</name>
        <t>Any holder can redact an event or truncate a record, and the result still verifies. A verifier reports which entries are redacted and whether the record is truncated. What the missing events said, and who removed them, stays unknown to the verifier.</t>
        <t>A reader that draws a conclusion from the absence of an event <bcp14>SHOULD</bcp14> treat each redacted or removed entry as a possible instance of that event. A redacted entry inside an activity may have been a scaffold. A redacted or removed entry may have been a withdrawal of consent.</t>
      </section>
      <section anchor="sec-producer">
        <name>Rewriting by the Producer</name>
        <t>The producer holds the signing key, so it can rewrite a record and sign a new head. Detection requires that another party received a head before the rewrite. A time stamping authority <xref target="RFC3161"/> can provide evidence that a head existed before a given time. This document does not define how to use one. The <tt>time</tt> of a head is a statement by the signer and proves nothing about when the head was signed.</t>
      </section>
      <section anchor="sec-keys">
        <name>Trusted Keys</name>
        <t>A signature that verifies shows that the holder of a trusted key signed a head. Any trusted key authenticates any record. A verifier that trusts the keys of several producers needs to know which producer each key belongs to. The result of verification names the key for that purpose.</t>
      </section>
      <section anchor="sec-device">
        <name>Where the Producer Runs</name>
        <t>A signature shows that the holder of a key signed a head. When the producer runs on a device that the learner controls, the learner may control the key. A verifier needs to know where signing took place before it treats a record as evidence about that learner.</t>
      </section>
      <section anchor="sec-guess">
        <name>Guessing Redacted Events</name>
        <t>An event can have few possible values. Without a salt, a party could hash each candidate and compare the result with <tt>event_hash</tt>. The salt prevents this only if it is unpredictable and is deleted with the event. A producer <bcp14>MUST NOT</bcp14> reuse a salt and <bcp14>MUST NOT</bcp14> derive one from the content of the event.</t>
        <t>The position and time of a redacted entry stay visible, as do the entries around it. Together they can suggest what was removed.</t>
        <t>Members named <tt>content_hash</tt> and <tt>config_hash</tt> carry no salt. A party can test a guess at a short or predictable text against such a hash. Redaction of the event removes these members with it.</t>
      </section>
      <section anchor="sec-names">
        <name>Meaning of Event Names</name>
        <t>A hash covers the name of an event. No hash covers <tt>event_schemas</tt>, the list that ties a namespace to a schema URI. For a registered namespace (<xref target="iana-event"/>), the name alone identifies the schema. For any other namespace, a holder can replace the URI in the list, and with it the apparent meaning of the events. A reader that depends on such events needs to know the schemas of the producer from another source.</t>
      <t>For a private vocabulary or companion profile, a producer <bcp14>SHOULD</bcp14> place the schema or profile identifier, version, digest algorithm, and digest of the exact specification or schema bytes in a header extension before creating entries. The header hash already covers such an extension; no change to <xref target="hashes"/> is needed. The profile needs to define the digest scope and retrieval rules. A reader should check the protected binding rather than rely only on the unprotected <tt>event_schemas</tt> list. A digest commits to bytes, not to the correctness, availability, or equivalence of their meaning. Existing records without such bindings remain unchanged and require external knowledge; this revision does not retrofit commitments into old records.</t></section>
      <section anchor="sec-hash">
        <name>Hash Algorithms</name>
        <t>The integrity of a record rests on the collision resistance of the hash algorithm. A checksum or any other function that is not a cryptographic hash <bcp14>MUST NOT</bcp14> be used.</t>
        <t>All hashes in a record use one algorithm, so a record cannot move to another. A producer that needs a new algorithm starts a new record.</t>
      </section>
      <section anchor="sec-canon">
        <name>Canonical Form</name>
        <t>Two parsers that read the same text differently can disagree about a hash, or agree about a hash while they hold different values. Sections 4, 6, and 8.2 of <xref target="RFC8259"/> describe three such cases: member names that are not unique, numbers beyond the range or precision of a double, and strings with unpaired surrogates. The rules in <xref target="canonical"/> address these cases, and step 1 of <xref target="verification"/> applies them before any hash is computed.</t>
      </section>
      <section anchor="sec-input">
        <name>Untrusted Input</name>
        <t>A log file can come from a source that is not trusted. Implementations need limits on file size, nesting depth, and string length.</t>
      </section>
    </section>
    <section anchor="privacy">
      <name>Privacy Considerations</name>
      <t>A record describes the behavior of a person in detail, and that person can be a child. The considerations of <xref target="RFC6973"/> apply. This section covers points that are specific to this format.</t>
      <section anchor="priv-ids">
        <name>Identifiers</name>
        <t>The <tt>subject</tt> member, and the <tt>id</tt> member of a party that is a person, <bcp14>MUST NOT</bcp14> hold a name, an email address, or an identifier taken from another system, such as a student number.</t>
        <t>A <bcp14>RECOMMENDED</bcp14> construction is HMAC <xref target="RFC2104"/> with SHA-256 over the internal identifier, under a secret key that the producer keeps, with at least 128 bits of the output retained. A hash without a key is not sufficient, because identifiers can be guessed and hashed.</t>
        <t>The subject identifier is part of the header and stays fixed for the life of a record. Every party that receives the record sees the same identifier and can link the record to other copies.</t>
      </section>
      <section anchor="priv-others">
        <name>People Other Than the Subject</name>
        <t>A record can describe instructors, peers, and guardians, and a dialog event can hold their words. The data is then about them as well. Their identifiers follow <xref target="priv-ids"/>.</t>
      </section>
      <section anchor="priv-min">
        <name>Data Minimization</name>
        <ul>
          <li>A producer <bcp14>SHOULD</bcp14> record an event only if the event has a use.</li>
          <li>A producer <bcp14>SHOULD</bcp14> use no more precision in <tt>time</tt> than its purpose needs. Precision cannot be reduced later, because <tt>time</tt> is hashed.</li>
          <li>Text written by a learner <bcp14>SHOULD</bcp14> be left out unless it is needed.</li>
        </ul>
      </section>
      <section anchor="priv-redaction">
        <name>Limits of Redaction</name>
        <t>Redaction removes the content of an event. The entry retains a commitment and a producer-reported time. It does not establish that an action occurred or that the time is accurate. Copies of the content in other events, or outside the record, are not affected.</t>
        <t>The <tt>event_hash</tt> of a redacted entry remains as well. A party that kept a copy of the event can still show that the event belonged to the record. Truncation or deletion of the record removes that hash.</t>
        <t>Redaction may not be enough after consent is withdrawn. Truncation or deletion of the record removes more. Unless it is redacted or removed, the <tt>consent:withdrawn</tt> event remains in the record as the trace of the withdrawal.</t>
      </section>
      <section anchor="priv-reid">
        <name>Correlation and Identification</name>
        <t>The actions and times in a record can be combined with other data about a person. Sections 5.2.1 and 5.2.2 of <xref target="RFC6973"/> describe how such correlation can lead to identification. A record <bcp14>SHOULD</bcp14> be treated as personal data whatever identifiers it carries.</t>
      </section>
      <section anchor="priv-keys">
        <name>Signing Keys</name>
        <t>The <tt>kid</tt> of a signed head is the same in every record signed with one key. A key used only for one learner can link that learner's records despite different subject identifiers. A device-specific key groups that device's records and may cover several learners. A deployment-wide key groups the deployment's records but does not by itself distinguish one learner from another. Other metadata can still support linkage.</t>
      </section>
      <section anchor="priv-storage">
        <name>Storage and Transfer</name>
        <t>Records <bcp14>SHOULD</bcp14> be encrypted in transit and at rest. Access to them <bcp14>SHOULD</bcp14> be controlled and logged. A holder <bcp14>SHOULD</bcp14> set a retention period.</t>
      </section>
      <section anchor="priv-children">
        <name>Children</name>
        <t>A learner can be a child. This document does not address compliance with any law.</t><t>Inferred emotional states, stated confidence, and agency reports (<xref target="event-families"/>) can be sensitive, especially for children. A deployment that collects them needs a declared purpose. An inferred state about one learner carries the uncertainty of its detector and does not establish that learner's state.</t>
      </section>
    </section>
    <section anchor="iana">
      <name>IANA Considerations</name>
      <section anchor="iana-urn">
        <name>URN Sub-namespace</name>
        <t>IANA is requested to register the following in the "IETF URN Sub-namespace for Registered Protocol Parameter Identifiers" registry <xref target="RFC3553"/>:</t>
        <dl newline="false" spacing="compact">
          <dt>Registry name:</dt>
          <dd>learnlog</dd>
          <dt>Specification:</dt>
          <dd>This document</dd>
          <dt>Repository:</dt>
          <dd>https://www.iana.org/assignments/learnlog</dd>
          <dt>Index value:</dt>
          <dd>The part of a URN that follows <tt>urn:ietf:params:learnlog:</tt>, which has the form <tt>file:&lt;identifier&gt;</tt> or <tt>events:&lt;namespace&gt;</tt>. No transformation is needed.</dd>
        </dl>
      </section>
      <section anchor="iana-file">
        <name>File Schema URIs</name>
        <t>IANA is requested to create the "learnlog File Schema URIs" registry at https://www.iana.org/assignments/learnlog. The registration policy is Expert Review <xref target="RFC8126"/>. The expert checks that the URI has the form given in <xref target="file-uris"/> and that a public specification defines the file schema. An entry holds a file schema URI, a description, and a reference. The initial entries are:</t>
        <dl newline="false" spacing="compact">
          <dt>File Schema URI:</dt>
          <dd>urn:ietf:params:learnlog:file:contained</dd>
          <dt>Description:</dt>
          <dd>A file that holds one or more records</dd>
          <dt>Reference:</dt>
          <dd><xref target="file-contained"/></dd>
        </dl>
        <dl newline="false" spacing="compact">
          <dt>File Schema URI:</dt>
          <dd>urn:ietf:params:learnlog:file:sequential</dd>
          <dt>Description:</dt>
          <dd>A file that holds one record and grows by appending</dd>
          <dt>Reference:</dt>
          <dd><xref target="file-seq"/></dd>
        </dl>
      </section>
      <section anchor="iana-event">
        <name>Event Schema URIs</name>
        <t>IANA is requested to create the "learnlog Event Schema URIs" registry at the same location. The registration policy is Expert Review <xref target="RFC8126"/>. The expert checks the following:</t>
        <ul>
          <li>The URI has the form given in <xref target="event-names"/>.</li>
          <li>A public specification defines the event types, with CDDL for the data of each.</li>
          <li>A schema for a new namespace uses a namespace identifier that is not yet registered.</li>
          <li>A schema that extends a registered namespace uses the URI of that namespace with a fragment identifier, and leaves the registered event types unchanged.</li>
        </ul>
        <t>An entry holds an event schema URI, a namespace, the event types, a description, and a reference. The initial entries are:</t>
        <dl newline="false" spacing="compact">
          <dt>Event Schema URI:</dt>
          <dd>urn:ietf:params:learnlog:events:consent</dd>
          <dt>Namespace:</dt>
          <dd>consent</dd>
          <dt>Event Types:</dt>
          <dd>granted, withdrawn</dd>
          <dt>Description:</dt>
          <dd>Consent given or withdrawn</dd>
          <dt>Reference:</dt>
          <dd><xref target="consent"/></dd>
        </dl>
        <dl newline="false" spacing="compact">
          <dt>Event Schema URI:</dt>
          <dd>urn:ietf:params:learnlog:events:assist</dd>
          <dt>Namespace:</dt>
          <dd>assist</dd>
          <dt>Event Types:</dt>
          <dd>help_requested, scaffold_provided</dd>
          <dt>Description:</dt>
          <dd>Help requested and scaffolds provided</dd>
          <dt>Reference:</dt>
          <dd><xref target="assist"/></dd>
        </dl>
        <dl newline="false" spacing="compact">
          <dt>Event Schema URI:</dt>
          <dd>urn:ietf:params:learnlog:events:dialog</dd>
          <dt>Namespace:</dt>
          <dd>dialog</dd>
          <dt>Event Types:</dt>
          <dd>message</dd>
          <dt>Description:</dt>
          <dd>Turns in an exchange with another party</dd>
          <dt>Reference:</dt>
          <dd><xref target="dialog"/></dd>
        </dl>
      </section>
      <section anchor="iana-media">
        <name>Media Types</name>
        <t>IANA is requested to register the following media types <xref target="RFC6838"/>.</t>
        <section anchor="iana-media-json">
          <name>application/learnlog+json</name>
          <dl newline="false" spacing="compact">
            <dt>Type name:</dt>
            <dd>application</dd>
            <dt>Subtype name:</dt>
            <dd>learnlog+json</dd>
            <dt>Required parameters:</dt>
            <dd>N/A</dd>
            <dt>Optional parameters:</dt>
            <dd>N/A</dd>
            <dt>Encoding considerations:</dt>
            <dd>binary; see <xref target="ser-json"/></dd>
            <dt>Security considerations:</dt>
            <dd>See <xref target="security"/> and <xref target="privacy"/></dd>
            <dt>Interoperability considerations:</dt>
            <dd>See <xref target="canonical"/></dd>
            <dt>Published specification:</dt>
            <dd>This document</dd>
            <dt>Applications that use this media type:</dt>
            <dd>Software that writes, verifies, or analyzes learnlog records</dd>
            <dt>Fragment identifier considerations:</dt>
            <dd>As for application/json <xref target="RFC6839"/></dd>
            <dt>Additional information:</dt>
            <dd>
              <dl newline="false" spacing="compact">
                <dt>Deprecated alias names for this type:</dt>
                <dd>N/A</dd>
                <dt>Magic number(s):</dt>
                <dd>N/A</dd>
                <dt>File extension(s):</dt>
                <dd>.learnlog</dd>
                <dt>Macintosh file type code(s):</dt>
                <dd>N/A</dd>
              </dl>
            </dd>
            <dt>Person &amp; email address to contact for further information:</dt>
            <dd>See the Author's Address section</dd>
            <dt>Intended usage:</dt>
            <dd>COMMON</dd>
            <dt>Restrictions on usage:</dt>
            <dd>N/A</dd>
            <dt>Author:</dt>
            <dd>See the Author's Address section</dd>
            <dt>Change controller:</dt>
            <dd>IETF</dd>
            <dt>Provisional registration? (standards tree only):</dt>
            <dd>No</dd>
          </dl>
        </section>
        <section anchor="iana-media-seq">
          <name>application/learnlog+json-seq</name>
          <dl newline="false" spacing="compact">
            <dt>Type name:</dt>
            <dd>application</dd>
            <dt>Subtype name:</dt>
            <dd>learnlog+json-seq</dd>
            <dt>Required parameters:</dt>
            <dd>N/A</dd>
            <dt>Optional parameters:</dt>
            <dd>N/A</dd>
            <dt>Encoding considerations:</dt>
            <dd>binary; see <xref target="ser-seq"/></dd>
            <dt>Security considerations:</dt>
            <dd>See <xref target="security"/> and <xref target="privacy"/></dd>
            <dt>Interoperability considerations:</dt>
            <dd>See <xref target="canonical"/></dd>
            <dt>Published specification:</dt>
            <dd>This document</dd>
            <dt>Applications that use this media type:</dt>
            <dd>Software that writes, verifies, or analyzes learnlog records</dd>
            <dt>Fragment identifier considerations:</dt>
            <dd>As for application/json-seq <xref target="RFC8091"/></dd>
            <dt>Additional information:</dt>
            <dd>
              <dl newline="false" spacing="compact">
                <dt>Deprecated alias names for this type:</dt>
                <dd>N/A</dd>
                <dt>Magic number(s):</dt>
                <dd>N/A</dd>
                <dt>File extension(s):</dt>
                <dd>.slearnlog</dd>
                <dt>Macintosh file type code(s):</dt>
                <dd>N/A</dd>
              </dl>
            </dd>
            <dt>Person &amp; email address to contact for further information:</dt>
            <dd>See the Author's Address section</dd>
            <dt>Intended usage:</dt>
            <dd>COMMON</dd>
            <dt>Restrictions on usage:</dt>
            <dd>N/A</dd>
            <dt>Author:</dt>
            <dd>See the Author's Address section</dd>
            <dt>Change controller:</dt>
            <dd>IETF</dd>
            <dt>Provisional registration? (standards tree only):</dt>
            <dd>No</dd>
          </dl>
        </section>
      </section>
    </section>
  </middle>
  <back>
    <references>
      <name>References</name>
      <references>
        <name>Normative References</name>
        <reference anchor="RFC2119" target="https://www.rfc-editor.org/info/rfc2119">
          <front>
            <title>Key words for use in RFCs to Indicate Requirement Levels</title>
            <author initials="S." surname="Bradner" fullname="S. Bradner"/>
            <date month="March" year="1997"/>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="2119"/>
          <seriesInfo name="DOI" value="10.17487/RFC2119"/>
        </reference>
        <reference anchor="RFC8174" target="https://www.rfc-editor.org/info/rfc8174">
          <front>
            <title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
            <author initials="B." surname="Leiba" fullname="B. Leiba"/>
            <date month="May" year="2017"/>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="8174"/>
          <seriesInfo name="DOI" value="10.17487/RFC8174"/>
        </reference>
        <reference anchor="RFC3339" target="https://www.rfc-editor.org/info/rfc3339">
          <front>
            <title>Date and Time on the Internet: Timestamps</title>
            <author initials="G." surname="Klyne" fullname="G. Klyne"/>
            <author initials="C." surname="Newman" fullname="C. Newman"/>
            <date month="July" year="2002"/>
          </front>
          <seriesInfo name="RFC" value="3339"/>
          <seriesInfo name="DOI" value="10.17487/RFC3339"/>
        </reference>
        <reference anchor="RFC3986" target="https://www.rfc-editor.org/info/rfc3986">
          <front>
            <title>Uniform Resource Identifier (URI): Generic Syntax</title>
            <author initials="T." surname="Berners-Lee" fullname="T. Berners-Lee"/>
            <author initials="R." surname="Fielding" fullname="R. Fielding"/>
            <author initials="L." surname="Masinter" fullname="L. Masinter"/>
            <date month="January" year="2005"/>
          </front>
          <seriesInfo name="STD" value="66"/>
          <seriesInfo name="RFC" value="3986"/>
          <seriesInfo name="DOI" value="10.17487/RFC3986"/>
        </reference>
        <reference anchor="RFC4648" target="https://www.rfc-editor.org/info/rfc4648">
          <front>
            <title>The Base16, Base32, and Base64 Data Encodings</title>
            <author initials="S." surname="Josefsson" fullname="S. Josefsson"/>
            <date month="October" year="2006"/>
          </front>
          <seriesInfo name="RFC" value="4648"/>
          <seriesInfo name="DOI" value="10.17487/RFC4648"/>
        </reference>
        <reference anchor="RFC6920" target="https://www.rfc-editor.org/info/rfc6920">
          <front>
            <title>Naming Things with Hashes</title>
            <author initials="S." surname="Farrell" fullname="S. Farrell"/>
            <author initials="D." surname="Kutscher" fullname="D. Kutscher"/>
            <author initials="C." surname="Dannewitz" fullname="C. Dannewitz"/>
            <author initials="B." surname="Ohlman" fullname="B. Ohlman"/>
            <author initials="A." surname="Keranen" fullname="A. Keranen"/>
            <author initials="P." surname="Hallam-Baker" fullname="P. Hallam-Baker"/>
            <date month="April" year="2013"/>
          </front>
          <seriesInfo name="RFC" value="6920"/>
          <seriesInfo name="DOI" value="10.17487/RFC6920"/>
        </reference>
        <reference anchor="RFC7464" target="https://www.rfc-editor.org/info/rfc7464">
          <front>
            <title>JavaScript Object Notation (JSON) Text Sequences</title>
            <author initials="N." surname="Williams" fullname="N. Williams"/>
            <date month="February" year="2015"/>
          </front>
          <seriesInfo name="RFC" value="7464"/>
          <seriesInfo name="DOI" value="10.17487/RFC7464"/>
        </reference>
        <reference anchor="RFC7493" target="https://www.rfc-editor.org/info/rfc7493">
          <front>
            <title>The I-JSON Message Format</title>
            <author initials="T." surname="Bray" fullname="T. Bray" role="editor"/>
            <date month="March" year="2015"/>
          </front>
          <seriesInfo name="RFC" value="7493"/>
          <seriesInfo name="DOI" value="10.17487/RFC7493"/>
        </reference>
        <reference anchor="RFC7515" target="https://www.rfc-editor.org/info/rfc7515">
          <front>
            <title>JSON Web Signature (JWS)</title>
            <author initials="M." surname="Jones" fullname="M. Jones"/>
            <author initials="J." surname="Bradley" fullname="J. Bradley"/>
            <author initials="N." surname="Sakimura" fullname="N. Sakimura"/>
            <date month="May" year="2015"/>
          </front>
          <seriesInfo name="RFC" value="7515"/>
          <seriesInfo name="DOI" value="10.17487/RFC7515"/>
        </reference>
        <reference anchor="RFC7518" target="https://www.rfc-editor.org/info/rfc7518">
          <front>
            <title>JSON Web Algorithms (JWA)</title>
            <author initials="M." surname="Jones" fullname="M. Jones"/>
            <date month="May" year="2015"/>
          </front>
          <seriesInfo name="RFC" value="7518"/>
          <seriesInfo name="DOI" value="10.17487/RFC7518"/>
        </reference>
        <reference anchor="RFC8259" target="https://www.rfc-editor.org/info/rfc8259">
          <front>
            <title>The JavaScript Object Notation (JSON) Data Interchange Format</title>
            <author initials="T." surname="Bray" fullname="T. Bray" role="editor"/>
            <date month="December" year="2017"/>
          </front>
          <seriesInfo name="STD" value="90"/>
          <seriesInfo name="RFC" value="8259"/>
          <seriesInfo name="DOI" value="10.17487/RFC8259"/>
        </reference>
        <reference anchor="RFC8610" target="https://www.rfc-editor.org/info/rfc8610">
          <front>
            <title>Concise Data Definition Language (CDDL): A Notational Convention to Express Concise Binary Object Representation (CBOR) and JSON Data Structures</title>
            <author initials="H." surname="Birkholz" fullname="H. Birkholz"/>
            <author initials="C." surname="Vigano" fullname="C. Vigano"/>
            <author initials="C." surname="Bormann" fullname="C. Bormann"/>
            <date month="June" year="2019"/>
          </front>
          <seriesInfo name="RFC" value="8610"/>
          <seriesInfo name="DOI" value="10.17487/RFC8610"/>
        </reference>
        <reference anchor="RFC8785" target="https://www.rfc-editor.org/info/rfc8785">
          <front>
            <title>JSON Canonicalization Scheme (JCS)</title>
            <author initials="A." surname="Rundgren" fullname="A. Rundgren"/>
            <author initials="B." surname="Jordan" fullname="B. Jordan"/>
            <author initials="S." surname="Erdtman" fullname="S. Erdtman"/>
            <date month="June" year="2020"/>
          </front>
          <seriesInfo name="RFC" value="8785"/>
          <seriesInfo name="DOI" value="10.17487/RFC8785"/>
        </reference>
        <reference anchor="RFC2104" target="https://www.rfc-editor.org/info/rfc2104">
          <front>
            <title>HMAC: Keyed-Hashing for Message Authentication</title>
            <author initials="H." surname="Krawczyk" fullname="H. Krawczyk"/>
            <author initials="M." surname="Bellare" fullname="M. Bellare"/>
            <author initials="R." surname="Canetti" fullname="R. Canetti"/>
            <date month="February" year="1997"/>
          </front>
          <seriesInfo name="RFC" value="2104"/>
          <seriesInfo name="DOI" value="10.17487/RFC2104"/>
        </reference>
        <reference anchor="RFC7638" target="https://www.rfc-editor.org/info/rfc7638">
          <front>
            <title>JSON Web Key (JWK) Thumbprint</title>
            <author initials="M." surname="Jones" fullname="M. Jones"/>
            <author initials="N." surname="Sakimura" fullname="N. Sakimura"/>
            <date month="September" year="2015"/>
          </front>
          <seriesInfo name="RFC" value="7638"/>
          <seriesInfo name="DOI" value="10.17487/RFC7638"/>
        </reference>
        <reference anchor="RFC8126" target="https://www.rfc-editor.org/info/rfc8126">
          <front>
            <title>Guidelines for Writing an IANA Considerations Section in RFCs</title>
            <author initials="M." surname="Cotton" fullname="M. Cotton"/>
            <author initials="B." surname="Leiba" fullname="B. Leiba"/>
            <author initials="T." surname="Narten" fullname="T. Narten"/>
            <date month="June" year="2017"/>
          </front>
          <seriesInfo name="BCP" value="26"/>
          <seriesInfo name="RFC" value="8126"/>
          <seriesInfo name="DOI" value="10.17487/RFC8126"/>
        </reference>
        <reference anchor="RFC9562" target="https://www.rfc-editor.org/info/rfc9562">
          <front>
            <title>Universally Unique IDentifiers (UUIDs)</title>
            <author initials="K." surname="Davis" fullname="K. Davis"/>
            <author initials="B." surname="Peabody" fullname="B. Peabody"/>
            <author initials="P." surname="Leach" fullname="P. Leach"/>
            <date month="May" year="2024"/>
          </front>
          <seriesInfo name="RFC" value="9562"/>
          <seriesInfo name="DOI" value="10.17487/RFC9562"/>
        </reference>
        <reference anchor="FIPS180" target="https://doi.org/10.6028/NIST.FIPS.180-4">
          <front>
            <title>Secure Hash Standard (SHS)</title>
            <author>
              <organization>National Institute of Standards and Technology</organization>
            </author>
            <date month="August" year="2015"/>
          </front>
          <seriesInfo name="FIPS PUB" value="180-4"/>
          <seriesInfo name="DOI" value="10.6028/NIST.FIPS.180-4"/>
        </reference>
      </references>
      <references>
        <name>Informative References</name>
        <reference anchor="RFC3161" target="https://www.rfc-editor.org/info/rfc3161">
          <front>
            <title>Internet X.509 Public Key Infrastructure Time-Stamp Protocol (TSP)</title>
            <author initials="C." surname="Adams" fullname="C. Adams"/>
            <author initials="P." surname="Cain" fullname="P. Cain"/>
            <author initials="D." surname="Pinkas" fullname="D. Pinkas"/>
            <author initials="R." surname="Zuccherato" fullname="R. Zuccherato"/>
            <date month="August" year="2001"/>
          </front>
          <seriesInfo name="RFC" value="3161"/>
          <seriesInfo name="DOI" value="10.17487/RFC3161"/>
        </reference>
        <reference anchor="RFC3553" target="https://www.rfc-editor.org/info/rfc3553">
          <front>
            <title>An IETF URN Sub-namespace for Registered Protocol Parameters</title>
            <author initials="M." surname="Mealling" fullname="M. Mealling"/>
            <author initials="L." surname="Masinter" fullname="L. Masinter"/>
            <author initials="T." surname="Hardie" fullname="T. Hardie"/>
            <author initials="G." surname="Klyne" fullname="G. Klyne"/>
            <date month="June" year="2003"/>
          </front>
          <seriesInfo name="BCP" value="73"/>
          <seriesInfo name="RFC" value="3553"/>
          <seriesInfo name="DOI" value="10.17487/RFC3553"/>
        </reference>
        <reference anchor="RFC6838" target="https://www.rfc-editor.org/info/rfc6838">
          <front>
            <title>Media Type Specifications and Registration Procedures</title>
            <author initials="N." surname="Freed" fullname="N. Freed"/>
            <author initials="J." surname="Klensin" fullname="J. Klensin"/>
            <author initials="T." surname="Hansen" fullname="T. Hansen"/>
            <date month="January" year="2013"/>
          </front>
          <seriesInfo name="BCP" value="13"/>
          <seriesInfo name="RFC" value="6838"/>
          <seriesInfo name="DOI" value="10.17487/RFC6838"/>
        </reference>
        <reference anchor="RFC6839" target="https://www.rfc-editor.org/info/rfc6839">
          <front>
            <title>Additional Media Type Structured Syntax Suffixes</title>
            <author initials="T." surname="Hansen" fullname="T. Hansen"/>
            <author initials="A." surname="Melnikov" fullname="A. Melnikov"/>
            <date month="January" year="2013"/>
          </front>
          <seriesInfo name="RFC" value="6839"/>
          <seriesInfo name="DOI" value="10.17487/RFC6839"/>
        </reference>
        <reference anchor="RFC6973" target="https://www.rfc-editor.org/info/rfc6973">
          <front>
            <title>Privacy Considerations for Internet Protocols</title>
            <author initials="A." surname="Cooper" fullname="A. Cooper"/>
            <author initials="H." surname="Tschofenig" fullname="H. Tschofenig"/>
            <author initials="B." surname="Aboba" fullname="B. Aboba"/>
            <author initials="J." surname="Peterson" fullname="J. Peterson"/>
            <author initials="J." surname="Morris" fullname="J. Morris"/>
            <author initials="M." surname="Hansen" fullname="M. Hansen"/>
            <author initials="R." surname="Smith" fullname="R. Smith"/>
            <date month="July" year="2013"/>
          </front>
          <seriesInfo name="RFC" value="6973"/>
          <seriesInfo name="DOI" value="10.17487/RFC6973"/>
        </reference>
        <reference anchor="RFC7942" target="https://www.rfc-editor.org/info/rfc7942">
          <front>
            <title>Improving Awareness of Running Code: The Implementation Status Section</title>
            <author initials="Y." surname="Sheffer" fullname="Y. Sheffer"/>
            <author initials="A." surname="Farrel" fullname="A. Farrel"/>
            <date month="July" year="2016"/>
          </front>
          <seriesInfo name="BCP" value="205"/>
          <seriesInfo name="RFC" value="7942"/>
          <seriesInfo name="DOI" value="10.17487/RFC7942"/>
        </reference>
        <reference anchor="RFC8091" target="https://www.rfc-editor.org/info/rfc8091">
          <front>
            <title>A Media Type Structured Syntax Suffix for JSON Text Sequences</title>
            <author initials="E." surname="Wilde" fullname="E. Wilde"/>
            <date month="February" year="2017"/>
          </front>
          <seriesInfo name="RFC" value="8091"/>
          <seriesInfo name="DOI" value="10.17487/RFC8091"/>
        </reference>
        <reference anchor="RFC8725" target="https://www.rfc-editor.org/info/rfc8725">
          <front>
            <title>JSON Web Token Best Current Practices</title>
            <author initials="Y." surname="Sheffer" fullname="Y. Sheffer"/>
            <author initials="D." surname="Hardt" fullname="D. Hardt"/>
            <author initials="M." surname="Jones" fullname="M. Jones"/>
            <date month="February" year="2020"/>
          </front>
          <seriesInfo name="BCP" value="225"/>
          <seriesInfo name="RFC" value="8725"/>
          <seriesInfo name="DOI" value="10.17487/RFC8725"/>
        </reference>
        <reference anchor="RFC8792" target="https://www.rfc-editor.org/info/rfc8792">
          <front>
            <title>Handling Long Lines in Content of Internet-Drafts and RFCs</title>
            <author initials="K." surname="Watsen" fullname="K. Watsen"/>
            <author initials="E." surname="Auerswald" fullname="E. Auerswald"/>
            <author initials="A." surname="Farrel" fullname="A. Farrel"/>
            <author initials="Q." surname="Wu" fullname="Q. Wu"/>
            <date month="June" year="2020"/>
          </front>
          <seriesInfo name="RFC" value="8792"/>
          <seriesInfo name="DOI" value="10.17487/RFC8792"/>
        </reference>
        <reference anchor="RFC9162" target="https://www.rfc-editor.org/info/rfc9162">
          <front>
            <title>Certificate Transparency Version 2.0</title>
            <author initials="B." surname="Laurie" fullname="B. Laurie"/>
            <author initials="E." surname="Messeri" fullname="E. Messeri"/>
            <author initials="R." surname="Stradling" fullname="R. Stradling"/>
            <date month="December" year="2021"/>
          </front>
          <seriesInfo name="RFC" value="9162"/>
          <seriesInfo name="DOI" value="10.17487/RFC9162"/>
        </reference>
        <reference anchor="RFC9901" target="https://www.rfc-editor.org/info/rfc9901">
          <front>
            <title>Selective Disclosure for JSON Web Tokens</title>
            <author initials="D." surname="Fett" fullname="D. Fett"/>
            <author initials="K." surname="Yasuda" fullname="K. Yasuda"/>
            <author initials="B." surname="Campbell" fullname="B. Campbell"/>
            <date month="November" year="2025"/>
          </front>
          <seriesInfo name="RFC" value="9901"/>
          <seriesInfo name="DOI" value="10.17487/RFC9901"/>
        </reference>
        <reference anchor="QLOG" target="https://datatracker.ietf.org/doc/html/draft-ietf-quic-qlog-main-schema-14">
          <front>
            <title>qlog: Structured Logging for Network Protocols</title>
            <author initials="R." surname="Marx" fullname="Robin Marx" role="editor"/>
            <author initials="L." surname="Niccolini" fullname="Luca Niccolini" role="editor"/>
            <author initials="M." surname="Seemann" fullname="Marten Seemann" role="editor"/>
            <author initials="L." surname="Pardue" fullname="Lucas Pardue" role="editor"/>
            <date day="6" month="July" year="2026"/>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-ietf-quic-qlog-main-schema-14"/>
          <refcontent>Work in Progress</refcontent>
        </reference>
        <reference anchor="XAPI" target="https://standards.ieee.org/ieee/9274.1.1/7321/">
          <front>
            <title>IEEE Standard for Learning Technology--JavaScript Object Notation (JSON) Data Model Format and Representational State Transfer (RESTful) Web Service for Learner Experience Data Tracking and Access</title>
            <author>
              <organization>IEEE</organization>
            </author>
            <date month="October" year="2023"/>
          </front>
          <seriesInfo name="IEEE Std" value="9274.1.1-2023"/>
        <seriesInfo name="DOI" value="10.1109/IEEESTD.2023.10273185"/></reference>
        <reference anchor="XAPI103" target="https://github.com/adlnet/xAPI-Spec/blob/1.0.3/xAPI-Data.md"><front><title>Experience API Specification Version 1.0.3 Part Two Experience API Data</title><author><organization>Advanced Distributed Learning Initiative</organization></author><date year="2016"/></front></reference><reference anchor="CALIPER" target="https://www.imsglobal.org/spec/caliper/v1p2">
          <front>
            <title>Caliper Analytics Specification, Version 1.2</title>
            <author>
              <organization>1EdTech Consortium</organization>
            </author>
            <date day="27" month="March" year="2020"/>
          </front>
        </reference>
      <reference anchor="XAPI-PROFILES" target="https://adlnet.github.io/xapi-profiles/xapi-profiles-about.html"><front><title>xAPI Profiles</title><author><organization>Advanced Distributed Learning Initiative</organization></author><date/></front></reference><reference anchor="ISO20748" target="https://www.iso.org/standard/74449.html"><front><title>Information technology for learning, education and training -- Learning analytics interoperability -- Part 3: Guidelines for data interoperability</title><author><organization>ISO/IEC</organization></author><date month="March" year="2020"/></front><seriesInfo name="ISO/IEC TS" value="20748-3:2020"/></reference><reference anchor="DATASHOP" target="https://doi.org/10.1111/tops.12035"><front><title>LearnLab's DataShop: A Data Repository and Analytics Tool Set for Cognitive Science</title><author initials="K. R." surname="Koedinger" fullname="Kenneth R. Koedinger"/><author initials="J. C." surname="Stamper" fullname="John C. Stamper"/><author initials="B." surname="Leber" fullname="Brett Leber"/><author initials="A." surname="Skogsholm" fullname="Alida Skogsholm"/><date month="July" year="2013"/></front><seriesInfo name="Topics in Cognitive Science" value="5(3), pp. 668-669"/><seriesInfo name="DOI" value="10.1111/tops.12035"/></reference><reference anchor="RUSTICI-AI" target="https://rusticisoftware.com/blog/you-cant-spell-xapi-without-ai/"><front><title>You Can't Spell xAPI Without AI</title><author initials="S." surname="Kalnoske" fullname="Stephen Kalnoske"><organization>Rustici Software</organization></author><date year="2026" month="May" day="12"/></front></reference><reference anchor="GIFT-XAPI" target="https://www.gifttutoring.org/projects/gift/wiki/XAPI_Statements_2025-1"><front><title>xAPI Statements 2025-1</title><author><organization>GIFT</organization></author><date/></front></reference><reference anchor="TLA-AI" target="https://tlatoolbox.com/profiles/89dbe88e-4c1f-462d-a024-dba07c348f7d.json"><front><title>AI-Mediated Learning Interactions Profile</title><author><organization>inXsol LLC / TLA Toolbox</organization></author><date/></front><seriesInfo name="Draft version" value="0.1.0"/><refcontent>Version 0.1.0; the source metadata gives a generatedAtTime value of 2026-03-19T00:00:00Z, not a publication date. Retrieved 20 October 2026.</refcontent></reference><reference anchor="LEARNLOG-PREPRINT"><front><title>Preserving assistance meaning in cross-source learning analytics</title><author initials="K." surname="Srivastava" fullname="Kumar Srivastava"><organization>Quantum Learning Machines</organization></author><date year="2026" month="October"/></front><refcontent>Preprint submitted to EdArXiv; in moderation at the time of writing</refcontent></reference><reference anchor="HINTS-DATA" target="https://doi.org/10.5281/zenodo.17551130"><front><title>Supplementary materials for the paper "Understanding student interaction with AI-powered next-step hints: Strategies and challenges"</title><author initials="A." surname="Birillo"/><author initials="A." surname="Rostovskii"/><author initials="Y." surname="Golubev"/><author initials="H." surname="Keuning"/><date year="2025"/></front><seriesInfo name="DOI" value="10.5281/zenodo.17551130"/><refcontent>Version 1, Zenodo data set</refcontent></reference><reference anchor="IDETRACE" target="https://github.com/divadbaroon/TutorTrace_dataset_and_benchmark/tree/bc7990370da18a611a122255ebc3e475bd8a3fc6"><front><title>IDETrace</title><author><organization>IDETrace authors</organization></author><date year="2026"/></front><refcontent>Version 1.0.0, repository snapshot at commit bc7990370da18a611a122255ebc3e475bd8a3fc6</refcontent></reference><reference anchor="ASSISTMENTS" target="https://sites.google.com/site/assistmentsdata/datasets/2012-13-school-data-with-affect"><front><title>2012-13 School Data with Affect</title><author><organization>ASSISTments, Worcester Polytechnic Institute</organization></author><date/></front><refcontent>Data set, retrieved 20 October 2026</refcontent></reference><reference anchor="LEARNLOG-EPISODES" target="https://github.com/quantumlearningmachines/qlm-measure/tree/64cac37bd929e99261b75a09cf11d05024e07d8b/research/learnlog-episodes"><front><title>Learnlog assistance episodes across three released datasets</title><author><organization>Quantum Learning Machines</organization></author><date year="2026" month="October"/></front><refcontent>qlm-measure repository, research/learnlog-episodes, commit 64cac37bd929e99261b75a09cf11d05024e07d8b</refcontent></reference><reference anchor="DATASHOP-FORMAT" target="https://pslcdatashop.web.cmu.edu/dtd/guide/"><front><title>Guide to the Tutor Message format (v4)</title><author><organization>DataShop</organization></author><date year="2013" month="April" day="15"/></front></reference></references>
    </references>
    <section anchor="example">
      <name>Example</name>
      <t>This appendix shows one record in full. Every hash in it can be recomputed from the text of this document.</t>
      <section anchor="ex-schema">
        <name>An Example Event Schema</name>
        <t>The example uses a private event schema with the namespace <tt>sim</tt> and the URI <tt>https://example.org/102026/sim</tt>. It defines two event types for a simulation.</t>
        <figure anchor="fig-ex-schema">
          <name>Event schema of the example</name>
          <sourcecode type="cddl" name="example-sim.cddl">SimPredictionCommitted = {
    prediction: text,
    ? confidence: "guess" / "fairly_sure" / "certain",
    * $$sim-prediction-committed-extension
}

SimExplanationFiled = {
    explanation: text,
    * $$sim-explanation-filed-extension
}

$Event /= event-type&lt;"sim:prediction_committed",
                     SimPredictionCommitted&gt;
$Event /= event-type&lt;"sim:explanation_filed", SimExplanationFiled&gt;
</sourcecode>
        </figure>
      </section>
      <section anchor="ex-file">
        <name>A Complete File</name>
        <t>The file in <xref target="fig-ex-file"/> holds one record with eight entries and one signed head. Entry 4 is a hint from a tutoring system. Entries 5 and 6 are an exchange with an AI chat assistant. Entry 7 is a spoken hint from a teacher, transcribed by software.</t>
        <t>The <tt>content_hash</tt> and <tt>config_hash</tt> values are hashes of the texts in <xref target="tab-ex-texts"/>. Each text is one line, with single spaces between words and no line ending.</t>
        <table anchor="tab-ex-texts">
          <name>Texts behind the content and configuration hashes</name>
          <thead>
            <tr>
              <th>Entry</th>
              <th>Member</th>
              <th>Text</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>4</td>
              <td>content_hash</td>
              <td>Look for a number that divides both 12 and 16.</td>
            </tr>
            <tr>
              <td>4</td>
              <td>config_hash</td>
              <td>example tutor configuration 7</td>
            </tr>
            <tr>
              <td>6</td>
              <td>content_hash</td>
              <td>Simplifying a ratio means dividing both parts by the same number.</td>
            </tr>
            <tr>
              <td>6</td>
              <td>config_hash</td>
              <td>example chat configuration 3</td>
            </tr>
          </tbody>
        </table>
        <t>The explanation in entry 8 holds the character U+00BE, written in the figure as an escape. In the canonical form, that character is the two octets C2 BE.</t>
        <figure anchor="fig-ex-file">
          <name>A complete contained file</name>
          <sourcecode type="json">=============== NOTE: '\' line wrapping per RFC 8792 ================

{
  "file_schema": "urn:ietf:params:learnlog:file:contained",
  "serialization_format": "application/learnlog+json",
  "title": "Example record",
  "records": [
    {
      "header": {
        "record_id": "66a921d7-55be-4589-afcc-84ea4d2c26ef",
        "subject": "L-fdda6e0e3413590a303a3737d4176389",
        "hash_alg": "sha-256",
        "created": "2026-10-07T14:00:00Z",
        "producer": {"name": "example-sim", "version": "1.4.0"}
      },
      "event_schemas": [
        "urn:ietf:params:learnlog:events:consent",
        "urn:ietf:params:learnlog:events:assist",
        "urn:ietf:params:learnlog:events:dialog",
        "https://example.org/102026/sim"
      ],
      "entries": [
        {
          "seq": 1,
          "time": "2026-10-07T14:00:05Z",
          "event": {
            "name": "consent:granted",
            "salt": "SUe3daeuE7ZsSfKRHA9RkQ",
            "actor": {"role": "guardian", "kind": "human"},
            "data": {"scopes": ["instruction", "research"]}
          },
          "event_hash": "0e144a6edfb39ded22fab2dba4f60a69dc9fc72cdfe\
7006ab78001e5ac11deef",
          "prev_hash": "dcfc869849b6088bb8cba0761987f4fa90a181335e20\
a95b120bb0e8dfcd5556",
          "entry_hash": "ff3f6b48ed7a77379e68a08f6a0c2bfa71bdc757671\
ceb88d905e5d307638433"
        },
        {
          "seq": 2,
          "time": "2026-10-07T14:01:10Z",
          "event": {
            "name": "sim:prediction_committed",
            "salt": "yqUo0sZAN2gYAhADsPJjKw",
            "session": "s-01",
            "activity": {
              "id": "https://example.org/activities/ratio-mixing",
              "version": "2.0",
              "variant": "seed-8675309"
            },
            "data": {
              "prediction": "3:4",
              "confidence": "fairly_sure"
            }
          },
          "event_hash": "1b878146c3e51d20d0411979dc7fbac81da5fd08846\
9a725b9afcf8395c2a518",
          "prev_hash": "ff3f6b48ed7a77379e68a08f6a0c2bfa71bdc757671c\
eb88d905e5d307638433",
          "entry_hash": "ac64c6867336279286d1837e60312b56b106978d648\
db62360f211306d65b6e5"
        },
        {
          "seq": 3,
          "time": "2026-10-07T14:01:42Z",
          "event": {
            "name": "assist:help_requested",
            "salt": "eKmBaP2uRROEiC4HVbWZfA",
            "to": {
              "role": "instructor",
              "kind": "software",
              "id": "example-tutor"
            },
            "session": "s-01",
            "activity": {
              "id": "https://example.org/activities/ratio-mixing",
              "version": "2.0",
              "variant": "seed-8675309"
            },
            "data": {"type": "hint"}
          },
          "event_hash": "5485100233e98c7bca12183cf9f9da23ee91f8bb029\
d82e46ddab320661cc51b",
          "prev_hash": "ac64c6867336279286d1837e60312b56b106978d648d\
b62360f211306d65b6e5",
          "entry_hash": "afa914e1952ca0d6878b901e32279a09f1462f252df\
b3f289ea846c63b048e06"
        },
        {
          "seq": 4,
          "time": "2026-10-07T14:01:43.200Z",
          "event": {
            "name": "assist:scaffold_provided",
            "salt": "DgQiQwSuZLoHwnBLIox60g",
            "actor": {
              "role": "instructor",
              "kind": "software",
              "id": "example-tutor"
            },
            "session": "s-01",
            "activity": {
              "id": "https://example.org/activities/ratio-mixing",
              "version": "2.0",
              "variant": "seed-8675309"
            },
            "instrument": {
              "source": "model",
              "model": "example-tutor-2026-09",
              "config_hash": "c9f3b7ca43165de9e1df7bc7ec6cff14e7498d\
a4250a6df8e8f1885a9b4aeea8",
              "parameters": {"temperature": 0.2}
            },
            "data": {
              "type": "hint",
              "request": 3,
              "level": 1,
              "reveals_answer": false,
              "content_hash": "86c2216ed616d2d0097475056db432d1f9f76\
e1ac5e712742157a3fe64ded70d"
            }
          },
          "event_hash": "b4bd1e82ee9019fb8ece8ab96192a64fdc953fc9ab2\
7b2d046664ac8525cc789",
          "prev_hash": "afa914e1952ca0d6878b901e32279a09f1462f252dfb\
3f289ea846c63b048e06",
          "entry_hash": "3b8fb5a69bc9701329888b081538f1cc243a2fadb14\
8f6fc2f619de4f7671f61"
        },
        {
          "seq": 5,
          "time": "2026-10-07T14:02:30Z",
          "event": {
            "name": "dialog:message",
            "salt": "HySIGmOXiD_e0G3jppGMVg",
            "to": {
              "role": "assistant",
              "kind": "software",
              "id": "example-chat"
            },
            "session": "s-01",
            "activity": {
              "id": "https://example.org/activities/ratio-mixing",
              "version": "2.0",
              "variant": "seed-8675309"
            },
            "data": {
              "modality": "text",
              "content": "What does simplifying a ratio mean?"
            }
          },
          "event_hash": "a83a6d085d8ae61dfe3b3783ecaea3c51d52588d7a4\
c2182245236b2b0b7d2dd",
          "prev_hash": "3b8fb5a69bc9701329888b081538f1cc243a2fadb148\
f6fc2f619de4f7671f61",
          "entry_hash": "4cf6025ac1e0aa85ed8a9f1e3c678f544a73319d1e0\
8eecb3fbe6ae695b65a2d"
        },
        {
          "seq": 6,
          "time": "2026-10-07T14:02:33.500Z",
          "event": {
            "name": "dialog:message",
            "salt": "wf_VFXE_2WNouptIJguFuQ",
            "actor": {
              "role": "assistant",
              "kind": "software",
              "id": "example-chat"
            },
            "session": "s-01",
            "activity": {
              "id": "https://example.org/activities/ratio-mixing",
              "version": "2.0",
              "variant": "seed-8675309"
            },
            "instrument": {
              "source": "model",
              "model": "example-chat-2026-08",
              "config_hash": "fcfd5153bc5c0d1f52417f66ab078c749eb21f\
4cebb5504cf274821a79a17aa5"
            },
            "data": {
              "modality": "text",
              "reply_to": 5,
              "content_hash": "fb4ede48d160a985aeca77af68fcc7abdc3af\
cdbff6781574146ecd02c2b45ed"
            }
          },
          "event_hash": "0d8d14d15cb3a606a857e86fc4fbe838a9ec11dfa3e\
bdb73a5eef53bbb96d4c9",
          "prev_hash": "4cf6025ac1e0aa85ed8a9f1e3c678f544a73319d1e08\
eecb3fbe6ae695b65a2d",
          "entry_hash": "43f068599a10f02217bc7287ca082d99818aa1fd92f\
a3e12115cf1a7d5bfc23b"
        },
        {
          "seq": 7,
          "time": "2026-10-07T14:03:05Z",
          "event": {
            "name": "assist:scaffold_provided",
            "salt": "lMgpY68f-AMZ-mEMbfR3cg",
            "actor": {
              "role": "instructor",
              "kind": "human",
              "id": "T-c1a8a3a416c9098d21b2baf451464304"
            },
            "session": "s-01",
            "activity": {
              "id": "https://example.org/activities/ratio-mixing",
              "version": "2.0",
              "variant": "seed-8675309"
            },
            "instrument": {
              "source": "model",
              "model": "example-transcriber-1"
            },
            "data": {
              "type": "hint",
              "level": 2,
              "reveals_answer": false,
              "modality": "speech",
              "content": "Try it with the smaller numbers first."
            }
          },
          "event_hash": "3e305baf5a3fad47f1e9c3d412d799fdd18926ff159\
169a26034af74e6214f91",
          "prev_hash": "43f068599a10f02217bc7287ca082d99818aa1fd92fa\
3e12115cf1a7d5bfc23b",
          "entry_hash": "4a0646f97bb877a347b7ba98eb6d1a7d9e0e3e0978e\
d5706b0e07b7ef6ec4668"
        },
        {
          "seq": 8,
          "time": "2026-10-07T14:04:20Z",
          "event": {
            "name": "sim:explanation_filed",
            "salt": "y0_vEXCW8JNMCBA0MnLr2w",
            "session": "s-01",
            "activity": {
              "id": "https://example.org/activities/ratio-mixing",
              "version": "2.0",
              "variant": "seed-8675309"
            },
            "data": {
              "explanation": "12 out of 16 is \u00be. Both divide b\
y 4."
            }
          },
          "event_hash": "3689aa3e1f8fdea9494f10a9607ffd84db805ad0475\
f57077129a34cd8ebb5d0",
          "prev_hash": "4a0646f97bb877a347b7ba98eb6d1a7d9e0e3e0978ed\
5706b0e07b7ef6ec4668",
          "entry_hash": "d2b47063f53bcce905ef687c5f7f1c3f008078d0a3e\
ece78d2bc54bf8c1ca5b5"
        }
      ],
      "signed_heads": [
        {
          "head": {
            "record_hash": "dcfc869849b6088bb8cba0761987f4fa90a18133\
5e20a95b120bb0e8dfcd5556",
            "seq": 8,
            "entry_hash": "d2b47063f53bcce905ef687c5f7f1c3f008078d0a\
3eece78d2bc54bf8c1ca5b5",
            "time": "2026-10-07T14:04:21Z"
          },
          "signature": "eyJhbGciOiJFUzI1NiIsImtpZCI6IkNPSWFUYTctOFoz\
VElOYmhBOFJjczZmcjAyWlRERzExMzM3MVQzR01aUlUifQ..9SbLqNvt-_gfXkz4sYqC\
znBecvxy1YojXXOujGN2pdy5jJ7IZOOw37pxBsrdjr7K4p52Rqlo4va5MGFbP5vVWg"
        }
      ]
    }
  ]
}
</sourcecode>
        </figure>
      </section>
      <section anchor="ex-values">
        <name>Intermediate Values</name>
        <t>The canonical form of the header is:</t>
        <artwork>=============== NOTE: '\' line wrapping per RFC 8792 ================

{"created":"2026-10-07T14:00:00Z","hash_alg":"sha-256","producer":{"\
name":"example-sim","version":"1.4.0"},"record_id":"66a921d7-55be-45\
89-afcc-84ea4d2c26ef","subject":"L-fdda6e0e3413590a303a3737d4176389"}
</artwork>
        <t>Its SHA-256 hash is the record hash, which is also the <tt>prev_hash</tt> of entry 1:</t>
        <artwork>dcfc869849b6088bb8cba0761987f4fa90a181335e20a95b120bb0e8dfcd5556
</artwork>
        <t>The canonical form of the event in entry 2 is:</t>
        <artwork>=============== NOTE: '\' line wrapping per RFC 8792 ================

{"activity":{"id":"https://example.org/activities/ratio-mixing","var\
iant":"seed-8675309","version":"2.0"},"data":{"confidence":"fairly_s\
ure","prediction":"3:4"},"name":"sim:prediction_committed","salt":"y\
qUo0sZAN2gYAhADsPJjKw","session":"s-01"}
</artwork>
        <t>Its hash is the <tt>event_hash</tt> of entry 2:</t>
        <artwork>1b878146c3e51d20d0411979dc7fbac81da5fd088469a725b9afcf8395c2a518
</artwork>
        <t>The canonical form of the object that is hashed for entry 2 is:</t>
        <artwork>=============== NOTE: '\' line wrapping per RFC 8792 ================

{"event_hash":"1b878146c3e51d20d0411979dc7fbac81da5fd088469a725b9afc\
f8395c2a518","prev_hash":"ff3f6b48ed7a77379e68a08f6a0c2bfa71bdc75767\
1ceb88d905e5d307638433","record_hash":"dcfc869849b6088bb8cba0761987f\
4fa90a181335e20a95b120bb0e8dfcd5556","seq":2,"time":"2026-10-07T14:0\
1:10Z"}
</artwork>
        <t>Its hash is the <tt>entry_hash</tt> of entry 2:</t>
        <artwork>ac64c6867336279286d1837e60312b56b106978d648db62360f211306d65b6e5
</artwork>
      </section>
      <section anchor="ex-head">
        <name>The Signed Head</name>
        <t>The head was signed with the private key that matches this public key:</t>
        <sourcecode type="json">{
  "kty": "EC",
  "crv": "P-256",
  "x": "hLOF5epJKOENr_YLdw3iuvlSeuuUdVewL1BqcNtOKYw",
  "y": "PwQdWqmdT3_EOKIQvttI8kFeZLUePMifTFD5CpM-1WY"
}
</sourcecode>
        <t>The JWK Thumbprint of the key, used as <tt>kid</tt>, is:</t>
        <artwork>COIaTa7-8Z3TINbhA8Rcs6fr02ZTDG113371T3GMZRU
</artwork>
        <t>The protected header is:</t>
        <artwork>{"alg":"ES256","kid":"COIaTa7-8Z3TINbhA8Rcs6fr02ZTDG113371T3GMZRU"}
</artwork>
        <t>The JWS payload is the canonical form of the head:</t>
        <artwork>=============== NOTE: '\' line wrapping per RFC 8792 ================

{"entry_hash":"d2b47063f53bcce905ef687c5f7f1c3f008078d0a3eece78d2bc5\
4bf8c1ca5b5","record_hash":"dcfc869849b6088bb8cba0761987f4fa90a18133\
5e20a95b120bb0e8dfcd5556","seq":8,"time":"2026-10-07T14:04:21Z"}
</artwork>
        <t>The value of <tt>signature</tt> in the file is:</t>
        <artwork>=============== NOTE: '\' line wrapping per RFC 8792 ================

eyJhbGciOiJFUzI1NiIsImtpZCI6IkNPSWFUYTctOFozVElOYmhBOFJjczZmcjAyWlRE\
RzExMzM3MVQzR01aUlUifQ..9SbLqNvt-_gfXkz4sYqCznBecvxy1YojXXOujGN2pdy5\
jJ7IZOOw37pxBsrdjr7K4p52Rqlo4va5MGFbP5vVWg
</artwork>
        <t>The private key is not given here, so an implementation cannot reproduce this value. The value can be verified with the public key above.</t>
      </section>
      <section anchor="ex-redaction">
        <name>Redaction and Truncation</name>
        <t>After entry 8 is redacted, it reads as shown below. Every hash in the record and the signed head verify as before.</t>
        <sourcecode type="json">=============== NOTE: '\' line wrapping per RFC 8792 ================

{
  "seq": 8,
  "time": "2026-10-07T14:04:20Z",
  "redacted": true,
  "event_hash": "3689aa3e1f8fdea9494f10a9607ffd84db805ad0475f5707712\
9a34cd8ebb5d0",
  "prev_hash": "4a0646f97bb877a347b7ba98eb6d1a7d9e0e3e0978ed5706b0e0\
7b7ef6ec4668",
  "entry_hash": "d2b47063f53bcce905ef687c5f7f1c3f008078d0a3eece78d2b\
c54bf8c1ca5b5"
}
</sourcecode>
        <t>If entries 1 to 5 are removed from the original record instead, the record gains the member shown below and its first remaining entry is entry 6. The signed head still verifies.</t>
        <sourcecode type="json">=============== NOTE: '\' line wrapping per RFC 8792 ================

{
  "truncated": {
    "seq": 5,
    "entry_hash": "4cf6025ac1e0aa85ed8a9f1e3c678f544a73319d1e08eecb3\
fbe6ae695b65a2d"
  }
}
</sourcecode>
      </section>
      <section anchor="ex-canonical">
        <name>Canonical Form Check</name>
        <t>The record above has few values that separate the canonical form of <xref target="RFC8785"/> from other compact JSON output with sorted member names. The JSON text in <xref target="fig-ex-canon"/> has more of them. It holds numbers in several spellings, a control character, and two member names whose order by UTF-16 code unit differs from their order by code point.</t>
        <figure anchor="fig-ex-canon">
          <name>A JSON text for checking the canonical form</name>
          <sourcecode type="json">{
  "numbers": [1e21, 1.0, 0.000001, 1e-7, -0, 0.2, 4.50, 2E-3,
              333333333.33333329],
  "string": "\u000f\u00be/\"\\\n",
  "\ud83d\ude00": 1,
  "\ufb33": 2,
  "a": null,
  "A": true
}
</sourcecode>
        </figure>
        <t>In the canonical form, the member names appear in the order A, a, numbers, string, U+1F600, U+FB33. The numbers appear as 1e+21, 1, 0.000001, 1e-7, 0, 0.2, 4.5, 0.002, and 333333333.3333333. The canonical form has 131 octets, shown here in hexadecimal:</t>
        <artwork>7b 22 41 22 3a 74 72 75 65 2c 22 61 22 3a 6e 75
6c 6c 2c 22 6e 75 6d 62 65 72 73 22 3a 5b 31 65
2b 32 31 2c 31 2c 30 2e 30 30 30 30 30 31 2c 31
65 2d 37 2c 30 2c 30 2e 32 2c 34 2e 35 2c 30 2e
30 30 32 2c 33 33 33 33 33 33 33 33 33 2e 33 33
33 33 33 33 33 5d 2c 22 73 74 72 69 6e 67 22 3a
22 5c 75 30 30 30 66 c2 be 2f 5c 22 5c 5c 5c 6e
22 2c 22 f0 9f 98 80 22 3a 31 2c 22 ef ac b3 22
3a 32 7d
</artwork>
        <t>The SHA-256 hash of those octets is:</t>
        <artwork>afbc268398163c4f856a4d881d1eb85ba85b85e609c8473a11ebaf56cf28a0d1
</artwork>
      </section>
    </section>
    <section anchor="impl" removeInRFC="true">
      <name>Implementation Status</name>
      <t>This section records the status of known implementations at the time of writing, as described in <xref target="RFC7942"/>.</t>
      <t>The following description of qlm-measure version 0.7.0 is retained from revision -00. It is an earlier implementation and does not establish conformance to this revision. qlm-measure version 0.7.0 (Quantum Learning Machines; TypeScript and Python; Apache 2.0 license; https://github.com/QuantumLearningMachines/qlm-measure) writes and verifies records that it labels schema 0.3. This document was derived from that format. Both chain entries by hash and allow an event to be redacted. Both place consent events in the chain. The differences are:</t>
      <ul>
        <li>The recorder of qlm-measure serializes hashed values with sorted member names. Its documentation lists <xref target="RFC8785"/> for schema 0.3 as planned.</li>
        <li>A record has no header. The first entry links to an empty string. No hash covers the studentScopeId member, which names the learner of the record.</li>
        <li>Events carry no salt.</li>
        <li>A record can hold attestations that refer to an entry by version or by hash. The verifier checks that reference and a payload hash. No signature is verified.</li>
        <li>Member names differ. For example, qlm-measure uses version and prevHash where this document uses <tt>seq</tt> and <tt>prev_hash</tt>.</li>
        <li>The kind of scaffold is a member of an observation event. No event type exists for a request for help or for a scaffold.</li>
      </ul>
      <t>Quantum Learning Machines plans to extend qlm-measure so that it writes records in the format of this document.</t>
      <t>A local Learnlog AI Use Profile v0.1.0 prototype adds proposed context fields, condition-assessment sidecars, two entirely synthetic examples, and a Python reference checker. It checks a subset consisting of contained, unsigned SHA-256 records without redaction or truncation and the example-profile constraints. It does not verify signatures, establish full CDDL conformance, or implement runtime context-update and capture-completion events. The examples contain no participant observations or learning-effect measurements. The prototype is not an independent implementation or an interoperability result. No public publication location is specified in this revision.</t>
    <t>The revision -02 preparation materials add an experimental assistance and import profile, together with source-specific transformer mappings and a downloadable pipeline. The profile distinguishes source-declared lifecycle observations and binds its definitions through existing header extension points. Released transcript turns and participant summaries remain different observation units; synthetic lifecycle fixtures are labeled as such. These materials do not demonstrate independently reviewed cross-product scaffolding measurement, full core conformance, or an improvement in research decisions. Their execution and source-fidelity checks are implementation evidence, not semantic or educational-effect validation.</t><t>The 18 October working materials add the assistance capture profile identified in <xref target="assistance-contract"/>, a JSON Schema, local Python validation, synthetic examples, a draft xAPI profile, and complete experimental xAPI and Caliper importer-observation encodings. Their executable checks concern the stated encoding mode and record the exact scope of validation. They do not establish that a carrier record is a native learner action. These additions leave the previously versioned companion profile and the core Learnlog representation unchanged. Structural, round-trip, and negative-case checks do not establish truthful capture, independent implementation, fewer measurement errors, lower integration effort, certification, or adoption.</t><t>The intended adoption path is through the transformer. External products can retain their native formats and need not adopt Learnlog, xAPI, or Caliper. An adapter can produce profile records when available source facts and a documented mapping support those assertions; an optional carrier binding packages the result. QLM's internal logging requirement for its own products is separate. The research adapters supplied with the existing demo use their earlier frozen profile; the new carrier encodings do not make those adapters conform to this assistance capture profile. A supported adapter for the new profile needs its own source-fidelity and mapping tests. The transformer cannot recover assistance or capture facts that the source did not record. Additional instrumentation is needed when a proposed measure requires missing observations.</t><t>For revision -03, two episode scripts apply the stage rules to three released research datasets and publish only aggregate counts <xref target="LEARNLOG-EPISODES"/>. They are analysis scripts for <xref target="three-releases"/>, not a profile implementation, and their mappings have not been independently reviewed.</t></section>
    <section anchor="changes-03" removeInRFC="true"><name>Changes in Revision -03</name><ul>
<li>Add the offered state for assistance a system or another actor initiates, established by a recorded initiation, and state that an absent request link does not establish an offer.</li>
<li>Add rules for the basis of an assertion: observed, self-reported, or inferred. An inferred assertion identifies its detector, version, output and scale, and inputs, and never becomes an observed value. Derived classifications are analytical results.</li>
<li>Add an informative account of the stage distinctions applied to three released research datasets, with the public episode scripts.</li>
<li>Propose, for discussion, event families beyond assistance: predict, test, explain, revise, defend, discern, transfer, recall, delayed recall, emotion and need, confidence when answering, and agency at session start and end. No event type, namespace, or CDDL member is added.</li>
<li>Add a privacy note on inferred emotion, stated confidence, and agency reports about children.</li>
<li>No core sourcecode block, hash computation, or Appendix A test vector is changed.</li></ul></section><section anchor="changes-02" removeInRFC="true"><name>Changes in Revision -02</name><ul>
<li>Position Learnlog as an assistance capture profile with xAPI and Caliper bindings, motivated by QLM Measure. Make the existing file representation optional. Specify assistance, achieved capture, explicit unknowns, and mapping requirements while acknowledging existing AI and tutoring profiles. Add experimental complete importer-observation encodings without changing core sourcecode or existing versioned profiles.</li><li>Clarify that provision does not establish reading, use, understanding, or learning, and distinguish provision from internal generation.</li>
<li>Clarify absent request linkage and separate delivery position from source-native hint depth. No CDDL field is changed.</li>
<li>Add guidance for historical import observations, original timestamps, relative offsets, importer actors, and source speakers.</li>
<li>Specify what concrete companion profiles need to define for assistance states, context changes, coverage, mappings, annotation provenance, and measurement compatibility.</li>
<li>Keep candidate AI conditions optional, unvalidated analytical prompts and applicability judgments outside the core format.</li>
<li>Add xAPI Profiles, ISO/IEC TS 20748-3:2020, and DataShop as related work; make no first-standard or absent-infrastructure claim.</li>
<li>Recommend protected schema and profile bindings through existing header extensions. Preserve the core hash algorithm and test vectors; do not rewrite historical records.</li>
<li>Omit an explicit submission stream attribute because the existing Datatracker record has no assigned stream. No stream assignment is requested by this editorial change.</li>
<li>Correct the reference binding to version 0.1.1-draft with separate xAPI account-system identity and legacy Statement-version handling; preserve the capture payload version. Preserve visible verification substep labels in reading copies. Qualify optional event context and producer-time assertions, clarify malformed recognized sequential entries, remove unpublished normative dependencies, and enumerate the informative candidate-condition labels.</li></ul></section><section anchor="changes-01" removeInRFC="true">
      <name>Changes in Revision -01</name>
      <ul>
        <li>Add prospective motivation for interpretable learning and assessment records when AI supplies assistance.</li>
        <li>Clarify that judgments about research applicability are outside the core format.</li>
        <li>Add non-normative companion-profile guidance for activity stage, scoped pedagogical permission, planned and asserted observation coverage, capture gaps, context changes, and review status.</li>
        <li>Add a hypothetical metacognitive prompt versus supplied-explanation example using the existing assist members.</li>
        <li>Clarify the distinction between recorder and producing-model provenance and the limits of configuration hashes and deterministic validation.</li>
        <li>Identify the need for explicit xAPI and Caliper mappings and describe the limited local synthetic profile prototype.</li>
        <li>Leave all existing CDDL, sourcecode examples, integrity rules, registries, and test vectors unchanged.</li>
      </ul>
    </section>
    <section anchor="issues" removeInRFC="true">
      <name>Open Issues</name>
      <ul>
        <li>The name of the format is open.</li>
        <li><xref target="RFC8785"/> is an Informational RFC from the Independent Submission stream. A normative reference to it from a Standards Track document is a downward reference. The alternative is to define the canonical form in this document. <xref target="RFC2104"/> is Informational as well.</li>
        <li>JSON text sequences follow <xref target="QLOG" section="11.2" sectionFormat="of"/>. JSON objects separated by line feeds are an alternative.</li>
        <li>Signed heads use JWS with ES256. Open points are explicit typing of the JWS, a second algorithm, key discovery, and whether COSE would serve better.</li>
        <li>A defined way to lodge heads with a third party would address <xref target="sec-tail"/> and <xref target="sec-producer"/>.</li>
        <li>A sequential file holds one record. A server that writes for many learners may want to mix records in one file.</li>
        <li>When supplied, <tt>session</tt> and <tt>activity</tt> are carried on each applicable event; both members are optional in the base Event definition. <xref target="QLOG" section="7.5" sectionFormat="of"/> lets a trace state such values once, as common fields. An equivalent here would have to fit the rule that each event is hashed by itself.</li>
        <li>The assist and dialog namespaces are small. Feedback on a response and questions put to the learner are candidates for generic event types. The values of ScaffoldType may belong in a registry.</li>
        <li>The roles instructor and assistant separate parties by teaching responsibility. Deployments may need finer roles.</li>
        <li>The consent scopes are a starting point.</li>
        <li>A reader ignores members that this document does not define, and outside a header, an event, or a head no hash covers them. A stricter rule may be safer.</li>
        <li>The <tt>content_hash</tt> and <tt>config_hash</tt> members carry no salt (<xref target="sec-guess"/>).</li>
        <li>A producer cannot yet state in the chain which entries it redacted. Such a statement would let a reader tell a redaction by the producer from one by a later holder (<xref target="sec-holder"/>).</li>
        <li><t>The unprotected <tt>event_schemas</tt> list remains unchanged. Independent review is needed for private profile binding conventions, immutable publication, digest scope, and behavior when a required definition is unavailable.</t></li><li>A subject identifier cannot differ by recipient (<xref target="priv-ids"/>).</li>
        <li>A record cannot change its hash algorithm. A way to continue a record in a new one may be needed.</li>
        <li>Mappings between learnlog events and xAPI statements or Caliper events, including assistance semantics, provenance, and information loss.</li>
        <li>Independent review and implementation of companion-profile requirements for scoped assistance permission and observation coverage, including explicit context changes and completion claims.</li>
        <li>A companion format for estimates that refer to entries.</li><li>Whether the offered state needs a core event type, or a profile member on <tt>assist:scaffold_provided</tt> that records initiation explicitly.</li><li>Which self-report instruments and scales, if any, the profile names for stated confidence and agency, and how their versions are identified.</li><li>Whether one profile should hold the proposed event families or existing xAPI and Caliper profiles are better homes for some; each family needs a worked dataset before it is specified.</li><li>A mechanism for declared, consented linking of the same learner across records, and its equivalent in xAPI and Caliper bindings, which have no Learnlog records.</li>
      <li>Independent evaluation of assistance-state definitions, source mappings, annotation agreement, measurement compatibility, and two-product interoperability. Successful structural validation or a matching digest is not semantic validation.</li><li>Independent research is needed to test whether applicability records improve decisions. This format cannot establish that AI invalidates a prior trial or that a trial needs repeating.</li><li>Independent comparison against existing AI and tutoring profiles, including overlap, hosting and governance options; whether a separate representation is justified; and validation of the proposed capture and knowledge-state distinctions.</li><li>Test the experimental importer-observation bindings with independent producers and carrier implementations; finalize controlled identifiers, publication and governance; and define native-action bindings only when source evidence supports required actor and time semantics. Publish any information loss and keep the separate evaluation toolkit's frozen encodings distinct.</li></ul>
    </section>
  </back>
</rfc>