Seal a review submission
Assembles the ten ruled review-package components from a declared source corpus, a TOTAL disposition ledger and a bidirectional source-to-rule map, then seals them as an immutable review subject together with their exact canonical bytes.
RECORDS which bytes a reviewer will be shown, in which named slot, under which policy version, in what order, at what time, attributed to which principal.
PROVES byte equality under meshqu-review-package/v1, and nothing else. It does not prove source truth, source completeness, correct interpretation, human review or authority.
Source custody. A manifest item naming custodian urn:meshqu:custodian:meshqu asserts that MeshQu HOLDS those bytes, and the seal refuses unless it does: the server looks for a finalised, read-back-verified object at that tenant’s content address, in the same transaction that seals, and returns source_item_not_finalised_in_custody if there is none or source_custody_attestation_disagrees_with_manifest if the digest or length disagrees. Custody is MeshQu-controlled and tenant-scoped; upload the bytes through /v1/source-uploads first. Items naming any other custodian are unchecked here — MeshQu resolves no external locator and makes no claim about a custodian’s store.
The availability field sealed into the manifest stays NOT_CHECKED in every case, deliberately: availability is a CHECKED OBSERVATION made at a moment, and sealing one would freeze a time-varying fact into an immutable subject. Current availability is reported separately by GET /v1/review-submissions/{submissionId}?check_source_availability=true.
Resubmission creates a NEW subject. Posting identical bytes again returns a different id with the next submission_sequence; it never returns or updates the earlier one. A judgment recorded against one subject can never be applied to another.
Assembly failures return 422 carrying every violation found, each with its own distinct code (unit_without_disposition, mapped_unit_without_rule, internal_control_claims_source_mapping, and the rest of the closed set) and the offending identifier, so a defect class is diagnosable rather than reported as “validation failed”.
This route ratifies nothing, activates nothing and records no decision. It is not a receipt and no receipt binds its output.
Authorizations
MeshQu API key passed as a bearer token: Authorization: Bearer mqu_…. Mint one in the console (Settings → API keys).
Tenant UUID for multi-tenant isolation. Required on all authenticated routes — validated before authentication (middleware/tenant.ts), so a missing or non-UUID header returns 400 (MISSING_TENANT_ID / INVALID_TENANT_ID) before the API key is checked.
Path Parameters
Policy id.
Policy version number.
x >= 1Body
Everything needed to assemble the ten ruled component slots. Four slots — the corpus (source_manifest_items / source_units), clause_ledger, candidate_policy and source_rule_map — must be genuinely present: a source-bound review package with no corpus, ledger, candidate or map is not one, and every totality and bidirectionality rule over it would be vacuously true.
Everything needed to assemble the ten ruled component slots. Four slots — the corpus (source_manifest_items / source_units), clause_ledger, candidate_policy and source_rule_map — must be genuinely present: a source-bound review package with no corpus, ledger, candidate or map is not one, and every totality and bidirectionality rule over it would be vacuously true.
The candidate policy as projected into the package. Any field beyond candidate_rules is carried VERBATIM into the component and therefore into its digest — the assembler adds nothing and removes nothing.
The bidirectional source-to-rule map. BOTH directions are stored and must be exact transposes. Storing one and deriving the other would make disagreement impossible to observe: two directions that must agree is a check, one direction plus a derivation is a restatement.
Independently authored cases and expected outcomes. Carried verbatim into the digest.
The displayed server/tool validation results. Separated from case_pack deliberately, so changing a RESULT changes the review subject rather than silently reusing the digest of the expectations alone.
The displayed case-execution results. Separated from case_pack for the same reason.
The model trace, as shown. Carried verbatim into the digest.
The source_manifest_items of the immediately preceding sealed submission, when one exists. Present ONLY so "the bytes moved while the declared identity stayed still" is detectable. It is not part of any component and never enters a digest.
Response
Default Response
The submission’s own identity — distinct per submission even when the policy version and the bytes are unchanged (ruling B1).
The mutable workflow container the candidate came from. NOT the review identity: one policy version yields many sealed subjects over time.
A server-derived counter of DISTINCT CONSECUTIVE active-rule projections sealed for this policy version, starting at 1. It increments when the new submission’s active_rules_hash differs from the previous submission’s, and carries forward when it does not. Named for what it counts: meshqu.policy_versions holds no draft revision counter, so this is NOT a count of author edits and does not move for an edit that leaves the active-rule projection unchanged (a renamed rule, a changed description, a different case pack). Two submissions may therefore share a candidate_revision and still be different subjects — they are told apart by id and review_package_digest, which is why the currentness check has three legs.
x >= 1Submission ordinal within the policy version, from 1. An ORDERING fact, not an identity — id is the identity.
x >= 1sha256(UTF8("meshqu-review-package/v1") || 0x00 || UTF8(canonicalJsonV2(envelope))). RECORDS which exact canonical bytes the reviewer was shown. PROVES byte equality under that named profile and nothing else — not source truth, completeness, correct interpretation, human review or authority. NOT a receipt field.
^[0-9a-f]{64}$The ten ruled component digests, keyed by component name. A closed registry: an eleventh key is invalid and a missing key is invalid.
sha256(canonicalJson(projectActiveRules(rules))) under meshqu-canonical/v0 — NOT v2, and deliberately OUTSIDE the ten component slots (ruling D2), so that comparing it against a recomputation from the product path is a real check rather than one artefact validating itself. It means "this is the projection the evaluator will run", which is a different claim from the package digest’s "these are the bytes a human read". Neither implies the other.
^[0-9a-f]{64}$When the seal was created. Not when a human read it.
The principal the server attributed the submit act to. Records attribution; proves nothing about human authorship, competence or independent judgment.