# Deployment Evidence Profile (DEP) v1 — Specification

**Status:** DOCUMENTED (spec) + IMPLEMENTED (reference module `src/deploymentProfile.ts`, additive fields in `ScopeDeclaration`, profile-aware validator in `src/dbd.ts`) · **Backward compatibility:** guaranteed additive.

## 1. Why this layer exists

The Decision Boundary Disclosure (DBD) mechanism as originally shipped grades a receipt's coverage against a **sector schema** — a broad catalogue of every domain that *could* be relevant in that sector. That is a good starting point but it is too coarse for the real world: a psychiatric ward and a cardiology ward are both "Healthcare" yet a receipt from either should not be graded against the union of both catalogues.

The DEP layer inserts a per-deployment specialisation between the sector schema and the receipt:

```
Sector Schema  →  Deployment Evidence Profile  →  Receipt scope_declaration
```

- **Sector schema** — "which domains might be relevant in this sector?" (Signatrust-maintained.)
- **Deployment Evidence Profile** — "for this deployment, which are `required`, `optional`, or `not_applicable`, and why?" (Customer-maintained, immutable-versioned.)
- **Receipt `scope_declaration`** — "for this individual decision, which of the profile's applicable domains were evaluated?" (Per-decision, signed into the body.)

Coverage classification is then computed against the profile's applicable set, not the raw sector schema. This directly addresses the review question raised in the recent architecture discussions: *what context was actually taken into account for this specific class of decision at this specific deployment?*

## 2. Non-goals

- The DEP layer does **not** claim to prove that the profile itself covers every real-world factor.
- It does **not** grant "full" coverage automatically. Downgrading a schema-declared `core` domain to `optional` or `not_applicable` in a profile is a hard validation error.
- It does **not** replace the sector schema. It is a strictly additive specialisation and receipts issued without a profile pin continue to be validated exactly as before.

## 3. Schema-side additions (backward compatible)

Sector-schema domains gain two optional fields (default preserves current semantics):

```json
{
  "domain_id": "ecg_result",
  "description": "…",
  "applicability": "conditional",
  "applicability_rules": [
    {
      "rule_id": "required_for_cardiac_decision_profiles",
      "required_if": "service_type = cardiology"
    }
  ]
}
```

- `applicability` — `"core" | "conditional" | "deployment_specific"`. When absent, defaults to `"core"`. Every existing sector schema therefore continues to behave exactly as before: all domains are treated as `core` and no profile can downgrade them.
- `applicability_rules` — advisory. Drives adequacy *warnings* when a profile marks a conditional domain as `not_applicable` while its `deployment_context` matches a `required_if` predicate. Warnings do not fail validation.

The supported `required_if` grammar is intentionally minimal:

```
key = value
key in [v1, v2, v3]
```

Unrecognised syntax evaluates to `false` (fail-safe: it never raises a warning it cannot justify).

## 4. Deployment Evidence Profile shape

```json
{
  "deployment_profile_id": "hospital-a-cardiology.v1",
  "version": "1",
  "sector_schema_id": "medical_diagnosis_support.v1",
  "sector_schema_hash": "sha256:…",
  "title": "Hospital A — Cardiology Diagnostic AI",
  "deployment_context": { "service_type": "cardiology", "region": "EU" },
  "domains": [
    { "domain_id": "symptom_pattern_match", "status": "required" },
    { "domain_id": "ecg_result",            "status": "required" },
    { "domain_id": "psychiatric_assessment","status": "not_applicable",
      "reason": "service_not_provided" }
  ]
}
```

- `deployment_profile_id` — stable id including a version suffix (`.v1`, `.v2`, …).
- `sector_schema_hash` — MUST match the current registered hash of `sector_schema_id`. Prevents accidental drift.
- Every `status: "not_applicable"` entry MUST carry a structured `reason` from the vocabulary: `service_not_provided`, `regulatory_exemption`, `out_of_scope_for_this_use_case`, `data_source_unavailable_at_deployment`, `other`.

### 4.1 Versioning and immutability

A DEP is **versioned and pinned**, not perpetually immutable. If the deployment's real-world scope changes (new device, new procedure, regulatory update), the correct move is to publish a new `deployment_profile_id` (or bump `version`) and start pinning receipts to it. The old profile file MUST remain retrievable so historic receipts continue to describe the profile that was in effect when they were issued.

Any semantic edit to an existing pinned file changes its hash → the pin mismatches → receipts pinned to the old hash still verify against the archived old file, and new receipts pinned to the new hash reference the new file. The chain is preserved either way.

## 5. Receipt-side additions (backward compatible)

`ScopeDeclaration` gains two optional fields:

```jsonc
{
  "schema_version": "1.0",
  "sector_schema_id": "medical_diagnosis_support.v1",
  "sector_schema_hash": "sha256:…",
  "deployment_profile_id": "hospital-a-cardiology.v1",        // NEW, optional
  "deployment_profile_hash": "sha256:…",                       // NEW, optional
  "domains_evaluated": [ … ],
  "domains_excluded":  [ … ],
  "coverage_classification": "full",
  "disclosure_statement": "…"
}
```

- Both fields are optional. Legacy receipts that omit them canonicalise byte-for-byte identically (guaranteed by the deterministic canonicalizer's undefined-drop rule and confirmed by a regression matrix row).
- Both fields must be supplied together (a receipt with only one is rejected).

## 6. Validation rules

### 6.0 Backward-compatibility contract (non-negotiable)

The presence of `deployment_profile_id` in a receipt's `scope_declaration` is the **only switch** that activates profile-aware validation. Every other consequence of the DEP layer is dormant until that switch is set.

```
if no deployment_profile_id:
    use legacy sector-schema DBD validation exactly as before
    (universe = full sector-schema domain set; wording unchanged;
     applicability tags on the schema are IGNORED on this path)

if deployment_profile_id exists:
    validate sector schema
    validate deployment profile against sector schema
    validate receipt scope against the pinned deployment profile
    (universe = profile's required ∪ optional)
```

Two consequences follow that are enforced by dedicated regression rows (see `dep-compat` category in `scripts/regression-matrix.ts`):

- **Legacy schema + legacy receipt → byte-for-byte identical validation outcome.** In particular, `disclosure_statement` retains its pre-DEP wording ("all recognized domains", "every domain in this schema version was addressed"). Because that string is signed into the receipt body, any drift here would be a hard break.
- **New sector-schema `applicability` tags MUST NOT retroactively change validation of a receipt that omits `deployment_profile_id`.** A schema author may add `applicability: "conditional"` to a domain later; a receipt that does not pin a profile still needs that domain in either `domains_evaluated` or `domains_excluded`, exactly as before.

### 6.1 Deployment Evidence Profile

1. `sector_schema_id` must resolve in the sector-schema registry.
2. `sector_schema_hash` must equal the current registered hash of that schema.
3. Every sector-schema domain MUST appear in the profile's `domains[]` (silent omission is not allowed). Extra ids are rejected.
4. Every `status: "not_applicable"` MUST carry a structured `reason`.
5. No sector-schema `core` domain may be downgraded to `optional` or `not_applicable` — this is what stops strategic under-scoping.

Adequacy report (advisory, does not fail validation): counts of required / optional / not_applicable, list of unjustified exclusions, and rule-based adequacy warnings.

### 6.2 Receipt `scope_declaration` under a pinned profile

When a receipt supplies `deployment_profile_id` + `deployment_profile_hash`:

1. Both fields required together.
2. The profile must resolve, its hash must match, and its `sector_schema_id` must equal the receipt's `sector_schema_id`.
3. Coverage universe = profile's `required ∪ optional`. Every domain in that universe MUST appear in either `domains_evaluated` or `domains_excluded`.
4. Domains marked `not_applicable` in the profile MUST NOT appear in the receipt at all (they are implicit exclusions).
5. `coverage_classification`:
   - `full` — every applicable domain was evaluated.
   - `partial` — ≥ 50% of the applicable domains were evaluated.
   - `minimal` — < 50%.
   - `undeclared` — applicable universe is empty (edge case).

When the receipt does NOT supply a profile pin, the validator falls back to the original schema-only rule: `domains_evaluated ∪ domains_excluded = full sector-schema domain set`. This is what preserves 100% backward compatibility for every receipt already issued.

## 7. Adequacy vs coverage — a design principle

The DEP layer deliberately separates two concepts that the flat DBD used to conflate:

- **Profile adequacy** — is the profile itself reasonable for its sector? (Measured by the adequacy report.)
- **Receipt coverage** — did this individual receipt address everything the profile requires? (Measured by `coverage_classification`.)

A customer can build a weak profile with many `not_applicable` domains, but the adequacy report will expose that separately from the receipt's coverage class. This means "full" on a receipt is not automatically reassuring — a reviewer must also inspect the pinned profile's adequacy.

## 8. What the offline verifier checks

- Re-computes `deployment_profile_hash` if the profile file is supplied to the verifier and compares against the receipt's pinned hash.
- Re-runs the universe rule (evaluated ∪ excluded = profile's applicable set).
- Does NOT auto-download the profile from `signatrust.net`. The customer is expected to archive the profile alongside the receipts.
- Reports the adequacy report as `INFO` (not PASS/FAIL) — the same receipt can be cryptographically valid while its profile is adequacy-weak.

## 9. What Signatrust does not claim

- Signatrust does not decide whether a deployment profile is *legally* adequate.
- Signatrust does not enforce which applicability rules a sector schema must carry — schema authors are responsible for their own rule design.
- The presence of a pinned profile does not prove completeness in the general sense: it proves that *within the applicable universe declared by the pinned profile at that time*, the receipt disclosed evaluation vs exclusion for every domain.
