Mandate signed record
=====================

Harbour Square Legal LLP, 19 Oct 2026, 17:36 to 18:17 America/Toronto.
Made and signed 19 Oct 2026, 18:20 America/Toronto.

report/report.pdf is the printed report for this window. register/decisions.json lists every decision
and the rules that applied to it.

To check that nothing changed since signing, drop this .zip on mandateco.ca/verify.html or run
mandate-verify.mjs from that page, and compare the signing key's fingerprint:
9a914bf1310855175c7d868ad434987075412f627b2e8c18e5634e0bfcba045f

The sections below are for auditors and for anyone checking the files by hand.

PACK DETAILS: tenant id 165cb232-e69d-4385-add7-f8e392a8398d. The window in UTC, as
manifest.json carries it: 2026-10-19T21:36:57.0000000+00:00 to 2026-10-19T22:17:17.0000000+00:00.
Made and signed at 2026-10-19T22:20:17.2844557+00:00.

SCOPE: In-path evidence only. This pack covers the decisions and policy changes that
passed through Mandate for the selected window. It is NOT an inventory of all AI use in
your organization. The gaps this tenant has declared are listed in scope/blind-spots.json:
every blind spot recorded as open as of the generation time above, with its owner,
compensating control, and review cadence. Blind spots are current state, not windowed.
summary/decision-mix.json breaks the window's decisions down by connector and retention
class, so the pack states how much of its evidence is content-inspected and how much is
metadata-only, and its responses block counts the chat answers of requests not held for approval: digested,
retained, inspected, rewritten or withheld. register/purposes.json lists the purpose registry (slug and display label,
including retired entries) as of the same generation time; register/decisions.json refers
to purposes by slug.

REPORT: report/report.pdf is a printable summary of this window for readers who will not open
the JSON: what happened to the requests, the rules that fired most, the requests held for
approval, the state of the record, and how to check the pack. report/summary.json carries the
same figures, each naming the file it is re-derived from.

RULE BASIS: summary/decision-mix.json also separates the window's decisions by rule basis:
binding, voluntary, or unlabeled. Basis is the tenant's own label on its own rule, derived
from the policy version in force when each decision was made; rules carrying no label count
as unlabeled, and the absence of a label is never treated as either claim. A decision that
fired rules of both kinds counts under both buckets, so basis counts can exceed the
per-decision totals. An outcome under a voluntary rule is a house-standard outcome and is
never presentable as a breach of a binding obligation.

FRAMEWORKS: summary/framework-coverage.json lists, for each policy, the framework
references the tenant's authors recorded on its active rules (for example an EU AI Act
article or an OWASP item), and separates the window's decisions by those references. The
mapping reflects the active policy as of the generation time above; the decision breakdown
follows the policy version in force when each decision was made. A reference is the
tenant's own annotation on its own rule: Mandate records it and verifies nothing about it,
and nothing in the file is a statement that any framework obligation is satisfied.
Decisions under rules carrying no reference count as unmapped, and the absence of a
reference is never treated as a gap.

ATTACHMENTS: summary/attachments.json counts the window's decisions by the kind of attachment
the request carried (image, audio, file, or unknown for a part the inventory could not recognise),
as recorded on each decision by the attachment inventory, and counts the attached documents
whose text was read and those that could not be.
It names the earliest inventoried decision this tenant holds and whether the inventory covered
the whole window, part of it, or none of it; decisions made before the inventory existed are
never counted as attachment-free. Images and audio are recorded, never inspected.

MANDATES: register/mandates.json lists the tenant's mandate registry as of the same
generation time: each recorded authority grant with its owner, effective window, declared
scope, and lifecycle state, including revoked and expired grants. A decision row's
mandateSlug names the mandate the presented credential was bound to at write time,
whatever the decision was. On an allowed decision, the reference supports 'executed
under this grant'. A decision denied FOR its mandate says so explicitly: it is a blocked
decision whose rule hits carry the policy.mandate_not_valid marker (or, on a resumed hold,
policy.resume_mandate_not_valid); a blocked decision without that marker was blocked by
policy, and the reference only records which grant the credential operated under. It
does not show the grant was wise. A call outside the grant's scope (a consequential tool
class or a purpose the grant does not list) carries a policy.mandate_scope_drift marker,
_held when it was held for approval, _blocked when it was refused (ADR-0062).
Rows without the field were recorded by a credential bound to no mandate.
AGENT EXPOSURE: summary/agent-exposure.json answers which agents, under whose authority, doing
what, with what oversight. It counts the agents active and revoked at the end of the window; the
live credentials agents act with, bound to a valid mandate, bound to none, or bound to one that
is revoked, expired or not yet in effect, as of generation; the holds raised for agents in the
window by channel and how they ended (approved, denied, or expired with nobody deciding); the
agents registered and revoked in the window; and the decisions flagged, held or refused for
being outside an agent's mandate, with the date that check began and whether it covered the
whole window. A tenant with no agents carries the file with zeros. Counts, never judgments.

PEOPLE: a row in register/decisions.json may carry a person field: the work email or user name
a person signed in to the AI web gateway with, as this tenant issued it. It is present only on
requests sent through that gateway by a credential the tenant issued to that person, and absent
on every other row. The hash chain fixes a salted seal of that name, and the row's personSalt
lets the verifier recompute it, so a name changed after the fact no longer matches its record;
erased on request, the name and its salt go and the seal left behind reveals nothing. The
person is the holder of a credential the tenant's administrator issued and
saw once at issue, so the field attributes a request to that credential's holder, not to a
verified individual, and a shared credential attributes to its holder.

IMPORTED ACTIVITY: imports/<feed>.json, when present, lists what an AI vendor reported through its own
audit feed (for example Microsoft 365 Copilot). Mandate did not see or decide these requests; each row
carries the SHA-256 of the vendor's record as received, and the file states how much of the window
the feed covered. register/decisions.json and the decision counts never include these rows.

CHANGE TRAIL: mutations/policy-trail.json lists every recorded administrative action in the
window: policy changes alongside the other configuration changes and access-view records the
operator log holds. A policy activation or deactivation row carries a transition record
stating why the prior rule set went away (superseded, repealed, or an emergency change), the
operator's typed reason, and the version in force before the change. The record is written
to an append-only operator log at the moment of the change and is not editable afterward.
Within about a minute it is also sealed into the same hash chain as the decisions: a record
with connector change_trail whose correlationId is the entry's id carries the SHA-256 of what
the row says, who acted as they stood when it was sealed, and why. A row's actorSalt lets the
verifier recompute the seal of the actor's e-mail. Only how a policy version differs from the
one before is left out of the seal: each version's own text and hash carry that.
Rows recorded before transition records shipped carry no transition field.
A row whose actorType is mandate_support records an action taken on this tenant by Mandate
as the operator of the service; it carries no personal identity by design. Quote that row's
id to Mandate as the support reference and Mandate can account for the action from its own
records. A mutationReason field, where present, is the reason the operator typed.
A row whose actorType is system is a change Mandate made by itself, such as an alert sent, a
held request that ran out or a retention sweep; one whose actorType is directory was made by
the tenant's own directory through directory sync. Neither is an operator action and neither
names a person.

ASSURANCE: assurance/detector-catch-rate.json states how the shipped detector library
measured against Mandate's own synthetic, annotated test corpus at the accepted baseline
run named inside the file. It describes the software build, not this tenant: the same file
ships in every pack and contains no tenant data. It is not a measurement of your authored
policy, of live traffic, or of model behaviour. Detection is signal-grade, never the control,
and a recorded catch rate does not verify behaviour in production. A detector shipped
before its first accepted baseline run is listed with an explicit not-yet-baselined status
rather than a number. A detector that matches the attachments a request carried rather than
its text is named out of scope under the library, because no text corpus can measure it.

THE RECORDS: chain/links.json carries every audit record in the sealed windows this pack
covers, in chain order, as the exact bytes Mandate hashed (record, base64). A record removed
under a privacy request or by retention appears by its recorded hash only, with erasedBy naming
the entry that recorded its erasure; chain/erasures.json carries those entries, whenever they
were made, and the verifier refuses a removal no entry recorded. Each record names
the hash of the one before it; each checkpoint's signed Merkle root recomputes from the
records in its window; each decision in register/decisions.json names its record's hash
(recordSha256); and policy/versions.json carries each policy version's text with its SHA-256,
which every record it decided carries. The free verifier checks all of it offline. When the
file says truncated, it carries part of the records: it stops at its size cap, and when the
register was cut to its row cap, it starts at the sealed window of the register's oldest
decision. Checkpoints whose records it does not carry are reported as not covered.

LISTS OF NAMES: a rule's list of names, a client list for example, appears in the policy text
as a seal and a count, never as the names. The seal is the SHA-256 of the names with a random
salt, so it reveals nothing about them and a guessed name cannot be checked against it, yet any
change to the list changes it. policy/lists.json names every sealed list. When the export
includes the names, it also carries each list's names and salt, and the verifier recomputes
every seal from them and finds it in its policy version's text.
This export leaves the names out. Your administrator can export the same period with them.

Re-verify offline: each file's SHA-256 is listed in manifest.json, and the manifest carries
an Ed25519 signature over that hash list (plus the pack identity, the export window, and the
truncated / checkpointsTruncated / mutationsTruncated completeness flags). Recompute the hashes
and verify the signature with the embedded public key. Confirm its SHA-256 fingerprint matches
the one above and the one on the checkpoints you already trust. No network access is required.
Checkpoints under checkpoints/ verify independently against the same audit chain.
