diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aa79dd5..0bd1357 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/setup-python@v5 with: python-version: '${{ matrix.python }}' - - run: python -m pip install -e '.[dev]' -e ./packages/pytest-narrative-contracts + - run: python -m pip install -e '.[dev]' -e ./packages/pytest-mateprobe - run: pytest - run: ruff check . && ruff format --check . && mypy - run: python benchmarks/run.py --variants 1 --output /tmp/narrative-benchmark @@ -21,20 +21,20 @@ jobs: - name: Replay captured model outputs offline run: | python benchmarks/natural.py replay --input benchmarks/natural-results --output /tmp/natural-replay - python scripts/compare_replay.py benchmarks/natural-results/evaluation/reports.json /tmp/natural-replay/reports.json --old-version 0.1.0a2 --new-version 0.1.0a3 + python scripts/compare_replay.py benchmarks/natural-results/evaluation/reports.json /tmp/natural-replay/reports.json --old-version 0.1.0a2 --new-version 0.1.0a4 cmp benchmarks/natural-results/evaluation/summary.json /tmp/natural-replay/summary.json - name: Replay remote outputs and frozen mutation audit run: | python benchmarks/natural.py replay --input benchmarks/remote-results --output /tmp/remote-replay - python scripts/compare_replay.py benchmarks/remote-results/evaluation/reports.json /tmp/remote-replay/reports.json --old-version 0.1.0a2 --new-version 0.1.0a3 + python scripts/compare_replay.py benchmarks/remote-results/evaluation/reports.json /tmp/remote-replay/reports.json --old-version 0.1.0a2 --new-version 0.1.0a4 cmp benchmarks/remote-results/evaluation/summary.json /tmp/remote-replay/summary.json - python benchmarks/real_mutations.py evaluate --prepared benchmarks/real-mutation-results/prepared --output /tmp/real-mutations + python benchmarks/replay_renamed_mutations.py --output /tmp/real-mutations cmp benchmarks/real-mutation-results/evaluation/summary.json /tmp/real-mutations/summary.json - python scripts/compare_replay.py benchmarks/real-mutation-results/evaluation/cases.json /tmp/real-mutations/cases.json --old-version 0.1.0a2 --new-version 0.1.0a3 --report-path result.report + python scripts/compare_replay.py benchmarks/real-mutation-results/evaluation/cases.json /tmp/real-mutations/cases.json --old-version 0.1.0a2 --new-version 0.1.0a4 --report-path result.report python examples/five_minute_demo.py - name: Audit existing validators offline run: python examples/audit_existing_validator.py --output /tmp/validator-audit - - run: python -m build && python -m build packages/pytest-narrative-contracts + - run: python -m build && python -m build packages/pytest-mateprobe - name: Source mutation regression checks if: matrix.python == '3.12' run: python benchmarks/mutate_guards.py --output /tmp/source-mutations.json @@ -72,7 +72,7 @@ jobs: run: | python -m pip install build twine python -m build --outdir /tmp/core-dist - python -m build packages/pytest-narrative-contracts --outdir /tmp/plugin-dist + python -m build packages/pytest-mateprobe --outdir /tmp/plugin-dist python -m twine check --strict /tmp/core-dist/* /tmp/plugin-dist/* python -m pip install /tmp/core-dist/*.whl /tmp/plugin-dist/*.whl -r requirements-integrations.txt - name: Exercise installed APIs, docs and pytest plugin diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 9b7fbf8..0d72496 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -14,21 +14,21 @@ concurrency: cancel-in-progress: true jobs: - published-examples: + installed-examples: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: '3.11' - - name: Install the published alpha, not this checkout - run: >- - python -m pip install --index-url https://pypi.org/simple - narrative-contracts==0.1.0a3 pytest-narrative-contracts==0.1.0a3 - -r requirements-integrations.txt + - name: Build and install MateProbe wheels outside the source tree + run: | + python -m pip install build + python -m build --wheel --outdir /tmp/mateprobe-wheels + python -m build --wheel packages/pytest-mateprobe --outdir /tmp/mateprobe-wheels + python -m pip install /tmp/mateprobe-wheels/*.whl -r requirements-integrations.txt - run: python scripts/check_alpha_install.py --output /tmp/published-docs-evidence - run: python scripts/check_launch_package.py --output /tmp/launch-evidence - - run: python benchmarks/replay_agent_readiness.py --output /tmp/a3-agent-readiness - uses: actions/upload-artifact@v4 if: always() with: @@ -36,7 +36,22 @@ jobs: path: | /tmp/published-docs-evidence /tmp/launch-evidence - /tmp/a3-agent-readiness + + historical-a3-trial: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + - name: Install the historical a3 trial dependencies + run: python -m pip install narrative-contracts==0.1.0a3 pytest-narrative-contracts==0.1.0a3 + - run: python benchmarks/replay_agent_readiness.py --output /tmp/a3-agent-readiness + - uses: actions/upload-artifact@v4 + if: always() + with: + name: historical-a3-agent-readiness + path: /tmp/a3-agent-readiness historical-a2-trial: runs-on: ubuntu-latest @@ -72,7 +87,7 @@ jobs: deploy: if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' - needs: [build, published-examples, historical-a2-trial] + needs: [build, installed-examples, historical-a2-trial, historical-a3-trial] runs-on: ubuntu-latest permissions: pages: write diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index a497488..06992e6 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -10,10 +10,9 @@ on: version: description: 'Released artifact version' type: choice - default: 0.1.0a3 + default: 0.1.0a4 options: - - 0.1.0a3 - - 0.1.0a2 + - 0.1.0a4 package: description: 'Package selection for publishing or recovery' type: choice @@ -29,7 +28,7 @@ concurrency: cancel-in-progress: false jobs: verify: - if: github.repository == 'Mate4b/narrative-contracts' && github.ref == 'refs/heads/main' + if: github.repository == 'Mate4b/mateprobe' && github.ref == 'refs/heads/main' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -41,23 +40,19 @@ jobs: GH_TOKEN: ${{ github.token }} RELEASE_VERSION: ${{ inputs.version }} run: | - gh release download "v${RELEASE_VERSION}" --repo Mate4b/narrative-contracts \ + gh release download "v${RELEASE_VERSION}" --repo Mate4b/mateprobe \ --pattern '*.whl' --pattern '*.tar.gz' --dir downloaded - if [ "$RELEASE_VERSION" = "0.1.0a3" ]; then - python scripts/stage_pypi_a3.py --source downloaded --output pypi-dist - else - python scripts/stage_pypi_a2.py --source downloaded --output pypi-dist - fi + python scripts/stage_pypi_a4.py --source downloaded --output pypi-dist - name: Check metadata and wheel imports env: RELEASE_VERSION: ${{ inputs.version }} run: | python -m pip install twine pytest pypi-dist/*.whl python -m twine check --strict pypi-dist/* - python -I -c 'from importlib.metadata import version; import narrative_contracts, os; assert version("narrative-contracts") == version("pytest-narrative-contracts") == narrative_contracts.__version__ == os.environ["RELEASE_VERSION"]' + python -I -c 'from importlib.metadata import version; import mateprobe, os; assert version("mateprobe") == version("pytest-mateprobe") == mateprobe.__version__ == os.environ["RELEASE_VERSION"]' python examples/five_minute_demo.py - - name: Exercise a3 installed APIs and integrations - if: inputs.version == '0.1.0a3' + - name: Exercise a4 installed APIs and integrations + if: inputs.version == '0.1.0a4' run: | python -m pip install -r requirements-integrations.txt python scripts/check_alpha_install.py --output /tmp/alpha-install @@ -74,10 +69,10 @@ jobs: fail-fast: false max-parallel: 1 matrix: - prefix: ${{ fromJSON(inputs.package == 'core' && '["narrative_contracts"]' || inputs.package == 'plugin' && '["pytest_narrative_contracts"]' || '["narrative_contracts","pytest_narrative_contracts"]') }} + prefix: ${{ fromJSON(inputs.package == 'core' && '["mateprobe"]' || inputs.package == 'plugin' && '["pytest_mateprobe"]' || '["mateprobe","pytest_mateprobe"]') }} environment: name: pypi - url: https://pypi.org/project/narrative-contracts/ + url: https://pypi.org/project/mateprobe/ permissions: id-token: write steps: diff --git a/AGENTS.md b/AGENTS.md index d91c384..91a58cb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,4 +1,4 @@ -# Working on Narrative Contracts +# Working on MateProbe by Mate4B ## Install and verify @@ -7,7 +7,7 @@ Use Python 3.11+. From the repository root: ```sh python -m venv .venv . .venv/bin/activate -python -m pip install -e '.[dev]' -e ./packages/pytest-narrative-contracts +python -m pip install -e '.[dev]' -e ./packages/pytest-mateprobe pytest ruff check . ruff format --check . @@ -16,18 +16,20 @@ mypy For documentation builds, install `requirements-docs.txt` and run `python scripts/build_docs.py`. For published-package examples, use a **separate** -environment with `narrative-contracts==0.1.0a3`, `pytest-narrative-contracts==0.1.0a3`, +environment with `mateprobe==0.1.0a4`, `pytest-mateprobe==0.1.0a4`, and `pydantic==2.13.5`, then run `python scripts/check_published_docs.py`; an editable checkout cannot verify published compatibility. See [the agent integration guide](docs/agent-guide.md) for consumer instructions. ## Version boundary -The current release is `0.1.0a3`, including independent audits and state bindings. +The current release is `0.1.0a4`, including independent audits and state bindings. Verify built distributions in an isolated environment with `python scripts/check_alpha_install.py --output /tmp/alpha-check` (install the optional integration requirements first). Keep frozen a2 research artifacts and -the agent-adoption replay pinned to a2; do not rewrite historical evidence. +the agent-adoption replay pinned to a2 and agent-readiness replay pinned to a3; +do not rewrite historical evidence. Use `benchmarks/replay_renamed_mutations.py` +for the frozen real-output mutation replay against MateProbe. Keep the agent guide, `llms.txt`, and API reference consistent with installable artifacts. ## Public repository boundary diff --git a/CHANGELOG.md b/CHANGELOG.md index 45b6986..ecc4726 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 0.1.0a4 — 2026-09-29 + +- Renamed the project to **MateProbe by Mate4B**. The MIT license is unchanged. +- New distributions: `mateprobe` and `pytest-mateprobe`; Python imports are + `mateprobe` and `pytest_mateprobe`; CLI is `mateprobe`. +- The pytest fixture is now `mateprobe`, with `--mateprobe-report` for JSON output. +- Contract and audit behavior, report schema versions, and frozen research evidence + are unchanged. Runtime report metadata identifies version `0.1.0a4`. +- See [the migration guide](docs/migration-mateprobe.md). Historical releases retain + their original names and bytes. + ## 0.1.0a3 — 2026-09-29 - Audit existing validators through paired cases, finding attribution, controls, and explicit obligation inventories. diff --git a/LICENSE b/LICENSE index 207b8b1..b141333 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ MIT License Copyright (c) 2026 Narrative Contracts contributors +Copyright (c) 2026 Mate4B and MateProbe contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index c807d99..f949b27 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# Narrative Contracts +# MateProbe by Mate4B **Test whether your Python validator catches invalid AI outputs and preserves valid ones.** -Narrative Contracts audits your existing validator with paired input variants: +MateProbe audits your existing validator with paired input variants: known faults and valid controls. It reports missed faults, rejections for the wrong reason, incomplete evidence, and execution errors. You supply the cases and policy; the audit runs without an LLM judge. @@ -11,21 +11,23 @@ Here, mutations are changes to the **samples passed to the validator**. The audi does not rewrite its Python source. See [how to test AI output validators](docs/testing-ai-output-validators.md) for the workflow and how it fits with ordinary pytest and source mutation testing. -Alpha `0.1.0a3`. Python 3.11+. Core runtime has zero third-party dependencies and makes no model or network calls. The optional pytest plugin adds a fixture and JSON reports. +Alpha `0.1.0a4`. Python 3.11+. Core runtime has zero third-party dependencies and makes no model or network calls. The optional pytest plugin adds a fixture and JSON reports. The library checks structured state invariants and explicitly labelled lexical heuristics. It does **not** certify arbitrary prose as truthful, meaningful, or good writing. A passing declaration check only establishes consistency of the supplied declarations with supplied authoritative state. -[Documentation](https://mate4b.github.io/narrative-contracts/) · -[Agent integration guide](docs/agent-guide.md) · [Published API](docs/api-a3.md) · +[Documentation](https://mate4b.github.io/mateprobe/) · +[Agent integration guide](docs/agent-guide.md) · [Published API](docs/api-a4.md) · [Pydantic recipe](docs/pydantic.md) · [Documentation index for agents](llms.txt) [Try the one-file audit](docs/first-audit.md): a refund claim without a matching receipt, a retained prose survivor, and a pytest regression. +Previously **Narrative Contracts**. Existing users: see the [migration guide](docs/migration-mateprobe.md). + ## Audit an existing validator You do not need to change your validator to start. Wrap its existing result and -supply the failures and valid variations you care about. The APIs below are available in **0.1.0a3**; older `0.1.0a2` wheels do not include them. +supply the failures and valid variations you care about. The APIs below are available in **0.1.0a4**; older `0.1.0a2` wheels do not include them. Pytest can express every individual assertion. This library supplies paired baseline/variant execution, targeted finding attribution, valid controls, honest @@ -92,37 +94,35 @@ An LLM judge may evaluate open-ended properties outside these predicates. The tw can coexist; this library does not claim to replace every judge or guardrail system. See [choosing an evaluation method](docs/choosing-an-evaluator.md). -## Install from PyPI +## Install the current alpha -Both alpha packages are published and verified: -[narrative-contracts](https://pypi.org/project/narrative-contracts/0.1.0a3/) and -[pytest-narrative-contracts](https://pypi.org/project/pytest-narrative-contracts/0.1.0a3/). +Install the pinned alpha from PyPI with: ```sh -python -m pip install narrative-contracts==0.1.0a3 pytest-narrative-contracts==0.1.0a3 +python -m pip install mateprobe==0.1.0a4 pytest-mateprobe==0.1.0a4 ``` -Install only `narrative-contracts==0.1.0a3` if you do not need the pytest integration. +Install only `mateprobe==0.1.0a4` if you do not need the pytest integration. ## Install from this checkout ```sh -git clone https://github.com/Mate4b/narrative-contracts.git -cd narrative-contracts +git clone https://github.com/Mate4b/mateprobe.git +cd mateprobe python -m venv .venv . .venv/bin/activate -pip install -e '.[dev]' -e ./packages/pytest-narrative-contracts +pip install -e '.[dev]' -e ./packages/pytest-mateprobe pytest ``` -Both packages use the MIT license. Installable wheels and sdists are published in -[GitHub Releases](https://github.com/Mate4b/narrative-contracts/releases); PyPI is not required. +Both packages use the MIT license. Versioned wheels and sdists are distributed through +[GitHub Releases](https://github.com/Mate4b/mateprobe/releases). To install the pinned alpha without a checkout: ```sh python -m pip install \ - https://github.com/Mate4b/narrative-contracts/releases/download/v0.1.0a3/narrative_contracts-0.1.0a3-py3-none-any.whl \ - https://github.com/Mate4b/narrative-contracts/releases/download/v0.1.0a3/pytest_narrative_contracts-0.1.0a3-py3-none-any.whl + https://github.com/Mate4b/mateprobe/releases/download/v0.1.0a4/mateprobe-0.1.0a4-py3-none-any.whl \ + https://github.com/Mate4b/mateprobe/releases/download/v0.1.0a4/pytest_mateprobe-0.1.0a4-py3-none-any.whl ``` The core wheel can also be installed alone. Installation downloads packages; evaluation itself @@ -134,7 +134,7 @@ a preserved valid variation, and a prose contradiction that passes. ## State-conditioned checks ```python -from narrative_contracts import ( +from mateprobe import ( Claim, Context, DeclaredClaimsConsistent, @@ -179,15 +179,15 @@ A claim under `reject` cannot use facts from `accept`. Missing snapshots or fact Install the second package for automatic `pytest11` discovery: ```python -def test_outcome(narrative): - narrative.check(document, context, contracts) +def test_outcome(mateprobe): + mateprobe.check(document, context, contracts) ``` ```sh -pytest --narrative-report=contract-results.json +pytest --mateprobe-report=contract-results.json ``` -`narrative.audit(cases, contracts, detection=0.9, preservation=0.95)` checks a mutation campaign. Both denominators must exist; an empty suite cannot claim perfect performance. See [mutation examples](examples/mutation_audit.py). Distributed pytest report merging is not yet supported; use a serial run with `--narrative-report`. +`mateprobe.audit(cases, contracts, detection=0.9, preservation=0.95)` checks a mutation campaign. Both denominators must exist; an empty suite cannot claim perfect performance. See [mutation examples](examples/mutation_audit.py). Distributed pytest report merging is not yet supported; use a serial run with `--mateprobe-report`. ## What is included @@ -226,14 +226,14 @@ acceptance must not be called semantic accuracy. ## JSON and CLI ```sh -narrative-contracts examples/valid.json --output report.json +mateprobe examples/valid.json --output report.json ``` Exit status: `0` accepted, `1` rejected, `2` invalid input/configuration/I/O. Configuration accepts only built-in contract types and known fields. It never evaluates Python expressions. Library extensions use the `Contract` protocol, not untrusted imports from JSON. ## LifeCard integration -`narrative_contracts.adapters.lifecard_document` maps a card to stable surface paths and an individual state reference for every outcome. Supply post-state snapshots calculated by the trusted engine, not by the generating LLM. The adapter does not modify LifeCard or execute effects. See [example](examples/lifecard_adapter.py) and [migration guide](docs/lifecard.md). +`mateprobe.adapters.lifecard_document` maps a card to stable surface paths and an individual state reference for every outcome. Supply post-state snapshots calculated by the trusted engine, not by the generating LLM. The adapter does not modify LifeCard or execute effects. See [example](examples/lifecard_adapter.py) and [migration guide](docs/lifecard.md). ## Independent support workflow @@ -250,7 +250,7 @@ python benchmarks/natural.py replay --input benchmarks/natural-results --output ``` Replay requires no model, network or API key. Output directories must be new. Submit -[counterexamples](https://github.com/Mate4b/narrative-contracts/issues/new/choose) with a minimal +[counterexamples](https://github.com/Mate4b/mateprobe/issues/new/choose) with a minimal bundle and evidence; see [contribution guidance](CONTRIBUTING.md). Publication does not imply that independent reviewers have validated the method. @@ -262,7 +262,7 @@ ruff check . ruff format --check . mypy python -m build --no-isolation -python -m build --no-isolation packages/pytest-narrative-contracts +python -m build --no-isolation packages/pytest-mateprobe python benchmarks/run.py ``` diff --git a/benchmarks/expanded_mutations.py b/benchmarks/expanded_mutations.py index 698f573..9cab90c 100644 --- a/benchmarks/expanded_mutations.py +++ b/benchmarks/expanded_mutations.py @@ -15,7 +15,7 @@ from pathlib import Path from typing import Any -from narrative_contracts import ( +from mateprobe import ( Claim, Context, DeclaredClaimsConsistent, @@ -29,8 +29,8 @@ StateChanged, Surface, ) -from narrative_contracts.model import plain -from narrative_contracts.mutations import ( +from mateprobe.model import plain +from mateprobe.mutations import ( CampaignReport, MutationCase, Relation, diff --git a/benchmarks/mutate_guards.py b/benchmarks/mutate_guards.py index 4110b96..f0de7ca 100644 --- a/benchmarks/mutate_guards.py +++ b/benchmarks/mutate_guards.py @@ -82,7 +82,7 @@ def main(): with tempfile.TemporaryDirectory(prefix="narrative-mutant-") as directory: src = Path(directory) / "src" shutil.copytree(ROOT / "src", src, ignore=shutil.ignore_patterns("__pycache__")) - path = src / "narrative_contracts" / filename + path = src / "mateprobe" / filename original = path.read_text() if original.count(old) != 1: results.append( @@ -112,7 +112,7 @@ def main(): valid = [r for r in results if r["outcome"] in ("killed", "survived")] hashes = { p.name: hashlib.sha256(p.read_bytes()).hexdigest() - for p in sorted((ROOT / "src/narrative_contracts").glob("*.py")) + for p in sorted((ROOT / "src/mateprobe").glob("*.py")) } payload = { "scope": "Eight hand-selected source mutants; not an exhaustive mutation score", diff --git a/benchmarks/natural.py b/benchmarks/natural.py index 3cad757..97c69a0 100644 --- a/benchmarks/natural.py +++ b/benchmarks/natural.py @@ -20,7 +20,7 @@ from datetime import datetime, timezone from pathlib import Path -from narrative_contracts import ( +from mateprobe import ( Claim, Context, DeclaredClaimsConsistent, @@ -31,7 +31,7 @@ Surface, evaluate, ) -from narrative_contracts.model import digest +from mateprobe.model import digest ROOT = Path(__file__).resolve().parents[1] PROFILE = "natural-pilot-v1" diff --git a/benchmarks/real_mutations.py b/benchmarks/real_mutations.py index 0c20ae3..17dac27 100644 --- a/benchmarks/real_mutations.py +++ b/benchmarks/real_mutations.py @@ -13,8 +13,8 @@ from dataclasses import asdict from pathlib import Path -from narrative_contracts.model import digest -from narrative_contracts.mutations import MutationCase, Relation, Sample, Target, Validity, audit +from mateprobe.model import digest +from mateprobe.mutations import MutationCase, Relation, Sample, Target, Validity, audit ROOT = Path(__file__).resolve().parents[1] SPEC = importlib.util.spec_from_file_location( diff --git a/benchmarks/replay_renamed_mutations.py b/benchmarks/replay_renamed_mutations.py new file mode 100644 index 0000000..2ad39ae --- /dev/null +++ b/benchmarks/replay_renamed_mutations.py @@ -0,0 +1,68 @@ +"""Replay the frozen generator unchanged against MateProbe in an isolated subprocess. + +Only its historical import names are bound to the renamed library. Frozen source, +corpus checks, labels and rule configuration are not rewritten or bypassed. +""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import subprocess +import sys +import tempfile +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +FROZEN = ROOT / "benchmarks/real-mutation-results/prepared" + + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--output", type=Path, required=True) + args = parser.parse_args() + raw = (FROZEN / "prepared.json").read_bytes() + if hashlib.sha256(raw).hexdigest() != (FROZEN / "SHA256SUMS").read_text().split()[0]: + raise ValueError("Prepared corpus hash mismatch") + payload = json.loads(raw) + with tempfile.TemporaryDirectory(prefix="mateprobe-frozen-replay-") as directory: + root = Path(directory) + scripts = root / "benchmarks" + scripts.mkdir() + for source, target, key in ( + ("generator-source.py.txt", "real_mutations.py", "generator_sha256"), + ("adapter-source.py.txt", "natural.py", "adapter_sha256"), + ): + content = (FROZEN / source).read_bytes() + if hashlib.sha256(content).hexdigest() != payload[key]: + raise ValueError(f"Frozen source hash mismatch: {source}") + (scripts / target).write_bytes(content) + bootstrap = """ +import runpy, sys +import mateprobe, mateprobe.model, mateprobe.mutations +# Compatibility is confined to this replay process, not installed as public aliases. +sys.modules['narrative_contracts'] = mateprobe +sys.modules['narrative_contracts.model'] = mateprobe.model +sys.modules['narrative_contracts.mutations'] = mateprobe.mutations +script, prepared, output = sys.argv[1:] +sys.argv = [script, 'evaluate', '--prepared', prepared, '--output', output] +runpy.run_path(script, run_name='__main__') +""" + subprocess.run( + [ + sys.executable, + "-I", + "-c", + bootstrap, + str(scripts / "real_mutations.py"), + str(FROZEN), + str(args.output.resolve()), + ], + cwd=root, + check=True, + ) + + +if __name__ == "__main__": + main() diff --git a/benchmarks/run.py b/benchmarks/run.py index 47dd602..8a61379 100644 --- a/benchmarks/run.py +++ b/benchmarks/run.py @@ -14,7 +14,7 @@ from pathlib import Path from time import perf_counter_ns -from narrative_contracts import ( +from mateprobe import ( Claim, Context, DeclaredClaimsConsistent, @@ -29,8 +29,8 @@ Surface, evaluate, ) -from narrative_contracts.model import plain -from narrative_contracts.mutations import ( +from mateprobe.model import plain +from mateprobe.mutations import ( MutationCase, Relation, Sample, diff --git a/docs/adoption-levels.md b/docs/adoption-levels.md index fdb0a0e..c60c138 100644 --- a/docs/adoption-levels.md +++ b/docs/adoption-levels.md @@ -1,6 +1,6 @@ # Adopt the audit progressively -Requires `narrative-contracts==0.1.0a3`. These APIs are not available in a2. +Requires `mateprobe==0.1.0a4`. These APIs are not available in a2. You do not need to change your validator to start: Level 0 can wrap its existing boolean result and show raw survivors, regressions, and unattributed rejections. @@ -26,7 +26,7 @@ the corresponding valid control. ## Level 0: an existing boolean function ```python -from narrative_contracts.validator_audit import Verdict +from mateprobe.validator_audit import Verdict def adapt(sample): @@ -113,7 +113,7 @@ In the refund demo the prose contradiction is still one of the nine faults. If your already schema-validated output is nested, make its projection explicit: ```python -from narrative_contracts import check_fields +from mateprobe import check_fields def refund_projection(data): diff --git a/docs/agent-adoption-protocol.md b/docs/agent-adoption-protocol.md index 84414b1..11b89eb 100644 --- a/docs/agent-adoption-protocol.md +++ b/docs/agent-adoption-protocol.md @@ -1,5 +1,9 @@ # Documentation-guided agent smoke protocol v1 +> Historical evidence/reference from Narrative Contracts. The current project is +> **MateProbe by Mate4B**; see the [migration guide](migration-mateprobe.md). +> Original package names, versions and recorded results below identify that earlier work. + This is a maintainer-run usability smoke test. It is not independent human validation, a discovery/ranking study, or an estimate of agent success rates. The task and checks below are fixed before the trial. A single agent receives @@ -9,7 +13,7 @@ the implementation conversation. ## Task supplied to the agent Using only the public documentation at -`https://mate4b.github.io/narrative-contracts/llms.txt` as the starting point and +`https://mate4b.github.io/narrative-contracts/llms.txt` (the original, frozen trial URL) as the starting point and the published PyPI packages, create a small pytest integration for a shipping assistant. Use a new isolated Python environment and do not read the local Narrative Contracts repository, its source, or the library implementation. diff --git a/docs/agent-adoption.md b/docs/agent-adoption.md index 47a4daa..aa95b0a 100644 --- a/docs/agent-adoption.md +++ b/docs/agent-adoption.md @@ -1,5 +1,9 @@ # Agent integration trial +> Historical evidence/reference from Narrative Contracts. The current project is +> **MateProbe by Mate4B**; see the [migration guide](migration-mateprobe.md). +> Original package names, versions and recorded results below identify that earlier work. + This page preserves the historical a2 trial. See the separate [discovery and a3 integration observations](agent-readiness.md) for the newer tasks. diff --git a/docs/agent-guide.md b/docs/agent-guide.md index 4340a7c..9703892 100644 --- a/docs/agent-guide.md +++ b/docs/agent-guide.md @@ -1,6 +1,6 @@ -# Integrate Narrative Contracts in an agent project +# Integrate MateProbe by Mate4B in an agent project -This guide targets **published alpha 0.1.0a3**, Python 3.11+. Use it when an +This guide targets **alpha 0.1.0a4**, Python 3.11+. Use it when an application has authoritative state and needs to test explicit declarations in generated outputs against that state. The check itself makes no model or network calls. @@ -9,17 +9,17 @@ and the [one-file audit](first-audit.md). `audit_validator` accepts a sample-onl adapter and caller-authored baseline/variant pairs; you do not need to migrate to the `Document`/`Claim` representation used in the state-check example below. -## Install the published API +## Install the alpha API ```sh python -m venv .venv . .venv/bin/activate -python -m pip install narrative-contracts==0.1.0a3 pytest-narrative-contracts==0.1.0a3 +python -m pip install mateprobe==0.1.0a4 pytest-mateprobe==0.1.0a4 ``` -The second package supplies pytest and the automatically discovered `narrative` +The second package supplies pytest and the automatically discovered `mateprobe` fixture. Do not also load it with `-p`. Pin the alpha versions rather than assuming -an unqualified pip install selects a prerelease. See [API reference](api-a3.md). +an unqualified pip install selects a prerelease. See [API reference](api-a4.md). ## Choose the source of authority @@ -43,7 +43,7 @@ prose-only failure that remains outside the guarantee. ```python import pytest -from narrative_contracts import ( +from mateprobe import ( Claim, Context, DeclaredClaimsConsistent, @@ -53,7 +53,7 @@ from narrative_contracts import ( ) -def test_reply_contract(narrative): +def test_reply_contract(mateprobe): # This snapshot is provided by the application, independently of the output. context = Context({"current": {"ticket.status": "resolved"}}) rules = (DeclaredClaimsConsistent("ticket-claims", ("reply",)),) @@ -70,7 +70,7 @@ def test_reply_contract(narrative): ) ) - accepted = narrative.check(reply("resolved", "Your ticket is resolved."), context, rules) + accepted = mateprobe.check(reply("resolved", "Your ticket is resolved."), context, rules) assert accepted.accepted and accepted.complete wrong = reply("open", "Your ticket remains open.") @@ -84,10 +84,10 @@ def test_reply_contract(narrative): ) # The fixture records the report and raises on rejection. with pytest.raises(AssertionError): - narrative.check(wrong, context, rules) + mateprobe.check(wrong, context, rules) # Correct declaration, contradictory prose: deliberately still accepted. - prose_gap = narrative.check( + prose_gap = mateprobe.check( reply("resolved", "Your ticket remains open."), context, rules, @@ -96,7 +96,7 @@ def test_reply_contract(narrative): ``` ```sh -python -m pytest -q test_reply.py --narrative-report=contract-results.json +python -m pytest -q test_reply.py --mateprobe-report=contract-results.json ``` Expected: one passing test with three recorded evaluations. The prose challenge @@ -130,8 +130,8 @@ and preservation separately, with exclusions and missed faults visible. ## API version `check_fields`, `CompareFields`, `AllowedTransition`, and the independent -validator-audit API are included in **0.1.0a3**. Older a2 wheels do not provide them. -See [the a3 API index](api-a3.md) and [adoption levels](adoption-levels.md). Pin the +validator-audit API are included in **0.1.0a4**. Older a2 wheels do not provide them. +See [the current a4 API index](api-a4.md) and [adoption levels](adoption-levels.md). Pin the package version rather than silently switching a consumer to `main`. For free-form factual correctness or writing quality, this library alone is diff --git a/docs/agent-readiness.md b/docs/agent-readiness.md index b3a676a..53f9e19 100644 --- a/docs/agent-readiness.md +++ b/docs/agent-readiness.md @@ -1,5 +1,9 @@ # Discovery and integration observations +> Historical evidence/reference from Narrative Contracts. The current project is +> **MateProbe by Mate4B**; see the [migration guide](migration-mateprobe.md). +> Original package names, versions and recorded results below identify that earlier work. + We ran two separate tasks with fresh-context `gpt-5.6-luna` agents: finding a tool from a testing problem without a supplied package name, and integrating published `0.1.0a3` from its public documentation. The criteria were recorded beforehand. diff --git a/docs/api-a2.md b/docs/api-a2.md index fe4c0b8..cb10686 100644 --- a/docs/api-a2.md +++ b/docs/api-a2.md @@ -1,5 +1,9 @@ # Published API: 0.1.0a2 +> Historical evidence/reference from Narrative Contracts. The current project is +> **MateProbe by Mate4B**; see the [migration guide](migration-mateprobe.md). +> Original package names, versions and recorded results below identify that earlier work. + This reference covers the installed PyPI alpha, not every symbol on `main`. Use Python 3.11+ and pin `narrative-contracts==0.1.0a2`. See the [complete runnable example](agent-guide.md#a-complete-pytest-example). diff --git a/docs/api-a3.md b/docs/api-a3.md index 42c2a12..09fc085 100644 --- a/docs/api-a3.md +++ b/docs/api-a3.md @@ -1,5 +1,9 @@ # Published API: 0.1.0a3 +> Historical evidence/reference from Narrative Contracts. The current project is +> **MateProbe by Mate4B**; see the [migration guide](migration-mateprobe.md). +> Original package names, versions and recorded results below identify that earlier work. + ```sh python -m pip install narrative-contracts==0.1.0a3 pytest-narrative-contracts==0.1.0a3 ``` diff --git a/docs/api-a4.md b/docs/api-a4.md new file mode 100644 index 0000000..c3d6543 --- /dev/null +++ b/docs/api-a4.md @@ -0,0 +1,46 @@ +# MateProbe API: 0.1.0a4 + +```sh +python -m pip install mateprobe==0.1.0a4 pytest-mateprobe==0.1.0a4 +``` + +Python 3.11+; the pytest plugin is optional. Core runtime has zero third-party +dependencies. You do not need to change your validator to start auditing it. + +## Audit an existing validator + +Import `AuditCase`, `Obligation`, `Verdict`, `ValidatorAuditReport`, and +`audit_validator` from `mateprobe.validator_audit`. Import `Relation` +and `Validity` from `mateprobe.mutations`. + +- [Runnable audit and result semantics](validator-audit.md). +- [Progressive adoption](adoption-levels.md), beginning with a boolean adapter. +- [Keep a survivor as a regression test](policy-regression.md), including the + `mateprobe.audit_validator(...)` pytest fixture and retained JSON artifacts. +- [Independent evidence flags and provenance](audit-provenance.md). +- [JSON Schema and Pydantic integration trials](external-validator-integrations.md). + +The adapter receives only the sample, not its expected findings. A rejection with +no matching finding IDs is unattributed, not targeted detection. Errors and +unknown variants remain in the denominator. The caller defines and justifies +obligations, fault cases, and valid controls; the library does not infer labels. + +## State-conditioned checks + +`check_fields`, `CompareFields`, and `AllowedTransition` are available from +`mateprobe`. See [field bindings and bounded relations](state-bindings.md). + +The Document/Claim constructors, contracts, evaluation states, CLI and original +mutation API retain the [a2 reference signatures](api-a2.md). Install a4 when +combining those APIs with the new helpers. The historical reference's a2 install +commands are retained for reproducibility. + +## Report compatibility + +External-validator reports use schema 2, including independent obligation flags; +`assessment` remains a display summary. The pytest envelope stays at schema 1 and +each nested report carries its own format. Package/report `library_version` is +`0.1.0a4`. Package versions and report schema versions describe different things. + +No result certifies arbitrary prose as truthful or establishes general policy +coverage. Review [scope and limits](development-scope.md) before using a score. diff --git a/docs/architecture.md b/docs/architecture.md index 0abec0f..cb21854 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -18,8 +18,8 @@ flowchart LR ## Packages -- `narrative-contracts`: immutable models, protocol, rules, strict JSON loader, CLI, adapters and mutation audit. Standard library only. -- `pytest-narrative-contracts`: optional pytest entry point, fixture, assertion diagnostics and report export. +- `mateprobe`: immutable models, protocol, rules, strict JSON loader, CLI, adapters and mutation audit. Standard library only. +- `pytest-mateprobe`: optional pytest entry point, fixture, assertion diagnostics and report export. - `benchmarks`: authored feasibility corpus, explicit simple baseline, controls and source-code mutation experiment. - `paper`: draft, limitations, references and the protocol for an independently validated study. diff --git a/docs/audit-provenance.md b/docs/audit-provenance.md index d61602b..6db9b78 100644 --- a/docs/audit-provenance.md +++ b/docs/audit-provenance.md @@ -1,6 +1,6 @@ # Audit provenance and report schema 2 -The external-validator audit uses report schema **2** in alpha 0.1.0a3. +The external-validator audit uses report schema **2** in alpha 0.1.0a4. The original state-evaluation and mutation-report formats are unchanged. The pytest JSON envelope remains schema 1; each nested report has its own schema. @@ -12,8 +12,9 @@ Every external audit includes: - `schema_version`: the report format, independent from the package version. - `provenance`: null unless explicitly supplied, or a Git snapshot labelled by source. -Version 0.1.0a3 identifies the new audit APIs and report metadata. Historical -0.1.0a2 wheels and frozen benchmark artifacts retain their original versions. +Version 0.1.0a4 identifies the renamed MateProbe distribution. The audit APIs and +report schema introduced in Narrative Contracts 0.1.0a3 are unchanged. Historical +a2/a3 wheels and frozen benchmark artifacts retain their original versions. A package version alone does not identify a local patch or external validator; retain code/configuration and explicit provenance for development comparisons. @@ -41,8 +42,8 @@ Scores, exclusions, challenge scopes, and denominators are unchanged. ## Explicitly observe a repository ```python -from narrative_contracts.provenance import observe_git -from narrative_contracts.validator_audit import audit_validator +from mateprobe.provenance import observe_git +from mateprobe.validator_audit import audit_validator git = observe_git("/path/to/validator-repository") report = audit_validator( @@ -69,7 +70,7 @@ reproducibility requires them. External dependencies need their own version reco ## Caller-supplied CI metadata ```python -from narrative_contracts.provenance import supplied_git_provenance +from mateprobe.provenance import supplied_git_provenance git = supplied_git_provenance(commit="0123456789abcdef0123456789abcdef01234567") # source="supplied", dirty=None (unknown), no Git command executed. diff --git a/docs/choosing-an-evaluator.md b/docs/choosing-an-evaluator.md index f61901c..7270d00 100644 --- a/docs/choosing-an-evaluator.md +++ b/docs/choosing-an-evaluator.md @@ -1,6 +1,6 @@ # Choosing an evaluation method -Use Narrative Contracts when the application can express a checkable predicate +Use MateProbe by Mate4B when the application can express a checkable predicate over trusted state, explicit declarations, or a deliberately narrow text pattern. It evaluates those predicates without inference calls. This is a scoped guarantee, not general factuality or writing-quality evaluation. diff --git a/docs/development-scope.md b/docs/development-scope.md index 4bac7cc..502fb4b 100644 --- a/docs/development-scope.md +++ b/docs/development-scope.md @@ -1,7 +1,7 @@ -# Alpha a3 scope: audit existing validators +# Alpha a4 scope: audit existing validators -Version 0.1.0a3 packages the audit and state-binding APIs developed after a2. -Install `narrative-contracts==0.1.0a3` for these additions; a2 wheels do not include them. Frozen benchmark artifacts are not rewritten. +Version 0.1.0a4 packages the audit and state-binding APIs developed after a2. +Install `mateprobe==0.1.0a4` for these additions; a2 wheels do not include them. Frozen benchmark artifacts are not rewritten. ## Delivered scope diff --git a/docs/external-validator-integrations.md b/docs/external-validator-integrations.md index 4e5beca..be15339 100644 --- a/docs/external-validator-integrations.md +++ b/docs/external-validator-integrations.md @@ -1,10 +1,10 @@ # External validator integrations -Requires `narrative-contracts==0.1.0a3`. These APIs are not available in a2. +Requires `mateprobe==0.1.0a4`. These APIs are not available in a2. These opt-in, offline examples audit authored domain obligations against two existing third-party validator configurations. They do not add either library to core -`narrative-contracts`, and make no claim about production coverage or adoption. +`mateprobe`, and make no claim about production coverage or adoption. The jsonschema example authors an IPv4 obligation. JSON Schema's `format` keyword is an annotation by default, so `Draft202012Validator(schema)` accepts `999.1.1.1` and diff --git a/docs/first-audit.md b/docs/first-audit.md index 26e2279..53137da 100644 --- a/docs/first-audit.md +++ b/docs/first-audit.md @@ -1,7 +1,7 @@ # Your first validator audit **Bring an existing validator. You do not need to adopt Document, Claim, or a new -policy language.** This small example targets published `0.1.0a3` and runs offline +policy language.** This small example targets alpha `0.1.0a4` and runs offline after installation. The walkthrough is designed for a short demo; five minutes is a presentation target, not measured integration time. @@ -16,10 +16,10 @@ Python 3.11+ is required. In a new directory: ```sh python -m venv .venv . .venv/bin/activate -python -m pip install narrative-contracts==0.1.0a3 pytest-narrative-contracts==0.1.0a3 -curl -fL https://mate4b.github.io/narrative-contracts/examples/first_audit.py -o first_audit.py +python -m pip install mateprobe==0.1.0a4 pytest-mateprobe==0.1.0a4 +curl -fL https://mate4b.github.io/mateprobe/examples/first_audit.py -o first_audit.py python first_audit.py --output audit-report -python -m pytest first_audit.py --narrative-report=pytest-audit.json +python -m pytest first_audit.py --mateprobe-report=pytest-audit.json ``` On Windows, activate with `.venv\Scripts\Activate.ps1` in PowerShell and download @@ -59,7 +59,7 @@ threshold acknowledges that survivor; it does not move the challenge out of the score or allow another missed fault to replace it. To see a regression fail, temporarily replace `after` with `before` in the -`narrative.audit_validator(...)` call inside `test_refund_policy`, then rerun pytest. +`mateprobe.audit_validator(...)` call inside `test_refund_policy`, then rerun pytest. The command fails and `pytest-audit.json` retains the findings. Restore `after`. A validator that rejects all samples fails baseline validation as well. diff --git a/docs/integration-feedback.md b/docs/integration-feedback.md index c99403e..c8e67e0 100644 --- a/docs/integration-feedback.md +++ b/docs/integration-feedback.md @@ -4,7 +4,7 @@ The useful question is whether an audit found a policy failure worth fixing and keeping as a regression. Getting stuck or finding nothing useful also matters. An integration report is voluntary; external evaluation is not a release gate. -[Open an integration report](https://github.com/Mate4b/narrative-contracts/issues/new?template=integration-report.yml). +[Open an integration report](https://github.com/Mate4b/mateprobe/issues/new?template=integration-report.yml). Use synthetic or sanitized examples; never paste credentials, customer records, or unreviewed production traces. Evidence and exception messages can contain application data even when the report stores input digests instead of raw inputs. diff --git a/docs/launch.md b/docs/launch.md index 1948966..a90dd68 100644 --- a/docs/launch.md +++ b/docs/launch.md @@ -2,7 +2,7 @@ An assertion like `assert not validate(sample)` can pass for the wrong reason: you expected `customer_mismatch`, but the validator rejected `malformed_input`. -Narrative Contracts audits paired cases and reports that as an **unattributed +MateProbe by Mate4B audits paired cases and reports that as an **unattributed rejection**, rather than a successful targeted detection. Bring your existing Python validator. Define the failures you care about and @@ -41,5 +41,5 @@ welcome. Remove private data before sharing. These are alpha tools with authored examples. We have not established independent adoption, reduced engineering time, or general semantic accuracy. -[Source and issues](https://github.com/Mate4b/narrative-contracts) · -[Published API](api-a3.md) · [Scope and limits](development-scope.md) +[Source and issues](https://github.com/Mate4b/mateprobe) · +[Published API](api-a4.md) · [Scope and limits](development-scope.md) diff --git a/docs/lifecard-validator-audit.md b/docs/lifecard-validator-audit.md index d932b1c..e2fc79f 100644 --- a/docs/lifecard-validator-audit.md +++ b/docs/lifecard-validator-audit.md @@ -1,6 +1,6 @@ # LifeCard validator audit -Requires `narrative-contracts==0.1.0a3`. These APIs are not available in a2. +Requires `mateprobe==0.1.0a4`. These APIs are not available in a2. `examples/lifecard_validator_audit.py` is an opt-in, read-only demonstration of the validator audit API against a local LifeCard checkout. It imports diff --git a/docs/migration-mateprobe.md b/docs/migration-mateprobe.md new file mode 100644 index 0000000..dc37ba3 --- /dev/null +++ b/docs/migration-mateprobe.md @@ -0,0 +1,42 @@ +# Migrating to MateProbe + +MateProbe by Mate4B is the renamed distribution and import namespace for the +current release. Update application configuration and test code as follows: + +| Before | Current | +| --- | --- | +| `narrative-contracts` | `mateprobe` | +| `pytest-narrative-contracts` | `pytest-mateprobe` | +| `narrative_contracts` | `mateprobe` | +| `pytest_narrative_contracts` | `pytest_mateprobe` | +| `narrative` pytest fixture | `mateprobe` pytest fixture | +| `--narrative-report` | `--mateprobe-report` | +| `narrative-contracts` CLI | `mateprobe` CLI | + +For example, change imports such as `from narrative_contracts import ...` to +`from mateprobe import ...`, rename a test parameter from `narrative` to +`mateprobe`, and install the two current distributions with: + +```sh +python -m pip install mateprobe==0.1.0a4 pytest-mateprobe==0.1.0a4 +``` + +The a2 and a3 records, reports, research artifacts, and references describe the +versions that produced them and remain frozen. The old published distributions +also remain available; this migration does not rewrite or republish them. + +Built-in rule configuration `type` identifiers retain their original +`narrative_contracts.*` strings as stable report metadata, not executable imports. +This preserves configuration digests and frozen replay comparisons. Custom rules +continue to use their own fully qualified class names. + +To replay the original real-output mutation experiment against the renamed library, +use an environment with MateProbe installed, then run +`python benchmarks/replay_renamed_mutations.py --output /tmp/mateprobe-replay`. +It verifies and executes the frozen generator and adapter bytes in a subprocess, +with historical import names bound to MateProbe only in that process. The original +source and corpus integrity checks remain active. + +The replay command produces results; CI separately checks the summary byte for byte +and compares case reports with `scripts/compare_replay.py`, allowing only the +declared `library_version` transition from `0.1.0a2` to `0.1.0a4`. diff --git a/docs/natural-benchmark.md b/docs/natural-benchmark.md index a4b8916..ce2053f 100644 --- a/docs/natural-benchmark.md +++ b/docs/natural-benchmark.md @@ -1,5 +1,9 @@ # Real-model pilot and deterministic replay +> Historical evidence/reference from Narrative Contracts. The current project is +> **MateProbe by Mate4B**; see the [migration guide](migration-mateprobe.md). +> Original package names, versions and recorded results below identify that earlier work. + This pilot uses actual locally generated outputs from Qwen3 and Gemma3 on fictional, authored scenarios. It is distinct from synthetic mutations and from production traffic. The frozen design is in [release-protocol.md](../paper/release-protocol.md). diff --git a/docs/policy-regression.md b/docs/policy-regression.md index 01eb57b..2237529 100644 --- a/docs/policy-regression.md +++ b/docs/policy-regression.md @@ -1,7 +1,7 @@ # Turn a survivor into a regression check This guide shows how to preserve a real audit failure while fixing the validator -that caused it. It uses the `audit_validator` API from `narrative-contracts==0.1.0a3`; it does +that caused it. It uses the `audit_validator` API from `mateprobe==0.1.0a4`; it does not require replacing the validator or adding a new policy language. The example is self-contained and uses no private application data. @@ -11,8 +11,8 @@ Suppose refunds are allowed only in USD and only up to the supplied limit. The existing validator checks the amount but forgets the currency rule: ```python -from narrative_contracts.mutations import Relation, Validity -from narrative_contracts.validator_audit import ( +from mateprobe.mutations import Relation, Validity +from mateprobe.validator_audit import ( AuditCase, Obligation, Verdict, @@ -132,8 +132,8 @@ validator instead of copying this illustrative implementation. Keep the cases unchanged when revising the validator. ```python -def test_refund_policy(narrative): - narrative.audit_validator( +def test_refund_policy(mateprobe): + mateprobe.audit_validator( validate_refund_after, cases, obligations=obligations, @@ -142,7 +142,7 @@ def test_refund_policy(narrative): ``` ```sh -python -m pytest tests/test_refund_policy.py --narrative-report=refund-audit.json +python -m pytest tests/test_refund_policy.py --mateprobe-report=refund-audit.json ``` The default gate requires targeted detection of every eligible fault and preservation diff --git a/docs/pydantic.md b/docs/pydantic.md index 974c7dd..113a707 100644 --- a/docs/pydantic.md +++ b/docs/pydantic.md @@ -1,13 +1,13 @@ # Pydantic reply boundary This offline recipe uses Pydantic v2 to validate a structured reply before it -becomes a `narrative-contracts` document. Install the published packages in a +becomes a `mateprobe` document. Install the published packages in a fresh Python 3.11+ environment: ```sh python -m venv .venv . .venv/bin/activate -python -m pip install narrative-contracts==0.1.0a3 'pydantic>=2,<3' +python -m pip install mateprobe==0.1.0a4 'pydantic>=2,<3' ``` Run it from the repository root: @@ -35,7 +35,7 @@ The example demonstrates three boundaries: match state. The released package checks declarations against state; it does not infer truth from free prose. -The recipe targets the published `narrative-contracts==0.1.0a3` API and does not +The recipe targets the published `mateprobe==0.1.0a4` API and does not use the additional field-checking, relation, or audit APIs. Pydantic is an example dependency only; it is not added to the package runtime dependencies. diff --git a/docs/pypi-publishing.md b/docs/pypi-publishing.md index 2f4b2b5..535f945 100644 --- a/docs/pypi-publishing.md +++ b/docs/pypi-publishing.md @@ -1,13 +1,26 @@ -# Published packages and verification +# Packages and verification -## Current release: 0.1.0a3 +## MateProbe 0.1.0a4 + +The project is now **MateProbe by Mate4B**. The renamed distributions are +`mateprobe` and `pytest-mateprobe`, version `0.1.0a4`. See the +[migration guide](migration-mateprobe.md) and [current API](api-a4.md). +The [GitHub alpha release](https://github.com/Mate4b/mateprobe/releases/tag/v0.1.0a4) +contains wheels and source distributions built from commit +`e842d66faa5d2dd6d8cc9a7dd8abd7e31bc49078`. The [artifact manifest](../scripts/release-a4.json) +pins their SHA-256 hashes. Publication under the new PyPI names is pending; +use the release wheels in the meantime. The historical verification records below +apply to their original distributions. + + +## Historical release: Narrative Contracts 0.1.0a3 ```sh python -m pip install narrative-contracts==0.1.0a3 pytest-narrative-contracts==0.1.0a3 ``` Python 3.11+ is required; the pytest plugin is optional. The independent validator -audit and state-field bindings are available in a3. See [the API index](api-a3.md) +audit and state-field bindings are available in a3. See [the historical API index](api-a3.md) and [first audit recipe](first-audit.md). [PyPI core](https://pypi.org/project/narrative-contracts/0.1.0a3/) · diff --git a/docs/quickstart.md b/docs/quickstart.md index 7981f71..3964aa9 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -1,25 +1,25 @@ # Five-minute quickstart -This walkthrough is fully offline. It uses the deterministic `0.1.0a3` API and +This walkthrough is fully offline. It uses the deterministic `0.1.0a4` API and does not call a model, a service, or a network endpoint while evaluating a document. The runnable version is [`examples/five_minute_demo.py`](../examples/five_minute_demo.py). -## Install from PyPI +## Install the alpha -A fresh Python 3.11+ environment can install both published alpha packages: +A fresh Python 3.11+ environment can install both alpha packages: ```sh python -m venv .venv . .venv/bin/activate -python -m pip install narrative-contracts==0.1.0a3 pytest-narrative-contracts==0.1.0a3 +python -m pip install mateprobe==0.1.0a4 pytest-mateprobe==0.1.0a4 ``` -The pytest plugin is optional. It adds the `narrative` pytest fixture and the -`--narrative-report` option; the core package has no pytest dependency. When +The pytest plugin is optional. It adds the `mateprobe` pytest fixture and the +`--mateprobe-report` option; the core package has no pytest dependency. When working from this checkout, the equivalent editable install is: ```sh -python -m pip install -e '.[dev]' -e ./packages/pytest-narrative-contracts +python -m pip install -e '.[dev]' -e ./packages/pytest-mateprobe ``` ## Run the offline tour @@ -33,7 +33,7 @@ If you have the repository checkout, run from its root: Without a checkout, download the pinned example, then run it with your activated environment: ```sh -curl -fsSL https://raw.githubusercontent.com/Mate4b/narrative-contracts/fe771c43e8c56a7aee71509dbea316af650b01b2/examples/five_minute_demo.py -o five_minute_demo.py +curl -fsSL https://raw.githubusercontent.com/Mate4b/mateprobe/v0.1.0a4/examples/five_minute_demo.py -o five_minute_demo.py python five_minute_demo.py ``` @@ -77,10 +77,10 @@ pytest plugin. A minimal test can reuse the same objects from an application module: ```python -from narrative_contracts import Claim, Context, DeclaredClaimsConsistent, Document, Surface +from mateprobe import Claim, Context, DeclaredClaimsConsistent, Document, Surface -def test_reply_contract(narrative): +def test_reply_contract(mateprobe): document = Document( ( Surface( @@ -93,7 +93,7 @@ def test_reply_contract(narrative): ) context = Context({"current": {"request.status": "ready"}}) contracts = (DeclaredClaimsConsistent("reply-claims", ("reply",)),) - report = narrative.check(document, context, contracts) + report = mateprobe.check(document, context, contracts) assert report.accepted and report.complete ``` @@ -101,7 +101,7 @@ Run it serially and optionally write the structured reports: ```sh .venv/bin/pytest \ - --narrative-report=contract-results.json + --mateprobe-report=contract-results.json ``` The plugin records checks and mutation audits in JSON. It does not add prose diff --git a/docs/real-output-mutations.md b/docs/real-output-mutations.md index 178a695..b492894 100644 --- a/docs/real-output-mutations.md +++ b/docs/real-output-mutations.md @@ -1,5 +1,9 @@ # Mutation audit on real model outputs +> Historical evidence/reference from Narrative Contracts. The current project is +> **MateProbe by Mate4B**; see the [migration guide](migration-mateprobe.md). +> Original package names, versions and recorded results below identify that earlier work. + This campaign applies authored mutations to the 32 captured responses from the user's installed `gemma4:26b-mlx-hermes` and `qwen3.8:27b` tags. It makes no new model calls. These are fictional public scenarios, not production LifeCard sessions. All 32 original diff --git a/docs/roadmap.md b/docs/roadmap.md index 21899f1..c06ca65 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -1,6 +1,12 @@ # Roadmap and release gates -## Public alpha a3 +## MateProbe alpha a4 + +The current namespace is `mateprobe`, with optional `pytest-mateprobe` integration. +Version `0.1.0a4` renames the project under Mate4B while preserving MIT licensing, +rule behavior and historical research artifacts. See the [migration guide](migration-mateprobe.md). + +## Historical public alpha a3 - [x] Audit existing validators through plain samples and normalized verdicts. - [x] Independent evidence flags, actionable reports and optional Git provenance. @@ -25,7 +31,7 @@ adoption and corpus relevance remain open questions, not claims established by C The release provenance records the verified commit and CI run. The release verification records identify the exact published state. -Both a2 and a3 are available on PyPI; a3 is the current documented install target. +Both a2 and a3 remain available on PyPI under their original Narrative Contracts names. ## Additional published evidence diff --git a/docs/state-bindings.md b/docs/state-bindings.md index b8cf7ca..cb26981 100644 --- a/docs/state-bindings.md +++ b/docs/state-bindings.md @@ -1,6 +1,6 @@ # State bindings and relational contracts -Requires `narrative-contracts==0.1.0a3`. These APIs are not available in a2. +Requires `mateprobe==0.1.0a4`. These APIs are not available in a2. These optional helpers cover small, explicit relationships between structured output and state. They do not parse prose, evaluate expressions, or introduce @@ -16,7 +16,7 @@ relationship: refund eligibility, approval, and completed execution are differen facts and must not be equated. ```python -from narrative_contracts import check_fields +from mateprobe import check_fields report = check_fields( output={"reservation_confirmed": True}, # Structured output from the LLM. @@ -62,9 +62,9 @@ All behavior is version 1 and deterministic. Configure relationships as data and use the regular engine, for example: ```python -from narrative_contracts.engine import evaluate -from narrative_contracts.model import Context, Document, Surface -from narrative_contracts.relations import AllowedTransition, FieldRef +from mateprobe.engine import evaluate +from mateprobe.model import Context, Document, Surface +from mateprobe.relations import AllowedTransition, FieldRef document = Document((Surface("out", ""),)) context = Context({"before": {"status": "draft"}, "after": {"status": "ready"}}) @@ -80,7 +80,7 @@ report = evaluate(document, context, (rule,)) For a proposed amount and a trusted limit: ```python -from narrative_contracts import CompareFields +from mateprobe import CompareFields context = Context({"action": {"cents": 5000}, "trusted": {"limit_cents": 6000}}) rule = CompareFields( diff --git a/docs/support-workflow.md b/docs/support-workflow.md index b19e432..612f8b8 100644 --- a/docs/support-workflow.md +++ b/docs/support-workflow.md @@ -49,7 +49,7 @@ load the example-local `BranchSelection` rule; use the Python API for the full trusted-branch guarantee: ```sh -.venv/bin/narrative-contracts examples/support_workflow.json +.venv/bin/mateprobe examples/support_workflow.json ``` For a small deterministic benchmark corpus, import diff --git a/docs/testing-ai-output-validators.md b/docs/testing-ai-output-validators.md index dfd838f..62c3af5 100644 --- a/docs/testing-ai-output-validators.md +++ b/docs/testing-ai-output-validators.md @@ -7,10 +7,10 @@ description: Audit an existing Python validator with invalid output samples and You already validate structured responses from an LLM or agent. How do you know the validator catches the policy violations you care about, while accepting valid -changes? **Narrative Contracts runs your validator against paired samples and +changes? **MateProbe by Mate4B runs your validator against paired samples and reports what it detects, misses, or rejects for an unrelated reason.** -The independent `audit_validator` API is available in published `0.1.0a3` for +The independent `audit_validator` API is available in published `0.1.0a4` for Python 3.11+. It works with an adapter around your existing function; adopting the library's built-in state contracts or `Document`/`Claim` representation is optional. @@ -54,7 +54,7 @@ value must stay accepted. Rejecting every input cannot establish useful coverage | Source mutation testing | Program source, with tests run against altered implementations | Do the tests detect these changes to the implementation? | | Ordinary pytest assertions | Whatever fixtures and assertions you write | Does the code satisfy these explicit test expectations? | -These approaches can be used together. Narrative Contracts adds paired execution, +These approaches can be used together. MateProbe by Mate4B adds paired execution, finding attribution, obligation inventories, and JSON/Markdown reports to a supplied corpus. It does not edit validator source or automatically invent and label mutations. Every individual expectation can also be expressed directly in pytest. @@ -67,8 +67,8 @@ Every individual expectation can also be expressed directly in pytest. invalid variants, and valid controls. Expected findings belong to cases, never inside the adapter's output. 3. Call `audit_validator`, inspect individual outcomes, and retain survivors and errors. -4. Use the optional `narrative.audit_validator` pytest fixture to set thresholds and - write `--narrative-report=contract-results.json`. Assert important case outcomes +4. Use the optional `mateprobe.audit_validator` pytest fixture to set thresholds and + write `--mateprobe-report=contract-results.json`. Assert important case outcomes so a new miss cannot silently replace an acknowledged gap. See [the adapter API](validator-audit.md), [real Pydantic and JSON Schema adapters](external-validator-integrations.md), diff --git a/docs/validator-audit.md b/docs/validator-audit.md index b195607..a47d48e 100644 --- a/docs/validator-audit.md +++ b/docs/validator-audit.md @@ -1,8 +1,8 @@ # Audit your existing validator -Requires `narrative-contracts==0.1.0a3`. These APIs are not available in a2. -Install the published core and optional pytest plugin from PyPI; see the -[a3 installation command](api-a3.md). An editable checkout is only needed for development. +Requires `mateprobe==0.1.0a4`. These APIs are not available in a2. +After publication, install the core and optional pytest plugin from PyPI; see the +[migration guide](migration-mateprobe.md). An editable checkout is only needed for development. Use `audit_validator` when you already have a validator and want to test its failure modes. It does not require `Document`, `Context`, `Surface`, model calls, @@ -24,8 +24,8 @@ explains schema 2 and optional observed/supplied Git metadata. ## Minimal runnable example ```python -from narrative_contracts.mutations import Relation, Validity -from narrative_contracts.validator_audit import AuditCase, Obligation, Verdict, audit_validator +from mateprobe.mutations import Relation, Validity +from mateprobe.validator_audit import AuditCase, Obligation, Verdict, audit_validator # Your existing code. Keep its real finding IDs rather than using a generic "bad". @@ -141,8 +141,8 @@ One run does not measure stochastic stability or latency. ## Pytest and reports ```python -def test_refund_guard(narrative): - narrative.audit_validator( +def test_refund_guard(mateprobe): + mateprobe.audit_validator( validate_refund, cases, obligations=obligations, @@ -152,9 +152,9 @@ def test_refund_guard(narrative): ) ``` -Run `pytest --narrative-report=report.json`. The plugin records failed audits +Run `pytest --mateprobe-report=report.json`. The plugin records failed audits before raising an assertion, so CI reports retain the cases that failed. -Existing `narrative.check` and `narrative.audit` remain available. +Existing `mateprobe.check` and `mateprobe.audit` remain available. ## Full demonstrations diff --git a/docs/verification.md b/docs/verification.md index cbf73e0..21b2952 100644 --- a/docs/verification.md +++ b/docs/verification.md @@ -1,5 +1,9 @@ # Alpha a2 verification record +> Historical evidence/reference from Narrative Contracts. The current project is +> **MateProbe by Mate4B**; see the [migration guide](migration-mateprobe.md). +> Original package names, versions and recorded results below identify that earlier work. + Verified locally on 2026-09-29 with CPython 3.12.13 on macOS arm64. The development tool versions are recorded in `requirements-dev.lock`. diff --git a/docs_theme/main.html b/docs_theme/main.html index 180c2ca..cc3456d 100644 --- a/docs_theme/main.html +++ b/docs_theme/main.html @@ -1,7 +1,7 @@ {% extends "base.html" %} {% block htmltitle %} {% if page and page.file.src_uri == 'index.md' %} -
Published alpha: 0.1.0a3 · Historical evidence retains its original version. +
Published alpha: 0.1.0a4 · Historical evidence retains its original version. {% if page %}Read Markdown · {% endif %} Agent documentation index
diff --git a/examples/audit_existing_validator.py b/examples/audit_existing_validator.py index ca3be24..6ecd41b 100644 --- a/examples/audit_existing_validator.py +++ b/examples/audit_existing_validator.py @@ -13,9 +13,9 @@ from pathlib import Path from typing import Any -from narrative_contracts.mutations import Relation, Validity -from narrative_contracts.provenance import observe_git -from narrative_contracts.validator_audit import AuditCase, Obligation, Verdict, audit_validator +from mateprobe.mutations import Relation, Validity +from mateprobe.provenance import observe_git +from mateprobe.validator_audit import AuditCase, Obligation, Verdict, audit_validator Sample = dict[str, Any] diff --git a/examples/external_validators.py b/examples/external_validators.py index 517e40d..be57eca 100644 --- a/examples/external_validators.py +++ b/examples/external_validators.py @@ -13,9 +13,9 @@ from pydantic import BaseModel, HttpUrl, ValidationError from pydantic.networks import UrlConstraints -from narrative_contracts.mutations import Relation, Validity -from narrative_contracts.provenance import observe_git -from narrative_contracts.validator_audit import AuditCase, Obligation, Verdict, audit_validator +from mateprobe.mutations import Relation, Validity +from mateprobe.provenance import observe_git +from mateprobe.validator_audit import AuditCase, Obligation, Verdict, audit_validator def _path(path: Any) -> str: diff --git a/examples/first_audit.py b/examples/first_audit.py index 5eebcf1..b71348c 100644 --- a/examples/first_audit.py +++ b/examples/first_audit.py @@ -1,4 +1,4 @@ -"""Small, offline refund audit for published narrative-contracts 0.1.0a3. +"""Small, offline refund audit for published mateprobe 0.1.0a4. Receipts and labels are authored fixtures. No API is called and no refund occurs. In an application, trusted receipts must come from backend instrumentation. @@ -10,8 +10,8 @@ import json from pathlib import Path -from narrative_contracts.mutations import Relation, Validity -from narrative_contracts.validator_audit import AuditCase, Obligation, Verdict, audit_validator +from mateprobe.mutations import Relation, Validity +from mateprobe.validator_audit import AuditCase, Obligation, Verdict, audit_validator def existing_validator(sample): @@ -103,10 +103,10 @@ def after(sample): } -def test_refund_policy(narrative): +def test_refund_policy(mateprobe): # Keep the prose challenge in the score. Allow exactly this known survivor; # exact per-case assertions prevent a different missed fault from replacing it. - report = narrative.audit_validator( + report = mateprobe.audit_validator( after, CASES, obligations=OBLIGATIONS, diff --git a/examples/five_minute_demo.py b/examples/five_minute_demo.py index 2a9f264..63d8dcc 100644 --- a/examples/five_minute_demo.py +++ b/examples/five_minute_demo.py @@ -1,4 +1,4 @@ -"""Five-minute, offline walkthrough of the narrative-contracts API. +"""Five-minute, offline walkthrough of the mateprobe API. The transition below is ordinary application code. It produces scalar state snapshots before a reply is constructed; the contract runner only checks the @@ -10,7 +10,7 @@ from dataclasses import dataclass -from narrative_contracts import ( +from mateprobe import ( Claim, Context, DeclaredClaimsConsistent, @@ -23,9 +23,9 @@ Surface, evaluate, ) -from narrative_contracts.engine import Contract -from narrative_contracts.model import Scalar -from narrative_contracts.mutations import ( +from mateprobe.engine import Contract +from mateprobe.model import Scalar +from mateprobe.mutations import ( MutationCase, Relation, Sample, diff --git a/examples/lifecard_adapter.py b/examples/lifecard_adapter.py index 3e02884..febeee1 100644 --- a/examples/lifecard_adapter.py +++ b/examples/lifecard_adapter.py @@ -1,7 +1,7 @@ """Minimal original fixture demonstrating the LifeCard adapter without importing its engine.""" -from narrative_contracts import Claim, Context, DeclaredClaimsConsistent, StateChanged, evaluate -from narrative_contracts.adapters import lifecard_document +from mateprobe import Claim, Context, DeclaredClaimsConsistent, StateChanged, evaluate +from mateprobe.adapters import lifecard_document card = { "title_template": "Una propuesta", diff --git a/examples/lifecard_validator_audit.py b/examples/lifecard_validator_audit.py index 0198641..3717493 100644 --- a/examples/lifecard_validator_audit.py +++ b/examples/lifecard_validator_audit.py @@ -82,8 +82,8 @@ def _with_variant( def _run(root: Path) -> tuple[Any, dict[str, str], dict[str, Any]]: - from narrative_contracts.mutations import Relation, Validity - from narrative_contracts.validator_audit import ( + from mateprobe.mutations import Relation, Validity + from mateprobe.validator_audit import ( AuditCase, Obligation, Verdict, diff --git a/examples/mutation_audit.py b/examples/mutation_audit.py index 115644d..544d8c3 100644 --- a/examples/mutation_audit.py +++ b/examples/mutation_audit.py @@ -2,8 +2,8 @@ import json -from narrative_contracts import Context, Document, MinimumTokens, Surface -from narrative_contracts.mutations import ( +from mateprobe import Context, Document, MinimumTokens, Surface +from mateprobe.mutations import ( MutationCase, Relation, Sample, diff --git a/examples/pydantic_reply.py b/examples/pydantic_reply.py index af2ad8d..8ec6bb6 100644 --- a/examples/pydantic_reply.py +++ b/examples/pydantic_reply.py @@ -1,4 +1,4 @@ -"""Pydantic v2 schema boundary for a narrative-contracts reply. +"""Pydantic v2 schema boundary for a mateprobe reply. The branch and authoritative state are selected by application code. Pydantic validates the structured reply and projects only an explicit allowlist into @@ -11,7 +11,7 @@ from pydantic import BaseModel, ConfigDict, ValidationError -from narrative_contracts import ( +from mateprobe import ( Claim, Context, DeclaredClaimsConsistent, diff --git a/examples/support_workflow.py b/examples/support_workflow.py index 97c1b3f..e3c15d3 100644 --- a/examples/support_workflow.py +++ b/examples/support_workflow.py @@ -13,7 +13,7 @@ from dataclasses import dataclass from typing import Literal -from narrative_contracts import ( +from mateprobe import ( Claim, Context, DeclaredClaimsConsistent, @@ -28,8 +28,8 @@ Surface, evaluate, ) -from narrative_contracts.engine import Contract -from narrative_contracts.model import Check, Scalar +from mateprobe.engine import Contract +from mateprobe.model import Check, Scalar Branch = Literal["refund_eligible", "refund_ineligible"] Variant = Literal[ diff --git a/llms.txt b/llms.txt index 338ecfa..8bed4fa 100644 --- a/llms.txt +++ b/llms.txt @@ -1,45 +1,45 @@ -# Narrative Contracts +# MateProbe by Mate4B > Test existing Python validators with known-invalid AI output samples and valid controls. Paired input audits report missed faults, targeted detections, unrelated rejections, unknowns and errors. Built-in contracts also check explicit declarations against application state; arbitrary prose truthfulness is outside the guarantee. For questions such as "How do I test whether my validator catches invalid agent outputs?", start with the problem guide below. Audit mutations are caller-supplied input variants, not edits to validator source. The library does not automatically generate or justify the cases. Offline deterministic results require a deterministic wrapped validator; the audit does not make an arbitrary callback deterministic. -Install the published alpha with `python -m pip install narrative-contracts==0.1.0a3 pytest-narrative-contracts==0.1.0a3` (Python 3.11+). The pytest plugin is optional. Version a3 includes `check_fields`, relational rules, and the independent validator-audit API. +Install the current alpha with `python -m pip install mateprobe==0.1.0a4 pytest-mateprobe==0.1.0a4` (Python 3.11+). The pytest plugin is optional. Version a4 includes `check_fields`, relational rules, and the independent validator-audit API. Supply authoritative state and branch selection from trusted application code. Validate required output fields before mapping them to `Claim` objects. A complete, accepted report covers configured predicates on supplied inputs, not every assertion in the prose. Evaluation needs no model calls; installation needs network access. Mutation labels and their provenance come from the caller. ## Start here -- [How to test AI output validators in Python](https://mate4b.github.io/narrative-contracts/docs/testing-ai-output-validators.md): problem-to-API guide, wrong-reason rejection, valid controls, and the distinction from source mutation testing. -- [First validator audit](https://mate4b.github.io/narrative-contracts/docs/first-audit.md): one downloadable file, published a3, two completion faults, one valid control and one retained prose challenge. -- [Integration feedback](https://mate4b.github.io/narrative-contracts/docs/integration-feedback.md): report relevant survivors, regressions, blockers, or no useful finding. +- [How to test AI output validators in Python](https://mate4b.github.io/mateprobe/docs/testing-ai-output-validators.md): problem-to-API guide, wrong-reason rejection, valid controls, and the distinction from source mutation testing. +- [First validator audit](https://mate4b.github.io/mateprobe/docs/first-audit.md): one downloadable file, two completion faults, one valid control and one retained prose challenge. +- [Integration feedback](https://mate4b.github.io/mateprobe/docs/integration-feedback.md): report relevant survivors, regressions, blockers, or no useful finding. -- [Agent integration guide](https://mate4b.github.io/narrative-contracts/docs/agent-guide.md): install, authority boundary, complete pytest example, and version selection. -- [Five-minute quickstart](https://mate4b.github.io/narrative-contracts/docs/quickstart.md): accepted output, detected fault, and a prose-only contradiction that passes. -- [Published 0.1.0a3 API](https://mate4b.github.io/narrative-contracts/docs/api-a3.md): imports, constructors, result states, pytest, and mutation entry points. -- [Pydantic recipe](https://mate4b.github.io/narrative-contracts/docs/pydantic.md): strict schema validation followed by explicit state checks. +- [Agent integration guide](https://mate4b.github.io/mateprobe/docs/agent-guide.md): install, authority boundary, complete pytest example, and version selection. +- [Five-minute quickstart](https://mate4b.github.io/mateprobe/docs/quickstart.md): accepted output, detected fault, and a prose-only contradiction that passes. +- [Current a4 API](https://mate4b.github.io/mateprobe/docs/api-a4.md): imports, result states, pytest, and mutation entry points. +- [Pydantic recipe](https://mate4b.github.io/mateprobe/docs/pydantic.md): strict schema validation followed by explicit state checks. ## Scope and evidence -- [Choosing an evaluator](https://mate4b.github.io/narrative-contracts/docs/choosing-an-evaluator.md): deterministic contracts, schemas, lexical heuristics, and model judges. -- [Contract semantics](https://mate4b.github.io/narrative-contracts/docs/contracts.md): typed equality, unknowns, errors, policies, and mutation denominators. -- [Real-output mutations](https://mate4b.github.io/narrative-contracts/docs/real-output-mutations.md): 32 captured responses, 384 authored variants, and 64 accepted prose-only challenges; no semantic accuracy claim. +- [Choosing an evaluator](https://mate4b.github.io/mateprobe/docs/choosing-an-evaluator.md): deterministic contracts, schemas, lexical heuristics, and model judges. +- [Contract semantics](https://mate4b.github.io/mateprobe/docs/contracts.md): typed equality, unknowns, errors, policies, and mutation denominators. +- [Real-output mutations](https://mate4b.github.io/mateprobe/docs/real-output-mutations.md): 32 captured responses, 384 authored variants, and 64 accepted prose-only challenges; no semantic accuracy claim. ## Audit existing validators -You do not need to change your validator to start. These APIs are available in a3. +You do not need to change your validator to start. These APIs are available in a4. -- [Audit an existing validator](https://mate4b.github.io/narrative-contracts/docs/validator-audit.md): paired samples, expected finding IDs, controls and CI reports without adopting Document/Claim. -- [Adoption levels](https://mate4b.github.io/narrative-contracts/docs/adoption-levels.md): boolean acceptance, findings, scopes and incomplete evidence; boolean rejection alone is not targeted detection. -- [Audit provenance](https://mate4b.github.io/narrative-contracts/docs/audit-provenance.md): report schema 2, library version, corpus digest and optional supplied/observed Git metadata. -- [External validator trials](https://mate4b.github.io/narrative-contracts/docs/external-validator-integrations.md): authored configuration tests with real JSON Schema and Pydantic engines; not independent adopter evidence. +- [Audit an existing validator](https://mate4b.github.io/mateprobe/docs/validator-audit.md): paired samples, expected finding IDs, controls and CI reports without adopting Document/Claim. +- [Adoption levels](https://mate4b.github.io/mateprobe/docs/adoption-levels.md): boolean acceptance, findings, scopes and incomplete evidence; boolean rejection alone is not targeted detection. +- [Audit provenance](https://mate4b.github.io/mateprobe/docs/audit-provenance.md): report schema 2, library version, corpus digest and optional supplied/observed Git metadata. +- [External validator trials](https://mate4b.github.io/mateprobe/docs/external-validator-integrations.md): authored configuration tests with real JSON Schema and Pydantic engines; not independent adopter evidence. -- [Policy regression guide](https://mate4b.github.io/narrative-contracts/docs/policy-regression.md): keep survivors as permanent pytest checks and share sanitized feedback. +- [Policy regression guide](https://mate4b.github.io/mateprobe/docs/policy-regression.md): keep survivors as permanent pytest checks and share sanitized feedback. ## Optional -- [Discovery and a3 integration observations](https://mate4b.github.io/narrative-contracts/docs/agent-readiness.md): target not recommended in one bounded search task; new a3 integration with four consumer tests and twenty reviewer checks, with limits retained. -- [Agent integration trial](https://mate4b.github.io/narrative-contracts/docs/agent-adoption.md): one prompted integration with frozen code and replay; not evidence of unprompted recommendations. -- [Alpha a3 scope](https://mate4b.github.io/narrative-contracts/docs/development-scope.md): delivered APIs and explicit limits. -- [Repository](https://github.com/Mate4b/narrative-contracts): source, issues, and releases. -- [Contributing](https://mate4b.github.io/narrative-contracts/CONTRIBUTING.md): report missed violations and false rejections with minimal evidence. +- [Discovery and a3 integration observations](https://mate4b.github.io/mateprobe/docs/agent-readiness.md): target not recommended in one bounded search task; new a3 integration with four consumer tests and twenty reviewer checks, with limits retained. +- [Agent integration trial](https://mate4b.github.io/mateprobe/docs/agent-adoption.md): one prompted integration with frozen code and replay; not evidence of unprompted recommendations. +- [Alpha a4 scope](https://mate4b.github.io/mateprobe/docs/development-scope.md): delivered APIs and explicit limits. +- [Repository](https://github.com/Mate4b/mateprobe): source, issues, and releases. +- [Contributing](https://mate4b.github.io/mateprobe/CONTRIBUTING.md): report missed violations and false rejections with minimal evidence. diff --git a/mkdocs.yml b/mkdocs.yml index 17172cf..e3f7aea 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,7 +1,7 @@ -site_name: Narrative Contracts -site_url: https://mate4b.github.io/narrative-contracts/ +site_name: MateProbe by Mate4B +site_url: https://mate4b.github.io/mateprobe/ site_description: Test Python validators with invalid AI output samples and valid controls. Find missed faults and wrong-reason rejections with pytest audit reports. -repo_url: https://github.com/Mate4b/narrative-contracts +repo_url: https://github.com/Mate4b/mateprobe edit_uri: '' docs_dir: .docs-src site_dir: site @@ -29,7 +29,8 @@ nav: - Integration feedback: docs/integration-feedback.md - Quickstart: docs/quickstart.md - Agent integration: docs/agent-guide.md - - Published API: docs/api-a3.md + - Migration to MateProbe: docs/migration-mateprobe.md + - Published API: docs/api-a4.md - Pydantic recipe: docs/pydantic.md - Guarantees: - Choosing an evaluator: docs/choosing-an-evaluator.md @@ -43,7 +44,7 @@ nav: - Expanded mutations: docs/mutation-campaign.md - Technical report: paper/draft.md - Validator audits: - - Alpha a3 scope: docs/development-scope.md + - Alpha a4 scope: docs/development-scope.md - Validator audits: docs/validator-audit.md - Adoption levels: docs/adoption-levels.md - Policy regression: docs/policy-regression.md diff --git a/packages/pytest-narrative-contracts/LICENSE b/packages/pytest-mateprobe/LICENSE similarity index 95% rename from packages/pytest-narrative-contracts/LICENSE rename to packages/pytest-mateprobe/LICENSE index 207b8b1..b141333 100644 --- a/packages/pytest-narrative-contracts/LICENSE +++ b/packages/pytest-mateprobe/LICENSE @@ -1,6 +1,7 @@ MIT License Copyright (c) 2026 Narrative Contracts contributors +Copyright (c) 2026 Mate4B and MateProbe contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/packages/pytest-narrative-contracts/README.md b/packages/pytest-mateprobe/README.md similarity index 66% rename from packages/pytest-narrative-contracts/README.md rename to packages/pytest-mateprobe/README.md index 6a79a20..94921c9 100644 --- a/packages/pytest-narrative-contracts/README.md +++ b/packages/pytest-mateprobe/README.md @@ -1,16 +1,16 @@ -# pytest-narrative-contracts +# pytest-mateprobe -Optional pytest integration for `narrative-contracts`, version `0.1.0a3`. +Optional pytest integration for `mateprobe`, version `0.1.0a4`. Install both packages from the repository root: ```sh -pip install -e . -e ./packages/pytest-narrative-contracts +pip install -e . -e ./packages/pytest-mateprobe ``` -The `narrative` fixture exposes `check(document, context, contracts, policy=None)` and +The `mateprobe` fixture exposes `check(document, context, contracts, policy=None)` and `audit(cases, contracts, detection=1.0, preservation=1.0, policy=None)`. Failures identify -the rule, status, surface and evidence. `--narrative-report=report.json` writes all reports, +the rule, status, surface and evidence. `--mateprobe-report=report.json` writes all reports, including failed checks. Audit thresholds require both violation and preservation cases. Pytest discovers this package through the `pytest11` entry point. The core library has @@ -18,12 +18,12 @@ no pytest dependency. Serial JSON reports only; distributed report merging is no Alpha software; MIT license. General free-text truthfulness is outside its guarantees. -## Audit existing validators (a3) +## Audit existing validators (a4) -Version 0.1.0a3 provides `narrative.audit_validator(validator, cases, +Version 0.1.0a4 provides `mateprobe.audit_validator(validator, cases, obligations=..., validator_id=..., detection=1.0, preservation=1.0)` for existing validators. It records JSON results before asserting thresholds, including -failures. Install both packages at `0.1.0a3`; this API is not in older a2 wheels. See [the audit guide](../../docs/validator-audit.md). +failures. Install both packages at `0.1.0a4`; this API is not in older a2/a3 wheels. See [the audit guide](../../docs/validator-audit.md). `audit_validator` also accepts optional `provenance=...` from the explicit Git helpers. Its nested report uses schema 2; the aggregate pytest envelope remains diff --git a/packages/pytest-mateprobe/pyproject.toml b/packages/pytest-mateprobe/pyproject.toml new file mode 100644 index 0000000..baf948d --- /dev/null +++ b/packages/pytest-mateprobe/pyproject.toml @@ -0,0 +1,24 @@ +[build-system] +requires = ["hatchling>=1.27,<2"] +build-backend = "hatchling.build" + +[project] +name = "pytest-mateprobe" +version = "0.1.0a4" +description = "Pytest fixtures and reports for mateprobe" +readme = "README.md" +requires-python = ">=3.11" +license = "MIT" +dependencies = ["mateprobe==0.1.0a4", "pytest>=8,<10"] +classifiers = ["Development Status :: 3 - Alpha", "Framework :: Pytest", "Programming Language :: Python :: 3"] + +[project.urls] +Repository = "https://github.com/Mate4b/mateprobe" +Issues = "https://github.com/Mate4b/mateprobe/issues" +Documentation = "https://mate4b.github.io/mateprobe/" + +[project.entry-points.pytest11] +mateprobe = "pytest_mateprobe.plugin" + +[tool.hatch.build.targets.wheel] +packages = ["src/pytest_mateprobe"] diff --git a/packages/pytest-mateprobe/src/pytest_mateprobe/__init__.py b/packages/pytest-mateprobe/src/pytest_mateprobe/__init__.py new file mode 100644 index 0000000..2bb577b --- /dev/null +++ b/packages/pytest-mateprobe/src/pytest_mateprobe/__init__.py @@ -0,0 +1 @@ +"""Optional pytest integration for mateprobe.""" diff --git a/packages/pytest-narrative-contracts/src/pytest_narrative_contracts/plugin.py b/packages/pytest-mateprobe/src/pytest_mateprobe/plugin.py similarity index 77% rename from packages/pytest-narrative-contracts/src/pytest_narrative_contracts/plugin.py rename to packages/pytest-mateprobe/src/pytest_mateprobe/plugin.py index 58e58dc..7841e33 100644 --- a/packages/pytest-narrative-contracts/src/pytest_narrative_contracts/plugin.py +++ b/packages/pytest-mateprobe/src/pytest_mateprobe/plugin.py @@ -8,10 +8,10 @@ import pytest -from narrative_contracts import Context, Contract, Document, Policy, Report, evaluate -from narrative_contracts.mutations import CampaignReport, MutationCase, audit -from narrative_contracts.provenance import GitProvenance -from narrative_contracts.validator_audit import ( +from mateprobe import Context, Contract, Document, Policy, Report, evaluate +from mateprobe.mutations import CampaignReport, MutationCase, audit +from mateprobe.provenance import GitProvenance +from mateprobe.validator_audit import ( AuditCase, Obligation, ValidatorAuditReport, @@ -24,7 +24,7 @@ @dataclass -class NarrativeAssertions: +class MateProbeAssertions: records: list[dict[str, Any]] = field(default_factory=list) node_id: str = "" @@ -78,25 +78,25 @@ def audit_validator( def pytest_addoption(parser: pytest.Parser) -> None: - parser.getgroup("narrative-contracts").addoption( - "--narrative-report", metavar="PATH", help="Write narrative contract reports as JSON" + parser.getgroup("mateprobe").addoption( + "--mateprobe-report", metavar="PATH", help="Write mateprobe contract reports as JSON" ) def pytest_configure(config: pytest.Config) -> None: config.stash[_REPORTS] = [] # Avoid silently losing worker results; distributed report merging is not implemented. - if config.getoption("narrative_report") and getattr(config.option, "numprocesses", None): - raise pytest.UsageError("--narrative-report currently requires a non-xdist run") + if config.getoption("mateprobe_report") and getattr(config.option, "numprocesses", None): + raise pytest.UsageError("--mateprobe-report currently requires a non-xdist run") @pytest.fixture -def narrative(request: pytest.FixtureRequest) -> NarrativeAssertions: - return NarrativeAssertions(request.config.stash[_REPORTS], request.node.nodeid) +def mateprobe(request: pytest.FixtureRequest) -> MateProbeAssertions: + return MateProbeAssertions(request.config.stash[_REPORTS], request.node.nodeid) def pytest_sessionfinish(session: pytest.Session, exitstatus: int) -> None: - path = session.config.getoption("narrative_report") + path = session.config.getoption("mateprobe_report") if path: output = {"schema_version": 1, "reports": session.config.stash[_REPORTS]} try: @@ -104,4 +104,4 @@ def pytest_sessionfinish(session: pytest.Session, exitstatus: int) -> None: json.dumps(output, ensure_ascii=False, indent=2) + "\n", encoding="utf-8" ) except OSError as exc: - raise pytest.UsageError(f"Cannot write narrative report: {exc}") from exc + raise pytest.UsageError(f"Cannot write mateprobe report: {exc}") from exc diff --git a/packages/pytest-narrative-contracts/src/pytest_narrative_contracts/py.typed b/packages/pytest-mateprobe/src/pytest_mateprobe/py.typed similarity index 100% rename from packages/pytest-narrative-contracts/src/pytest_narrative_contracts/py.typed rename to packages/pytest-mateprobe/src/pytest_mateprobe/py.typed diff --git a/packages/pytest-narrative-contracts/pyproject.toml b/packages/pytest-narrative-contracts/pyproject.toml deleted file mode 100644 index caf457a..0000000 --- a/packages/pytest-narrative-contracts/pyproject.toml +++ /dev/null @@ -1,24 +0,0 @@ -[build-system] -requires = ["hatchling>=1.27,<2"] -build-backend = "hatchling.build" - -[project] -name = "pytest-narrative-contracts" -version = "0.1.0a3" -description = "Pytest fixtures and reports for narrative-contracts" -readme = "README.md" -requires-python = ">=3.11" -license = "MIT" -dependencies = ["narrative-contracts==0.1.0a3", "pytest>=8,<10"] -classifiers = ["Development Status :: 3 - Alpha", "Framework :: Pytest", "Programming Language :: Python :: 3"] - -[project.urls] -Repository = "https://github.com/Mate4b/narrative-contracts" -Issues = "https://github.com/Mate4b/narrative-contracts/issues" -Documentation = "https://mate4b.github.io/narrative-contracts/" - -[project.entry-points.pytest11] -narrative_contracts = "pytest_narrative_contracts.plugin" - -[tool.hatch.build.targets.wheel] -packages = ["src/pytest_narrative_contracts"] diff --git a/packages/pytest-narrative-contracts/src/pytest_narrative_contracts/__init__.py b/packages/pytest-narrative-contracts/src/pytest_narrative_contracts/__init__.py deleted file mode 100644 index 3442f19..0000000 --- a/packages/pytest-narrative-contracts/src/pytest_narrative_contracts/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Optional pytest integration for narrative-contracts.""" diff --git a/paper/draft.md b/paper/draft.md index e6533d3..282a32e 100644 --- a/paper/draft.md +++ b/paper/draft.md @@ -1,12 +1,16 @@ -# State-Conditioned Narrative Contracts: Deterministic Validation with Mutation and Invariance Audits +# MateProbe: Deterministic State-Conditioned Validation with Mutation and Invariance Audits **Technical report, alpha artifact v0.1.0a2 with follow-up evidence — 29 September 2026.** +The project was renamed to **MateProbe by Mate4B** in `0.1.0a4`. Reported experiments +retain their original package versions, names and frozen artifacts; the rename is +not a new evaluation. + Status: public engineering report, not submitted or peer reviewed. The artifact includes synthetic author-constructed cases, a second independent software integration, and a small frozen corpus of actual model generations. There are no independent human labels or LLM-judge labels. Natural-output acceptance is not semantic accuracy. ## Abstract -Language-generation systems that maintain authoritative structured state can evaluate a useful subset of output obligations without another language model. We present Narrative Contracts, a dependency-free Python library that separates exact predicates over structured state and declared claims from lexical heuristics over prose. Contracts produce four-valued outcomes with branch-local scope, versioned configuration and diagnostic evidence. An accompanying mutation audit requires attributable detections and valid-variation controls, and exposes equivalent, unreviewed and baseline-invalid cases rather than counting them as successful tests. A synthetic feasibility corpus contains 720 paired cases in 48 shared scenario groups. The strict profile detects 384 of 480 authored faults and accepts 192 of 240 valid variations. Retained failures expose three boundaries: semantic restatement without lexical overlap, contradictions outside supplied claim annotations and negated phrases rejected by regex. An additional 21-case mutation campaign retains five survivors and one valid-control regression. A frozen 32-request pilot with two local model families records structural failures and contract findings without assigning semantic-accuracy labels. A follow-up 32-response corpus from larger user-installed models supports a 384-case authored mutation audit: 64 declaration faults, 96 lexical faults and 96 schema faults are detected; 48 changed controls are preserved and 16 no-ops excluded. All 64 inserted prose-only contradictions pass and are reported separately from in-scope scores. A separate customer-support workflow demonstrates trusted branch selection outside LifeCard. These artifacts illustrate behavior and limitations; they do not establish natural-output accuracy, superiority to LLM judges or broad cross-domain generalization. Independent evaluation is future work, not a prerequisite for releasing the engineering artifact. +Language-generation systems that maintain authoritative structured state can evaluate a useful subset of output obligations without another language model. We present MateProbe (released as Narrative Contracts for the experiments reported here), a dependency-free Python library that separates exact predicates over structured state and declared claims from lexical heuristics over prose. Contracts produce four-valued outcomes with branch-local scope, versioned configuration and diagnostic evidence. An accompanying mutation audit requires attributable detections and valid-variation controls, and exposes equivalent, unreviewed and baseline-invalid cases rather than counting them as successful tests. A synthetic feasibility corpus contains 720 paired cases in 48 shared scenario groups. The strict profile detects 384 of 480 authored faults and accepts 192 of 240 valid variations. Retained failures expose three boundaries: semantic restatement without lexical overlap, contradictions outside supplied claim annotations and negated phrases rejected by regex. An additional 21-case mutation campaign retains five survivors and one valid-control regression. A frozen 32-request pilot with two local model families records structural failures and contract findings without assigning semantic-accuracy labels. A follow-up 32-response corpus from larger user-installed models supports a 384-case authored mutation audit: 64 declaration faults, 96 lexical faults and 96 schema faults are detected; 48 changed controls are preserved and 16 no-ops excluded. All 64 inserted prose-only contradictions pass and are reported separately from in-scope scores. A separate customer-support workflow demonstrates trusted branch selection outside LifeCard. These artifacts illustrate behavior and limitations; they do not establish natural-output accuracy, superiority to LLM judges or broad cross-domain generalization. Independent evaluation is future work, not a prerequisite for releasing the engineering artifact. ## 1. Problem and intended contribution diff --git a/pyproject.toml b/pyproject.toml index c239fd3..702c6dd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,29 +3,29 @@ requires = ["hatchling>=1.27,<2"] build-backend = "hatchling.build" [project] -name = "narrative-contracts" -version = "0.1.0a3" -description = "Deterministic contracts and mutation audits for state-conditioned language generation" +name = "mateprobe" +version = "0.1.0a4" +description = "Mutation testing for your validators, by Mate4B" readme = "README.md" requires-python = ">=3.11" license = "MIT" -authors = [{name = "Narrative Contracts contributors"}] +authors = [{name = "Mate4B and MateProbe contributors"}] classifiers = ["Development Status :: 3 - Alpha", "Programming Language :: Python :: 3", "Topic :: Software Development :: Testing"] dependencies = [] [project.urls] -Repository = "https://github.com/Mate4b/narrative-contracts" -Issues = "https://github.com/Mate4b/narrative-contracts/issues" -Documentation = "https://mate4b.github.io/narrative-contracts/" +Repository = "https://github.com/Mate4b/mateprobe" +Issues = "https://github.com/Mate4b/mateprobe/issues" +Documentation = "https://mate4b.github.io/mateprobe/" [project.optional-dependencies] dev = ["pytest>=8,<10", "ruff>=0.11,<1", "mypy>=1.15,<2", "build>=1.2,<2"] [project.scripts] -narrative-contracts = "narrative_contracts.cli:main" +mateprobe = "mateprobe.cli:main" [tool.hatch.build.targets.wheel] -packages = ["src/narrative_contracts"] +packages = ["src/mateprobe"] [tool.hatch.build.targets.sdist] exclude = [ @@ -47,4 +47,4 @@ select = ["E4", "E7", "E9", "F", "I"] [tool.mypy] python_version = "3.11" strict = true -files = ["src", "packages/pytest-narrative-contracts/src"] +files = ["src", "packages/pytest-mateprobe/src"] diff --git a/scripts/build_docs.py b/scripts/build_docs.py index c9acde8..72e3518 100644 --- a/scripts/build_docs.py +++ b/scripts/build_docs.py @@ -16,8 +16,8 @@ ROOT = Path(__file__).resolve().parents[1] STAGE = ROOT / ".docs-src" SITE = ROOT / "site" -SITE_URL = "https://mate4b.github.io/narrative-contracts/" -REPO_URL = "https://github.com/Mate4b/narrative-contracts/blob/main/" +SITE_URL = "https://mate4b.github.io/mateprobe/" +REPO_URL = "https://github.com/Mate4b/mateprobe/blob/main/" LINK = re.compile(r"(!?\[[^\]\n]*\]\()([^\s)]+)(\))") EXAMPLES = ( "examples/first_audit.py", @@ -53,6 +53,8 @@ "docs/lifecard-validator-audit.md", "docs/lifecard.md", "docs/mutation-campaign.md", + "docs/migration-mateprobe.md", + "docs/api-a4.md", "docs/natural-benchmark.md", "docs/policy-regression.md", "docs/pydantic.md", diff --git a/scripts/check_alpha_install.py b/scripts/check_alpha_install.py index 1198ed1..5a2dbf3 100644 --- a/scripts/check_alpha_install.py +++ b/scripts/check_alpha_install.py @@ -1,4 +1,4 @@ -"""Exercise a3 APIs and pytest using installed distributions, never editable source.""" +"""Exercise a4 APIs and pytest using installed distributions, never editable source.""" from __future__ import annotations @@ -19,13 +19,13 @@ def main() -> None: args = parser.parse_args() output = args.output.resolve() output.mkdir(parents=True, exist_ok=False) - verify(output, "0.1.0a3") + verify(output, "0.1.0a4") text = (ROOT / "docs/policy-regression.md").read_text() blocks = re.findall(r"^```python\n(.*?)^```", text, re.MULTILINE | re.DOTALL) test = output / "test_policy_regression.py" test.write_text("\n\n".join(blocks)) commands = [ - ["-m", "pytest", "-q", "-c", os.devnull, str(test), "--narrative-report=policy-audit.json"], + ["-m", "pytest", "-q", "-c", os.devnull, str(test), "--mateprobe-report=policy-audit.json"], [str(ROOT / "examples/audit_existing_validator.py"), "--output", str(output / "refund")], [str(ROOT / "examples/external_validators.py"), "--output", str(output / "integrations")], ] @@ -37,7 +37,7 @@ def main() -> None: if result.returncode: raise RuntimeError(result.stdout + result.stderr) report = json.loads((output / "policy-audit.json").read_text())["reports"][0] - assert report["library_version"] == "0.1.0a3" + assert report["library_version"] == "0.1.0a4" assert report["summary"]["counts"] == {"detected": 2, "preserved": 1} flags = report["summary"]["obligations"][0] assert flags["has_fault_tests"] and flags["has_controls"] @@ -46,7 +46,7 @@ def main() -> None: json.dumps( { "status": "passed", - "library_version": "0.1.0a3", + "library_version": "0.1.0a4", "policy_regression": report["summary"], "checks": [ "installed paths and versions", @@ -63,7 +63,7 @@ def main() -> None: ) + "\n" ) - print("a3 installed-package regression, plugin, refund and third-party examples passed.") + print("a4 installed-package regression, plugin, refund and third-party examples passed.") if __name__ == "__main__": diff --git a/scripts/check_launch_package.py b/scripts/check_launch_package.py index ccd1938..126a380 100644 --- a/scripts/check_launch_package.py +++ b/scripts/check_launch_package.py @@ -1,4 +1,4 @@ -"""Check the one-file launch recipe using installed a3 packages, outside the checkout.""" +"""Check the one-file launch recipe using installed a4 packages, outside the checkout.""" from __future__ import annotations @@ -38,17 +38,17 @@ def run(label: str, command: list[str], expected: int = 0) -> str: "installation", [ "-c", - "import json, importlib.metadata as m, narrative_contracts as n; " + "import json, importlib.metadata as m, mateprobe as n; " "print(json.dumps({'path': n.__file__, 'runtime': n.__version__, 'versions': " - "{p: m.version(p) for p in ['narrative-contracts', 'pytest-narrative-contracts']}}))", + "{p: m.version(p) for p in ['mateprobe', 'pytest-mateprobe']}}))", ], ) ) package_path = Path(installation["path"]).resolve() if package_path.is_relative_to(ROOT) or "site-packages" not in package_path.parts: raise RuntimeError("Use installed distributions, not an editable checkout") - assert installation["runtime"] == "0.1.0a3" - assert set(installation["versions"].values()) == {"0.1.0a3"} + assert installation["runtime"] == "0.1.0a4" + assert set(installation["versions"].values()) == {"0.1.0a4"} script = output / "first_audit.py" shutil.copyfile(ROOT / "examples/first_audit.py", script) run("demo", [str(script), "--output", str(output / "reports")]) @@ -78,7 +78,7 @@ def check_pytest(label: str, path: Path, exit_code: int) -> dict: "-c", os.devnull, str(path), - f"--narrative-report={report_path}", + f"--mateprobe-report={report_path}", ], expected=exit_code, ) @@ -121,7 +121,7 @@ def check_pytest(label: str, path: Path, exit_code: int) -> dict: } (output / "summary.json").write_text(json.dumps(summary, indent=2) + "\n") print( - "Launch recipe passed from installed a3; removing the fix and reject-all fail with reports." + "Launch recipe passed from installed a4; removing the fix and reject-all fail with reports." ) diff --git a/scripts/check_published_docs.py b/scripts/check_published_docs.py index b8e5b98..d4f4605 100644 --- a/scripts/check_published_docs.py +++ b/scripts/check_published_docs.py @@ -21,7 +21,7 @@ ) -def verify(output: Path, version: str = "0.1.0a3") -> None: +def verify(output: Path, version: str = "0.1.0a4") -> None: def run(label: str, *args: str) -> str: result = subprocess.run( [sys.executable, "-I", *args], cwd=output, capture_output=True, text=True @@ -35,16 +35,16 @@ def run(label: str, *args: str) -> str: run( "installation", "-c", - "import json, importlib.metadata as m, narrative_contracts as n; " + "import json, importlib.metadata as m, mateprobe as n; " "print(json.dumps({'module': n.__file__, 'versions': " "{p: m.version(p) for p in " - "['narrative-contracts', 'pytest-narrative-contracts', 'pytest', 'pydantic']}}))", + "['mateprobe', 'pytest-mateprobe', 'pytest', 'pydantic']}}))", ) ) module_path = Path(installation["module"]).resolve() if module_path.is_relative_to(ROOT) or "site-packages" not in module_path.parts: raise RuntimeError(f"Expected an isolated installed package, got {module_path}") - for package in ("narrative-contracts", "pytest-narrative-contracts"): + for package in ("mateprobe", "pytest-mateprobe"): if installation["versions"][package] != version: raise RuntimeError(f"Wrong published version of {package}") @@ -64,7 +64,7 @@ def run(label: str, *args: str) -> str: "-q", "-c", os.devnull, - "--narrative-report=contract-results.json", + "--mateprobe-report=contract-results.json", str(output), ) records = json.loads((output / "contract-results.json").read_text())["reports"] @@ -78,7 +78,7 @@ def run(label: str, *args: str) -> str: run( "cli", "-m", - "narrative_contracts.cli", + "mateprobe.cli", str(ROOT / "examples/valid.json"), "--output", str(output / "cli-report.json"), @@ -103,13 +103,13 @@ def run(label: str, *args: str) -> str: def main() -> None: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--output", type=Path, help="New directory for retained evidence") - parser.add_argument("--version", default="0.1.0a3") + parser.add_argument("--version", default="0.1.0a4") args = parser.parse_args() if args.output: args.output.mkdir(parents=True, exist_ok=False) verify(args.output.resolve(), args.version) else: - with tempfile.TemporaryDirectory(prefix="narrative-published-docs-") as directory: + with tempfile.TemporaryDirectory(prefix="mateprobe-published-docs-") as directory: verify(Path(directory), args.version) diff --git a/scripts/release-a4.json b/scripts/release-a4.json new file mode 100644 index 0000000..0e49bba --- /dev/null +++ b/scripts/release-a4.json @@ -0,0 +1,11 @@ +{ + "version": "0.1.0a4", + "tag": "v0.1.0a4", + "source_commit": "e842d66faa5d2dd6d8cc9a7dd8abd7e31bc49078", + "sha256": { + "mateprobe-0.1.0a4-py3-none-any.whl": "462419a35c7f3cdecfde6609748331aaaaf77aeaca7836bb2db89a01ea79f1ca", + "mateprobe-0.1.0a4.tar.gz": "b7af4085f13cf96ebca572d844ff8fe1a68afacc5666783dc456a9ea16238924", + "pytest_mateprobe-0.1.0a4-py3-none-any.whl": "7dfab82a48c27224c9e2b30f705530c9cde13dabb0891322ba772a5ca0cc1fef", + "pytest_mateprobe-0.1.0a4.tar.gz": "4c57f2ed9e0385413681199e641e0a3b825d15a7f91e581bbb004a0a9b578746" + } +} diff --git a/scripts/stage_pypi_a4.py b/scripts/stage_pypi_a4.py new file mode 100644 index 0000000..3ab9b79 --- /dev/null +++ b/scripts/stage_pypi_a4.py @@ -0,0 +1,37 @@ +"""Verify and stage the four a4 release distributions against committed hashes.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import shutil +from pathlib import Path + + +def stage(source: Path, output: Path) -> None: + manifest = json.loads(Path(__file__).with_name("release-a4.json").read_text()) + expected_names = { + f"{package}-0.1.0a4{suffix}" + for package in ("mateprobe", "pytest_mateprobe") + for suffix in ("-py3-none-any.whl", ".tar.gz") + } + if manifest["version"] != "0.1.0a4" or set(manifest["sha256"]) != expected_names: + raise ValueError("Unexpected release manifest") + if output.exists(): + raise FileExistsError("Output directory must be new") + for name, expected in manifest["sha256"].items(): + if hashlib.sha256((source / name).read_bytes()).hexdigest() != expected: + raise ValueError(f"Released artifact hash mismatch: {name}") + output.mkdir(parents=True) + for name in expected_names: + shutil.copyfile(source / name, output / name) + print("Verified and staged four original v0.1.0a4 release artifacts") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--source", required=True, type=Path) + parser.add_argument("--output", required=True, type=Path) + args = parser.parse_args() + stage(args.source, args.output) diff --git a/src/narrative_contracts/__init__.py b/src/mateprobe/__init__.py similarity index 100% rename from src/narrative_contracts/__init__.py rename to src/mateprobe/__init__.py diff --git a/src/narrative_contracts/adapters.py b/src/mateprobe/adapters.py similarity index 100% rename from src/narrative_contracts/adapters.py rename to src/mateprobe/adapters.py diff --git a/src/narrative_contracts/bindings.py b/src/mateprobe/bindings.py similarity index 100% rename from src/narrative_contracts/bindings.py rename to src/mateprobe/bindings.py diff --git a/src/narrative_contracts/cli.py b/src/mateprobe/cli.py similarity index 100% rename from src/narrative_contracts/cli.py rename to src/mateprobe/cli.py diff --git a/src/narrative_contracts/engine.py b/src/mateprobe/engine.py similarity index 76% rename from src/narrative_contracts/engine.py rename to src/mateprobe/engine.py index afc2e15..fd9a1b8 100644 --- a/src/narrative_contracts/engine.py +++ b/src/mateprobe/engine.py @@ -6,6 +6,29 @@ from .model import Check, Context, Document, Kind, Policy, Report, Status, digest, plain +# Stable wire identifiers for rules released before the project rename. These +# strings are report metadata, not import paths; changing them changes digests. +_LEGACY_RULE_TYPES = { + f"mateprobe.{module}.{name}": f"narrative_contracts.{module}.{name}" + for module, names in ( + ( + "rules", + ( + "RequiredFact", + "DeclaredClaimsConsistent", + "StateChanged", + "MinimumTokens", + "LexicalRestatement", + "ForbiddenPattern", + "SettledPremise", + "NoRepeatedText", + ), + ), + ("relations", ("CompareFields", "AllowedTransition")), + ) + for name in names +} + class Contract(Protocol): @property @@ -25,8 +48,9 @@ class Rule: kind: ClassVar[Kind] = Kind.INVARIANT def configuration(self) -> Mapping[str, Any]: + identity = f"{type(self).__module__}.{type(self).__qualname__}" return { - "type": f"{type(self).__module__}.{type(self).__qualname__}", + "type": _LEGACY_RULE_TYPES.get(identity, identity), "version": self.version, "kind": self.kind.value, "parameters": plain(self), diff --git a/src/narrative_contracts/model.py b/src/mateprobe/model.py similarity index 98% rename from src/narrative_contracts/model.py rename to src/mateprobe/model.py index 9f7a5aa..410e097 100644 --- a/src/narrative_contracts/model.py +++ b/src/mateprobe/model.py @@ -184,4 +184,4 @@ def assert_accepted(self) -> None: for c in self.checks if self.policy.blocks(c) ] - raise AssertionError("Narrative contract failure:\n" + "\n".join(messages)) + raise AssertionError("MateProbe contract failure:\n" + "\n".join(messages)) diff --git a/src/narrative_contracts/mutations.py b/src/mateprobe/mutations.py similarity index 100% rename from src/narrative_contracts/mutations.py rename to src/mateprobe/mutations.py diff --git a/src/narrative_contracts/provenance.py b/src/mateprobe/provenance.py similarity index 100% rename from src/narrative_contracts/provenance.py rename to src/mateprobe/provenance.py diff --git a/src/narrative_contracts/py.typed b/src/mateprobe/py.typed similarity index 100% rename from src/narrative_contracts/py.typed rename to src/mateprobe/py.typed diff --git a/src/narrative_contracts/relations.py b/src/mateprobe/relations.py similarity index 100% rename from src/narrative_contracts/relations.py rename to src/mateprobe/relations.py diff --git a/src/narrative_contracts/rules.py b/src/mateprobe/rules.py similarity index 100% rename from src/narrative_contracts/rules.py rename to src/mateprobe/rules.py diff --git a/src/narrative_contracts/serialization.py b/src/mateprobe/serialization.py similarity index 100% rename from src/narrative_contracts/serialization.py rename to src/mateprobe/serialization.py diff --git a/src/narrative_contracts/text.py b/src/mateprobe/text.py similarity index 100% rename from src/narrative_contracts/text.py rename to src/mateprobe/text.py diff --git a/src/narrative_contracts/validator_audit.py b/src/mateprobe/validator_audit.py similarity index 100% rename from src/narrative_contracts/validator_audit.py rename to src/mateprobe/validator_audit.py diff --git a/src/narrative_contracts/version.py b/src/mateprobe/version.py similarity index 74% rename from src/narrative_contracts/version.py rename to src/mateprobe/version.py index 30992df..da283aa 100644 --- a/src/narrative_contracts/version.py +++ b/src/mateprobe/version.py @@ -1,3 +1,3 @@ """Package version shared by deterministic report formats and the public API.""" -LIBRARY_VERSION = "0.1.0a3" +LIBRARY_VERSION = "0.1.0a4" diff --git a/tests/test_audit_review_report.py b/tests/test_audit_review_report.py index c5572ac..a48f193 100644 --- a/tests/test_audit_review_report.py +++ b/tests/test_audit_review_report.py @@ -2,11 +2,11 @@ from typing import Any -from narrative_contracts.model import plain -from narrative_contracts.mutations import Relation, Validity -from narrative_contracts.provenance import GitProvenance, supplied_git_provenance -from narrative_contracts.validator_audit import AuditCase, Obligation, Verdict, audit_validator -from narrative_contracts.version import LIBRARY_VERSION +from mateprobe.model import plain +from mateprobe.mutations import Relation, Validity +from mateprobe.provenance import GitProvenance, supplied_git_provenance +from mateprobe.validator_audit import AuditCase, Obligation, Verdict, audit_validator +from mateprobe.version import LIBRARY_VERSION def accepted(value: object) -> Verdict: @@ -149,7 +149,7 @@ def test_default_audit_does_not_inspect_git(monkeypatch: Any) -> None: def fail_if_called(*args: object, **kwargs: object) -> None: raise AssertionError("audit unexpectedly inspected Git") - monkeypatch.setattr("narrative_contracts.provenance.subprocess.run", fail_if_called) + monkeypatch.setattr("mateprobe.provenance.subprocess.run", fail_if_called) obligation = Obligation("state", "State remains valid") report = audit_validator( accepted, diff --git a/tests/test_benchmark.py b/tests/test_benchmark.py index 0b0c466..e679636 100644 --- a/tests/test_benchmark.py +++ b/tests/test_benchmark.py @@ -1,8 +1,8 @@ import importlib.util from pathlib import Path -from narrative_contracts.model import digest -from narrative_contracts.mutations import audit +from mateprobe.model import digest +from mateprobe.mutations import audit spec = importlib.util.spec_from_file_location( "benchmark", Path(__file__).parents[1] / "benchmarks/run.py" diff --git a/tests/test_bindings.py b/tests/test_bindings.py index b6db74b..5f9aeaf 100644 --- a/tests/test_bindings.py +++ b/tests/test_bindings.py @@ -1,7 +1,7 @@ import pytest -from narrative_contracts.bindings import check_fields -from narrative_contracts.model import Status +from mateprobe.bindings import check_fields +from mateprobe.model import Status def test_bindings_are_type_sensitive_and_report_mismatch(): diff --git a/tests/test_contracts.py b/tests/test_contracts.py index ccde372..e8ee764 100644 --- a/tests/test_contracts.py +++ b/tests/test_contracts.py @@ -2,7 +2,7 @@ import pytest -from narrative_contracts import ( +from mateprobe import ( Claim, Context, DeclaredClaimsConsistent, @@ -20,7 +20,7 @@ Surface, evaluate, ) -from narrative_contracts.model import Check, Kind +from mateprobe.model import Check, Kind def document(text="A concrete scene with enough different words to describe the consequence."): @@ -209,3 +209,19 @@ def evaluate(self, document, context): report = check(InvalidStatus("bad")) assert report.checks[0].status == Status.ERROR assert not report.accepted + + +def test_rename_preserves_builtin_rule_identity_without_masking_custom_types(): + from mateprobe import RequiredFact + from mateprobe.engine import Rule + + assert RequiredFact("fact", "x", 1).configuration()["type"] == ( + "narrative_contracts.rules.RequiredFact" + ) + + class CustomFact(RequiredFact): + pass + + custom = CustomFact("custom", "x", 1) + assert custom.configuration()["type"] == (f"{CustomFact.__module__}.{CustomFact.__qualname__}") + assert Rule("base").configuration()["type"] == "mateprobe.engine.Rule" diff --git a/tests/test_expanded_mutations.py b/tests/test_expanded_mutations.py index f9de522..6bf3345 100644 --- a/tests/test_expanded_mutations.py +++ b/tests/test_expanded_mutations.py @@ -4,7 +4,7 @@ import sys from pathlib import Path -from narrative_contracts.mutations import Relation, Validity, audit +from mateprobe.mutations import Relation, Validity, audit spec_path = Path(__file__).parents[1] / "benchmarks" / "expanded_mutations.py" spec = importlib.util.spec_from_file_location("expanded_mutations", spec_path) diff --git a/tests/test_external_validator_demo.py b/tests/test_external_validator_demo.py index b779d79..c408350 100644 --- a/tests/test_external_validator_demo.py +++ b/tests/test_external_validator_demo.py @@ -2,7 +2,7 @@ import json from pathlib import Path -from narrative_contracts.validator_audit import audit_validator +from mateprobe.validator_audit import audit_validator PATH = Path(__file__).resolve().parents[1] / "examples" / "audit_existing_validator.py" spec = importlib.util.spec_from_file_location("external_validator_demo", PATH) @@ -31,9 +31,9 @@ def test_demo_finds_missing_execution_checks_without_hiding_prose_survivor(): def test_external_audit_plugin_records_failure_and_success(pytester, monkeypatch): monkeypatch.setenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD", "1") pytester.makepyfile(""" -from narrative_contracts.validator_audit import AuditCase, Obligation, Verdict -from narrative_contracts.mutations import Relation, Validity -from narrative_contracts.provenance import supplied_git_provenance +from mateprobe.validator_audit import AuditCase, Obligation, Verdict +from mateprobe.mutations import Relation, Validity +from mateprobe.provenance import supplied_git_provenance metadata = supplied_git_provenance("a" * 40) @@ -45,22 +45,22 @@ def test_external_audit_plugin_records_failure_and_success(pytester, monkeypatch ) obligations = (Obligation("positive", "Input must be positive"),) -def test_correct(narrative): - narrative.audit_validator( +def test_correct(mateprobe): + mateprobe.audit_validator( lambda n: Verdict(n > 0, () if n > 0 else ("negative",)), cases, obligations=obligations, validator_id="correct/1", provenance=metadata, ) -def test_broken(narrative): - narrative.audit_validator( +def test_broken(mateprobe): + mateprobe.audit_validator( lambda n: Verdict(True), cases, obligations=obligations, validator_id="broken/1", provenance=metadata, ) """) path = pytester.path / "report.json" result = pytester.runpytest_subprocess( - "-p", "pytest_narrative_contracts.plugin", "--narrative-report", str(path) + "-p", "pytest_mateprobe.plugin", "--mateprobe-report", str(path) ) result.assert_outcomes(passed=1, failed=1) result.stdout.fnmatch_lines(["*Validator audit failure:*", "*fault*survived*"]) diff --git a/tests/test_external_validators.py b/tests/test_external_validators.py index 3c4dbac..0cfc2b0 100644 --- a/tests/test_external_validators.py +++ b/tests/test_external_validators.py @@ -10,7 +10,7 @@ pytest.skip("Integration requires jsonschema 4+", allow_module_level=True) pytest.importorskip("pydantic", minversion="2.0") -from narrative_contracts.validator_audit import audit_validator +from mateprobe.validator_audit import audit_validator PATH = Path(__file__).resolve().parents[1] / "examples" / "external_validators.py" spec = importlib.util.spec_from_file_location("external_validators", PATH) diff --git a/tests/test_io_and_plugin.py b/tests/test_io_and_plugin.py index d098754..3db7c24 100644 --- a/tests/test_io_and_plugin.py +++ b/tests/test_io_and_plugin.py @@ -4,9 +4,9 @@ import pytest -from narrative_contracts import Claim, Context, DeclaredClaimsConsistent, evaluate -from narrative_contracts.adapters import lifecard_document -from narrative_contracts.serialization import load_bundle +from mateprobe import Claim, Context, DeclaredClaimsConsistent, evaluate +from mateprobe.adapters import lifecard_document +from mateprobe.serialization import load_bundle def bundle(): @@ -46,7 +46,7 @@ def test_cli_exit_codes_and_json(tmp_path, state, exit_code): path = tmp_path / "input.json" path.write_text(json.dumps(data)) proc = subprocess.run( - [sys.executable, "-m", "narrative_contracts.cli", str(path)], capture_output=True, text=True + [sys.executable, "-m", "mateprobe.cli", str(path)], capture_output=True, text=True ) assert proc.returncode == exit_code assert json.loads(proc.stdout)["accepted"] == (exit_code == 0) @@ -56,7 +56,7 @@ def test_cli_invalid_input_exit_two(tmp_path): path = tmp_path / "bad.json" path.write_text('{"contracts": [{"type": "unknown"}]}') proc = subprocess.run( - [sys.executable, "-m", "narrative_contracts.cli", str(path)], capture_output=True, text=True + [sys.executable, "-m", "mateprobe.cli", str(path)], capture_output=True, text=True ) assert proc.returncode == 2 assert "Invalid contract bundle" in proc.stderr @@ -93,18 +93,18 @@ def test_lifecard_adapter_preserves_branch_identity(): def test_plugin_fails_test_with_actionable_diagnostics_and_writes_report(pytester, monkeypatch): monkeypatch.setenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD", "1") pytester.makepyfile(""" -from narrative_contracts import Context, Document, Surface, RequiredFact +from mateprobe import Context, Document, Surface, RequiredFact -def test_fact(narrative): - narrative.check(Document((Surface("body", "Text"),)), Context({"current": {"x": 1}}), +def test_fact(mateprobe): + mateprobe.check(Document((Surface("body", "Text"),)), Context({"current": {"x": 1}}), (RequiredFact("must-be-two", "x", 2),)) """) report = pytester.path / "report.json" result = pytester.runpytest_subprocess( - "-p", "pytest_narrative_contracts.plugin", "--narrative-report", str(report) + "-p", "pytest_mateprobe.plugin", "--mateprobe-report", str(report) ) result.assert_outcomes(failed=1) - result.stdout.fnmatch_lines(["*Narrative contract failure:*", "*must-be-two*"]) + result.stdout.fnmatch_lines(["*MateProbe contract failure:*", "*must-be-two*"]) payload = json.loads(report.read_text()) assert payload["reports"][0]["accepted"] is False @@ -112,17 +112,17 @@ def test_fact(narrative): def test_plugin_audit_fixture(pytester, monkeypatch): monkeypatch.setenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD", "1") pytester.makepyfile(""" -from narrative_contracts import Context, Document, Surface, MinimumTokens -from narrative_contracts.mutations import Sample, MutationCase, Relation, Target, Validity, replace_text +from mateprobe import Context, Document, Surface, MinimumTokens +from mateprobe.mutations import Sample, MutationCase, Relation, Target, Validity, replace_text -def test_audit(narrative): +def test_audit(mateprobe): base = Sample(Document((Surface("body", "one two three four five six"),)), Context({})) bad = MutationCase("bad", "short", Relation.VIOLATION, base, replace_text(base, "body", "x"), (Target("thin", "LOW_LEXICAL_CONTENT", "body"),), Validity.VALID, "short") good = MutationCase("good", "space", Relation.PRESERVE, base, replace_text(base, "body", " one two three four five six "), validity=Validity.VALID, provenance="spacing") - narrative.audit((bad, good), (MinimumTokens("thin", ("body",), 6, 4),)) + mateprobe.audit((bad, good), (MinimumTokens("thin", ("body",), 6, 4),)) """) - result = pytester.runpytest_subprocess("-p", "pytest_narrative_contracts.plugin") + result = pytester.runpytest_subprocess("-p", "pytest_mateprobe.plugin") result.assert_outcomes(passed=1) diff --git a/tests/test_mutations.py b/tests/test_mutations.py index 58d6939..a50103c 100644 --- a/tests/test_mutations.py +++ b/tests/test_mutations.py @@ -2,8 +2,8 @@ import pytest -from narrative_contracts import Context, Document, MinimumTokens, Rule, Status, Surface -from narrative_contracts.mutations import ( +from mateprobe import Context, Document, MinimumTokens, Rule, Status, Surface +from mateprobe.mutations import ( MutationCase, Relation, Sample, diff --git a/tests/test_natural.py b/tests/test_natural.py index ad3c7fa..5f1974b 100644 --- a/tests/test_natural.py +++ b/tests/test_natural.py @@ -8,8 +8,8 @@ import pytest -from narrative_contracts import evaluate -from narrative_contracts.model import digest +from mateprobe import evaluate +from mateprobe.model import digest SPEC = importlib.util.spec_from_file_location( "natural_benchmark", Path(__file__).parents[1] / "benchmarks/natural.py" diff --git a/tests/test_provenance.py b/tests/test_provenance.py index 636f665..21ee94e 100644 --- a/tests/test_provenance.py +++ b/tests/test_provenance.py @@ -5,8 +5,8 @@ import pytest -from narrative_contracts.model import plain -from narrative_contracts.provenance import GitProvenance, observe_git, supplied_git_provenance +from mateprobe.model import plain +from mateprobe.provenance import GitProvenance, observe_git, supplied_git_provenance SHA = "a" * 40 @@ -72,7 +72,7 @@ def test_observe_git_unavailable_is_unknown( def missing(*args: object, **kwargs: object) -> None: raise FileNotFoundError("git") - monkeypatch.setattr("narrative_contracts.provenance.subprocess.run", missing) + monkeypatch.setattr("mateprobe.provenance.subprocess.run", missing) result = observe_git(tmp_path) assert result == GitProvenance(None, None, "observed", "git_unavailable") @@ -81,6 +81,6 @@ def test_observe_timeout_is_unknown(monkeypatch: pytest.MonkeyPatch, tmp_path: P def timeout(*args: object, **kwargs: object) -> None: raise subprocess.TimeoutExpired("git", 5.0) - monkeypatch.setattr("narrative_contracts.provenance.subprocess.run", timeout) + monkeypatch.setattr("mateprobe.provenance.subprocess.run", timeout) result = observe_git(tmp_path) assert result == GitProvenance(None, None, "observed", "timeout") diff --git a/tests/test_real_mutations.py b/tests/test_real_mutations.py index 1a06a38..567bb70 100644 --- a/tests/test_real_mutations.py +++ b/tests/test_real_mutations.py @@ -6,7 +6,7 @@ import pytest -from narrative_contracts.model import digest +from mateprobe.model import digest ROOT = Path(__file__).parents[1] SPEC = importlib.util.spec_from_file_location( diff --git a/tests/test_relations.py b/tests/test_relations.py index 0381c99..9ffa2df 100644 --- a/tests/test_relations.py +++ b/tests/test_relations.py @@ -1,8 +1,8 @@ import pytest -from narrative_contracts.engine import evaluate -from narrative_contracts.model import Context, Document, Status, Surface -from narrative_contracts.relations import AllowedTransition, CompareFields, FieldRef +from mateprobe.engine import evaluate +from mateprobe.model import Context, Document, Status, Surface +from mateprobe.relations import AllowedTransition, CompareFields, FieldRef DOC = Document((Surface("out", ""),)) diff --git a/tests/test_support_workflow.py b/tests/test_support_workflow.py index eae0ea2..f24298a 100644 --- a/tests/test_support_workflow.py +++ b/tests/test_support_workflow.py @@ -6,7 +6,7 @@ import pytest -from narrative_contracts.model import Kind, Status +from mateprobe.model import Kind, Status MODULE_PATH = Path(__file__).parents[1] / "examples" / "support_workflow.py" SPEC = importlib.util.spec_from_file_location("support_workflow", MODULE_PATH) diff --git a/tests/test_validator_audit.py b/tests/test_validator_audit.py index 5b95ed9..f45fe31 100644 --- a/tests/test_validator_audit.py +++ b/tests/test_validator_audit.py @@ -4,8 +4,8 @@ import pytest -from narrative_contracts.mutations import Relation, Validity -from narrative_contracts.validator_audit import ( +from mateprobe.mutations import Relation, Validity +from mateprobe.validator_audit import ( AuditCase, Obligation, Verdict,