Metadata-Version: 2.5
Name: setspec
Version: 0.4.0
Summary: Every versioned data contract that crosses an application boundary: benchmark results, capability evidence, event/error envelopes, prompt records.
Project-URL: Homepage, https://github.com/JPKell/SetSpec
Project-URL: Documentation, https://github.com/JPKell/SetSpec/tree/main/docs
Project-URL: Changelog, https://github.com/JPKell/SetSpec/blob/main/CHANGELOG.md
Author: Local AI Suite contributors
License-Expression: Apache-2.0
License-File: LICENSE
Classifier: Development Status :: 2 - Pre-Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: baseaicore<0.5,>=0.4
Requires-Dist: jinja2<4,>=3.1
Requires-Dist: pydantic<3,>=2.9
Provides-Extra: dev
Requires-Dist: import-linter<3,>=2.0; extra == 'dev'
Requires-Dist: jsonschema<5,>=4.23; extra == 'dev'
Requires-Dist: mypy<2,>=1.11; extra == 'dev'
Requires-Dist: pytest-cov<6,>=5; extra == 'dev'
Requires-Dist: pytest-randomly<4,>=3; extra == 'dev'
Requires-Dist: pytest<10,>=9.0.3; extra == 'dev'
Requires-Dist: respx<1,>=0.21; extra == 'dev'
Requires-Dist: ruff<1,>=0.6; extra == 'dev'
Requires-Dist: types-jsonschema<5,>=4.23; extra == 'dev'
Description-Content-Type: text/markdown

# SetSpec

Every versioned data contract that crosses an application boundary: benchmark results, capability evidence, event/error envelopes, prompt records.

**Status:** `0.3.0` — Phases 1–2, 3A and 4 complete, and **the v1.0 contracts are frozen.** Eight
payload types are published at `1.0` — `model.identity`, `machine.profile`, `benchmark.result`,
`benchmark.run_summary`, `capability.evidence`, `benchmark.evidence_bundle`,
`benchmark.goal_pack` and `benchmark.calibration_report` — each with generated JSON Schema and at
least three golden payloads shipped as package data. `setspec.DRAFT_SCHEMAS` is empty, which is
where the freeze is readable at runtime rather than only stated here; from now on a new optional
field is a minor bump and anything else is a major, enforced by a snapshot diff in CI.

The [schema catalogue](docs/schemas.md) lists every payload type, its artifacts, and the
cross-field rules the JSON Schema cannot express. Event and error envelopes (Phase 3) and prompt
records (Phase 5) are not yet written and are therefore not part of the freeze. See the
[development plan](docs/packages/setspec/development-plan.md) for what each phase adds.

Part of the **Local AI Suite**.

## Install

```bash
pip install setspec
```

## Quickstart

Write a document, then read it back:

```python
from setspec import GeneratorInfo, SchemaVersion, dump_envelope, load_envelope

generator = GeneratorInfo(name="freeweight", version="1.0.0")
document = dump_envelope(
    {"tokens_per_second": 42.0},
    schema="benchmark.result",
    version=SchemaVersion(1, 0),
    generator=generator,
)

envelope = load_envelope(document, expect="benchmark.result", supported=[SchemaVersion(1, 0)])
assert envelope.payload == {"tokens_per_second": 42.0}
```

`dump_envelope` returns canonical JSON: byte-identical for equal input, on every platform and
Python version, so a document can be hashed and diffed as well as read.

Payload types come in two halves generated from one definition — a strict `Out` for writers and a
preserving `In` for readers (ADR-0009 rule 4):

```python
from setspec import PayloadDefinition, payload_models
from setspec.serialization import MeasurementField


class ResultFields(PayloadDefinition):
    reading: MeasurementField
    unit: str


ResultOut, ResultIn = payload_models(ResultFields)

# A reader keeps what a newer writer added, so a re-export loses nothing.
received = ResultIn.model_validate({"reading": 1.5, "unit": "ms", "confidence": 0.87})
assert received.extras == {"confidence": 0.87}
```

A measurement this environment cannot provide is `UNSUPPORTED`, which serializes as the string
`"unsupported"` — never `null`, never `0`
(ADR-0016).

Phase 2's payload types live in their own versioned modules, not the top-level package, so that a
future `benchmark.result 2.0` can coexist with `v1` rather than racing it for one name
(ADR-0009 rule 6):

```python
from setspec.capability.v1 import CapabilityEvidenceOut

evidence = CapabilityEvidenceOut.model_validate(
    {
        "model": {
            "provider_kind": "ollama",
            "provider_model_name": "qwen3.5:9b-q8_0",
            "artifact_digest": None,
            "identity_confidence": "name_only",
            "canonical_id": "ollama/qwen3.5:9b-q8_0@unknown",
            "observed_at": "2026-08-20T09:00:00.000Z",
        },
        "runtime_profile_hash": "a" * 16,
        "machine_fingerprint": "b" * 64,
        "capability_id": "coding.python",  # unenumerated specialization of the known root "coding"
        "score": 0.82,
        "confidence": 0.71,
        "sample_count": 40,
        "excluded_count": 2,
        "dispersion": 0.09,
        "measured_at": "2026-08-20T00:00:00.000Z",
        "computed_at": "2026-08-22T00:00:00.000Z",
        "policy_version": "1.0",
        "vocabulary_version": "1.0",
        "environment": {"provider_kind": "ollama", "provider_version": "0.32.13"},
    }
)
assert evidence.capability_id == "coding.python"
```

See [docs/packages/setspec/spec.md](docs/packages/setspec/spec.md) §7 for the full public API and
§20 for the acceptance criteria.

## Documentation

Project documentation lives under [`docs/`](docs/README.md). Start with [`docs/README.md`](docs/README.md).

| Read this | For |
|---|---|
| [docs/packages/setspec/spec.md](docs/packages/setspec/spec.md) | Purpose, scope, non-goals, public contracts, configuration, acceptance criteria |
| [docs/packages/setspec/development-plan.md](docs/packages/setspec/development-plan.md) | The phased build plan: goals, work, tests, acceptance criteria per phase |

## Development

```bash
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pre-commit install
pytest -m "not live and not performance"
```

See [`CONTRIBUTING.md`](CONTRIBUTING.md) for the full workflow and [`SECURITY.md`](SECURITY.md) for
how to report a vulnerability.

## License

Apache-2.0 — see [`LICENSE`](LICENSE).
