MRK-2.0: Machine-Ready Knowledge, maturity layer
Status Released 2026-10-19
| Standard version | MRK-2.0 |
|---|---|
| Article format versions | 1.0 (or mrk_version absent) and 1.1. MRK-2.0 adds no format version (§13). |
| Date | 2026-10-19 |
| Editor | Jason O'Donnell (Contextkeeping) |
| Canonical URL | https://machinereadyknowledge.com/mrk-2.0 |
| Authority | docs/mrk-evolution/mrk-2.0/SPEC.md (this file) |
| Supersedes the normative parts of | graduation-ladder.md, retrieval-conformance.md, reuse-transclusion.md (these files remain as rationale) |
Abstract
MRK-1.0 defines a machine-ready article: YAML front matter that validates against a JSON Schema, plus four editorial clauses. MRK-1.1 adds optional fields to that format. MRK-2.0 is a maturity layer over the 1.1 article format. It does not change the article format. It defines:
- five producer conformance classes (rungs L0 to L4) and how a tool assigns them;
- the graph projection of a corpus to RDF and the SHACL integrity gate over it;
- declared transclusion and the drift gate that keeps reused text honest;
- the consumer clause R (R1 to R7) and two consumer conformance classes, C1 and C2;
- a citation object, an evidence document, and the rules for claiming conformance;
- versioning and compatibility rules for the standard and the article format.
Every requirement has a stable identifier and is mapped to the check that tests it in Appendix C. Requirements that can only be attested say so and name the evidence.
Contents
- Conventions and terminology
- Scope and relationship to MRK-1.0 and MRK-1.1
- Article format
- Producer conformance classes (L0 to L4)
- Namespace and identifiers
- Graph projection
- Integrity gate
- Reuse: declared transclusion and drift
- Clause R: retrieval conformance
- Consumer conformance classes and the citation object
- Conformance evidence
- Corpus reporting and conformance claims
- Versioning and compatibility
- Security and privacy considerations
- Changelog from MRK-1.1
- Appendix A: Normative artifacts
- Appendix B: Reference tools
- Appendix C: Conformance-test mapping
- Appendix D: Resolved inconsistencies
- Appendix E: Open issues
1. Conventions and terminology
1.1 Requirement language
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC 2119] [RFC 8174] when, and only when, they appear in all capitals, as shown here.
1.2 Requirement identifiers
Each requirement is a list item that begins with its identifier in bold, for example C-R5-1. Identifiers are stable. A later edition never reuses or renumbers an identifier; a withdrawn requirement keeps its identifier with the note "withdrawn". The prefixes are:
| Prefix | Section | Prefix | Section |
|---|---|---|---|
A- | 3 Article format | C-R1- … C-R7- | 9 Clause R |
P-G-, P-L0- … P-L4- | 4 Producer classes | C-L- | 10 Consumer classes |
N- | 5 Namespace | E- | 11 Evidence |
G- | 6 Projection | K- | 12 Reporting and claims |
I- | 7 Integrity gate | V- | 13 Versioning |
D- | 8 Reuse and drift | S- | 14 Security |
T- | Appendix B Reference tools |
Text outside requirement items is informative unless it defines a term.
1.3 Terminology
- Article: one unit of knowledge, identified by
id, with front matter and (for Markdown) a body. - Front matter: the YAML mapping that describes an article. It is either a
.yamlfile or the---fenced block at the start of a.mdfile. - Section: an addressable part of an article, identified by
article-id#slug. - Corpus: the set of articles scored, projected or served together.
- Producer: whoever authors and publishes a corpus. Producer tool: software that validates, scores, lints or projects a corpus.
- Consumer: software that assembles answers from MRK articles (a retriever or answer assembler).
- Reader context: the facts about the person an answer is for (applicability dimensions, visibility scope, entitlements, locale, intent role), as given to a consumer.
- Rung: one of the producer classes L0 to L4.
- Gate: a mechanical check whose failure blocks a rung or a build.
- Attestation: a statement by a person or organisation that a property holds, which no tool checks. An attested requirement names the evidence that supports it.
- Evidence: a machine-readable record of a conformance run (§11).
- Projection: the transform from front matter to RDF defined in §6.
- Canonical JSON, article digest, suite digest: defined in §11.2.
2. Scope and relationship to MRK-1.0 and MRK-1.1
MRK-2.0 is a version of the standard, not of the article format. The article format of MRK-2.0 is MRK-1.1, which is a strict superset of MRK-1.0 for every instance that does not reuse a 1.1 field name with a non-conforming value (§13, Appendix D item 23).
MRK-2.0 has three conformance targets:
- Articles and corpora (producer classes, §4), scored by a producer tool.
- Consumers (consumer classes, §10), tested by the conformance suite.
- Producer tools and validators, which must follow the processing rules in §3, §6 to §8, §11 and §13.
MRK-2.0 does not judge prose quality, coverage, or whether an answer is correct. Those remain human review.
3. Article format
The article format is defined by mrk-1.1.schema.json and, for instances that declare or default to format 1.0, mrk-1.0.schema.json (Appendix A). The four MRK-1.0 editorial clauses continue to hold.
- A-1 An article's front matter MUST validate against
mrk-1.1.schema.json(JSON Schema 2020-12) to be scored above L0. - A-2 Front matter MUST be either a
.yamlfile whose top-level value is a mapping, or a.mdfile that begins with a line---, followed by a YAML mapping, followed by a line---. - A-3 (MRK-1.0 §1) A section marked
self_contained: trueMUST be safe to read in isolation: it MUST NOT depend on another section for its meaning ("as described above") and MUST state its prerequisites inside itself. This is attested. Evidence: theself_containedflag, set by a human at the curation gate. Tools check only the flag's value. - A-4 (MRK-1.0 §2) Applicability MUST be stated as data in
applies_to, with at least one dimension. - A-5 (MRK-1.0 §3) An article
idor sectionidMUST NOT change after it is assigned and MUST NOT be reused after retirement. This is attested. Evidence: the corpus's revision-control history. Tools check only the identifier format. - A-6 The part of a section
idbefore#MUST equal theidof the article that contains the section. - A-7 (MRK-1.0 §4)
owner,verifiedandlifecycleMUST be present.verifiedMUST be an RFC 3339 full-date (YYYY-MM-DD). - A-8 A producer tool MUST convert a YAML date or timestamp value (for example an unquoted
verified: 2026-07-30) to its ISO 8601 string before schema validation, and MUST validateformat: "date"as an assertion, not only an annotation. - A-9 A producer tool MUST parse YAML with a safe loader that constructs only plain mappings, sequences and scalars.
4. Producer conformance classes (L0 to L4)
Each article sits on exactly one rung. Rungs measure machine-readiness depth. They are reached by adding front matter and running the gates, never by rewriting prose.
| Rung | Name | Summary |
|---|---|---|
| L0 | Prose | Not contracted. |
| L1 | Contracted | Schema-valid; editorial flags hold. |
| L2 | Typed | L1 plus section roles, type shape, visibility. |
| L3 | Linked | L2 plus at least one edge, concepts, clean projection. |
| L4 | Reasoned | L3 plus SHACL clean, drift clean, and graph-first retrieval measured or attested. |
4.1 General rules
- P-G-1 A producer tool MUST assign each article exactly one rung: the highest rung whose criteria, and the criteria of every lower rung, all hold.
- P-G-2 A rung is all-or-nothing. An article that meets only some of a rung's criteria MUST be reported at the rung below.
- P-G-3 For each article below L4, a producer tool MUST report the first unmet criterion of the next rung, in the order the criteria are listed in §4.2 to §4.6.
- P-G-4 A rung MUST NOT depend on the age of
verifiedor on the value oflifecycle. Freshness is a separate signal (consumers handle it under R2).
4.2 L0 Prose
- P-L0-1 An article whose front matter is missing, fails A-1, or fails any L1 criterion MUST be reported as L0.
4.3 L1 Contracted
- P-L1-1 The front matter validates against
mrk-1.1.schema.json(A-1, after A-8 normalisation). - P-L1-2 Every section has
self_contained: true. - P-L1-3
applies_tois present and has at least one dimension. - P-L1-4
owner,verifiedandlifecycleare present. - P-L1-5 Every section
idsatisfies A-6.
L1 is checked against the 1.1 schema, which does not require plan and region. L1 is therefore not the same as MRK-1.0 conformance (see K-7).
4.4 L2 Typed
- P-L2-1 Every section has a
role. - P-L2-2 If
typeisissue-resolution, the sections include the rolescause,resolutionandverification. Iftypeistask, the sections include the roleprocedure. This criterion applies whatever themrk_version. Othertypevalues, includingconceptandreference, impose no section shape in MRK-2.0. - P-L2-3
visibilityis present.
4.5 L3 Linked
- P-L3-1 The article has at least one edge: a non-empty
relationslist, or at least one section with a non-emptyrequireslist. - P-L3-2
conceptsis non-empty on the article or on at least one section. - P-L3-3 The article's front matter, normalised per §6, projects to RDF with the context without error.
4.6 L4 Reasoned
L4 has a mechanical half (P-L4-1, P-L4-2) and an operational half (P-L4-3), which is either measured by evidence or attested by the producer.
- P-L4-1 The article has no SHACL violation attributed to it (§7) when the whole corpus graph is validated against
shapes.ttl. - P-L4-2 If the article is a
.mdfile, the drift gate (§8) reports no finding for any of its sections. - P-L4-3 Graph-first retrieval of the article MUST be either measured or attested. It is measured when at least one evidence document supplied to the tool satisfies all of: it validates against
mrk-evidence.schema.json; its self-hash verifies (E-5); itssuite_sha256equals the tool's suite digest (E-4);levels.C2is"pass";self_testis"rejected-broken-consumer";corpus.articles[id]equals the article's current digest (E-3); for every article that is the target of an edge in one of the article's derived cases (E-6),corpus.articlesrecords that target's digest and it equals the current digest of that article in the corpus being scored; at least one derived case of the article has a non-emptyexpectandok: true; and no derived case of the article hasok: false. A derived case with an emptyexpectmeasures nothing: it never counts towards the "at least one". It is attested when no evidence measures it and the producer passes the attestation option (--graph-first). An attested L4 is an attestation. Evidence: the producer's own statement, recorded asl4_basis: "attested". - P-L4-4 A producer tool MUST record the basis of every L4 result as
measuredorattested. When both apply, the basis MUST bemeasured. - P-L4-5 A producer tool MUST NOT assign L4 without running the SHACL gate. If the SHACL engine is unavailable, the tool MUST fail rather than fall back.
- P-L4-6 When an L3 article is not promoted, a producer tool MUST report the most specific reason, checked in this order: SHACL violation; drift finding; evidence digest mismatch (the article is not covered, or its digest, or the digest of one of its edge targets, differs: "evidence predates this article"); evidence from a different suite; evidence without a C2 pass or whose self-test did not reject the broken consumer; no derived case that measures this article (E-6); a derived case of this article failed; neither measured nor attested. Evidence that does not validate or whose self-hash fails is reported before the evidence reasons (digest mismatch onward), but after a SHACL violation or drift finding, which are about the article itself.
The rule, as a producer tool applies it:
for each article a with rung(a) == 3:
if a.id in shacl_failures: L3, "L4: SHACL - <message>"
elif a is .md and drift(a) != []: L3, "L4: drift - <finding>"
elif some evidence e measures a (P-L4-3): L4, basis = measured
elif --graph-first: L4, basis = attested
else: L3, most specific reason (P-L4-6)
5. Namespace and identifiers
- N-1 The MRK vocabulary IRI is
https://machinereadyknowledge.com/ns#. Every MRK class and property (for examplemrk:Article,mrk:Section,mrk:sectionRole,mrk:requires) MUST be in this namespace, and the context's@vocabandmrkprefix and themrkprefix inshapes.ttlMUST all equal it. - N-2 The publisher of this standard MUST serve a human-readable description of the vocabulary at
https://machinereadyknowledge.com/ns. - N-3 Article and section IRIs are formed by resolving
idagainst the context's@base(https://machinereadyknowledge.com/kb/); concept IRIs are formed againsthttps://machinereadyknowledge.com/vocab/. These IRIs are identifiers only. They, and all/kb/and/vocab/IRIs shown in examples, are illustrative and not dereferenceable. Consumers and producer tools MUST NOT dereference them.
6. Graph projection
The projection turns a corpus into one RDF graph. The context cannot express two transforms, so they are applied to each article's front matter first.
- G-1 The projection MUST use
mrk-1.1.context.jsonld(JSON-LD 1.1) exactly as published (Appendix A). - G-2 Before the context is applied, the article mapping MUST get
"@type": "mrk:Article", and each item ofsectionsMUST get"@type": "mrk:Section". - G-3 Before the context is applied,
relationsMUST be removed and expanded. For each{rel, target}: ifrelis one ofdepends_on,prerequisite_of,references,part_of,see_also,applies_with, appendtargetto the list under the key namedrel; ifrelissupersedes, setsupersedestotargetonly ifsupersedesis not already set. Otherrelvalues are dropped. - G-4 A corpus MUST be projected as a single JSON-LD document whose
@graphlists every normalised article, so cross-article edges resolve within one graph. - G-5 Dates MUST be serialised as ISO 8601 strings before projection, so that
verifiedprojects as anxsd:dateliteral. - G-6 The projection MUST be deterministic: the same corpus MUST project to isomorphic graphs.
7. Integrity gate
shapes.ttl (Appendix A) is normative. JSON Schema checks one article; SHACL checks the corpus graph, where cross-article references can be resolved.
- I-1 The corpus graph MUST be validated against
shapes.ttlas a whole (not per article) with a SHACL Core processor. - I-2 Each
mrk:ArticleMUST have exactly onedcterms:title, at least onemrk:verifiedof datatypexsd:date, amrk:lifecyclein the 1.1 enumeration, an optionalmrk:visibilityin the 1.1 enumeration, and at least onemrk:hasSectionof classmrk:Section(mrk:ArticleShape). - I-3 Each
mrk:SectionMUST have exactly onemrk:sectionRolein the 1.1 role enumeration and anmrk:selfContainedof datatypexsd:boolean(mrk:SectionShape). - I-4 Every object of
mrk:requiresMUST be amrk:Sectionin the corpus graph (mrk:RequiresIntegrityShape). - I-5 Every object of
mrk:dependsOnMUST be amrk:Articlein the corpus graph (mrk:DependsOnIntegrityShape). Adepends_onrelation therefore MUST target an article id, not a section id. - I-6 Every object of
dcterms:replaces(fromsupersedes) MUST be amrk:Articlein the corpus graph (mrk:SupersedesIntegrityShape). - I-7 Targets of
prerequisite_of,references,part_of,see_alsoandapplies_withSHOULD resolve to a node in the corpus graph. The 2.0 shapes do not check these. - I-8 A producer tool MUST attribute each SHACL violation to an article. If the focus node has an
rdf:typein the graph, the violation belongs to the article of that node. Otherwise the focus node is a dangling target, and the violation belongs to every article whose node, or one of whose section nodes, is the subject of a triple with that target as object.
8. Reuse: declared transclusion and drift
MRK-1.0 §1 requires each section to restate its prerequisites. That creates copies. MRK-2.0 keeps the copy inline, declares where it came from, and gates drift between copy and source.
8.1 Grammar
- D-1 A transcluded block MUST open with
<!-- mrk:transclude TARGET -->and close with<!-- /mrk:transclude -->, where whitespace inside the comment delimiters is free andTARGETis a section id matching^[a-z][a-z0-9-]*-[0-9]+#[a-z][a-z0-9-]*$. Blocks MUST NOT nest. - D-2 In a
.mdbody, each section MUST begin with a level-2 heading ending in{#slug}. The section body is the text from that heading to the next such heading or the end of the file, and corresponds to the front-matter sectionarticle-id#slug.
8.2 Rules
- D-3
TARGETMUST resolve to a section present in the linted corpus. - D-4
TARGETMUST be listed in therequiresof the front-matter section that encloses the block. - D-5 A block MUST NOT name its own enclosing section as
TARGET. The target is the canonical source; edits are made there and copies are updated to match. - D-6 The block MUST be grounded in its source: the grounding score MUST be at least 0.5. The score is |W(block) ∩ W(canonical)| / |W(block)|, where W(text) is the set of lower-cased tokens matching
[a-zA-Z0-9_]+that are longer than two characters and not in the stop list below, and canonical is the target section's body with every transcluded block removed. A block with an empty W passes. - D-7 A drift linter MUST exit with a non-zero status when any of D-3 to D-6 fails, and MUST name the section, the target and the failed rule.
Stop list (normative for D-6): a an the of to in on and or is are was were be been for with that this it its as at by from into if then so no not up out you your they them their.
9. Clause R: retrieval conformance
MRK-1.0 specifies the article. Clause R specifies the consumer that turns articles into an answer. R1 to R7 are the rule groups; the numbered items are the requirements.
R1 Applicability
- C-R1-1 A consumer MUST evaluate each candidate's
applies_toagainst the reader context and MUST NOT quote a section whose applicability excludes the reader. - C-R1-2 For
plan,region,product,component,audience,roleandjurisdiction: a dimension constrained by the article and not supplied by the reader context MUST NOT match. Only the explicit value"all"matches every reader. - C-R1-3 For
version,version_min,version_max,version_exactand eachdimensionsentry: a consumer that does not evaluate the dimension MUST treat the article as not matching, unless the value is"all".
R2 Freshness and lifecycle
- C-R2-1 A consumer MUST read
verifiedandlifecycle, and MUST NOT serve a section from an article whoselifecycleisretiredorexpired. - C-R2-2 A consumer SHOULD downweight an article whose
verifieddate is older than its freshness window (365 days unless the consumer declares another), and SHOULD mark its citationsstale: true. - C-R2-3 A consumer SHOULD prefer a
currentsource over asupersededone for the same claim, and SHOULD mark citations from asupersededarticlesuperseded: true. Superseded articles are downweighted, not refused. - C-R2-4 A consumer MUST take the reference date for freshness as an input, so that a conformance run can fix it.
R3 Safety
- C-R3-1 A consumer MUST NOT surface an article whose
visibilityranks above the reader's scope (order:public<internal<confidential). An absentvisibilitymeanspublic. - C-R3-2 A consumer MUST NOT surface an article with a non-empty
entitlementunless the reader holds every listed key. - C-R3-3 A consumer MUST NOT surface an article whose
localeprimary language subtag differs from the reader's, unless the reader context explicitly accepts that locale as a fallback. - C-R3-4 A consumer MUST declare a default locale and MUST apply it to articles with no
locale.
R4 Sections, not pages
- C-R4-1 A consumer MUST retrieve and quote addressable sections and cite them as
article-id#slug, not whole pages or arbitrary character windows. - C-R4-2 A consumer MUST quote only sections with
self_contained: true.
R5 Graph before similarity
- C-R5-1 When articles carry
relationsorrequiresand a projected graph is available, a consumer SHOULD resolve retrieval by graph traversal and use vector similarity only to interpret the query or shortlist candidates. At C2 this is a MUST, tested by C-R5-2 to C-R5-5. - C-R5-2 At C2, for a reader context whose matching articles have no edges, the graph path MUST return the same set of section ids as the flat path.
- C-R5-3 At C2, when a matched article
depends_onanother, the graph path MUST add the target article'sself_contained: truesections. - C-R5-4 At C2, when a matched section
requiresanother section, the graph path MUST add the required section. - C-R5-5 At C2, every section the graph path adds MUST pass the R1 to R4 gates for the same reader context.
R6 Citation
- C-R6-1 Every claim in an assembled answer MUST carry a citation object that validates against
answer-object.schema.json, withsection,articleandverified. - C-R6-2 A citation's
articleMUST equal the part of itssectionbefore#, and itsverifiedMUST equal that article'sverified. - C-R6-3 A citation's
version, when present and not null, MUST be the article digest (§11.2) in the formsha256:<64 hex>. It MUST NOT carry themrk_versionvalue. - C-R6-4 Citations from the graph path SHOULD carry
staleandsupersededon the same terms as the flat path.
R7 Role-aware assembly
- C-R7-1 When sections carry
roleand the reader context gives an intent role, a consumer SHOULD assemble from sections of that role (for example a fix fromresolutionorprocedure, an explanation fromcauseorconcept), not by position or similarity alone. At C2 this is a MUST.
10. Consumer conformance classes and the citation object
| Class | Name | Requirements |
|---|---|---|
| C1 | Filtered | R1, R2, R3, R4, R6 (the mandatory items). Works over an L1 corpus with metadata filtering. |
| C2 | Graph-aware | C1 plus R5 and R7, which become mandatory. Needs an L3 or higher corpus and the projection. |
- C-L-1 A consumer conforms to C1 when it satisfies every MUST in R1, R2, R3, R4 and R6, and passes every suite case graded on its flat path (
answer) alone. A C1 claim MUST NOT require a graph path. - C-L-2 A consumer conforms to C2 when it conforms to C1, satisfies C-R5-1 to C-R5-5 and C-R7-1 as MUST, passes every
parityandr5_contractsuite case, and, when a corpus is given, passes every derived case for that corpus (§11.3). - C-L-3 A consumer under test MUST expose this interface: it is constructed as
Class(article_paths);answer(ctx)returns a list of citations, or a pair(citations, notes); for C2,answer_graph(ctx)returns a list of citations.ctxis a mapping that MAY contain the applicability dimensions,reader_scope,reader_entitlements,locale,accept_locales,intent_roleandtoday. - C-L-4 The conformance harness MUST reject the reference broken consumer (one that ignores R3) in its self-test. A run whose self-test does not reject it MUST NOT be recorded as passing.
The citation object is defined by answer-object.schema.json (Appendix A): required section, article, verified; optional version, stale, superseded; further properties allowed.
11. Conformance evidence
Evidence binds a consumer's conformance run to a specific suite and a specific corpus, so that L4's operational half and C1/C2 claims can be measured rather than only attested.
11.1 The evidence document
- E-1 An evidence document MUST be a JSON object that validates against
mrk-evidence.schema.jsonand has these members:mrk_evidence("1"),kind("retrieval-conformance"),spec("MRK-2.0"),tool(name,version),suite_sha256,consumer(entry,name,version),run_at(RFC 3339 date-time, UTC; required),today(full-date),levels(C1,"pass"or"fail";C2,"pass","fail"or"not-claimed", the last for a consumer that implements C1 only; L4 measured requires"pass"),suite_results(one entry per suite case withname,mode,rules,ok),self_test("rejected-broken-consumer"or"accepted-broken-consumer"),corpus(required;articles: map from article id to article digest, empty when no corpus was given;derived_cases: list ofarticle,edge,section,target,ctx,expect,ok, and, when present,missinganderror), andsha256.
Example (values shortened):
{
"mrk_evidence": "1",
"kind": "retrieval-conformance",
"spec": "MRK-2.0",
"tool": { "name": "mrk-tools", "version": "2.0.0" },
"suite_sha256": "4f1c…",
"consumer": { "entry": "acme_rag.mrk:Consumer", "name": "acme-rag", "version": "4.2.1" },
"run_at": "2026-10-19T14:03:00Z",
"today": "2026-10-19",
"levels": { "C1": "pass", "C2": "pass" },
"suite_results": [ { "name": "public-starter", "mode": "parity", "rules": "R1 R2 R3 R4 R6 R7", "ok": true } ],
"self_test": "rejected-broken-consumer",
"corpus": {
"articles": { "ck-ops-1": "sha256:9a0e…", "ck-ops-2": "sha256:77b2…" },
"derived_cases": [
{ "article": "ck-ops-2", "edge": "depends_on", "target": "ck-ops-1",
"section": "ck-ops-2#steps", "ctx": { "reader_scope": "public", "locale": "en", "intent_role": "procedure" },
"expect": ["ck-ops-2#steps", "ck-ops-1#resolution"], "ok": true }
]
},
"sha256": "c03d…"
}
11.2 Digests
- E-2 Canonical JSON of a value MUST be the UTF-8 encoding of
json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False, default=str)as produced by Python 3; date and datetime values are rendered as their ISO 8601 strings. Other implementations MUST produce identical bytes. - E-3 The article digest MUST be
"sha256:"followed by the lower-case hex SHA-256 of the canonical JSON of the article's front matter mapping. It covers front matter only; body changes are the drift gate's concern. - E-4 The suite digest MUST be the lower-case hex SHA-256 over, for each file under the suite's
fixtures/directory in ascending order of its POSIX path relative to the suite root, the bytes of that path, one zero byte, the file bytes and one zero byte; followed by the canonical JSON (E-2) of the suite's case list (thecasesarray ofcases.json), with every set rendered as a sorted array. Nothing else under the suite root is hashed and no byte is normalised (no newline or Unicode conversion), so a checkout MUST keep the suite files byte-for-byte; a repository SHOULD mark them as binary (-textin.gitattributes). - E-5 The
sha256member MUST be the lower-case hex SHA-256 of the canonical JSON of the evidence object with thesha256member removed.
11.3 Derived cases
- E-6 For a corpus run, the harness MUST generate exactly one derived case for each edge A to T, where an edge is either a
depends_onrelation of article A to article T, or a section s of A thatrequiressection T. For arequiresedge the case'ssectionis s. For adepends_onedge it is A's first section that has aroleandself_contained: true(an article with none yields no case for that edge). The reader context MUST be built from A: for each R1 dimension in A'sapplies_to, the first listed value (a scalar value is used as is);reader_scopefrom A'svisibility(defaultpublic);reader_entitlementsfrom A'sentitlement;localefrom A'slocaleif present;versionfrom A'sversion_exact, elseversion, elseversion_min, elseversion_max(the first listed value that is not"all");dimensionswith the first listed value of each entry that is not"all";intent_rolefrom the case'ssectionrole.expectMUST be the servable sections the edge must surface: the case'ssectionif it isself_contained: trueand servable under that context, plus the target sections that areself_contained: trueand servable under that context (for arequiresedge, section T; for adepends_onedge, every section of article T). Sections that are not servable are not expected. A case MUST be recordedok: trueonly ifexpectis a subset of the section ids returned byanswer_graph(ctx)and the call raised no error. A case with an emptyexpectis vacuous: it is recorded, but it never measures anything (P-L4-3). The evidence MUST record, incorpus.articles, the digest of A and of every article T that is the target of an edge of A that is present in the corpus; a target absent from the run is simply not recorded, and an article with an edge to it is not measured (P-L4-3, P-L4-6). - E-7 The harness MUST record in
todaythe reference date it passed to the consumer (C-R2-4). The standard suite fixes its own date (todayincases.json); corpus runs use the run date.todayis the date of the derived cases (the suite date when there is no corpus), and each derived case records the date it used asctx.today, so the evidence shows what every derived case ran with. The suite cases' fixed date is not recorded per case.
11.4 Verification
- E-8 An evidence verifier MUST check, in order: schema validity (E-1); the self-hash (E-5); that
suite_sha256equals its own suite digest (E-4); and, when given the corpus, that each listed article digest matches the current front matter (E-3). The verifier MUST also report a failed case: it MUST NOT pass evidence that contains a suite or derived case withok: false. The verifier re-runs the derived cases and comparesexpectandokonly when it is explicitly given a consumer and a corpus (the kit's--consumerwith--corpus); it MUST NOT import the consumer thatconsumer.entrynames on the evidence's own say-so, because that would execute code chosen by the evidence file. Giving a consumer without a corpus is a usage error.
Evidence proves integrity and freshness of a run, not who ran it (§14).
12. Corpus reporting and conformance claims
- K-1 A producer tool's corpus report MUST include the count of articles at each rung L0 to L4, the contract rate (share at L1 or above) and the graph-ready rate (share at L3 or above).
- K-2 Whenever any article reaches L4, the report MUST include one line giving the L4 basis counts:
L4 basis: N measured, M attested, however L4 was reached (evidence, attestation, or both). Measured and attested L4 both count as L4 in the distribution. - K-3 When asked for machine-readable output, a producer tool MUST emit, per article, its id, rung, next-rung reason (if any) and, for L4,
l4_basis. - K-4 A published producer claim MUST name the standard version (MRK-2.0), the corpus size, the contract and graph-ready rates, and for any L4 figure its basis split. Example: "MRK-2.0 producer; corpus kb/ (412 articles): contract 88%, graph-ready 64%, L4 12% (measured 12%, attested 0%)". This is attested by the publisher. Evidence: the retained producer-tool report (K-1 to K-3) the claim quotes.
- K-5 A producer SHOULD base L4 claims on measured evidence rather than attestation.
- K-6 A published consumer claim MUST name the class and cite the evidence digest. Example: "MRK-2.0 C2 consumer (acme-rag 4.2.1), evidence sha256:c03d…". This is attested by the publisher. Evidence: the evidence document whose
sha256is cited, which anyone can check with E-8. - K-7 A corpus MUST NOT be described as MRK-1.0 conformant on the strength of L1 alone. MRK-1.0 conformance requires validation against
mrk-1.0.schema.json. A producer tool MUST NOT label L1 as "MRK-1.0".
13. Versioning and compatibility
MRK has two version axes. The standard version (MRK-1.0, MRK-2.0) names this document and its conformance classes. The article-format version is the mrk_version value in front matter.
- V-1-1 (V1) MRK-2.0 defines no new
mrk_versionvalue. Its format versions are"1.0"(or absent) and"1.1". - V-2-1 (V2) A 2.0 producer tool or consumer MUST accept every instance that is valid under the schema of its format version (
mrk-1.0.schema.jsonfor absent or"1.0",mrk-1.1.schema.jsonfor"1.1"): it MUST process it without error, and a validator MUST report it valid. Scoring is separate: a rung is still decided by A-1. - V-2-2 (V2) An absent
mrk_versionMUST be treated as"1.0". - V-3-1 (V3) A consumer MUST NOT reject an article because it has properties the consumer does not recognise. It MAY ignore them.
- V-4-1 (V4) A minor format version (1.x) MUST be strictly additive: every instance valid under the previous minor version that does not use a newly added property name MUST remain valid.
- V-4-2 (V4) Every new constraint in a minor format version MUST apply only to instances that opt in by declaring that
mrk_version(or by using a property introduced in that version, such asprofile). - V-4-3 (V4)
mrk_versionvalues"2"and higher are reserved for a future breaking format change. MRK-2.0 instances MUST NOT use them. - V-5-1 (V5) A validator that meets an
mrk_versionit does not know MUST report it as unknown and MUST NOT validate the instance against an older schema in its place. - V-6-1 (V6) The validation keywords of a published schema
$idMUST NOT change. Itstitle,descriptionand$commenttext MAY be corrected. The validation fingerprint (Appendix A) is the test. - V-6-2 (V6)
mrk-1.0.schema.jsonis frozen: its bytes MUST NOT change. - V-6-3 (V6) At the release of MRK-2.0,
mrk-1.1.schema.jsonis frozen in its validation keywords (V-6-1).
14. Security and privacy considerations
- R3 (C-R3-1 to C-R3-4) is the safety gate. C-R5-5 applies it to every section the graph path adds, so dependency traversal cannot leak internal content into a public answer.
- Evidence is self-produced and unsigned. Its hashes show that a run is intact and matches the current corpus; they do not show who ran it. A verifier that needs more assurance re-runs the derived cases (E-8).
--consumerloads and runs third-party code with the caller's privileges.
- S-1 Producer tools, the conformance harness and the evidence verifier MUST NOT access the network at run time. Spec files are bundled with the tools or given with
--spec DIR.
15. Changelog from MRK-1.1
- Released 2026-10-19 as the first release of MRK-2.0. MRK-1.0 and MRK-1.1 are frozen formats; this release adds no format.
- No article-format change. No new
mrk_version. The 1.1 schema is frozen in its validation keywords as of this release (V-6-3). Its title and description were corrected editorially: the DRAFT label is dropped and the description points to /mrk-2.0. - New: producer classes L0 to L4 (§4), with measured or attested L4.
- New: namespace and identifier rules (§5).
- New: projection normalisation made normative (§6).
- New: SHACL integrity gate, with per-article attribution (§7).
- New: declared transclusion grammar and the drift gate, which joins L4 (§8).
- New: consumer clause R with classes C1 and C2 (§9, §10), and the citation object.
- New: evidence document, digests, corpus-derived cases and verification (§11).
- New: reporting and claim rules (§12).
- Clarified: section ids must belong to their article (A-6); YAML dates are normalised and
format: dateis asserted (A-8); a citationversionis the article digest, notmrk_version(C-R6-3); applicability dimensions a consumer cannot evaluate fail closed (C-R1-3); absent locale uses the consumer's declared default (C-R3-4). - Changed: validators must reject an unknown
mrk_versioninstead of falling back (V-5-1). - Reserved:
mrk_versionvalues"2"and higher (V-4-3). - Compatibility note (non-normative): an instance whose
verifiedis not an ISO date (for example2026-7-3,20260730, or a timestamp such as2026-07-30T10:00:00Z) or whose section id does not start with its article's id now scores L0, where the MRK-1.1-era scorer gave L1 (A-6, A-7, A-8). The 1.1 schema is unchanged; these checks are the scorer's, so an external 1.0 or 1.1 corpus can drop a rung until those fields are corrected.
Appendix A: Normative artifacts
SHA-256 values are of the files as released.
| Artifact | Served URL | Authority in repo | SHA-256 (file) |
|---|---|---|---|
| MRK-1.0 schema | /mrk-1.0.schema.json | sites/machinereadyknowledge/mrk-1.0.schema.json | 9eb5fc3489a7ebf979b45be20f4a731ac7a1eac9331ba92738420af05adcbbb9 |
| MRK-1.1 schema | /mrk-1.1.schema.json | docs/mrk-evolution/mrk-1.1/mrk-1.1.schema.json | 35390dd2c2b33ccb77e816e09fa71c62d8ebf28ccd0df87fa407958f2b9c2270 |
| JSON-LD context | /mrk-1.1.context.jsonld | docs/mrk-evolution/projection/mrk-1.1.context.jsonld | 94d1cde84293a5da9edd70d25345d8f238e11fd718b6f1799c60ae653db7421c |
| SHACL shapes | /shapes.ttl | docs/mrk-evolution/mrk-2.0/shapes.ttl | 2ac155c5b4f1ac63829fbb634dadf693ba33314c11cc7acb5eff3c5a92cc5732 |
| Citation object | /answer-object.schema.json | docs/mrk-evolution/mrk-2.0/conformance/answer-object.schema.json | a5320e35b726941172676cadb8139eecba1dccbdd620287d1d6e095e2b95bcc5 |
| Evidence schema | /mrk-evidence.schema.json | docs/mrk-evolution/mrk-2.0/evidence.schema.json | 7388af111073214a9dc7583f43271e1d0dbc7335608d431566d36af98471c606 |
| Vocabulary (Turtle) | /ns (Accept: text/turtle) → /ns.ttl | docs/mrk-evolution/ns/ns.ttl | 14fe57b62ebf626195f41b6902aa3aa4abe688f3c5be8565f79fab484b785d6a |
| Vocabulary (JSON-LD) | /ns (Accept: application/ld+json) → /ns.jsonld | docs/mrk-evolution/ns/ns.jsonld | 8580acf5008414deb6fc4a9ec98aac24a555bdd431d375e73413d2add0027212 |
Validation fingerprint. The SHA-256 of the canonical JSON (E-2) of a schema after removing every title, description and $comment member that is in schema position. Members of properties, $defs, patternProperties and dependentSchemas are names, not keywords, and are kept (so the 1.0 and 1.1 property named title stays in the fingerprint).
| Schema | Validation fingerprint |
|---|---|
mrk-1.0.schema.json | f7902cddbb48013409db44cf8fc970c3cfe0c33893d6b42cf8170b5dbda27f31 |
mrk-1.1.schema.json | 01a0e6322e50a23130b9477fb7a8178d6c684636a65853752cbd816e76e8b117 |
answer-object.schema.json | 78ddde558bfb7990f4e177d4d861d2c8ae98a76e692de4ada34bb8290267b02f |
Appendix B: Reference tools
The reference tools ship as one kit, mrk-tools.zip, served at /downloads/mrk-tools.zip. The old URL /downloads/mrk-ladder-scorer.zip redirects to it. Commands, run from the unzipped directory:
python -m mrk ladder [--graph-first] [--evidence FILE ...] [--spec DIR] [--json] PATHS...
python -m mrk conform [--consumer MODULE:CLASS] [--corpus PATHS...] [--self-test] [--evidence-out FILE]
python -m mrk drift PATHS.md...
python -m mrk project build|demo PATHS...
python -m mrk verify-evidence FILE [--corpus PATHS...]
python ladder.py ... # same as: python -m mrk ladder ...
The kit is a reference implementation. Conformance is defined by this document, not by the kit.
- T-1 The kit MUST bundle
mrk-1.0.schema.json,mrk-1.1.schema.json,mrk-1.1.context.jsonld,shapes.ttl,answer-object.schema.jsonandmrk-evidence.schema.json, each byte-identical to its served copy. - T-2 Every served copy of a normative artifact MUST be byte-identical to its authority in Appendix A.
- T-3 The kit's
requirements.txtMUST pin the full transitive dependency set with hashes, so thatpip install --require-hashes -r requirements.txtsucceeds. - T-4
python ladder.py ARGSMUST behave identically topython -m mrk ladder ARGS.
Appendix C: Conformance-test mapping
One row per requirement. Check names the test: a schema keyword (schema:), a SHACL shape (shacl:), a conformance-suite case (suite:), a drift-lint rule (drift:), a scorer rule (ladder:), an evidence-verifier step (verify:), a pre-upload gate check (gate:), or a repository test written for the requirement (test:). Where is the current implementation and the tests that exercise it; a test is named file::function, and the tests written for this appendix are in tests/test_appendix_c.py, named after the requirement they check (test_A_6_...). Status: exists (the check is in code and tests this requirement), partial (in code but incomplete, as noted and explained in Appendix E), missing (to be built), attested (cannot be machine-checked; the evidence is named in the requirement).
Current files: scorer = tools/mrk-tools/mrk/ladder.py; drift = tools/mrk-tools/mrk/drift.py; harness = tools/mrk-tools/mrk/conform.py; consumer = tools/mrk-tools/mrk/consumer.py; gate = tools/validate-machine-blocks.py. Suite case ids map to the current harness case names: public-starter (Starter / public / en), enterprise, internal, locale-de, r5-depends (graph completes the answer), self-test.
| ID | Check | Where | Status |
|---|---|---|---|
| A-1 | schema: whole mrk-1.1.schema.json; ladder:schema | scorer rung() | exists |
| A-2 | ladder:load (fence and mapping) | core.parse_yaml(), core.load_front_matter(); test_hardening.py::test_front_matter_must_be_a_mapping, test_hardening.py::test_markdown_with_unterminated_fence | exists |
| A-3 | schema: section.self_contained (flag value only) | schema; scorer clauses_hold() | attested |
| A-4 | schema: applies_to required, minProperties: 1 | schema | exists |
| A-5 | schema: id and section id pattern (format only) | schema | attested |
| A-6 | ladder:section-prefix | scorer clauses_hold(); test_appendix_c.py::test_A_6_section_id_prefix | exists |
| A-7 | schema: required owner/verified/lifecycle; verified format: date | schema; scorer make_validator() (a FormatChecker, so format is asserted), rung(); test_appendix_c.py::test_A_7_verified_must_be_a_full_date | exists |
| A-8 | ladder:date-normalise, ladder:format-assert | core.load_front_matter() (_iso_dates()), scorer make_validator(); test_appendix_c.py::test_A_8_unquoted_yaml_date_is_normalised_before_validation, test_A_8_a_yaml_timestamp_is_normalised_then_fails_the_date_format | exists |
| A-9 | ladder:safe-load; drift:safe-load | scorer, drift (yaml.safe_load) | exists |
| P-G-1 | ladder:rung | scorer rung() | exists |
| P-G-2 | ladder:rung (first failing check stops) | scorer rung() | exists |
| P-G-3 | ladder:next-rung message | scorer rung() return value | exists |
| P-G-4 | ladder:rung reads verified/lifecycle for presence only | scorer clauses_hold() | exists |
| P-L0-1 | ladder:schema, ladder:clauses | scorer | exists |
| P-L1-1 | ladder:schema | scorer rung() | exists |
| P-L1-2 | ladder:clauses (self_contained) | scorer clauses_hold() | exists |
| P-L1-3 | ladder:clauses (applies_to); schema minProperties: 1 | scorer, schema | exists |
| P-L1-4 | ladder:clauses (owner/verified/lifecycle) | scorer clauses_hold() | exists |
| P-L1-5 | ladder:section-prefix | scorer clauses_hold(); test_appendix_c.py::test_P_L1_5_section_id_prefix_is_an_l1_criterion | exists |
| P-L2-1 | ladder:typed (role) | scorer typed() | exists |
| P-L2-2 | ladder:typed (TYPE_SHAPES); schema allOf (under 1.1) | scorer typed() | exists |
| P-L2-3 | ladder:typed (visibility) | scorer typed() | exists |
| P-L3-1 | ladder:linked (edge) | scorer linked() | exists |
| P-L3-2 | ladder:linked (concepts) | scorer linked() | exists |
| P-L3-3 | ladder:project | core.build_graph_tolerant(); scorer score() reports the article at L2 with the reason; test_appendix_c.py::test_P_L3_3_an_unprojectable_article_is_attributed_not_an_abort (forced: no schema-valid article is known that fails to project) | exists |
| P-L4-1 | ladder:shacl | scorer shacl_failures() | exists |
| P-L4-2 | ladder:drift | scorer drift_failures(); test_review_m2.py::test_an_empty_or_all_stopword_transclusion_block_is_a_drift_finding | exists |
| P-L4-3 | ladder:evidence (measured); ladder:attest (--graph-first) | scorer score(), evidence.judge(); test_evidence.py::test_measured_l4_from_evidence, test_evidence.py::test_graph_first_alone_is_still_attested_l4 | exists |
| P-L4-4 | ladder:basis | scorer score() sets l4_basis; test_evidence.py::test_evidence_plus_graph_first_mixes_bases | exists |
| P-L4-5 | ladder:shacl (hard dependency on pyshacl) | scorer imports pyshacl unconditionally | exists |
| P-L4-6 | ladder:reason-order | scorer score(), evidence.judge() and evidence.STAGES; test_appendix_c.py::test_P_L4_6_the_earlier_reason_wins_when_two_apply (every adjacent pair), test_P_L4_6_a_failed_case_is_the_last_evidence_reason, test_P_L4_6_shacl_before_drift_before_evidence_before_neither | exists |
| N-1 | test: context @vocab/mrk, shapes.ttl mrk prefix and ns.ttl/ns.jsonld are one IRI | test_appendix_c.py::test_N_1_one_namespace_in_the_context_the_shapes_and_the_vocabulary | exists |
| N-2 | test: served ns.ttl/ns.jsonld/ns.html and the /ns negotiation rules | sites/machinereadyknowledge/.htaccess; test_appendix_c.py::test_N_2_ns_is_served_and_negotiated (files byte-match docs/; the .htaccess rules are evaluated for each Accept header; the live route cannot be tested offline, see Appendix E 8) | partial: live route untestable offline |
| N-3 | test: no network modules in the kit; every command runs with sockets blocked | test_offline.py::test_no_network_modules_are_imported_by_the_kit, test_offline.py::test_each_command_works_offline | exists |
| G-1 | gate:3b (context byte-match); T-1 bundle check | gate §3b | exists |
| G-2 | ladder:project golden graph (projection/graph.ttl isomorphism) | core.normalize(); test_projection.py::test_build_is_isomorphic_to_committed_graph_ttl, test_projection.py::test_build_matches_golden | exists |
| G-3 | ladder:project golden graph | core.normalize(); test_projection.py::test_build_is_isomorphic_to_committed_graph_ttl | exists |
| G-4 | ladder:project (single @graph) | core.build_graph() (the scorer uses core.build_graph_tolerant()) | exists |
| G-5 | shacl: mrk:ArticleShape mrk:verified sh:datatype xsd:date | shapes.ttl | exists |
| G-6 | ladder:project golden graph (rdflib isomorphism) | test_projection.py::test_build_is_isomorphic_to_committed_graph_ttl (rdflib isomorphism) | exists |
| I-1 | ladder:shacl (whole-corpus validation) | scorer shacl_failures() | exists |
| I-2 | shacl: mrk:ArticleShape | shapes.ttl | exists |
| I-3 | shacl: mrk:SectionShape | shapes.ttl | exists |
| I-4 | shacl: mrk:RequiresIntegrityShape | shapes.ttl | exists |
| I-5 | shacl: mrk:DependsOnIntegrityShape | shapes.ttl | exists |
| I-6 | shacl: mrk:SupersedesIntegrityShape | shapes.ttl | exists |
| I-7 | shacl: none, by design | shapes.ttl has no shape for these five edges; test_appendix_c.py::test_I_7_dangling_optional_edges_are_not_a_shacl_violation pins that (SHOULD only, see Appendix E 9) | partial: SHOULD, unchecked by design |
| I-8 | ladder:shacl attribution | scorer shacl_failures() | exists |
| D-1 | drift:grammar (TRANSCLUDE_RE) | drift analyze() (TRANSCLUDE_RE, OPEN_RE, MARKER_RE); test_appendix_c.py::test_D_1_nested_markers_are_reported, test_D_1_a_marker_that_never_closes_is_reported, test_D_1_well_formed_blocks_with_free_whitespace_are_clean | exists |
| D-2 | drift:anchors (HEADING_RE) | drift parse() | exists |
| D-3 | drift:dangling | drift analyze(); test_drift_lint.py::test_dangling_target_fails | exists |
| D-4 | drift:undeclared | drift analyze(); test_drift_lint.py::test_target_missing_from_requires_fails | exists |
| D-5 | drift:self-target | drift analyze(); test_appendix_c.py::test_D_5_a_block_may_not_target_its_own_section | exists |
| D-6 | drift:grounding (THRESHOLD = 0.5, words(), STOP) | drift | exists |
| D-7 | drift:exit (non-zero, names section/target) | drift run() (exit 1; each finding names the section and the target); test_appendix_c.py::test_D_7_findings_name_the_section_and_the_target_and_exit_non_zero | exists |
| C-R1-1 | suite:public-starter, suite:enterprise | harness | exists |
| C-R1-2 | test: unanswered constrained dimension | consumer applies(); test_conformance.py::test_r1_unanswered_constrained_dimension_is_no_match | exists |
| C-R1-3 | test: unevaluated dimension | consumer applies(); test_appendix_c.py::test_C_R1_3_a_constrained_dimension_the_reader_does_not_answer_does_not_match, test_C_R1_3_all_matches_every_reader; test_review_m3.py::test_C_R1_3_version_is_compared_as_dotted_numeric_segments, test_C_R1_3_a_value_that_cannot_be_compared_fails_closed, test_C_R1_3_dimensions_are_compared_per_key_like_the_named_dimensions | exists |
| C-R2-1 | test: retired and expired | consumer fresh(); test_conformance.py::test_r2_retired_article_never_served, test_appendix_c.py::test_C_R2_1_retired_and_expired_are_never_served | exists |
| C-R2-2 | test: stale flag | consumer fresh(); test_evidence.py::test_today_drives_the_freshness_flag | exists |
| C-R2-3 | test: superseded flag | consumer fresh(); test_appendix_c.py::test_C_R2_3_superseded_is_served_downweighted_and_flagged_not_dropped | exists |
| C-R2-4 | test: today passed in ctx | consumer fresh() reads ctx['today']; test_evidence.py::test_today_drives_the_freshness_flag, test_review_m2.py::test_verify_evidence_honours_today_in_the_rerun | exists |
| C-R3-1 | suite:internal; suite:self-test | harness | exists |
| C-R3-2 | test: entitlement | consumer visible(); test_appendix_c.py::test_C_R3_2_entitlement_is_required_to_surface_an_article | exists |
| C-R3-3 | suite:locale-de; test: regional fallback and accept_locales | consumer visible(); test_appendix_c.py::test_C_R3_3_regional_reader_falls_back_to_the_base_language_article, test_C_R3_3_a_different_language_needs_the_reader_to_accept_it | exists |
| C-R3-4 | test: default locale | consumer DEFAULT_LOCALE, visible(); test_appendix_c.py::test_C_R3_4_the_default_locale_is_declared_and_applied | exists |
| C-R4-1 | suite: every case (R4 # check) | harness conform.check() | exists |
| C-R4-2 | test: section not self-contained | consumer answer(), answer_graph(); test_appendix_c.py::test_C_R4_2_only_self_contained_sections_are_quoted | exists |
| C-R5-1 | suite:r5-depends | harness | exists |
| C-R5-2 | suite: parity cases (graph set == flat set) | harness conform.check() | exists |
| C-R5-3 | suite:r5-depends | harness | exists |
| C-R5-4 | suite:r5-depends; derived requires cases | consumer answer_graph() follows the requires closure; test_review_m2.py::test_conform_exits_1_when_a_derived_case_fails, test_evidence.py::test_evidence_validates_against_the_served_schema | exists |
| C-R5-5 | test: dependency that fails a gate | consumer answer_graph() (emit() applies servable()); test_appendix_c.py::test_C_R5_5_a_dependency_that_fails_a_gate_is_not_added, test_C_R5_5_a_required_section_in_a_gated_article_is_not_added | exists |
| C-R6-1 | suite: every case (answer-object.schema.json); schema required, pattern, format | harness _bad_citations() | exists |
| C-R6-2 | harness: citation coherence | harness conform._incoherent(); test_appendix_c.py::test_C_R6_2_a_citation_belongs_to_the_article_that_owns_its_section, test_C_R6_2_the_harness_rejects_a_citation_whose_article_does_not_own_the_section, test_C_R6_2_the_harness_rejects_a_verified_date_that_is_not_the_articles | exists |
| C-R6-3 | harness: citation version | consumer _cite() (the article digest); harness conform._incoherent(); test_appendix_c.py::test_C_R6_3_a_citation_version_is_the_article_digest_never_mrk_version, test_C_R6_3_the_harness_rejects_any_other_version | exists |
| C-R6-4 | test: graph citation flags | consumer answer_graph() (_cite(..., fresh())); test_appendix_c.py::test_C_R6_4_graph_citations_carry_stale_and_superseded | exists |
| C-R7-1 | suite: cases with intent_role | harness | exists |
| C-L-1 | suite: C1 verdict graded on answer() only | harness conform.check() grades C1 on answer(); test_evidence.py::test_c1_only_consumer_reports_c2_not_claimed, test_evidence.py::test_answer_graph_raising_not_implemented_means_not_claimed | exists |
| C-L-2 | suite: parity + r5_contract + derived cases | harness conform.derive_cases(), conform.run_conform(); test_evidence.py::test_derived_case_ids_come_from_the_corpus_not_a_template, test_review_m2.py::test_conform_exits_1_when_a_derived_case_fails | exists |
| C-L-3 | suite: --consumer MODULE:CLASS loader | harness conform.load_consumer(), conform.make() (--consumer); test_evidence.py::test_bad_consumer_entry | exists |
| C-L-4 | suite:self-test; verify:self-test | harness --self-test | exists |
| E-1 | verify:schema (mrk-evidence.schema.json) | evidence.problems(); test_review_m2.py::test_evidence_schema_requires_run_at_and_corpus_and_both_copies_are_identical, test_evidence.py::test_evidence_validates_against_the_served_schema | exists |
| E-2 | verify:canonical-json | core.canonical_json(); test_appendix_c.py::test_E_2_canonical_json_matches_an_independent_implementation (a hand-written encoder), test_E_2_and_E_3_article_digest_is_reproduced_from_the_written_algorithm, test_E_2_and_E_5_the_self_hash_is_reproduced_from_the_written_algorithm | exists |
| E-3 | verify:article-digest | core.article_digest(); test_evidence.py::test_editing_an_article_invalidates_its_evidence, test_evidence.py::test_body_edit_does_not_invalidate_digest, test_appendix_c.py::test_E_2_and_E_3_article_digest_is_reproduced_from_the_written_algorithm | exists |
| E-4 | verify:suite-digest | mrk.suite_sha256(); test_review_m2.py::test_suite_digest_is_the_one_spec_e4_defines, test_review_m2.py::test_a_file_outside_fixtures_and_cases_is_not_part_of_the_digest | exists |
| E-5 | verify:self-hash | evidence.seal(), evidence.problems(); test_evidence.py::test_hand_edited_evidence_fails_its_self_hash, test_evidence.py::test_verify_evidence_rejects_tampering, test_appendix_c.py::test_E_2_and_E_5_the_self_hash_is_reproduced_from_the_written_algorithm | exists |
| E-6 | conform:derived-cases | harness conform.derive_cases(); test_evidence.py::test_derived_case_ids_come_from_the_corpus_not_a_template, test_review_m2.py::test_changing_an_edge_target_demotes_the_dependent_article | exists |
| E-7 | conform:today | harness conform.run_conform() (suite cases: the suite date; derived cases: the run date; --today overrides both); test_appendix_c.py::test_E_7_corpus_runs_use_the_run_date_and_suite_cases_the_suite_date, test_E_7_an_explicit_today_applies_to_every_case, test_E_7_without_a_corpus_the_suite_date_is_recorded; test_review_m3_minor.py::test_m4_each_derived_case_records_the_date_it_used; test_evidence.py::test_today_is_injectable_and_validated | exists |
| E-8 | verify-evidence procedure | harness conform.verify(); test_evidence.py::test_verify_evidence_passes_and_detects_changes, test_evidence.py::test_verify_evidence_rejects_tampering | exists |
| K-1 | ladder:report (histogram, rates) | scorer report_text() | exists |
| K-2 | ladder:report (L4 basis line) | scorer report_text(); test_review_m2.py::test_graph_first_alone_prints_the_basis_line | exists |
| K-3 | ladder:--json | scorer report_json(); test_evidence.py::test_graph_first_alone_is_still_attested_l4, test_evidence.py::test_evidence_plus_graph_first_mixes_bases | exists |
| K-4 | publisher claim quoting a retained report | — | attested |
| K-5 | ladder:report basis counts | scorer report_text(); test_evidence.py::test_measured_l4_from_evidence | exists |
| K-6 | publisher claim citing evidence sha256; verify-evidence | — | attested |
| K-7 | ladder:report (L1 label) | scorer BARS[1] = "contracted"; test_appendix_c.py::test_K_7_l1_is_not_labelled_mrk_1_0; tests/golden/ladder-samples.txt | exists |
| V-1-1 | schema: mrk_version enum: ["1.0", "1.1"] | schema | exists |
| V-2-1 | gate:compat (1.0 specimen valid under 1.1; every estate instance valid under its version's schema) | gate §3 routing; test_backcompat.py::test_mrk_1_0_instances_still_validate_under_1_1, test_backcompat.py::test_declared_1_1_instances_validate_under_1_1 | exists |
| V-2-2 | gate:router (absent → 1.0) | gate §3 routing | exists |
| V-3-1 | gate:freeze fingerprint (no additionalProperties: false introduced) | gate §3c 1.1 fingerprint (fails on any new validation keyword, additionalProperties: false included) | exists |
| V-4-1 | gate:compat regression for each new minor | test_appendix_c.py::test_V_4_1_every_minor_version_is_strictly_additive_over_the_one_before (iterates every mrk-1.N.schema.json served) | exists |
| V-4-2 | schema: allOf if on mrk_version/profile; gate:freeze fingerprint | schema; gate §3c 1.1 fingerprint; test_backcompat.py::test_1_1_new_constraints_are_gated_behind_the_opt_in | exists |
| V-4-3 | schema: mrk_version enum | schema | exists |
| V-5-1 | gate:router (unknown → FAIL) | gate §3 (an unknown mrk_version fails); scorer rung(); test_hardening.py::test_unknown_mrk_version_message | exists |
| V-6-1 | gate:freeze fingerprint | gate §3c 1.1 fingerprint check | exists |
| V-6-2 | gate:freeze-1.0 (file SHA-256) | gate §3c MRK10_SHA256 check | exists |
| V-6-3 | gate:freeze-1.1 (fingerprint 01a0e632…) | gate §3c MRK11_FINGERPRINT check | exists |
| S-1 | gate:3c offline guard (no urllib, http.client, requests, socket) | spec.py reads bundled files or --spec DIR only; test_offline.py::test_no_network_modules_are_imported_by_the_kit, test_offline.py::test_each_command_works_offline | exists |
| T-1 | gate:3c (zip members byte-match) | tools/build-kit.py; test_kit.py::test_zip_is_current; gate §3c member byte-match | exists |
| T-2 | gate:3b byte-match (schema 1.1, context, shapes, answer-object) | gate §3b (schema 1.1, context, shapes, answer-object) and §3c (evidence schema) byte-match; test_review_m2.py::test_evidence_schema_requires_run_at_and_corpus_and_both_copies_are_identical | exists |
| T-3 | test: every requirement line has --hash | tools/mrk-tools/requirements.txt and tests/requirements.txt (both pip-compile --generate-hashes); test_appendix_c.py::test_T_3_every_requirement_line_carries_a_hash; CI installs both with --require-hashes | exists |
| T-4 | gate:shim (shim output equals module output on sample/) | test_kit.py::test_shim_matches_module_entry_point | exists |
Appendix D: Resolved inconsistencies
Where the design documents disagreed with each other or with the code, this specification follows the Gate B decisions (B1 maturity layer, B2 one kit, B3 corpus-bound evidence).
- Instance version "2.0". Pages describe "MRK-2.0 at L4"; the 1.1 schema's
mrk_versionenum is["1.0", "1.1"], so a "2.0" instance would score L0 in the shipped scorer. Resolved: MRK-2.0 is a standard version only; no newmrk_version(V-1-1); values "2" and up reserved (V-4-3). - Online scorer vs offline tools. The packaged scorer fetches spec files from the site by default. Resolved: no network at run time (S-1); files bundled or
--spec(T-1). - Two ladder implementations. The prototype
mrk-2.0/ladder.pyholds the whole corpus at L3 on any SHACL failure and treats pyshacl as optional; the packaged scorer attributes violations per article and requires pyshacl. Resolved: the packaged behaviour is normative (I-8, P-L4-5). - Drift and L4.
reuse-transclusion.mdputs drift-lint in the L4 gate; neither scorer runs it. Resolved: drift is part of L4's mechanical half for.mdarticles (P-L4-2). - L4 operational half. It was a bare flag. Resolved: measured by corpus-bound evidence or attested by flag, with the basis always reported (P-L4-3, P-L4-4, K-2).
- Per-corpus or per-article L4 evidence (ladder open question 3). Resolved: one run per corpus; acceptance per article by digest membership (P-L4-3, E-3).
- "No dangling
relTarget". The ladder text says SHACL rejects danglingrelTarget; the projection expands relations sorelTargetnever appears, and the shapes check onlyrequires,dependsOnandreplaces. Resolved: those three are mandatory (I-4 to I-6); other relation types are expected to resolve, with shapes to be added (I-7). - Stable ids as an L1 criterion. The ladder table lists stable ids as part of L1, but no single snapshot can show stability. Resolved: attested from revision history (A-5); not a scored criterion.
- L1 labelled "MRK-1.0 bar". L1 is checked against the 1.1 schema, which drops
plan/region. Resolved: L1 is not MRK-1.0 conformance (K-7); the label changes. The reference tool labels L1 "contracted". - Recognised types. The 1.1 schema description says
conceptandreferencegain section-shape rules; the schema has rules only forissue-resolutionandtask. Resolved: only those two have shapes (P-L2-2); the description is corrected editorially at release (V-6-1). - Type shapes vs
mrk_version. The schema enforces type shapes only undermrk_version: "1.1"; the scorer applies them always. Resolved: both stand. Schema validity is gated by version (V-4-2); the L2 criterion is not (P-L2-2). - C1 scope. The harness prints "C1 (R1-R4, R6, R7)";
retrieval-conformance.mddefines C1 as R1-R4 and R6. Resolved: R7 belongs to C2 (§10). - C1-only consumers. Parity cases call
answer_graphunconditionally, so a consumer without a graph path crashes. Resolved: C1 is graded onanswer()alone (C-L-1). - R6 "article version". No article
versionfield exists; the reference consumer citesmrk_version. Resolved without a new field:versionis the article digest or null, nevermrk_version(C-R6-3). - Graph-path citation flags.
answer_graphomitsstale/superseded. Resolved: C-R6-4. - Hard-coded date. The reference consumer fixes
TODAY = 2026-09-18. Resolved: the reference date is an input (C-R2-4) and recorded in evidence (E-7). - Absent locale. The reference consumer silently treats an absent
localeasen. Resolved: the consumer declares its default (C-R3-4). - Unevaluated dimensions. The reference consumer ignores
version*anddimensions, so such articles match every reader. Resolved: the reference consumer comparesversion*(dotted numeric segments; anything else by exact string) anddimensions(per key) with the reader context, and fails closed (C-R1-3) where the context does not answer or the values cannot be compared. - Retired articles. MRK-1.0 §4 says consumers "may" refuse retired articles; R2 says they must not serve them. Resolved: R2 governs consumers claiming C1 or C2 (C-R2-1); MRK-1.0 had no consumer conformance.
- Superseded articles (§R open question 3). Resolved: refuse
retiredandexpired; downweight and flagsuperseded(C-R2-1, C-R2-3). - Freshness in the ladder (ladder open question 1). Resolved: separate from rung (P-G-4).
- Partial L2 (ladder open question 2). Resolved: all-or-nothing, with the next-rung gap reported (P-G-2, P-G-3).
- Scope of the 1.0 → 1.1 theorem. Both schemas are open, so a valid 1.0 instance can carry a property that 1.1 later defined (for example
profile: custom) and fail 1.1. Resolved: tools accept instances under their own format version's schema (V-2-1); such an article scores L0 with the schema message as its gap; V-4-1 states additivity for instances that do not use new names. - Unknown
mrk_versionrouting.validate-machine-blocks.pyvalidates any non-"1.1" value against 1.0. Resolved: unknown values fail (V-5-1). - Fingerprint definition. The Gate B memo strips every
title/description/$commentrecursively, which would also delete the schema of the article property namedtitle. Resolved: annotation keywords are stripped only in schema position (Appendix A). - YAML dates. The scorer validates raw
yaml.safe_loadoutput, so an unquotedverified: 2026-07-30, as shown on the MRK-1.0 page, loads as a date and failstype: string; the scorer also does not assertformat: date. The repository's own samples quote dates to avoid this. Resolved: A-8. - Section id ownership. Nothing checks that a section id's prefix is its article's id, though citations and the projection depend on it. Resolved: A-6 and P-L1-5.
requiresin graph retrieval.requirescounts as an L3 edge, but the reference consumer follows onlydepends_on. Resolved: C2 follows both (C-R5-4); derived cases cover both (E-6).- Transclusion direction (reuse open question 3). Resolved: the marker's target is canonical; self-targets are forbidden (D-5). Cycles stay open (Appendix E).
- Marker syntax and metric (reuse open questions 1 and 2). Resolved: HTML comment markers (D-1) and the lexical grounding score at 0.5 (D-6); no embedding model.
- Namespace and
/kb/IRIs. The context's@basegives every adopter's articles IRIs undermachinereadyknowledge.com/kb/, and/nsis not served today. Resolved: IRIs are identifiers only and not dereferenced (N-3);/nsmust be served (N-2). - Stale status lines.
graduation-ladder.md,retrieval-conformance.md,reuse-transclusion.md,conformance/README.mdandmrk-1.1/CHANGES.mdsay "DRAFT" or "Nothing here is served" for content that #60 serves. Resolved: this document is the authority; those files carry a "rationale, superseded by SPEC.md" status line (changed at release).
Appendix E: Open issues
- Transclusion cycles (A copies from B, B copies from A). The drift gate tolerates them because canonical text excludes transcluded blocks; whether to forbid them is open.
- Evidence is unsigned. A signing scheme would need key distribution, which conflicts with offline use.
- An answer-envelope schema (the whole answer, not each citation).
- Further
r5_contractshapes: supersedes pruning and trust-tiered selection. - MRK-2.1 may require a measured L4 basis. MRK-2.0 keeps attestation so that existing results do not change.
- The recognised
typeset (howto,faq,policy) andprovenance.confidenceas an enum or a number, carried over from MRK-1.1. - Known limitation: the reference tool's CI runs on macOS arm64 with Python 3.12 and 3.14 only. The pytest job installs the hash-locked
tests/requirements.txt(--require-hashes) and the unzip-and-install step installs the kit's lock the same way. Linux, Windows and Python 3.13 are not run in CI. What is checked for them is the dependency locks:tests/test_locks.pyevaluates the metadata of every locked package for Windows, Linux and macOS under Python 3.12 and 3.14 and fails if a dependency active there is not locked. That check found one gap, now closed:pytestneedscoloramaon Windows only, sopip-compilerun on macOS leaves it out; it is pinned by hand, with asys_platform == "win32"marker and its hashes, intests/requirements.txt. This shows the locks are complete, not that the tool runs on those platforms. - Known limitation (N-2): the live route
https://machinereadyknowledge.com/nscannot be tested from the repository. The test reads the.htaccessnegotiation rules and checks that the files they name are served byte-identical todocs/; after an upload, request/nswith each Accept header to confirm the route (DEPLOY.md describes the rules). - I-7 is a SHOULD and the 2.0 shapes do not check the targets of
prerequisite_of,references,part_of,see_alsoandapplies_with; a dangling one does not block L4. Whether a later edition should add shapes, which would make a dangling optional edge an L4 blocker, is open.