Metadata-Version: 2.4
Name: ois-tools
Version: 0.7.0
Summary: Reference tools for the Open Intent Schema (OIS): validate, record and attest what was required, built and proven
Keywords: ois,open-intent-schema,requirements,traceability,evidence,in-toto,junit,coverage
License-Expression: Apache-2.0
License-File: LICENSE
Classifier: Development Status :: 2 - Pre-Alpha
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: 3.15
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Dist: cryptography>=50.0.2
Requires-Dist: jsonschema[format-nongpl]>=4.26.0
Requires-Dist: pyyaml>=6.0.3
Requires-Dist: rfc8785>=0.1.4
Requires-Python: >=3.13
Project-URL: Repository, https://github.com/openintentschema/tools
Project-URL: Documentation, https://github.com/openintentschema/tools/tree/main/docs
Project-URL: Changelog, https://github.com/openintentschema/tools/blob/main/CHANGELOG.md
Project-URL: Issues, https://github.com/openintentschema/tools/issues
Project-URL: Specification, https://github.com/openintentschema/openintentschema
Description-Content-Type: text/markdown

# ois-tools

[![PyPI](https://img.shields.io/pypi/v/ois-tools)](https://pypi.org/project/ois-tools/)
[![Checks](https://github.com/openintentschema/tools/actions/workflows/checks.yml/badge.svg)](https://github.com/openintentschema/tools/actions/workflows/checks.yml)

Reference tools for the [Open Intent Schema (OIS)](https://github.com/openintentschema/openintentschema),
the open data standard that links what was required to what was built and to the evidence in
between. One command, `ois`, records that evidence along the delivery lifecycle and checks it:

| Phase | What is recorded | Command |
| --- | --- | --- |
| Requirement | intent with acceptance criteria, its approval | `ois intent` |
| Design | design approval | `ois record decision --gate design-approval` |
| Implementation | code artifact, the context an agent was given | `ois record artifact` |
| Test | evidence per criterion | `ois junit`, `ois record evidence` |
| Merge | merge decision | `ois record decision --gate merge` |
| Release | build, release decision, signed passport with intent coverage | `ois record`, `ois passport --sign` |
| Operation | findings, their classification and resolution | `ois record finding`, `ois record supersede` |
| Review by others | check a passport and recompute its coverage | `ois validate`, `ois coverage` |

The tools record and check; they do not enforce. Whether a merge or a release is blocked, for
example below a coverage threshold or with an open critical finding, is the policy of the
project's pipeline, which reads the commands' exit codes and figures.

`ois validate` checks OIS records, bundles and Quality Passports and reports every problem with
its rule and level (specification section 11.1):

- **violation:** a MUST is broken; the input does not conform.
- **warning:** R8, R12 and R13 ask for a report; the input still conforms.
- **info:** a SHOULD is not followed.

**Status:** pre-alpha, targets specification v0.1. `ois validate` checks Appendix B and every rule
of section 5 that can be checked from the records at hand (R1–R5, R8–R17) and passes all
conformance fixtures, signed ones included. Signatures are verified with keys you trust (Ed25519
or ECDSA P-256); Sigstore keyless signatures are not verified yet. Up to version 0.5.0 the package was called `ois-validator`, with one command per
tool (`ois-validator`, `ois-junit`, …).

## Install

```bash
uvx --from ois-tools ois validate bundle.json    # run without installing
pip install ois-tools                            # or install; Python 3.13 or later
ois --help                                       # the commands
```

The commands work for projects in any language: they read JSON, YAML, JUnit XML and the local
Git checkout, not the code. Without Python, use [uv](https://docs.astral.sh/uv/) alone (`uvx`
fetches a Python on first use) or the container image, which holds `ois` and git:

```bash
docker run --rm --network none -v "$PWD:/work" --user "$(id -u):$(id -g)" \
  ghcr.io/openintentschema/ois-tools validate bundle.json
```

- The image runs `ois`; its arguments follow the image name.
- `-v "$PWD:/work"` mounts the current directory; paths are relative to it.
- `--user` writes files as you rather than as the image's user.
- `--network none` is optional and shows that no command needs the network.
- In a CI job that uses the image as its image, override the entrypoint (GitLab:
  `entrypoint: [""]`) and call `ois` in the script.
- Tags: `0.7.0`, `0.7` and `latest`. Each image carries a signed build provenance and an SBOM:
  `gh attestation verify oci://ghcr.io/openintentschema/ois-tools:0.7.0 --owner openintentschema`.

## Validate

```bash
ois validate bundle.json passport.json
ois validate --format json finding.json
cat bundle.json | ois validate -
```

Each file holds one JSON document: a single record, a bundle, or a passport as an in-toto
Statement v1. Exit code 0 means every file conforms, 1 means at least one violation, 2 means a
file could not be read. Reports point at the field concerned:

```
same-producer.json: violation [R5] $.records[12].derivedFrom: test and subject are both produced by https://ois.acme.example/actors/dev-agent
```

With `--format json`:

```json
[
  {
    "file": "same-producer.json",
    "conforms": false,
    "reports": [
      {
        "level": "violation",
        "rule": "R5",
        "message": "test and subject are both produced by https://ois.acme.example/actors/dev-agent",
        "path": "$.records[12].derivedFrom"
      }
    ]
  }
]
```

From Python:

```python
from ois_tools import validate

result = validate(document)  # a parsed JSON document
result.conforms  # False if any report is a violation
result.reports  # list of Report(level, rule, message, path)
```

## Record test results from JUnit XML

`ois junit` turns the JUnit XML that almost every test framework writes into a bundle, as the
Level 1 quick start in specification section 11.4 describes. Run it in the Git checkout the
tests ran in:

```bash
ois junit build/test-results/*.xml \
  --namespace https://ois.acme.example \
  --suite "Team Billing" \
  --run "$CI_PIPELINE_URL" \
  --output ois-bundle.json
```

| Option | Meaning |
| --- | --- |
| `--namespace` | URI under your organization's domain; every record id starts with it. Never requested. |
| `--suite` | Team or suite that owns the tests; names the test suite actor. |
| `--repo` | Git working tree the tests ran in (default `.`). |
| `--location` | Artifact location (default `git+<origin>@<HEAD>`, credentials removed). |
| `--run` | URI of the pipeline run, linked from every evidence record. |
| `--output` | Write the bundle to a file instead of standard output. |

The bundle holds an actor for the adapter and one for the test suite, a characterization intent
in draft with one criterion per test case, an artifact per test file (pinned by `sha256` and
`gitCommit`) and an evidence record per result with `derivedFrom: unknown`. `ois junit`
validates the bundle before writing it: exit code 0 writes it, 1 means it would not conform and
nothing is written, 2 means a report or the Git checkout could not be read.

The [getting started guide](https://openintentschema.io/getting-started/)
walks through it step by step, including a CI example.

## Write and approve intents

`ois intent` takes an intent written in YAML or JSON and does what is error-prone by hand:

```bash
ois intent seal INT-142.ois.yaml                      # criterion and intent digests (RFC 8785)
ois intent approve INT-142.ois.yaml --by https://ois.acme.example/actors/po-7f3a
ois intent next .ois/intents/INT-142-v1.ois.json      # draft of version 2, superseding version 1
ois intent compare .ois/intents/INT-142-v1.ois.json .ois/intents/INT-142-v2.ois.yaml
```

- `approve` writes the approved intent and its intent-approval decision, bound by the intent's
  digest (R1), to `.ois/intents/` and `.ois/decisions/` (specification section 7). It refuses an
  approved intent whose content no longer matches its digest: approved intents never change.
- `compare` shows per criterion whether evidence carries over to the new version or is stale
  (section 6.3); `criticality` does not count.
- YAML is read as section 7 requires: `2026-10-14T09:12:00Z` and `yes` stay strings, and tags,
  merge keys and non-string or duplicate keys are refused, so that digests do not depend on how
  a YAML library reads the text.

## Record artifacts, actors, evidence, decisions and findings

`ois record` writes every other record type, one record per command, to `.ois/<type>s/`. It
computes what is error-prone by hand, validates the record and prints the reference other records
use for it, so a pipeline can pass it on:

```bash
export OIS_NAMESPACE=https://ois.acme.example              # for derived ids, or --namespace

CI=$(ois record actor --kind tool --name "CI build" --version 7)
DEV=$(ois record actor --kind agent --name "Dev Agent" --version 2.1 --model acme-ai:coder:2026-09)
CODE=$(ois record artifact src/invoice.py --kind code --produced-by "$DEV")
BUILD=$(ois record artifact dist/billing-2026.10.4-py3-none-any.whl --kind build \
  --location pkg:pypi/billing@2026.10.4 --produced-by "$CI" --input "$CODE")
ois record decision --gate merge --subject "$CODE" --outcome passed --by "$CI"
```

| Command | Writes | Computed for you |
| --- | --- | --- |
| `actor` | a human, agent or tool | id from the name; the reference `<id>@<version>` |
| `artifact FILE` | an artifact of any kind | `sha256`; for a committed file also `gitCommit`, location `git+<origin>@<HEAD>` and path |
| `artifact --commit REPO` | the repository at `HEAD` | `gitCommit` and location |
| `artifact --digest ALG:HEX` | an artifact with a known digest, such as an image | id from name and digest |
| `evidence` | one result of one test for one criterion | id; measures from `--measure responseTimeP95=420ms` |
| `decision` | a gate outcome | `subjectDigest` from the subject among the records (R1, R16) |
| `finding` | a defect or incident | id |
| `supersede FILE` | the next record of a finding chain | id (`FND-18-r2`), `supersedes`, the unchanged fields |
| `bundle INPUT...` | a self-contained bundle, to stdout or `--output` | each record once, validated |

- **Records never change.** Recording the same thing again prints its reference and keeps the
  file; a different record under an existing id is refused (R10). A changed actor needs a new
  `--version`.
- **Files are located in Git.** A file must be committed and unchanged unless `--location` names
  where it lives, for example a Package URL for a build.
- **Rules are checked where the records allow it.** `decision` checks subject and outcome of the
  well-known gates (specification section 4.6) and, for `finding-resolution`, that the deciding
  actor took no part in the resolution (R14). `supersede` refuses a second successor (R14).
  `decision` and `supersede` look up references in the output directory and in `--records`.
- Exit code 0 writes the record, 1 means it would break a rule and nothing is written, 2 means a
  file could not be read or an argument is malformed.

## Build a Quality Passport

`ois passport` collects the records behind a release into an in-toto Statement with the OIS
passport predicate (specification section 8) and validates it before writing:

```bash
ois passport build .ois/ ci-bundles/ \
  --release-name billing --release-version 2026.10.4 \
  --build "$BUILD" \
  --issuer https://ois.acme.example/actors/passport-issuer@1 \
  --approve-release-by https://ois.acme.example/actors/release-manager-1d2e \
  --output passport.json
```

- Inputs are record or bundle files, or directories such as `.ois/`, in JSON or YAML. Identical
  records are merged, a draft gives way to its approved record, and two different records with the
  same id are refused (R10).
- The passport lists the latest approved version of each intent unless `--intent` names them, and
  embeds every input record. Its subject is the build artifact with its digest.
- `--approve-release-by` adds the release decision for the build when the records carry none
  (R16). Exit code 1 means the passport would break a rule and nothing was written; warnings such as
  R12 are printed and the passport is written.
- The passport reports intent coverage by the reference method of Appendix D in `assessments`
  (below); `--no-coverage` leaves it out.
- `--sign KEY` signs the passport with DSSE as it is built (below); the attested coverage inside
  it then counts these keys.

## Measure intent coverage

Intent coverage is the share of criteria in scope with counted evidence: passed, derived from the
intent, for the released build or one of its inputs, not revoked, observed after its subject was
created and, for a criterion with a target, meeting it (Appendix D). `ois coverage` shows per
criterion what counts and why other evidence does not:

```bash
ois coverage passport.json                        # also checks the coverage the passport declares
ois coverage .ois/ --build "$BUILD"               # before the passport: what is still missing
```

```
https://ois.acme.example/intents/INT-142@1#AC-1  not covered
    https://ois.acme.example/evidence/EV-9022: derivedFrom is unknown
https://ois.acme.example/intents/INT-142@1#AC-2  covered  https://ois.acme.example/evidence/EV-9021
intentCoverage 0.5 (declared 0.5)
```

| Measurement | Counts |
| --- | --- |
| `intentCoverage` | covered criteria in scope / criteria in scope |
| `intentCoverageAttested` | only evidence whose test context is attested: recorded by another actor who signed the passport (R12) |
| `intentCoverageWeighted` | criteria weighted by `criticality`: high 3, medium 2, low 1 |
| `intentCoverageDiverse` | only evidence whose test and subject come from a human, or from agents on different models |

A passport that lists several intents counts the criteria in scope of all of them together.
Exit code 1 means a declared value differs from the computed one, compared at the precision the
passport declares; 2 means a file could not be read or the release subject is missing.

## Sign bundles and passports

Signing makes records attributable (specification section 8.3, Level 3). A signature counts for an
actor whose `identity.keyId` is the key's id, and only for a verifier that trusts the key:

```bash
ois key generate agent-runtime            # writes agent-runtime.key (secret) and .pub, prints the key id
ois record actor --kind tool --name "Agent runtime" --version 4.3 --public-key agent-runtime.pub

ois sign bundle.json --key agent-runtime.key --output bundle.dsse.json   # or add to an envelope
ois passport build .ois/ … --sign issuer.key --sign agent-runtime.key --output passport.dsse.json

ois validate passport.dsse.json --trusted-key keys/       # a .pub file, a directory, or KEYID=FILE
```

- A **key id** is the hex SHA-256 of the public key (DER SubjectPublicKeyInfo), so a verifier
  derives it from the key it trusts. `ois key id FILE` prints it.
- **Trust** is the verifier's decision: only `--trusted-key` counts, never a key a document names.
- **R12:** a recorded context is attested when its `contextRecordedBy` actor signed the document.
  The runtime that ran an agent therefore signs the bundle or passport next to the producer;
  a DSSE envelope holds several signatures.
- **Reports** under the rule ID `signature` (proposed): a violation for an envelope without
  signatures, with the wrong payload type, or with a signature that does not verify, and the
  envelope then counts as unsigned; a warning for a key that is not trusted.
- Private keys are written readable by you only and are never needed for verification. Keep them
  in a secret store; an encrypted key is opened with `OIS_KEY_PASSPHRASE`.
- Sigstore keyless signing and transparency logs follow later.

## Privacy

None of the commands opens a network connection: the JSON Schema ships inside the package, and `ois junit` and
`ois record` read local files and the local Git checkout only. Installing the package contacts
PyPI, pulling the container image contacts GitHub (ghcr.io), and `uvx` without a local Python
downloads one from GitHub. Details and a diagram:
[Privacy and data flows](https://openintentschema.io/privacy/).

## Documentation

- [Specification, schema and conformance fixtures](https://github.com/openintentschema/openintentschema)
- [Getting started](https://openintentschema.io/getting-started/): first OIS bundle from JUnit XML
- [Architecture](docs/architecture.md): how the commands are built, and why
- [Development](docs/development.md): setup, checks, specification sync, adding a rule, releases
- [Changelog](CHANGELOG.md)

## Contributing

What `ois validate` accepts is decided in the specification repository; bug fixes, tooling and
documentation are welcome here. See [CONTRIBUTING.md](.github/CONTRIBUTING.md) and the
[Code of Conduct](.github/CODE_OF_CONDUCT.md). Security issues: [SECURITY.md](.github/SECURITY.md).

## License

Apache-2.0.
