# ICSD Python Tooling

Reference Python implementation and tooling baseline for
Invariant-Closed System Design (ICSD).
ICSD is a deterministic state-transition framework designed for invariant
validation, evidence generation, and auditable system behaviour.
This repository provides:

- Core primitives for invariant validation, transitions, and deterministic evidence.
- A CI-friendly CLI with stable non-zero exit semantics.
- A workstream-based collaboration model for concurrent contributors.

## What Is dpa-icsd? (Skim)

- `dpa-icsd` is the published package name on PyPI.
- `icsd` is the Python import namespace.
- `icsd-dpa` is the CLI command for CI and local checks.
- ICSD focuses on invariant-safe state transitions and deterministic evidence.
- The `v1.x` contract keeps CLI exit semantics and evidence schema compatibility stable.

## Why ICSD?

ICSD is designed so invalid states are rejected structurally, not repaired after
the fact. That reduces reactive error-handling complexity and makes evidence
trails deterministic for audit and automation.

## Status

Current package version: `1.1.0` (authoritative source: `src/icsd/_version.py`).
As of 2026-03-16, `v1.1.0` includes implemented core modules and CLI commands:

- `icsd-dpa demo [output.json]`
- `icsd-dpa verify <evidence.json>`
- `icsd-dpa lint <path>`
- Stable `verify`/`lint` JSON output for CI/audit pipelines.
- Versioned invariant policy profiles selectable by profile name.
- Authority-source metadata and transition-time authority validation hooks.
- Transparency-log adapter hooks for append/verify workflows.

The package-level public API remains intentionally narrow at the `v1.x` boundary.
See [Stability Policy](#stability-policy-v1x) before depending on import paths.

## Install

Published package identity:

- Distribution name: `dpa-icsd`
- Import module: `icsd`
- CLI entry point: `icsd-dpa`

Install from PyPI:

```bash
python -m pip install dpa-icsd
```

Install from local source (development):

```bash
python -m pip install -e ".[dev]"
```

## Quick Start (CLI)

```bash
python -m pip install dpa-icsd
icsd-dpa --version
icsd-dpa demo
icsd-dpa verify quickstart.evidence.json
icsd-dpa lint .
```

Notes:

- `icsd-dpa demo` writes deterministic quick-start evidence to
  `./quickstart.evidence.json`.
- `icsd-dpa demo custom.evidence.json` writes the same deterministic payload to
  a different path.
- `icsd-dpa verify quickstart.evidence.json` validates the generated evidence
  envelope, required fields, and hash-field format.
- `icsd-dpa lint .` lints the current working directory.
- For `icsd-dpa verify <evidence.json>`, you can use the generated quick-start
  artifact or an example evidence artifact from `examples/out/`.

## Quick Start (Python)

```python
from icsd.core import Invariant, InvariantSet, Transition


def positive_balance(state):
    return state["balance"] >= 0


inv = Invariant(name="balance_non_negative", check=positive_balance)
invset = InvariantSet([inv])

transition = Transition.simple(
    name="debit_demo",
    mutate=lambda state: state.update({"balance": state["balance"] - 1}),
    invariants=invset,
)

result = transition.apply(
    state={"balance": 10},
    entity_type="account",
    entity_id="acct-001",
    actor="user:quickstart",
    authority="policy/account-demo",
)

with open("quickstart.evidence.json", "w", encoding="utf-8") as f:
    f.write(result.evidence_json)

print("Evidence written to quickstart.evidence.json")
```

```bash
icsd-dpa verify quickstart.evidence.json
```

States and evidence metadata must remain JSON-compatible under ICSD's current
Python-reference canonicalisation rules. Today that means
`json.dumps(..., sort_keys=True, separators=(",", ":"), ensure_ascii=False, allow_nan=False)`
followed by UTF-8 encoding where bytes are needed. If values such as Python
`datetime` objects reach evidence hashing, signing, or `result.evidence_json`,
ICSD raises `EvidenceSerialisationError` from `icsd.core.errors`.
This is stable for the current implementation, but it is not RFC 8785 / JCS
canonical JSON and does not by itself claim generic standards-based
cross-language interoperability.

## What Happens Next?

- See the examples below for fuller invariant, profile, and adapter workflows.
- Maintainers and contributors should use [DEV_README.md](https://github.com/BenFisher3148/dpa-icsd/blob/main/DEV_README.md).
- Project changes are recorded in `CHANGELOG.md` under `[Unreleased]` first.

## Optional Speed Extras (Contract)

Core install remains dependency-light (`dependencies = []` in
`pyproject.toml`). Optional acceleration extras are available as an explicit
packaging contract and do not change ICSD behavior guarantees.

```bash
python -m pip install "dpa-icsd[speed-numpy]"
python -m pip install "dpa-icsd[speed-numba]"
python -m pip install "dpa-icsd[speed-extensions]"
```

For full contract details, see [Performance Contract](https://github.com/BenFisher3148/dpa-icsd/blob/main/docs/performance.md).

## NumPy Batch Invariant Helper (Optional)

`WS-0077` adds an optional NumPy-backed invariant helper for deterministic
array-field validation in batch workflows. This helper is a deep import:

```python
from icsd.adapters.numpy_invariants import NumpyBatchFieldInvariant
from icsd.core.invariants import Invariant, InvariantSet

invariants = InvariantSet(
    [
        Invariant(
            name="batch_values_numeric",
            check=NumpyBatchFieldInvariant(
                field="values",
                expected_ndim=1,
                min_value=0,
                require_finite=True,
            ),
        )
    ]
)
```

Install optional NumPy support with:

```bash
python -m pip install "dpa-icsd[speed-numpy]"
```

Behavior guarantees are unchanged. Missing NumPy returns a deterministic
invariant failure at call-time, and boolean arrays are intentionally rejected
by this numeric helper.

## Numba JIT Invariant Helper (Optional Experiment)

`WS-0078` adds `NumbaBatchFieldInvariant` as a deep-import experimental helper
that preserves NumPy reference invariant outcomes and deterministic envelopes.

```python
from icsd.adapters.numba_invariants import NumbaBatchFieldInvariant
from icsd.core.invariants import Invariant, InvariantSet

invariants = InvariantSet(
    [
        Invariant(
            name="batch_values_numeric",
            check=NumbaBatchFieldInvariant(
                field="values",
                expected_ndim=1,
                min_value=0,
                require_finite=True,
            ),
        )
    ]
)
```

Install optional Numba support with:

```bash
python -m pip install "dpa-icsd[speed-numba]"
```

If Numba is missing, unsupported, or its kernels fail, this helper falls back
to the NumPy reference path. This is expected behavior for the experiment.

## Cython Feasibility Harness (Optional Experiment)

`WS-0079` adds an internal feasibility harness for Cython speedup exploration:
- deterministic reference kernels and parity checks in
  `icsd.adapters._cython_feasibility`
- optional probe extension source at `icsd.adapters._cython_speedups.pyx`
- deterministic benchmark/profile scripts under `benchmarks/`

This workstream does not change runtime ICSD semantics.

Run feasibility benchmark (default: no build attempt):

```bash
python benchmarks/benchmark_cython_speedups.py --format json
```

Run best-effort build attempt:

```bash
python benchmarks/benchmark_cython_speedups.py --build-speedup --format json
```

Profile target selection:

```bash
python benchmarks/profile_cython_targets.py --format json
```

`build=False` speedup import is primarily useful when the extension has already
been built locally. Without a prebuilt local extension, unavailable speedups are
expected and reported as non-failing status.

## v1.x Migration Guide (from v0.x prerelease)

- Upgrade or pin to the stable release:

```bash
python -m pip install --upgrade dpa-icsd==1.1.0
```

- Use `icsd-dpa` as the CLI entry point in scripts/CI (`verify`, `lint`,
  and exit codes `0`/`1`/`2` are stable across `v1.x`).
- Keep package-root usage minimal (`icsd.__version__`); import runtime
  primitives from `icsd.core` and CLI internals from `icsd.cli`.
- Compatibility retained in `v1.x`: evidence schema `0.1` and lint profile
  name `policy-v0.1`.
- Breaking-change policy: documented public-contract breaks require the next
  major release (`2.x`).

## Domain-Neutral Examples

Runnable non-finance examples live in [`examples/`](https://github.com/BenFisher3148/dpa-icsd/blob/main/examples/README.md):

- legal case lifecycle with statutory policy cutover and authority-backed audit trail
- construction tolerance gating with inspection authority checks and evidence-level inspector/measurement provenance
- schema contract evolution with migration and temporal policy cutover
- supply-chain provenance with cold-chain custody continuity, per-hop provenance receipts, and out-of-range rejection
- IAM access governance with least privilege, approval evidence enforcement, and trustless authority-proposal refusal
- healthcare/lab sample custody with calibration-window policy cutover and release-authority gating
- safety-critical robotics leash flow with agent-constrained execution and emergency-stop override
- agent guardrails workflow with deterministic refusal reporting for unsafe autonomous actions
- event-pipeline schema governance with v1->v2 cutover, migration/quarantine, and stream-event emission
- D&D rules cutover demo with temporal profile provenance (`Standard` tier)
- trustless D&D dice-plugin proposal refusal demo (`Standard` tier)
- lite examples pack for minimal adoption (`invariants + transition + one rejection`)
- fun D&D lite examples for combat and character-sheet invariant learning

Run from repository root:

```bash
python examples/legal_case_lifecycle.py
python examples/construction_tolerance_gate.py
python examples/schema_evolution_contracts.py
python examples/supply_chain_provenance.py
python examples/iam_access_governance.py
python examples/lab_sample_workflow.py
python examples/robotics_safety_leash.py
python examples/agent_guardrails.py
python examples/event_pipeline_schema_governance.py
python examples/dnd_rules_cutover_standard.py
python examples/dnd_trustless_dice_proposal.py
python examples/lite_supply_chain_temperature_gate.py
python examples/lite_legal_case_required_fields.py
python examples/lite_construction_tolerance.py
python examples/lite_schema_contracts.py
python examples/dnd_combat_lite.py
python examples/dnd_character_sheet_lite.py
```

Each script emits JSON artifacts under `examples/out/` (gitignored).

## Learning ICSD via Toy Domains

ICSD is not limited to compliance-heavy systems. To learn the invariant and
refusal model quickly, start with toy game-state examples first:

- `python examples/dnd_combat_lite.py`
- `python examples/dnd_character_sheet_lite.py`

Then move to regulated-system examples and their conformance suites in
[ICSD Domain Index](https://dpa-cloud.co.uk/publications/technical/icsd/domains) (legal, construction, schema governance,
supply chain, IAM, healthcare, robotics, and event pipelines).

## Regulated Systems Guides

High-signal docs for domain mapping and rollout:

- [ICSD Domain Index](https://dpa-cloud.co.uk/publications/technical/icsd/domains): domain index mapping invariants,
  evidence/provenance sources, and policy-cutover patterns to runnable examples.
- [ICSD Adoption Ladder](https://dpa-cloud.co.uk/publications/technical/icsd/adoption-ladder): progressive adoption tiers
  (`Lite -> Standard -> Full`) with feature-to-tier mapping and concrete exit
  criteria.
- [AI Agent Integration Guide](https://dpa-cloud.co.uk/publications/technical/icsd/ai-agents): AI-agent guardrail integration workflow
  (`proposal -> context -> transition -> evidence/refusal`) with refusal handling
  and anti-pattern guidance.
- [FastAPI Integration Pattern](https://dpa-cloud.co.uk/publications/technical/icsd/fastapi-integration): docs-only wrapper
  pattern for request schema validation, authority proposal ingestion, and
  deterministic evidence/refusal API responses.
- [Policy Profile Authoring Guide](https://github.com/BenFisher3148/dpa-icsd/blob/main/docs/policy_profiles.md): maintainer-facing guidance for
  naming, versioning, activation windows, cutover safety, and reusable
  `InvariantProfileRegistry` templates.
- [Adoption Metrics](https://dpa-cloud.co.uk/publications/technical/icsd/adoption-metrics): manual-first PyPI download
  snapshot workflow via pypistats (`dpa-icsd`, daily/weekly/monthly counts).
- [Performance Contract](https://github.com/BenFisher3148/dpa-icsd/blob/main/docs/performance.md): optional speed-extras dependency
  contract (`speed-numpy`, `speed-numba`, `speed-extensions`) and semantic
  equivalence guardrails.

## Conformance Scenario Tests

Narrative non-finance conformance suites live in `tests/`:

- `tests/test_conformance_runner.py`
- `tests/test_conformance_legal.py`
- `tests/test_conformance_construction.py`
- `tests/test_conformance_schema.py`
- `tests/test_conformance_supply_chain.py`
- `tests/test_conformance_iam.py`
- `tests/test_conformance_lab.py`
- `tests/test_conformance_robotics.py`
- `tests/test_conformance_agent_guardrails.py`
- `tests/test_conformance_event_pipeline.py`
- `tests/test_conformance_dnd.py`

These scenarios intentionally validate ICSD behaviour claims without asserting formal
standards compliance:

- valid-by-construction state setup via `InvariantSet.construct_state(...)`
- transition application via `Transition.apply_with_context(...)` or `AgentExecutionContext`
- deterministic evidence integrity + provenance verification
- explicit rejection of invalid/unrepresentable transitions

Run only the conformance suites:

```bash
pytest -q tests/test_conformance_legal.py tests/test_conformance_construction.py tests/test_conformance_schema.py tests/test_conformance_supply_chain.py tests/test_conformance_iam.py tests/test_conformance_lab.py tests/test_conformance_robotics.py tests/test_conformance_agent_guardrails.py tests/test_conformance_event_pipeline.py tests/test_conformance_dnd.py
```

Single-entry umbrella conformance run:

```bash
pytest -q tests/test_conformance_runner.py
```

If your environment has restrictive temp-directory ACLs, pin pytest to a
project-local temp path:

```bash
PYTEST_ADDOPTS="--basetemp .ci_tmp/pytest" pytest -q
```

## Import Policy (v1.x)

- Package root import contract remains intentionally narrow in v1.x: `icsd.__version__`.
- Prefer deep imports for runtime primitives in v1.x, for example:
  `from icsd.core import EvidenceBundle`.
- Do not assume `from icsd import EvidenceBundle` is part of the stable root-import contract.

## Maintainer Docs

Release automation, PyPI namespace checks, and contributor workflow details live
in [DEV_README.md](https://github.com/BenFisher3148/dpa-icsd/blob/main/DEV_README.md).

## CLI Quick Reference

```bash
icsd-dpa --version
icsd-dpa demo [output.json]
icsd-dpa verify <evidence.json>
icsd-dpa verify <evidence.json> --format json
icsd-dpa lint <path>
icsd-dpa lint <path> --profile policy-v0.1 --format json
icsd-dpa lint <path> --jobs 4 --format json
```

Exit codes:

- `0`: success / no findings
- `1`: lint findings
- `2`: tool error (invalid args, unreadable file, invalid JSON, or evidence-envelope/schema/hash-field failure)

## Verify and Lint Output

- `icsd-dpa verify` validates the evidence envelope only: required fields,
  UTC/hash-field format, and nested signature/provenance record shape accepted
  by `validate_evidence_payload(...)`.
- `icsd-dpa verify <evidence.json> --format json`: machine-readable report on
  stdout with `command`, `path`, `status`, `exit_code`, `error_count`, and
  `errors`.
- `icsd-dpa lint <path> --format json`: machine-readable report on stdout with
  `command`, `path`, `profile`, `status`, `exit_code`, `finding_count`, and
  `findings`.
- `verify` and `lint` JSON reports use canonical compact JSON with sorted keys
  so CI/audit consumers can rely on stable wire output.
- `icsd-dpa verify` does not perform cryptographic signature verification,
  reconstruct `before_hash` / `after_hash` from external states, authenticate
  provenance, or confirm transparency inclusion.

- `baseline` (default): Python/JSON syntax checks only.
- `policy-v0.1`: Adds stricter structural checks:
  - Blocks dynamic execution calls (`eval`, `exec`) in Python sources.
  - Requires JSON root objects and validates evidence-shaped JSON payloads.
  - Profile name retains the `v0.1` suffix for backward compatibility in v1.x.
- `--format text` (default): human-readable stderr errors/findings.
- `--jobs 1` (default): deterministic single-threaded lint traversal.
- `--jobs > 1`: opt-in deterministic parallel lint traversal with stable output ordering.
- The guarantee is deterministic ordering and stable output envelopes, not
  blanket speedup across all workloads.
- Pure Python CPU-bound lint or invariant workloads may see limited or no
  improvement under thread-based parallelism.

## Python Example: Invariants

```python
from icsd.core.errors import InvariantFailure
from icsd.core.invariants import Invariant, InvariantSet


def owner_required(state: dict[str, str]) -> bool | InvariantFailure:
    owner = state.get("owner", "").strip()
    if owner:
        return True
    return InvariantFailure(
        invariant="owner_required",
        code="missing_owner",
        message="Owner must be set before transition execution.",
    )


invariants = InvariantSet(
    [
        Invariant(name="status_known", check=lambda s: s.get("status") in {"draft", "approved"}),
        Invariant(name="owner_required", check=owner_required),
    ]
)

failures = invariants.validate({"status": "draft", "owner": ""})
assert len(failures) == 1
```

## Python Example: Batch Validation (Single-Process)

```python
from icsd.core.batch import validate_states
from icsd.core.invariants import Invariant, InvariantSet

invariants = InvariantSet(
    [Invariant(name="non_negative_balance", check=lambda state: state["balance"] >= 0)]
)
report = validate_states(
    invariants,
    [{"balance": 100}, {"balance": -1}, {"balance": 50}],
    context={"batch_id": "batch-001"},
)
assert report.total_count == 3
assert report.passed_count == 2
assert report.failed_count == 1
assert report.as_dict()["results"][1]["status"] == "failed"
```

Batch validation in `icsd.core.batch` is deterministic and single-process by
design.

## Python Example: Batch Validation (Parallel Opt-In)

```python
from icsd.core.invariants import Invariant, InvariantSet
from icsd.tools.parallel import validate_states_parallel

invariants = InvariantSet(
    [Invariant(name="non_negative_balance", check=lambda state: state["balance"] >= 0)]
)
report = validate_states_parallel(
    invariants,
    [{"balance": 100}, {"balance": -1}, {"balance": 50}],
    context={"batch_id": "batch-001"},
    workers=4,
)
assert report.total_count == 3
assert report.passed_count == 2
assert report.failed_count == 1
```

`validate_states_parallel(...)` is explicitly opt-in. `workers=1` preserves the
existing sequential validation path, while `workers>1` uses deterministic
threaded execution that preserves input order. The contract is stable ordering
and report shape, not guaranteed faster execution for pure Python CPU-bound
checks.

## Python Example: Invariant-Safe Construction Helpers

```python
from icsd.core.evidence import AuthoritySource
from icsd.core.invariants import Invariant, InvariantSet
from icsd.core.transition import Transition

invariants = InvariantSet(
    [Invariant(name="non_negative_balance", check=lambda state: state["balance"] >= 0)]
)

transition = Transition(
    name="debit_account",
    mutate=lambda state: state.update({"balance": state["balance"] - 20}),
    invariants=invariants,
    authority_validator=Transition.require_authority_sources(minimum=1),
)

# State objects are copied and validated before use.
safe_state = invariants.construct_state({"balance": 100})

# Authority/profile checks can be validated before apply-time.
context = transition.build_context(
    authority="policy/account-debit",
    authority_sources=[
        AuthoritySource(
            source_type="registry",
            source_id="uk-companies-house",
            reference="company/00000006",
        )
    ],
)
result = transition.apply_with_context(
    state=safe_state,
    entity_type="account",
    entity_id="acct-001",
    actor="user:ops",
    context=context,
)
```

## Python Example: Legacy Migration and Quarantine

```python
from icsd.core.invariants import Invariant, InvariantSet
from icsd.core.migration import migrate_legacy_states

invariants = InvariantSet(
    [
        Invariant(name="non_negative_balance", check=lambda state: state["balance"] >= 0),
        Invariant(name="owner_required", check=lambda state: bool(state["owner"].strip())),
    ]
)


def repair_legacy(state: dict[str, object]) -> None:
    if isinstance(state.get("balance"), int) and state["balance"] < 0:
        state["balance"] = 0
    if isinstance(state.get("owner"), str) and not state["owner"].strip():
        state["owner"] = "migrated-owner"


report = migrate_legacy_states(
    [{"balance": -10, "owner": ""}, {"balance": 50, "owner": "ops"}],
    invariants=invariants,
    transform=repair_legacy,
)
assert report.migrated_count == 2
assert report.quarantined_count == 0
```

## Python Example: Versioned Invariant Profiles

```python
from icsd.core.invariants import (
    Invariant,
    InvariantProfile,
    InvariantProfileRegistry,
    InvariantSet,
)
from icsd.core.transition import Transition

registry = InvariantProfileRegistry(
    [
        InvariantProfile(
            name="uk-payroll",
            policy="policy/payroll-tax",
            policy_version="2026.03",
            invariants=InvariantSet(
                [Invariant(name="non_negative_pay", check=lambda state: state["pay"] >= 0)]
            ),
        )
    ]
)

transition = Transition(
    name="apply_payroll_adjustment",
    mutate=lambda state: state.update({"pay": state["pay"] - 20}),
    profile_registry=registry,
    default_profile="uk-payroll",
)
result = transition.apply(
    state={"pay": 100},
    entity_type="payroll_record",
    entity_id="pay-001",
    actor="user:ops",
    policy_version="2026.03",
)

assert result.after_state == {"pay": 80}
assert result.evidence.authority == "policy/payroll-tax@2026.03"
```

## Python Example: Temporal Policy Activation and Cutover

```python
from icsd.core.invariants import Invariant, InvariantProfile, InvariantProfileRegistry, InvariantSet
from icsd.core.transition import Transition

registry = InvariantProfileRegistry(
    [
        InvariantProfile(
            name="uk-payroll",
            policy="policy/payroll-tax",
            policy_version="2026.03",
            active_from="2026-01-01T00:00:00Z",
            active_until="2026-06-01T00:00:00Z",
            invariants=InvariantSet(
                [Invariant(name="non_negative_pay", check=lambda state: state["pay"] >= 0)]
            ),
        ),
        InvariantProfile(
            name="uk-payroll",
            policy="policy/payroll-tax",
            policy_version="2026.06",
            active_from="2026-06-01T00:00:00Z",
            invariants=InvariantSet(
                [Invariant(name="non_negative_pay", check=lambda state: state["pay"] >= 0)]
            ),
        ),
    ]
)
transition = Transition(
    name="apply_payroll_adjustment",
    mutate=lambda state: state.update({"pay": state["pay"] - 20}),
    profile_registry=registry,
    default_profile="uk-payroll",
)
before_cutover = transition.apply(
    state={"pay": 100},
    entity_type="payroll_record",
    entity_id="pay-001",
    actor="user:ops",
    timestamp="2026-05-31T23:59:59Z",
)
after_cutover = transition.apply(
    state={"pay": 100},
    entity_type="payroll_record",
    entity_id="pay-001",
    actor="user:ops",
    timestamp="2026-06-01T00:00:00Z",
)
assert before_cutover.evidence.authority == "policy/payroll-tax@2026.03"
assert after_cutover.evidence.authority == "policy/payroll-tax@2026.06"
```

For profile naming, versioning, cutover-window authoring, and reusable
registry templates, see
[docs/policy_profiles.md](https://github.com/BenFisher3148/dpa-icsd/blob/main/docs/policy_profiles.md).

## Python Example: Transition and Evidence

```python
from datetime import datetime, timezone

from icsd.core.evidence import AuthoritySource
from icsd.core.invariants import Invariant, InvariantSet
from icsd.core.transition import Transition

invariants = InvariantSet(
    [Invariant(name="non_negative_balance", check=lambda state: state["balance"] >= 0)]
)


def debit(state: dict[str, int]) -> None:
    state["balance"] -= 20


transition = Transition(name="debit_account", mutate=debit, invariants=invariants)
result = transition.apply(
    state={"balance": 100},
    entity_type="account",
    entity_id="acct-001",
    actor="user:ops",
    authority="policy/account-debit",
    authority_sources=[
        AuthoritySource(
            source_type="registry",
            source_id="uk-companies-house",
            reference="company/00000006",
        )
    ],
    transition_id="11111111-1111-4111-8111-111111111111",
    timestamp=datetime(2026, 3, 3, 10, 0, tzinfo=timezone.utc),
)

assert result.before_state == {"balance": 100}
assert result.after_state == {"balance": 80}
assert result.evidence.verify_integrity(
    before_state=result.before_state,
    after_state=result.after_state,
)
```

## Python Example: Minimal Transition Helper

```python
from icsd.core.invariants import Invariant, InvariantSet
from icsd.core.transition import Transition

transition = Transition.simple(
    name="debit_account",
    mutate=lambda state: state.update({"balance": state["balance"] - 20}),
    invariants=InvariantSet(
        [Invariant(name="non_negative_balance", check=lambda state: state["balance"] >= 0)]
    ),
)
```

`Transition.simple(...)` is constructor sugar for direct-invariant transitions.
It does not bypass invariant checks or authority validation hooks.

## Python Example: Authority Validation Hook

```python
from icsd.core.invariants import Invariant, InvariantSet
from icsd.core.transition import Transition

transition = Transition(
    name="debit_account",
    mutate=lambda state: state.update({"balance": state["balance"] - 20}),
    invariants=InvariantSet(
        [Invariant(name="non_negative_balance", check=lambda state: state["balance"] >= 0)]
    ),
    authority_validator=Transition.require_authority_sources(minimum=1),
)
```

## Python Example: Authority Adapter (Trustless Proposal Verification)

```python
from icsd.core.invariants import Invariant, InvariantSet
from icsd.core.transition import Transition


class ExternalAuthorityAdapter:
    def propose(self, *, current_state, authority_response):
        # External responses are proposal inputs, not direct truth.
        return {
            "changes": {
                "balance": authority_response["approved_balance"],
            },
            "source": "authority-api",
            "reference": authority_response["response_id"],
            "details": {"provider": "external-authority"},
        }


transition = Transition(
    name="debit_account",
    mutate=lambda state: state.update({"balance": state["balance"] - 5}),
    invariants=InvariantSet(
        [Invariant(name="non_negative_balance", check=lambda state: state["balance"] >= 0)]
    ),
    authority_adapter=ExternalAuthorityAdapter(),
)
result = transition.apply(
    state={"balance": 100},
    entity_type="account",
    entity_id="acct-001",
    actor="user:ops",
    authority="policy/account-debit",
    authority_response={"approved_balance": 60, "response_id": "resp-001"},
)
assert result.after_state == {"balance": 95}
assert result.evidence.provenance[0].source == "authority-api"
```

For a deterministic refusal-report pattern when an adapter proposes impossible
state, see `tests/test_trustless_authority_refusal.py`. When an accepted
adapter proposal includes both `source` and `reference`, ICSD persists that
proposal context into evidence provenance with the validated `proposal_changes`
and optional adapter `proposal_details`.

## Python Example: Schema Contract Adapters (JSON Schema + Pydantic)

```python
from pydantic import BaseModel, Field

from icsd.adapters import JsonSchemaInvariant, PydanticModelInvariant
from icsd.core.invariants import Invariant, InvariantSet

json_contract = JsonSchemaInvariant(
    schema={
        "type": "object",
        "required": ["status", "count"],
        "properties": {
            "status": {"type": "string", "enum": ["draft", "review", "approved"]},
            "count": {"type": "integer", "minimum": 0},
        },
        "additionalProperties": False,
    }
)


class ContractModel(BaseModel):
    status: str = Field(min_length=1)
    count: int = Field(ge=0)


pydantic_contract = PydanticModelInvariant(model=ContractModel)

invariants = InvariantSet(
    [
        Invariant(name="json_contract", check=json_contract),
        Invariant(name="pydantic_contract", check=pydantic_contract),
    ]
)
invariants.assert_valid({"status": "review", "count": 2})
```

`JsonSchemaInvariant` uses `jsonschema` when installed and falls back to a deterministic
subset validator (`type`, `required`, `properties`, `additionalProperties`, `enum`,
`const`, length/number bounds, and array item checks) when it is not.
`PydanticModelInvariant` requires optional `pydantic` to be installed.

## Python Example: Agent-Constrained Execution Context

```python
from icsd.core.context import AgentExecutionContext, AgentPolicyEnforcement
from icsd.core.invariants import Invariant, InvariantProfile, InvariantProfileRegistry, InvariantSet
from icsd.core.transition import Transition

registry = InvariantProfileRegistry(
    [
        InvariantProfile(
            name="retail-banking",
            policy="policy/account-debit",
            policy_version="2026.03",
            invariants=InvariantSet(
                [Invariant(name="non_negative_balance", check=lambda state: state["balance"] >= 0)]
            ),
        )
    ]
)
transition = Transition(
    name="debit_account",
    mutate=lambda state: state.update({"balance": state["balance"] - 5}),
    profile_registry=registry,
)
agent_context = AgentExecutionContext(
    transition,
    enforcement=AgentPolicyEnforcement(
        require_profile=True,
        require_policy_version=True,
        minimum_authority_sources=1,
    ),
)
result = agent_context.apply(
    state={"balance": 100},
    entity_type="account",
    entity_id="acct-001",
    actor="user:ops",
    profile="retail-banking",
    policy_version="2026.03",
    authority_sources=[
        {
            "source_type": "registry",
            "source_id": "uk-companies-house",
            "reference": "company/00000006",
        }
    ],
)
assert result.after_state == {"balance": 95}
```

## Python Example: Transparency Log Adapter

```python
from icsd.adapters import TransparencyLogReceipt, TransparencyVerification
from icsd.core.invariants import Invariant, InvariantSet
from icsd.core.transition import Transition


class MemoryLogAdapter:
    def append(self, *, evidence_payload):
        return TransparencyLogReceipt(adapter="memory-log", reference="entry/1")

    def verify(self, *, evidence_payload, receipt=None):
        return TransparencyVerification(verified=True, checkpoint="checkpoint-1")


transition = Transition(
    name="debit_account",
    mutate=lambda state: state.update({"balance": state["balance"] - 20}),
    invariants=InvariantSet(
        [Invariant(name="non_negative_balance", check=lambda state: state["balance"] >= 0)]
    ),
    transparency_log=MemoryLogAdapter(),
    verify_transparency_after_append=True,
)
```

Transparency append and verify both bind to the same canonical evidence payload.
If a transparency receipt is embedded back into `evidence.provenance`, that
`transparency-log/...` record is treated as append metadata rather than part of
the payload identity being verified.

## Python Example: Real-time Evidence Stream Adapter

```python
from icsd.adapters.transparency_stream import TransparencyStreamReceipt
from icsd.core.invariants import Invariant, InvariantSet
from icsd.core.transition import Transition


class AuditorFeedAdapter:
    def emit(self, *, event_payload):
        # event_payload includes evidence, signable_hash, and transition metadata.
        return TransparencyStreamReceipt(
            adapter="auditor-feed",
            event_id=f"evt/{event_payload['transition_id']}",
        )


transition = Transition(
    name="debit_account",
    mutate=lambda state: state.update({"balance": state["balance"] - 20}),
    invariants=InvariantSet(
        [Invariant(name="non_negative_balance", check=lambda state: state["balance"] >= 0)]
    ),
    transparency_stream=AuditorFeedAdapter(),
)
```

## Invisible Compliance Layer Guides

Deployment-oriented guidance for evidence-stream and auditor workflows:

- [Transparency Feed Deployment Recipe](https://dpa-cloud.co.uk/publications/technical/icsd/transparency-feed)
- [Auditor Subscription Guide](https://dpa-cloud.co.uk/publications/technical/icsd/auditor-subscribe)
- [ICSD Domain Index](https://dpa-cloud.co.uk/publications/technical/icsd/domains)
- [ICSD Adoption Ladder](https://dpa-cloud.co.uk/publications/technical/icsd/adoption-ladder)
- [FastAPI Integration Pattern](https://dpa-cloud.co.uk/publications/technical/icsd/fastapi-integration)
- [Whitepaper Integration](https://dpa-cloud.co.uk/publications/technical/icsd/whitepaper-integration)

Operational pattern:

1. A transition emits deterministic evidence and a stream event payload.
2. A stream adapter publishes the event payload to subscriber infrastructure.
3. A transparency-log adapter stores/returns inclusion receipts for later checks.
   Those receipt entries may be embedded in evidence provenance for audit
   convenience, but they are excluded from the canonical payload bound to the
   append/verify identity check.
4. Auditors subscribe to the feed and verify evidence locally.

Concrete CLI walkthrough:

```bash
python examples/legal_case_lifecycle.py
icsd-dpa verify examples/out/legal_case_04_appealed.evidence.json
```

That CLI step validates the evidence envelope only. Transparency receipt
verification, event/evidence correlation, and any signature or provenance trust
checks remain separate auditor steps.

## Whitepaper Integration (Non-Normative)

For whitepaper usage, see [Whitepaper Integration](https://dpa-cloud.co.uk/publications/technical/icsd/whitepaper-integration)
for a ready-to-use "Reference Implementation and Conformance Testing" section and
claim-to-test/example mapping.

The package is a reference implementation only; it does not define the ICSD
normative specification.

## Evidence Payload Shape (Schema v0.1, Supported in v1.x)

`icsd-dpa verify` validates the evidence JSON envelope, required fields,
UTC/hash-field format, and nested record shape accepted by
`validate_evidence_payload(...)`.
For CI/audit automation, `icsd-dpa verify <evidence.json> --format json`
emits a stable canonical report envelope separate from the evidence payload
being verified.
It does not verify cryptographic signatures, recompute `before_hash` /
`after_hash` from source states, authenticate provenance, or confirm
transparency inclusion.
The stored `before_hash`, `after_hash`, and signable-byte workflows are based
on the repository's current Python-reference canonicalisation rules. Consumers
in other languages must reproduce those exact bytes for parity today; the
project does not currently claim RFC 8785 / JCS-style canonical JSON
interoperability.
The full implementation-aligned reference is available in
[docs/evidence_schema_v0_1.md](https://github.com/BenFisher3148/dpa-icsd/blob/main/docs/evidence_schema_v0_1.md),
and the machine-readable artifact is published at
[docs/evidence_schema_v0_1.json](https://github.com/BenFisher3148/dpa-icsd/blob/main/docs/evidence_schema_v0_1.json).
Within `v1.x`, evidence schema `0.1` remains stable and compatible extension
keys remain permitted for forward-compatible consumers.

```json
{
  "schema_version": "0.1",
  "transition_id": "11111111-1111-4111-8111-111111111111",
  "entity_type": "account",
  "entity_id": "acct-001",
  "actor": "user:ops",
  "authority": "policy/account-debit",
  "authority_sources": [
    {
      "source_type": "registry",
      "source_id": "uk-companies-house",
      "reference": "company/00000006"
    }
  ],
  "timestamp": "2026-03-03T10:00:00Z",
  "before_hash": "d2ec8f050e788f96279be8f1a1d7e56ce3f180ebea047a1bf6f019f06f5f0a18",
  "after_hash": "55459e863d1c8fcdaff7ac18549f96db4495ea3a74c4d659e410cd554d65ff43"
}
```

## Stability Policy (v1.x)

- `1.x` releases follow semantic versioning: breaking changes to documented
  public contracts require a `2.x` release.
- Package root API guarantee in v1.x remains intentionally narrow:
  `icsd.__version__`.
- `icsd.core.*` and `icsd.cli.*` are supported import paths in v1.x;
  incompatible changes require a major-version bump.
- CLI command surface (`icsd-dpa --version`, `demo`, `verify`, `lint`) and
  exit-code semantics (`0`, `1`, `2`) are stable for v1.x automation.
- Evidence schema `0.1` and lint profile `policy-v0.1` remain supported in
  v1.x for compatibility.
- Breaking behavior changes should be labelled `Kind/Breaking` in issue/commit metadata.

## Credits

- Framework: [https://datatracker.ietf.org/doc/draft-dpa-icsd/](https://datatracker.ietf.org/doc/draft-dpa-icsd/)
- Author: [Benjamin Anthony Fisher](https://datatracker.ietf.org/person/b.fisher@dpa-cloud.co.uk)

## License

Apache-2.0. See [LICENSE](https://github.com/BenFisher3148/dpa-icsd/blob/main/LICENSE).
