KB-37BF rev 4

CP-06 Canonical Hash Encoding Ordering And Scalar Forms

12 min read Revision 4
fix7architecturecp06hash-determinism

07 - CP-06 Canonical Hash Encoding, Ordering, And Scalar Forms

Canonical Representation

Every hash payload is built from explicit keys using PG16 JSONB and then SHA-256:

digest(
  convert_to(
    jsonb_build_object(
      'domain', domain_text,
      'schema_version', schema_version_bigint,
      'payload', payload_jsonb
    )::text,
    'UTF8'
  ),
  'sha256'
)

Rules:

  • bytea/SHA-256 in payload: encode(value,'hex'), lowercase, exactly 64 ASCII hex characters; implicit bytea text cast and base64 are forbidden;
  • integers: JSONB integer form, no leading plus/zero, no exponent;
  • numeric: trim_scale(value) then JSONB numeric form; NaN/infinity and floating types forbidden;
  • boolean: JSON true/false;
  • UUID/OID: lowercase canonical UUID text / JSON integer OID;
  • timestamp: UTC via to_char(value AT TIME ZONE 'UTC','YYYY-MM-DD\"T\"HH24:MI:SS.US\"Z\"');
  • text: UTF-8, bytewise COLLATE "C" ordering; no locale-dependent comparison;
  • JSONB: explicit-key schema only; unknown/missing key fails; SQL NULL forbidden; optional value is JSON null; string "NULL" is ordinary text;
  • arrays: every jsonb_agg has an explicit total unique ORDER BY ... COLLATE "C" ending in immutable UUID/item ID; no unordered aggregate;
  • delimiter concatenation and MD5 are forbidden.

Total Orders

Manifest children (ordinal,item_id); active manifests (manifest_type_id,version_no,manifest_id); evidence (evidence_kind_id,evidence_id); dependencies (root_identity COLLATE "C",kind_id, dependency_identity COLLATE "C",item_id); dynamic targets (callsite_identity COLLATE "C", target_oid,item_id).

The following runtime aggregate orders and membership predicates are normative:

Hash key Source and membership Canonical total order Missing/empty rule
H02 signoff_binding_hashes signoff_binding where control_epoch = H02.control_epoch; each row must join manifest_activation on activation_id with the same requested_control_epoch; occupied slots must equal ACTIVE sealed signoff/quorum requirements in both EXCEPT directions (target_manifest_id, tier_id NULLS FIRST, action_id, quorum_profile_id, required_principal_class_id, slot_ordinal, reviewer_principal_id, signoff_id) Missing required, extra unknown, unresolved activation, or duplicate slot fails. Empty is allowed only when the ACTIVE sealed required-slot set is empty.
H05 measurements capability_measurement where run_id = capability_run.run_id; rows must equal the sealed measurement-requirement set in both EXCEPT directions (measurement_requirement_id, measured_at, measurement_id) Missing required or extra unknown row fails. Empty is allowed only when the sealed requirement set is empty.
H05 artifacts capability_artifact where run_id = capability_run.run_id; rows must satisfy the sealed artifact-requirement set and counts (artifact_requirement_id, created_at, capability_artifact_id) Missing required, insufficient count, or extra unknown row fails. Empty is allowed only when the sealed requirement set is empty.
H02 capability_evidence_hashes one H05 digest for every finalized capability_run where control_epoch = H02.control_epoch; every capability_id must resolve to an ACTIVE capability_manifest.item_id, and workload_profile_id must equal that row's bound workload; ACTIVE rows with required=true must each have at least one valid run (capability_id, workload_profile_id, finalized_at, run_id) finalized_at IS NULL, missing required capability run/digest, unresolved/inactive capability, or mismatched workload fails. Empty is allowed only when no ACTIVE capability has required=true and no matching finalized run exists.
H02 post_activation_verifier_state every post_activation_verifier_state row joined to manifest_activation by activation_id where requested_control_epoch = H02.control_epoch and executed_at IS NOT NULL; principal, evidence, state code, and ACTIVE authority-scope validity must resolve (activation_id, verifier_principal_id, verified_at, verifier_state_id) Every executed activation in the epoch requires at least one valid state row; any invalid/unresolved row fails. Empty is allowed only when no executed activation exists for the epoch.

All timestamps in these orders use the canonical UTC representation above. UUID ordering is by lowercase canonical UUID text under COLLATE "C". Required order fields may not be SQL NULL. Each order ends in an immutable row identity and is total even when preceding values collide. Physical insertion order, heap order, index choice, and query plan never affect a digest.

H04 Scope Hash

signoff_binding.scope_hash is SHA-256 under the canonical representation above with domain incomex.qt001.signoff-scope.v1 (H04_SCOPE_V1) and this exact ordered payload-key list:

Order Payload key Exact source
1 activation_id signoff_binding.activation_id
2 target_manifest_id signoff_binding.target_manifest_id
3 plan_content_hash signoff_binding.plan_content_hash
4 quorum_profile_id signoff_binding.quorum_profile_id
5 required_principal_class_id signoff_binding.required_principal_class_id
6 slot_ordinal signoff_binding.slot_ordinal
7 action_id signoff_binding.action_id
8 control_epoch signoff_binding.control_epoch

The payload is one explicit-key JSONB object. All eight values are required and SQL NULL fails; there is no optional signoff-scope field and no inferred/free-text scope. Including activation, target, plan, and epoch prevents a valid slot hash from being replayed into a different activation, manifest, plan, or epoch. The domain/schema identifier is a protocol version constant, not mutable policy; changing it requires a new reviewed canonicalizer version, not an adapter literal.

The owner-only generic signoff writer computes scope_hash; caller-supplied scope_hash is recomputed and must match. H04 consumes this exact stored/recomputed value. The slot-scoped UNIQUE constraint and the same-human separation guard consume the same eight-field definition. H04_SCOPE_V1 specifies the already-required signoff_binding.scope_hash sub-payload; it is not an eighth top-level hash contract. The exact top-level contract count remains H01..H07.

Exact Seven Contract Key Maps

Contract/domain Exact ordered payload keys
H01 incomex.qt001.plan-content.v1 plan_payload, policy_manifest, operator_manifest, tier_manifest, readiness_manifest_hash, capability_manifest, dependency_manifest_hash, hash_component_manifest, bootstrap_manifest, gateway_manifest, trigger_fingerprints, freeze_fingerprints, writer_source_hash
H02 incomex.qt001.control-state.v1 plan_content_hash, control_epoch, active_manifest_set, owner_acl_fingerprint, signoff_binding_hashes, capability_evidence_hashes, dependency_evidence_hashes, activation_state, post_activation_verifier_state
H03 incomex.qt001.readiness-manifest.v1 manifest_envelope, gates, policy_rules, fact_adapters, primitives
H04 incomex.qt001.signoff-binding.v1 target_id, plan_content_hash, scope_hash, tier_id, action_id, reviewer_principal_id, binder_principal_id, reviewer_human_identity_id, binder_human_identity_id, reviewer_evidence_hash, binding_evidence_hash, control_epoch, signed_at, bound_at, valid_until
H05 incomex.qt001.capability-evidence.v1 capability_manifest, measurement_requirements, artifact_requirements, workload_profile, run, measurements, artifacts, environment
H06 incomex.qt001.dependency-manifest.v1 manifest_envelope, roots, edges, source_hashes, analyzer_contract, analyzer_run, dynamic_targets
H07 incomex.qt001.activation.v1 manifest_type_code, parent_manifest_id, parent_payload_sha256, candidate_manifest_id, candidate_payload_sha256, added_set_sha256, retired_set_sha256, impact_evidence_sha256, rollback_packet_sha256, requested_control_epoch, quorum_profile_hash, requester_principal_id

H01 excludes signoff/approval/evidence/activation lifecycle times. H03 envelope excludes lifecycle timestamps. H04 permits JSON null for tier_id only when the active signoff requirement permits it. All H07 keys are required. Canonicalizer source/signature hashes and PG major version are bound in the active canonicalizer/catalog contract. A PostgreSQL major upgrade blocks activation until cross-version fixtures reproduce all expected digests.

Runtime-Evidence Column Bindings

  • H04 target_id = signoff_binding.target_manifest_id. All scalar H04 keys other than the two evidence hashes map one-to-one to named signoff_binding columns.
  • H04 reviewer_evidence_hash follows signoff_binding.reviewer_evidence_id -> evidence_registry.evidence_id -> evidence_registry.artifact_sha256.
  • H04 binding_evidence_hash follows signoff_binding.binding_evidence_id -> evidence_registry.evidence_id -> evidence_registry.artifact_sha256.
  • Missing/unresolvable evidence rows fail and invalidate signoff. Hashing either evidence UUID as evidence content is forbidden. Evidence identity is intentionally not a separate H04 key: identical evidence bytes with the same artifact_sha256 produce the same content-derived evidence hash even when registry UUIDs differ.
  • H04 excludes slot metadata from its published key list because the normative H04_SCOPE_V1 definition above hash-binds the complete activation/target/plan/slot/action/epoch scope.
  • H05 run = capability_run; measurements = capability_measurement; artifacts = capability_artifact; environment is folded into capability_run.environment_sha256/environment_evidence_id. No standalone environment table.
  • H02 signoff_binding_hashes, capability_evidence_hashes, and post_activation_verifier_state consume the exact source/membership/order rules above. The H02 signoff source uses real column signoff_binding.target_manifest_id; target_id is only the H04 payload-key alias.
  • H06 is unchanged: its inputs were already byte-defined.

The 11 runtime-evidence tables are non-authority facts. Their object/constraint identities are enumerated by sealed authority_scope_manifest #20 rows, but their values never define policy. Net new authority surfaces, readiness gates, and hash contracts are all zero.

Determinism Fixtures

Fixtures prove: identical logical bytea from different display settings yields identical hex and digest; row reorder/locale change does not change digest; numeric scale normalization is stable; timestamp timezone/session setting does not change digest; JSON null differs from string NULL; unknown/missing keys fail; each real input mutation changes the relevant digest.

Additional blocking fixtures prove:

  • H04: hashing reviewer_evidence_id or binding_evidence_id as content is rejected; a missing evidence row fails; changing artifact_sha256 changes H04; the same artifact hash under a different UUID leaves the content-derived evidence key unchanged.
  • H04 scope: reordering JSONB input keys leaves scope_hash unchanged; changing any of the eight scope fields changes scope_hash and H04; NULL in any scope field fails.
  • H05/H02 aggregates: two implementations over the same logical rows produce the same digest; insertion-order changes do not change the digest; a measured value/state/evidence mutation changes the digest; missing required rows fail; empty sets follow only their sealed expected-set rules.
Back to Knowledge Hub knowledge/dev/reports/architecture/codex-fix7-spec-artifact-correction-from-t1-proposals-2026-06-07/07-cp06-canonical-hash-encoding-order-numeric.md