{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://twinethos.com/schemas/pack-v0.3.schema.json",
  "title": "TwinEthos Pack Manifest (v0.3)",
  "description": "Distribution manifest for a pack (one instrument's rules). v0.3 adds: per-rule source-anchor + condition coverage, source-license exposure roll-up, canonical-condition coverage, promotion/review model, and typed relationship-integrity summary. Generated by tools/generate_manifests.py; every field is derived from the live rule/source/condition files.",
  "type": "object",
  "required": [
    "pack_id",
    "schema_version",
    "pack_version",
    "name",
    "description",
    "publisher",
    "distribution",
    "currency",
    "coverage",
    "rules"
  ],
  "additionalProperties": false,
  "properties": {
    "pack_id": {
      "type": "string",
      "pattern": "^[a-z0-9]+(?:[._-][a-z0-9]+)*$",
      "description": "Stable unique pack identifier. Never changes once published."
    },
    "schema_version": {
      "type": "string",
      "const": "0.3",
      "description": "Pack manifest schema version."
    },
    "rule_schema_version": {
      "type": "string",
      "description": "Rule schema version every rule in this pack validates against. Lets a consumer check compatibility before loading.",
      "default": "0.2"
    },
    "pack_version": {
      "type": "string",
      "pattern": "^\\d+\\.\\d+\\.\\d+$",
      "description": "Semver of the pack. MAJOR = rules removed or meanings changed incompatibly; MINOR = rules added; PATCH = wording/detection refined."
    },
    "name": {
      "type": "string",
      "minLength": 3
    },
    "description": {
      "type": "string",
      "minLength": 10
    },
    "instruments": {
      "type": "array",
      "description": "Which instruments this pack encodes. Usually one; may be several for a themed pack.",
      "items": {
        "type": "object",
        "required": [
          "slug",
          "short_name",
          "instrument_version"
        ],
        "additionalProperties": false,
        "properties": {
          "slug": {
            "type": "string",
            "description": "Registry slug, e.g. 'eu-ai-act'."
          },
          "short_name": {
            "type": "string"
          },
          "instrument_version": {
            "type": "string",
            "description": "Version/date of the source instrument encoded here."
          },
          "source_text_license": {
            "type": "string",
            "enum": [
              "A",
              "B",
              "C",
              "S"
            ]
          },
          "official_url": {
            "type": "string",
            "format": "uri"
          }
        }
      }
    },
    "publisher": {
      "type": "object",
      "required": [
        "name"
      ],
      "additionalProperties": false,
      "properties": {
        "name": {
          "type": "string"
        },
        "contact": {
          "type": "string"
        },
        "url": {
          "type": "string",
          "format": "uri"
        }
      }
    },
    "distribution": {
      "type": "object",
      "description": "Commercial and licensing terms for THIS PACK'S DATA (distinct from the source text's licence).",
      "required": [
        "tier",
        "data_license"
      ],
      "additionalProperties": false,
      "properties": {
        "tier": {
          "type": "string",
          "enum": [
            "open",
            "free_registered",
            "commercial",
            "internal"
          ],
          "description": "open=public, no account; free_registered=free with signup; commercial=paid subscription; internal=not for distribution."
        },
        "data_license": {
          "type": "string",
          "description": "Licence covering our authored content, e.g. 'CC-BY-4.0', 'Proprietary — subscription', 'Apache-2.0'."
        },
        "contains_verbatim_source_text": {
          "type": "boolean",
          "description": "True if any rule carries provenance.verbatim_text. Consumers and legal review need this at a glance — it is only ever true for Tier A/B sources."
        },
        "requires_byo_text": {
          "type": "boolean",
          "description": "True if the pack references Tier C standards the consumer must license separately. The bring-your-own-text case."
        },
        "redistribution_allowed": {
          "type": "boolean"
        },
        "notice": {
          "type": "string",
          "description": "Standing consumer notice for this pack's data (e.g. not legal advice; review status). Rendered by every consumer surface."
        }
      }
    },
    "currency": {
      "type": "object",
      "description": "THE COMMERCIAL PROMISE. What date the encoded law is current as of, and when it will be checked again. Subscribers pay for this field staying fresh.",
      "required": [
        "current_as_of"
      ],
      "additionalProperties": false,
      "properties": {
        "current_as_of": {
          "type": "string",
          "format": "date",
          "description": "The date on which the source instruments were last verified against their official source."
        },
        "last_reviewed": {
          "type": "string",
          "format": "date"
        },
        "next_review_due": {
          "type": "string",
          "format": "date"
        },
        "review_cadence_days": {
          "type": "integer",
          "minimum": 1
        },
        "known_pending_changes": {
          "type": "array",
          "description": "Amendments, rulemakings, or litigation known to be in flight that will affect this pack. Honest forward-looking signal.",
          "items": {
            "type": "object",
            "required": [
              "description"
            ],
            "additionalProperties": false,
            "properties": {
              "description": {
                "type": "string"
              },
              "expected_date": {
                "type": "string"
              },
              "affects_rules": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "source_url": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      }
    },
    "coverage": {
      "type": "object",
      "description": "Audit-grade honesty about scope: what we encoded, and what we deliberately did not. Prevents a consumer assuming a pack is exhaustive when it is intentionally filtered to AI-integration, code-detectable obligations.",
      "additionalProperties": false,
      "required": [
        "scope_statement",
        "provisions_reviewed",
        "obligations_encoded",
        "obligations_out_of_scope",
        "obligations_deferred",
        "obligations_not_applicable",
        "source_inventory_complete",
        "completeness"
      ],
      "properties": {
        "scope_statement": {
          "type": "string",
          "description": "Plain statement of the filter applied, e.g. 'AI-integration obligations detectable in code or repo artifacts only'."
        },
        "obligations_encoded": {
          "type": "integer",
          "minimum": 0
        },
        "obligations_out_of_scope": {
          "type": "integer",
          "minimum": 0
        },
        "obligations_deferred": {
          "type": "integer",
          "minimum": 0
        },
        "obligations_not_applicable": {
          "type": "integer",
          "minimum": 0
        },
        "provisions_reviewed": {
          "type": "integer",
          "minimum": 0
        },
        "source_inventory_complete": {
          "type": "boolean",
          "description": "True only when each source artifact represented by this pack has a complete-for-scope provision inventory."
        },
        "disposition_counts": {
          "type": "object",
          "additionalProperties": {
            "type": "integer",
            "minimum": 0
          },
          "description": "Derived count of source provision dispositions used to produce the coverage summary."
        },
        "out_of_scope_examples": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Named obligations deliberately excluded and why, e.g. 'Art.37 appoint a DPO — organisational duty, no code surface'."
        },
        "completeness": {
          "type": "string",
          "enum": [
            "complete_for_scope",
            "partial",
            "draft"
          ],
          "description": "complete_for_scope = every in-scope obligation of the instrument is encoded."
        }
      }
    },
    "extends": {
      "type": "object",
      "description": "Baseline + delta model. Lets a jurisdiction pack inherit a baseline pack and express only its differences — the answer to ~20 US state privacy laws overlapping ~85%.",
      "required": [
        "pack_id",
        "pack_version"
      ],
      "additionalProperties": false,
      "properties": {
        "pack_id": {
          "type": "string"
        },
        "pack_version": {
          "type": "string",
          "description": "Semver range or exact version of the baseline this delta targets."
        },
        "relationship": {
          "type": "string",
          "enum": [
            "jurisdiction_delta",
            "sector_delta",
            "extension"
          ],
          "description": "How this pack relates to its baseline."
        },
        "overrides": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Rule ids from the baseline that this pack replaces."
        },
        "excludes": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Rule ids from the baseline that do not apply here."
        }
      }
    },
    "rules": {
      "type": "array",
      "minItems": 1,
      "description": "Every rule in the pack, with its file, version, and content hash. The hash is what lets a consumer verify the pack was not altered in transit.",
      "items": {
        "type": "object",
        "required": [
          "id",
          "path",
          "rule_version"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string"
          },
          "path": {
            "type": "string",
            "description": "Path relative to the pack root."
          },
          "rule_version": {
            "type": "string",
            "pattern": "^\\d+\\.\\d+\\.\\d+$"
          },
          "content_hash": {
            "type": "string",
            "description": "sha256 of the rule file. Integrity + change detection."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "deprecated",
              "experimental"
            ],
            "default": "active",
            "description": "deprecated = superseded but retained so existing configs don't break."
          },
          "deprecated_by": {
            "type": "string",
            "description": "Rule id that replaces this one."
          },
          "condition_ref": {
            "type": "string",
            "description": "The canonical condition this rule normalizes to (for condition-coverage roll-up)."
          },
          "provenance_status": {
            "type": "string",
            "enum": [
              "memory_authored_draft",
              "primary_source_derived",
              "tier_c_citation_only",
              "indicative_unverified",
              "legal_reviewed",
              "publisher_authored"
            ],
            "description": "Per-rule provenance/promotion state."
          },
          "source_artifact_ids": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Source artifacts this rule anchors to."
          },
          "source_text_license": {
            "type": "string",
            "enum": [
              "A",
              "B",
              "C",
              "S"
            ],
            "description": "Highest source-license class this rule exposes (A=gov edict verbatim ok, B=incorporated standard, C=sold/licensed, text not stored)."
          },
          "anchor_count": {
            "type": "integer",
            "description": "Number of source anchors on this rule."
          },
          "relationship_targets_resolved": {
            "type": "boolean",
            "description": "True if every non-external relationship target on this rule resolves locally."
          }
        }
      }
    },
    "summary_stats": {
      "type": "object",
      "description": "DERIVED by the validator from the rule files. Cached here so a consumer can assess a pack without parsing every rule.",
      "additionalProperties": false,
      "properties": {
        "rule_count": {
          "type": "integer",
          "minimum": 0
        },
        "by_rule_kind": {
          "type": "object",
          "additionalProperties": {
            "type": "integer"
          }
        },
        "by_detection_shape": {
          "type": "object",
          "additionalProperties": {
            "type": "integer"
          }
        },
        "by_enforceability_tier": {
          "type": "object",
          "additionalProperties": {
            "type": "integer"
          }
        },
        "by_assessed_unit": {
          "type": "object",
          "additionalProperties": {
            "type": "integer"
          }
        },
        "ai_integration_patterns_covered": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "detection_surfaces_covered": {
          "type": "array",
          "items": {
            "type": "integer"
          }
        },
        "jurisdictions_covered": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "domains_covered": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      }
    },
    "compatibility": {
      "type": "object",
      "description": "What a consumer needs in order to use this pack.",
      "additionalProperties": false,
      "properties": {
        "min_rule_schema_version": {
          "type": "string"
        },
        "compiler_targets": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Formats this pack has been verified to compile to, e.g. ['semgrep','sarif']."
        },
        "requires_data_flow_engine": {
          "type": "boolean",
          "description": "True if the pack contains data_flow rules, which need a taint-capable scanner or an LLM consumer rather than simple pattern matching."
        }
      }
    },
    "release": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "released_date": {
          "type": "string",
          "format": "date"
        },
        "changelog_url": {
          "type": "string",
          "format": "uri"
        },
        "changes": {
          "type": "array",
          "description": "What changed in this pack version. The evergreen audit trail a subscriber reads on update.",
          "items": {
            "type": "object",
            "required": [
              "type",
              "description"
            ],
            "additionalProperties": false,
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "rule_added",
                  "rule_removed",
                  "rule_deprecated",
                  "rule_updated",
                  "law_amended",
                  "detection_improved",
                  "metadata_corrected"
                ]
              },
              "description": {
                "type": "string"
              },
              "rule_ids": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "driver": {
                "type": "string",
                "description": "What prompted the change, e.g. 'source text hash changed on 2026-09-01 (amendment)' — ties the change to evidence."
              }
            }
          }
        },
        "signature": {
          "type": "object",
          "description": "Detached signature over the manifest, so a consumer can verify authenticity.",
          "additionalProperties": false,
          "properties": {
            "algorithm": {
              "type": "string"
            },
            "value": {
              "type": "string"
            },
            "public_key_url": {
              "type": "string",
              "format": "uri"
            }
          }
        }
      }
    },
    "review": {
      "type": "object",
      "description": "Pack-level chain of custody, above the per-rule review field.",
      "additionalProperties": false,
      "properties": {
        "review_status": {
          "type": "string",
          "enum": [
            "source_derived_draft",
            "legal_reviewed",
            "published"
          ],
          "description": "Promotion state. source_derived_draft = anchored to primary source but not legally reviewed; legal_reviewed = passed named-reviewer legal gate; published = releasable."
        },
        "reviewed_by": {
          "type": "string"
        },
        "reviewed_date": {
          "type": "string",
          "format": "date"
        },
        "legal_review_notes": {
          "type": "string"
        },
        "graph_has_unresolved_external_refs": {
          "type": "boolean",
          "description": "True if the pack's graph contains external_authority forward-references (allowed in draft, disclosed at publish)."
        },
        "legal_review": {
          "type": "object",
          "description": "Legal-review record (RF-01).",
          "properties": {
            "reviewer": {
              "type": "string"
            },
            "reviewed_date": {
              "type": "string"
            },
            "disposition": {
              "type": "string"
            }
          }
        }
      }
    },
    "condition_coverage": {
      "type": "object",
      "description": "Canonical conditions referenced by this pack's rules.",
      "additionalProperties": false,
      "properties": {
        "conditions": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Distinct condition_ids referenced."
        },
        "count": {
          "type": "integer"
        },
        "shared_with_other_packs": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Condition_ids in this pack that are also referenced by rules in OTHER packs (the convergence surface)."
        }
      }
    },
    "source_provenance": {
      "type": "object",
      "description": "Roll-up of source-capture provenance for this pack.",
      "additionalProperties": false,
      "properties": {
        "source_artifacts": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "max_source_text_license": {
          "type": "string",
          "enum": [
            "A",
            "B",
            "C",
            "S"
          ]
        },
        "contains_verbatim_source_text": {
          "type": "boolean"
        },
        "all_anchors_c02_verified": {
          "type": "boolean",
          "description": "True if every non-Tier-C anchor's quoted_text_hash equals its source provision_hash. Citation-only anchors are verified separately."
        },
        "citation_only_anchors_verified": {
          "type": "boolean",
          "description": "True if every citation-only anchor resolves to exactly one encoded Tier C source provision whose provision_hash equals sha256(citation_path)."
        },
        "retrieval_methods": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      }
    },
    "graph_integrity": {
      "type": "object",
      "description": "Relationship-graph integrity summary for this pack.",
      "additionalProperties": false,
      "properties": {
        "relationship_edge_count": {
          "type": "integer"
        },
        "external_authority_edge_count": {
          "type": "integer"
        },
        "unresolved_target_count": {
          "type": "integer",
          "description": "Non-external targets that do not resolve. MUST be 0 for a publishable pack."
        }
      }
    }
  },
  "allOf": [
    {
      "$comment": "A pack claiming to contain verbatim text must not be built only from Tier C instruments",
      "if": {
        "properties": {
          "distribution": {
            "properties": {
              "contains_verbatim_source_text": {
                "const": true
              }
            },
            "required": [
              "contains_verbatim_source_text"
            ]
          }
        },
        "required": [
          "distribution"
        ]
      },
      "then": {
        "properties": {
          "instruments": {
            "type": "array",
            "contains": {
              "properties": {
                "source_text_license": {
                  "enum": [
                    "A",
                    "B"
                  ]
                }
              },
              "required": [
                "source_text_license"
              ]
            }
          }
        }
      }
    },
    {
      "$comment": "A delta pack must declare what it changes about its baseline",
      "if": {
        "required": [
          "extends"
        ]
      },
      "then": {
        "properties": {
          "extends": {
            "anyOf": [
              {
                "required": [
                  "overrides"
                ]
              },
              {
                "required": [
                  "excludes"
                ]
              }
            ]
          }
        }
      }
    }
  ]
}
