TemporalNodeShape
type: NodeShape
target class: TemporalNode
property:
  path: hasBeginning
  name: valid_from
  datatype: dateTime
  min count: 1
  max count: 1
  description: When the fact became valid in reality
property:
  path: hasEnd
  name: valid_to
  datatype: dateTime
  max count: 1
  description: When the fact ceased to be valid in reality
property:
  path: generatedAtTime
  name: asserted_at
  datatype: dateTime
  min count: 1
  max count: 1
  description: When the fact was recorded in the system
property:
  path: invalidatedAtTime
  name: retracted_at
  datatype: dateTime
  max count: 1
  description: When the fact was retracted from the system
property:
  path: wasAttributedTo
  name: source
  datatype: string
  min count: 1
  max count: 1
  description: Source of the information
property:
  path: extractorVersion
  name: extractor_version
  datatype: string
  min count: 1
  max count: 1
  description: Version of the extraction system

TaxpayerProfileShape
type: NodeShape
target class: TaxpayerProfile
property:
  path: taxpayerId
  name: taxpayer_id
  datatype: string
  min count: 1
  max count: 1
  pattern: ^[a-zA-Z0-9_-]+$
property:
  path: taxpayerType
  name: type
  in: Individual
  in: Partnership
  in: Company
  min count: 1
  max count: 1
property:
  path: utr
  name: utr
  datatype: string
  pattern: ^[0-9]{10}$
  max count: 1
  description: Unique Taxpayer Reference
property:
  path: niNumber
  name: ni_number
  datatype: string
  pattern: ^[A-CEGHJ-PR-TW-Z]{2}\d{6}[A-D]$
  max count: 1
  description: National Insurance Number
property:
  path: residence
  name: residence
  datatype: string
  max count: 1

DocumentShape
type: NodeShape
target class: Document
property:
  path: docId
  name: doc_id
  datatype: string
  min count: 1
  max count: 1
  pattern: ^doc_[a-f0-9]{16}$
property:
  path: documentKind
  name: kind
  in: bank_statement
  in: invoice
  in: receipt
  in: p_and_l
  in: balance_sheet
  in: payslip
  in: dividend_voucher
  in: property_statement
  in: prior_return
  in: letter
  in: certificate
  min count: 1
  max count: 1
property:
  path: checksum
  name: checksum
  datatype: string
  min count: 1
  max count: 1
  pattern: ^[a-f0-9]{64}$
  description: SHA-256 checksum of document content
property:
  path: fileSize
  name: file_size
  datatype: integer
  min inclusive: 0
  max count: 1
property:
  path: pageCount
  name: pages
  datatype: integer
  min inclusive: 1
  max count: 1

EvidenceShape
type: NodeShape
target class: Evidence
property:
  path: snippetId
  name: snippet_id
  datatype: string
  min count: 1
  max count: 1
  pattern: ^[a-zA-Z0-9_-]+$
property:
  path: docRef
  name: doc_ref
  datatype: string
  min count: 1
  max count: 1
property:
  path: page
  name: page
  datatype: integer
  min inclusive: 1
  min count: 1
  max count: 1
property:
  path: textHash
  name: text_hash
  datatype: string
  min count: 1
  max count: 1
  pattern: ^[a-f0-9]{64}$
  description: SHA-256 hash of extracted text
property:
  path: ocrConfidence
  name: ocr_confidence
  datatype: decimal
  min inclusive: 0.0
  max inclusive: 1.0
  max count: 1

IncomeItemShape
type: NodeShape
target class: IncomeItem
property:
  path: incomeType
  name: type
  in: employment
  in: self_employment
  in: property
  in: dividend
  in: interest
  in: other
  min count: 1
  max count: 1
property:
  path: grossAmount
  name: gross
  datatype: decimal
  min inclusive: 0.0
  min count: 1
  max count: 1
property:
  path: netAmount
  name: net
  datatype: decimal
  min inclusive: 0.0
  max count: 1
property:
  path: taxWithheld
  name: tax_withheld
  datatype: decimal
  min inclusive: 0.0
  max count: 1
property:
  path: currency
  name: currency
  datatype: string
  pattern: ^[A-Z]{3}$
  min count: 1
  max count: 1
property:
  path: periodStart
  name: period_start
  datatype: date
  max count: 1
property:
  path: periodEnd
  name: period_end
  datatype: date
  max count: 1

ExpenseItemShape
type: NodeShape
target class: ExpenseItem
property:
  path: expenseType
  name: type
  in: business
  in: property
  in: capital
  in: personal
  min count: 1
  max count: 1
property:
  path: amount
  name: amount
  datatype: decimal
  min inclusive: 0.0
  min count: 1
  max count: 1
property:
  path: currency
  name: currency
  datatype: string
  pattern: ^[A-Z]{3}$
  min count: 1
  max count: 1
property:
  path: allowable
  name: allowable
  datatype: boolean
  max count: 1
property:
  path: capitalizableFlag
  name: capitalizable_flag
  datatype: boolean
  max count: 1

PartyShape
type: NodeShape
target class: Party
property:
  path: partyId
  name: party_id
  datatype: string
  min count: 1
  max count: 1
property:
  path: name
  name: name
  datatype: string
  min count: 1
  max count: 1
  min length: 1
property:
  path: subtype
  name: subtype
  in: Employer
  in: Payer
  in: Bank
  in: Landlord
  in: Tenant
  in: Supplier
  in: Client
  max count: 1
property:
  path: vatNumber
  name: vat_number
  datatype: string
  pattern: ^GB[0-9]{9}$|^GB[0-9]{12}$
  max count: 1
  description: UK VAT registration number
property:
  path: utr
  name: utr
  datatype: string
  pattern: ^[0-9]{10}$
  max count: 1

AccountShape
type: NodeShape
target class: Account
property:
  path: accountId
  name: account_id
  datatype: string
  min count: 1
  max count: 1
property:
  path: iban
  name: iban
  datatype: string
  pattern: ^GB[0-9]{2}[A-Z]{4}[0-9]{14}$
  max count: 1
  description: UK IBAN format
property:
  path: sortCode
  name: sort_code
  datatype: string
  pattern: ^[0-9]{2}-[0-9]{2}-[0-9]{2}$
  max count: 1
property:
  path: accountNumber
  name: account_no
  datatype: string
  pattern: ^[0-9]{8}$
  max count: 1

PropertyAssetShape
type: NodeShape
target class: PropertyAsset
property:
  path: propertyId
  name: property_id
  datatype: string
  min count: 1
  max count: 1
property:
  path: address
  name: address
  datatype: string
  min count: 1
  max count: 1
  min length: 10
property:
  path: postcode
  name: postcode
  datatype: string
  pattern: ^[A-Z]{1,2}[0-9][A-Z0-9]?\s*[0-9][A-Z]{2}$
  max count: 1
property:
  path: usage
  name: usage
  in: residential
  in: furnished_holiday_letting
  in: commercial
  in: mixed
  max count: 1
property:
  path: ownershipShare
  name: ownership_share
  datatype: decimal
  min inclusive: 0.0
  max inclusive: 1.0
  max count: 1

TemporalConsistencyShape
type: NodeShape
target class: TemporalNode
sparql:
  message: valid_to must be after valid_from
  prefixes: vocab#
  select: SELECT $this WHERE { $this time:hasBeginning ?validFrom ; time:hasEnd ?validTo . FILTER (?validTo <= ?validFrom) }
sparql:
  message: asserted_at must be after valid_from
  prefixes: vocab#
  select: SELECT $this WHERE { $this time:hasBeginning ?validFrom ; prov:generatedAtTime ?assertedAt . FILTER (?assertedAt < ?validFrom) }

FinancialConsistencyShape
type: NodeShape
target class: IncomeItem
sparql:
  message: net amount cannot exceed gross amount
  prefixes: vocab#
  select: SELECT $this WHERE { $this tax:grossAmount ?gross ; tax:netAmount ?net . FILTER (?net > ?gross) }
sparql:
  message: tax withheld cannot exceed gross amount
  prefixes: vocab#
  select: SELECT $this WHERE { $this tax:grossAmount ?gross ; tax:taxWithheld ?tax . FILTER (?tax > ?gross) }

ProvenanceShape
type: NodeShape
target class: IncomeItem
target class: ExpenseItem
target class: Payment
property:
  path: derivedFrom
  name: derived_from_evidence
  class: Evidence
  min count: 1
  description: All financial facts must have evidence

DocumentIntegrityShape
type: NodeShape
target class: Document
sparql:
  message: Document must have at least one evidence item
  prefixes: vocab#
  select: SELECT $this WHERE { $this a tax:Document . FILTER NOT EXISTS { ?evidence tax:docRef $this . } }

CalculationTraceabilityShape
type: NodeShape
target class: Calculation
property:
  path: computesFormBox
  name: computes_form_box
  class: FormBox
  min count: 1
  max count: 1
  description: Each calculation must compute exactly one form box
property:
  path: basedOnRule
  name: based_on_rule
  class: Rule
  min count: 1
  description: Calculations must reference applicable rules