30-second answer

A Run is one resource evaluated: always a file. A RunSet is the evaluation of many: always a directory. An Assessment is what a person archives and sends: the RunSet plus the evidence, the versions, the manifest and the digests, split into a canonical half that is hashed and a derived half (the human reports) that is not. A Comparison relates two Assessments. The assessment_id derives from the canonical digests, so the same inputs produce the same identity, on any machine, at any hour.

What this proves

  • That the archive is self-verifying. verify recomputes the canonical digests and compares; it is deliberately independent of build, so the code that writes an Assessment is never the code that vouches for it.
  • That identity follows content. Removing one field from the evidence inside the engine's three-resources fixture changed the evidence digest and therefore the assessment_id. An assessment whose evidence changed is a different assessment, by construction.
  • That reports can be regenerated. Only the canonical half is hashed. A derived report can be re-rendered, translated or rebranded without touching the identity of what it describes.

What it does not prove

  • When it was really made. created_at is an input to the build, not a clock read, precisely so that the same inputs produce the same bytes. The timestamp is a claim carried inside the verified envelope, not a fact the envelope proves.
  • That the evidence was true. The digests bind the report to the evidence; the evidence's own provenance is what binds it to a tenant.

PowerShell

PowerShell
# Build and independently verify an Assessment from a RunSet
m365-governance assess --run-set .\runs\classification `
    --created-at 2026-08-10T09:00:00Z --out .\assessment
m365-governance verify .\assessment

Example output

Text
assessment_id : a-bdd9082a
canonical     : verified (manifest, run set, evidence, versions, hashes)
derived       : reports (not part of identity)

Explanation

The split answers a question auditors actually ask: which bytes am I trusting? The canonical half is the answer, and it is small and mechanical. Everything a reader touches, the rendered report, lives on the derived side, where it can be wrong, regenerated or improved without forging anything. Two guard rails keep the model honest: duplicate evidence for one resource raises an error rather than averaging, and a Comparison refers to Assessments by id plus canonical digest, never by path, refuses to compare two different tenants, and refuses an assessment that does not verify. Attribution inside a Comparison is never marked established without factors and a method, and an outcome may not be listed among its own causes.

Production considerations

  • Archive the Assessment directory, not the report PDF. The report is derived; the directory is the thing verify can defend years later.
  • If your pipeline stamps created_at from the build clock, two identical evaluations will produce two different artifacts and your dedup story dies. Pass the timestamp in.
  • SPO-CLASS-001, SPO-CLASS-003: the rules inside the engine's real classification Assessment fixture, built by the engine from a sanitized observation

References

Found something wrong? Suggest a correction. The article source is not public; the engine it cites is.