agentic-dataset-conformance 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agentic_dataset_conformance/__init__.py +68 -0
- agentic_dataset_conformance/__main__.py +4 -0
- agentic_dataset_conformance/cli.py +169 -0
- agentic_dataset_conformance/data/LICENSE +13 -0
- agentic_dataset_conformance/data/vectors/ad-001-descriptor-valid.json +28 -0
- agentic_dataset_conformance/data/vectors/ad-002-capability-registered.json +122 -0
- agentic_dataset_conformance/data/vectors/ad-003-grant-required-for-execution.json +30 -0
- agentic_dataset_conformance/data/vectors/ad-004-refusal-has-no-grant.json +75 -0
- agentic_dataset_conformance/data/vectors/ad-005-indeterminate-has-no-grant.json +41 -0
- agentic_dataset_conformance/data/vectors/ad-006-unknown-capability-denied.json +58 -0
- agentic_dataset_conformance/data/vectors/ad-007-authorization-scope-preserved.json +36 -0
- agentic_dataset_conformance/data/vectors/ad-008-cache-is-policy-scoped.json +80 -0
- agentic_dataset_conformance/data/vectors/ad-009-provenance-complete.json +52 -0
- agentic_dataset_conformance/data/vectors/ad-010-refusal-recorded.json +20 -0
- agentic_dataset_conformance/data/vectors/ad-011-dataset-revision-recorded.json +30 -0
- agentic_dataset_conformance/data/vectors/ad-012-policy-version-recorded.json +27 -0
- agentic_dataset_conformance/data/vectors/ad-013-mcp-preserves-scope.json +71 -0
- agentic_dataset_conformance/data/vectors/ad-014-a2a-preserves-scope.json +71 -0
- agentic_dataset_conformance/data/vectors/ad-015-prohibited-execution-rate-zero.json +476 -0
- agentic_dataset_conformance/data/worlds/reference.json +254 -0
- agentic_dataset_conformance/interface.py +133 -0
- agentic_dataset_conformance/mutations.py +283 -0
- agentic_dataset_conformance/runner.py +357 -0
- agentic_dataset_conformance/toy.py +350 -0
- agentic_dataset_conformance/verbs.md +43 -0
- agentic_dataset_conformance-0.1.0.dist-info/METADATA +116 -0
- agentic_dataset_conformance-0.1.0.dist-info/RECORD +32 -0
- agentic_dataset_conformance-0.1.0.dist-info/WHEEL +5 -0
- agentic_dataset_conformance-0.1.0.dist-info/entry_points.txt +2 -0
- agentic_dataset_conformance-0.1.0.dist-info/licenses/LICENSES/Apache-2.0.txt +202 -0
- agentic_dataset_conformance-0.1.0.dist-info/licenses/LICENSES/CC0-1.0.txt +121 -0
- agentic_dataset_conformance-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""The portable conformance suite for the agentic-dataset contract.
|
|
2
|
+
|
|
3
|
+
Fifteen normative assertions, AD-001 .. AD-015, as language-neutral executable
|
|
4
|
+
vectors plus a runner that evaluates them against any implementation through a
|
|
5
|
+
four-method interface. It imports no implementation, including the reference
|
|
6
|
+
one, and the vectors travel with it.
|
|
7
|
+
|
|
8
|
+
from agentic_dataset_conformance import load_suite, run
|
|
9
|
+
report = run(my_subject)
|
|
10
|
+
print(report.passed, [f.assertion for f in report.failures])
|
|
11
|
+
|
|
12
|
+
The vectors and worlds are the normative artifact and are dedicated to the
|
|
13
|
+
public domain under CC0-1.0; the software around them is Apache-2.0. Copy the
|
|
14
|
+
vectors into a Rust, Go or TypeScript project and write your own runner --
|
|
15
|
+
that is what they are for, and `export_vectors()` exists to make it one call.
|
|
16
|
+
|
|
17
|
+
See `verbs.md` beside this module for the control-verb vocabulary a subject
|
|
18
|
+
must understand, and `interface.py` for what it must expose.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import shutil
|
|
24
|
+
from importlib import resources
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
|
|
27
|
+
from .interface import ConformanceSubject, Observation, Scope
|
|
28
|
+
from .runner import (
|
|
29
|
+
INVARIANTS,
|
|
30
|
+
AssertionResult,
|
|
31
|
+
SubjectReport,
|
|
32
|
+
Vector,
|
|
33
|
+
VectorSuite,
|
|
34
|
+
load_suite,
|
|
35
|
+
run,
|
|
36
|
+
)
|
|
37
|
+
from .toy import ToyImplementation
|
|
38
|
+
|
|
39
|
+
__version__ = "0.1.0"
|
|
40
|
+
|
|
41
|
+
#: The fifteen assertion identifiers, in order.
|
|
42
|
+
ASSERTIONS = tuple(f"AD-{i:03d}" for i in range(1, 16))
|
|
43
|
+
|
|
44
|
+
__all__ = [
|
|
45
|
+
"ASSERTIONS", "AssertionResult", "ConformanceSubject", "INVARIANTS",
|
|
46
|
+
"Observation", "Scope", "SubjectReport", "ToyImplementation", "Vector",
|
|
47
|
+
"VectorSuite", "__version__", "export_vectors", "load_suite", "run",
|
|
48
|
+
"vectors_path",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def vectors_path():
|
|
53
|
+
"""The packaged normative data as a traversable resource."""
|
|
54
|
+
return resources.files(__package__) / "data"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def export_vectors(destination: str | Path) -> Path:
|
|
58
|
+
"""Copy the normative worlds and vectors out, for use anywhere.
|
|
59
|
+
|
|
60
|
+
They are CC0-1.0: no attribution required, no conditions. Vendor them into
|
|
61
|
+
another language's repository and write a runner there.
|
|
62
|
+
"""
|
|
63
|
+
destination = Path(destination)
|
|
64
|
+
destination.mkdir(parents=True, exist_ok=True)
|
|
65
|
+
with resources.as_file(vectors_path()) as data:
|
|
66
|
+
for sub in ("worlds", "vectors"):
|
|
67
|
+
shutil.copytree(data / sub, destination / sub, dirs_exist_ok=True)
|
|
68
|
+
return destination
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"""Command line for the portable conformance suite.
|
|
2
|
+
|
|
3
|
+
agentic-dataset-conformance run # against the built-in toy
|
|
4
|
+
agentic-dataset-conformance run --subject mod:make # against yours
|
|
5
|
+
agentic-dataset-conformance run --matrix # mutation characterisation
|
|
6
|
+
agentic-dataset-conformance vectors --list
|
|
7
|
+
agentic-dataset-conformance vectors --export ./vectors
|
|
8
|
+
|
|
9
|
+
`--subject` takes `module:attribute`. The attribute may be a subject, a
|
|
10
|
+
callable returning one, or a callable returning several. Nothing about
|
|
11
|
+
resolving it is specific to any implementation, which is the point: a
|
|
12
|
+
conforming implementation in another package is tested by naming it.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
import importlib
|
|
19
|
+
import json
|
|
20
|
+
import sys
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
from . import ASSERTIONS, export_vectors
|
|
24
|
+
from .runner import load_suite, run
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _resolve(spec: str) -> list:
|
|
28
|
+
module_name, _, attribute = spec.partition(":")
|
|
29
|
+
if not attribute:
|
|
30
|
+
raise SystemExit(f"--subject expects module:attribute, got {spec!r}")
|
|
31
|
+
sys.path.insert(0, str(Path.cwd()))
|
|
32
|
+
module = importlib.import_module(module_name)
|
|
33
|
+
target = getattr(module, attribute)
|
|
34
|
+
found = target() if callable(target) else target
|
|
35
|
+
return list(found) if isinstance(found, (list, tuple)) else [found]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _subjects(specs: list[str]) -> list:
|
|
39
|
+
if not specs:
|
|
40
|
+
from .toy import ToyImplementation
|
|
41
|
+
|
|
42
|
+
return [ToyImplementation()]
|
|
43
|
+
return [s for spec in specs for s in _resolve(spec)]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _matrix(rows: list[tuple[str, str, bool, list[str]]]) -> str:
|
|
47
|
+
labels = [f"M{i:02d}" for i in range(1, len(rows) + 1)]
|
|
48
|
+
out = [" " + " ".join(labels), " " + " ".join("---" for _ in labels)]
|
|
49
|
+
for assertion in ASSERTIONS:
|
|
50
|
+
cells = []
|
|
51
|
+
for _, target, ok, caught in rows:
|
|
52
|
+
if assertion == target:
|
|
53
|
+
cells.append(" T " if ok else " ! ")
|
|
54
|
+
elif assertion in caught:
|
|
55
|
+
cells.append(" x ")
|
|
56
|
+
else:
|
|
57
|
+
cells.append(" . ")
|
|
58
|
+
detected = sum(1 for c in cells if c.strip() in ("T", "x"))
|
|
59
|
+
out.append(f"{assertion} {' '.join(cells)} {detected}")
|
|
60
|
+
out.append("")
|
|
61
|
+
for label, (name, target, ok, _) in zip(labels, rows):
|
|
62
|
+
out.append(f"{label} {target} {name.removeprefix('mutant:')}"
|
|
63
|
+
+ ("" if ok else " ** NOT CAUGHT BY ITS TARGET **"))
|
|
64
|
+
caught_n = sum(1 for _, _, ok, _ in rows if ok)
|
|
65
|
+
covered = {t for _, t, _, _ in rows}
|
|
66
|
+
out += [
|
|
67
|
+
"",
|
|
68
|
+
f"target detection : {caught_n}/{len(rows)} mutants caught by their intended assertion",
|
|
69
|
+
f"cross-detection : {sum(len(c) for _, _, _, c in rows) / len(rows):.1f} "
|
|
70
|
+
"assertions per mutant on average",
|
|
71
|
+
f"coverage : {len(covered)}/15 assertions have a mutant of their own",
|
|
72
|
+
"",
|
|
73
|
+
"T = caught by its target assertion x = caught redundantly",
|
|
74
|
+
". = not detected ! = target failed to catch it",
|
|
75
|
+
]
|
|
76
|
+
return "\n".join(out)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _run(args: argparse.Namespace) -> int:
|
|
80
|
+
suite = load_suite(args.vectors)
|
|
81
|
+
reports = [run(s, suite) for s in _subjects(args.subject)]
|
|
82
|
+
failed = any(not r.passed for r in reports)
|
|
83
|
+
|
|
84
|
+
rows: list[tuple[str, str, bool, list[str]]] = []
|
|
85
|
+
if args.mutants or args.matrix:
|
|
86
|
+
from .mutations import TARGETS
|
|
87
|
+
|
|
88
|
+
for cls, target in TARGETS.items():
|
|
89
|
+
caught = [f.assertion for f in run(cls(), suite).failures]
|
|
90
|
+
rows.append((cls.name, target, target in caught, caught))
|
|
91
|
+
failed = failed or any(not ok for _, _, ok, _ in rows)
|
|
92
|
+
|
|
93
|
+
if args.json:
|
|
94
|
+
print(json.dumps({
|
|
95
|
+
"vectors": len(suite.vectors),
|
|
96
|
+
"subjects": [r.to_dict() for r in reports],
|
|
97
|
+
"mutants": [{"mutant": n, "target": t, "caught": ok, "caught_by": by}
|
|
98
|
+
for n, t, ok, by in rows],
|
|
99
|
+
}, indent=2))
|
|
100
|
+
return 1 if failed else 0
|
|
101
|
+
|
|
102
|
+
width = max((len(r.subject) for r in reports), default=10)
|
|
103
|
+
print(f"{len(suite.vectors)} vectors, {sum(len(v.steps) for v in suite.vectors)} steps, "
|
|
104
|
+
f"{len(reports)} subject(s)\n")
|
|
105
|
+
print(f"{'SUBJECT':<{width}} RESULT ASSERTIONS OBSERVATIONS")
|
|
106
|
+
for report in reports:
|
|
107
|
+
passed = len(report.results) - len(report.failures)
|
|
108
|
+
print(f"{report.subject:<{width}} {'PASS' if report.passed else 'FAIL':<6} "
|
|
109
|
+
f"{passed:>6}/{len(report.results)} {report.observations:>12}")
|
|
110
|
+
for failure in report.failures:
|
|
111
|
+
print(f" {failure.assertion}: {failure.detail}")
|
|
112
|
+
if rows and args.matrix:
|
|
113
|
+
print()
|
|
114
|
+
print(_matrix(rows))
|
|
115
|
+
elif rows:
|
|
116
|
+
print(f"\n{'MUTANT':<42} {'TARGET':<8} CAUGHT BY")
|
|
117
|
+
for name, target, ok, caught in rows:
|
|
118
|
+
print(f"{name:<42} {target:<8} {'' if ok else 'MISSED '}"
|
|
119
|
+
f"{','.join(caught) or 'nothing'}")
|
|
120
|
+
return 1 if failed else 0
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _vectors(args: argparse.Namespace) -> int:
|
|
124
|
+
if args.export:
|
|
125
|
+
where = export_vectors(args.export)
|
|
126
|
+
print(f"normative worlds and vectors written to {where} (CC0-1.0, "
|
|
127
|
+
"no attribution required)")
|
|
128
|
+
return 0
|
|
129
|
+
suite = load_suite(args.vectors)
|
|
130
|
+
print(f"{len(suite.vectors)} vectors, "
|
|
131
|
+
f"{sum(len(v.steps) for v in suite.vectors)} steps\n")
|
|
132
|
+
for vector in suite.vectors:
|
|
133
|
+
print(f"{vector.assertion} {vector.name:<44} {len(vector.steps):>3} steps")
|
|
134
|
+
print(f" rules out: {vector.rules_out}")
|
|
135
|
+
return 0
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def main(argv: list[str] | None = None) -> int:
|
|
139
|
+
parser = argparse.ArgumentParser(
|
|
140
|
+
prog="agentic-dataset-conformance",
|
|
141
|
+
description="Run AD-001..AD-015 against any implementation.",
|
|
142
|
+
)
|
|
143
|
+
sub = parser.add_subparsers(dest="command")
|
|
144
|
+
|
|
145
|
+
r = sub.add_parser("run", help="evaluate a subject against the vectors")
|
|
146
|
+
r.add_argument("--subject", action="append", default=[], metavar="MODULE:ATTR",
|
|
147
|
+
help="an implementation to test; repeatable. Defaults to the "
|
|
148
|
+
"built-in toy subject.")
|
|
149
|
+
r.add_argument("--mutants", action="store_true",
|
|
150
|
+
help="also check that broken variants are caught")
|
|
151
|
+
r.add_argument("--matrix", action="store_true",
|
|
152
|
+
help="print the mutation detection matrix (implies --mutants)")
|
|
153
|
+
r.add_argument("--vectors", metavar="DIR", default=None,
|
|
154
|
+
help="use vectors from DIR instead of the packaged ones")
|
|
155
|
+
r.add_argument("--json", action="store_true", help="machine-readable output")
|
|
156
|
+
r.set_defaults(func=_run)
|
|
157
|
+
|
|
158
|
+
v = sub.add_parser("vectors", help="list or export the normative vectors")
|
|
159
|
+
v.add_argument("--export", metavar="DIR", default=None,
|
|
160
|
+
help="copy the CC0 worlds and vectors into DIR")
|
|
161
|
+
v.add_argument("--vectors", metavar="DIR", default=None,
|
|
162
|
+
help="list vectors from DIR instead of the packaged ones")
|
|
163
|
+
v.set_defaults(func=_vectors)
|
|
164
|
+
|
|
165
|
+
args = parser.parse_args(argv)
|
|
166
|
+
if args.command is None:
|
|
167
|
+
parser.print_help()
|
|
168
|
+
return 0
|
|
169
|
+
return args.func(args)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
The files in this directory -- the normative worlds and conformance vectors --
|
|
2
|
+
are dedicated to the public domain under CC0 1.0 Universal.
|
|
3
|
+
|
|
4
|
+
SPDX-License-Identifier: CC0-1.0
|
|
5
|
+
Full text: ../../../LICENSES/CC0-1.0.txt
|
|
6
|
+
https://creativecommons.org/publicdomain/zero/1.0/
|
|
7
|
+
|
|
8
|
+
No attribution is required and no conditions are attached. They are meant to be
|
|
9
|
+
copied unchanged into implementations in other languages, and every condition
|
|
10
|
+
would be friction against the only thing they exist for.
|
|
11
|
+
|
|
12
|
+
The software in the parent package is Apache-2.0. The distribution as a whole
|
|
13
|
+
is therefore "Apache-2.0 AND CC0-1.0".
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"assertion": "AD-001",
|
|
3
|
+
"rules_out": "a dataset participating in admission without a well-formed contract",
|
|
4
|
+
"steps": [
|
|
5
|
+
{
|
|
6
|
+
"op": "register_descriptor",
|
|
7
|
+
"descriptor": {
|
|
8
|
+
"dataset": "broken-dataset",
|
|
9
|
+
"version": "1",
|
|
10
|
+
"description": "no revision, no capabilities"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"op": "request",
|
|
15
|
+
"principal": "process_engineer",
|
|
16
|
+
"text": "search broken",
|
|
17
|
+
"dataset": "broken-dataset",
|
|
18
|
+
"capability": "search",
|
|
19
|
+
"expect": {
|
|
20
|
+
"decision": "REFUSED",
|
|
21
|
+
"reason": "DESCRIPTOR_INVALID",
|
|
22
|
+
"granted": false,
|
|
23
|
+
"executed": false
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"world": "reference"
|
|
28
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
{
|
|
2
|
+
"assertion": "AD-002",
|
|
3
|
+
"rules_out": "an executable action with no capability metadata behind it",
|
|
4
|
+
"capability_surface_matches_descriptors": true,
|
|
5
|
+
"steps": [
|
|
6
|
+
{
|
|
7
|
+
"op": "register_descriptor",
|
|
8
|
+
"descriptor": {
|
|
9
|
+
"dataset": "purification-batches",
|
|
10
|
+
"version": "2026.08.31",
|
|
11
|
+
"revision": "s3-etag-4c1f9a",
|
|
12
|
+
"schema_version": "3",
|
|
13
|
+
"description": "Process and analytical data describing purification batches: recovery, yield, step conditions and in-process controls for downstream chromatography.",
|
|
14
|
+
"schemas": [
|
|
15
|
+
"batch_record",
|
|
16
|
+
"in_process_control",
|
|
17
|
+
"step_condition"
|
|
18
|
+
],
|
|
19
|
+
"capabilities": [
|
|
20
|
+
{
|
|
21
|
+
"name": "search",
|
|
22
|
+
"description": "find batches by free text over batch records",
|
|
23
|
+
"effect": "read",
|
|
24
|
+
"sensitivity": "internal",
|
|
25
|
+
"policy": null,
|
|
26
|
+
"arguments": [
|
|
27
|
+
"query"
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"name": "compare_batches",
|
|
32
|
+
"description": "compare recovery and step conditions across two batches",
|
|
33
|
+
"effect": "read",
|
|
34
|
+
"sensitivity": "internal",
|
|
35
|
+
"policy": "BPD-DATA-014",
|
|
36
|
+
"arguments": [
|
|
37
|
+
"batch_a",
|
|
38
|
+
"batch_b"
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"name": "calculate_yield",
|
|
43
|
+
"description": "compute step yield and cumulative recovery for a batch",
|
|
44
|
+
"effect": "compute",
|
|
45
|
+
"sensitivity": "internal",
|
|
46
|
+
"policy": null,
|
|
47
|
+
"arguments": [
|
|
48
|
+
"batch_id"
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"name": "detect_outliers",
|
|
53
|
+
"description": "flag anomalous batches against the historical distribution",
|
|
54
|
+
"effect": "compute",
|
|
55
|
+
"sensitivity": "confidential",
|
|
56
|
+
"policy": "BPD-DATA-021",
|
|
57
|
+
"arguments": [
|
|
58
|
+
"metric"
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"name": "phantom_export",
|
|
63
|
+
"effect": "read",
|
|
64
|
+
"sensitivity": "internal",
|
|
65
|
+
"description": "advertised with nothing behind it",
|
|
66
|
+
"policy": null,
|
|
67
|
+
"arguments": []
|
|
68
|
+
}
|
|
69
|
+
],
|
|
70
|
+
"prohibited": [
|
|
71
|
+
"delete_source",
|
|
72
|
+
"overwrite_batch_record",
|
|
73
|
+
"bypass_governance",
|
|
74
|
+
"expose_restricted_identifiers"
|
|
75
|
+
],
|
|
76
|
+
"policies": [
|
|
77
|
+
"BPD-DATA-014",
|
|
78
|
+
"BPD-DATA-021"
|
|
79
|
+
],
|
|
80
|
+
"provenance": {
|
|
81
|
+
"system": "volume",
|
|
82
|
+
"source": "s3"
|
|
83
|
+
},
|
|
84
|
+
"freshness": {
|
|
85
|
+
"maximum_age_s": 86400
|
|
86
|
+
},
|
|
87
|
+
"age_s": 3600,
|
|
88
|
+
"quality": {
|
|
89
|
+
"required_fields": [
|
|
90
|
+
"batch_ids",
|
|
91
|
+
"recovery"
|
|
92
|
+
]
|
|
93
|
+
},
|
|
94
|
+
"retention": {
|
|
95
|
+
"observations": 1000
|
|
96
|
+
},
|
|
97
|
+
"endpoints": {
|
|
98
|
+
"mcp": "stdio://purification"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"op": "grant",
|
|
104
|
+
"principal": "process_engineer",
|
|
105
|
+
"dataset": "purification-batches",
|
|
106
|
+
"capability": "phantom_export"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"op": "request",
|
|
110
|
+
"principal": "process_engineer",
|
|
111
|
+
"text": "export everything",
|
|
112
|
+
"dataset": "purification-batches",
|
|
113
|
+
"capability": "phantom_export",
|
|
114
|
+
"expect": {
|
|
115
|
+
"executed": false,
|
|
116
|
+
"result_present": false,
|
|
117
|
+
"error_contains": "not a registered capability"
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
],
|
|
121
|
+
"world": "reference"
|
|
122
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"assertion": "AD-003",
|
|
3
|
+
"rules_out": "execution reachable without an authorization artifact",
|
|
4
|
+
"note": "the general form is the cross-vector invariant executed => granted",
|
|
5
|
+
"steps": [
|
|
6
|
+
{
|
|
7
|
+
"op": "request",
|
|
8
|
+
"principal": "process_engineer",
|
|
9
|
+
"text": "Compare the recovery of batches B001 and B002",
|
|
10
|
+
"expect": {
|
|
11
|
+
"decision": "GRANTED",
|
|
12
|
+
"granted": true,
|
|
13
|
+
"executed": true
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"op": "request",
|
|
18
|
+
"principal": "process_engineer",
|
|
19
|
+
"text": "Calculate the yield for batch B003",
|
|
20
|
+
"grant_ttl_s": -1,
|
|
21
|
+
"expect": {
|
|
22
|
+
"decision": "GRANTED",
|
|
23
|
+
"executed": false,
|
|
24
|
+
"result_present": false,
|
|
25
|
+
"error_contains": "expired"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
"world": "reference"
|
|
30
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"assertion": "AD-004",
|
|
3
|
+
"rules_out": "a refusal that still mints authority",
|
|
4
|
+
"steps": [
|
|
5
|
+
{
|
|
6
|
+
"op": "request",
|
|
7
|
+
"principal": "process_engineer",
|
|
8
|
+
"text": "delete the source",
|
|
9
|
+
"dataset": "purification-batches",
|
|
10
|
+
"capability": "delete_source",
|
|
11
|
+
"prohibited": true,
|
|
12
|
+
"expect": {
|
|
13
|
+
"decision": "REFUSED",
|
|
14
|
+
"reason": "PROHIBITED_OPERATION",
|
|
15
|
+
"policy_id": "AD-POL-004",
|
|
16
|
+
"granted": false,
|
|
17
|
+
"executed": false
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"op": "request",
|
|
22
|
+
"principal": "external_auditor",
|
|
23
|
+
"text": "Compare the recovery of batches B001 and B002",
|
|
24
|
+
"dataset": "purification-batches",
|
|
25
|
+
"capability": "compare_batches",
|
|
26
|
+
"expect": {
|
|
27
|
+
"decision": "REFUSED",
|
|
28
|
+
"reason": "INSUFFICIENT_PRIVILEGE",
|
|
29
|
+
"granted": false,
|
|
30
|
+
"executed": false
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"op": "request",
|
|
35
|
+
"principal": "analyst",
|
|
36
|
+
"text": "detect outliers in recovery",
|
|
37
|
+
"dataset": "purification-batches",
|
|
38
|
+
"capability": "detect_outliers",
|
|
39
|
+
"expect": {
|
|
40
|
+
"decision": "REFUSED",
|
|
41
|
+
"granted": false,
|
|
42
|
+
"executed": false
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"op": "request",
|
|
47
|
+
"principal": "process_engineer",
|
|
48
|
+
"text": "Compare the recovery of batches B001 and B002",
|
|
49
|
+
"dataset": "purification-batches",
|
|
50
|
+
"capability": "compare_batches",
|
|
51
|
+
"expected_schema_version": "99",
|
|
52
|
+
"expect": {
|
|
53
|
+
"decision": "REFUSED",
|
|
54
|
+
"reason": "SCHEMA_VERSION_MISMATCH",
|
|
55
|
+
"granted": false,
|
|
56
|
+
"executed": false
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"op": "request",
|
|
61
|
+
"principal": "process_engineer",
|
|
62
|
+
"text": "search chromatography runs",
|
|
63
|
+
"dataset": "chromatography-results",
|
|
64
|
+
"capability": "search",
|
|
65
|
+
"freshness": 60,
|
|
66
|
+
"expect": {
|
|
67
|
+
"decision": "REFUSED",
|
|
68
|
+
"reason": "FRESHNESS_UNSATISFIABLE",
|
|
69
|
+
"granted": false,
|
|
70
|
+
"executed": false
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
],
|
|
74
|
+
"world": "reference"
|
|
75
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"assertion": "AD-005",
|
|
3
|
+
"rules_out": "unknown authority becoming permission",
|
|
4
|
+
"steps": [
|
|
5
|
+
{
|
|
6
|
+
"op": "request",
|
|
7
|
+
"principal": "process_engineer",
|
|
8
|
+
"text": "Compare the recovery of batches B001 and B002",
|
|
9
|
+
"evaluator": {
|
|
10
|
+
"reachable": false,
|
|
11
|
+
"latency_s": 0.0
|
|
12
|
+
},
|
|
13
|
+
"expect": {
|
|
14
|
+
"decision": "INDETERMINATE",
|
|
15
|
+
"reason": "EVALUATOR_UNAVAILABLE",
|
|
16
|
+
"policy_id": null,
|
|
17
|
+
"rationale_present": true,
|
|
18
|
+
"granted": false,
|
|
19
|
+
"executed": false
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"op": "request",
|
|
24
|
+
"principal": "process_engineer",
|
|
25
|
+
"text": "Compare the recovery of batches B001 and B002",
|
|
26
|
+
"evaluator": {
|
|
27
|
+
"reachable": true,
|
|
28
|
+
"latency_s": 5.0
|
|
29
|
+
},
|
|
30
|
+
"expect": {
|
|
31
|
+
"decision": "INDETERMINATE",
|
|
32
|
+
"reason": "EVALUATOR_TIMEOUT",
|
|
33
|
+
"policy_id": null,
|
|
34
|
+
"rationale_present": true,
|
|
35
|
+
"granted": false,
|
|
36
|
+
"executed": false
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
"world": "reference"
|
|
41
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"assertion": "AD-006",
|
|
3
|
+
"rules_out": "default-allow on an unregistered tool",
|
|
4
|
+
"steps": [
|
|
5
|
+
{
|
|
6
|
+
"op": "request",
|
|
7
|
+
"principal": "process_engineer",
|
|
8
|
+
"text": "do the thing",
|
|
9
|
+
"dataset": "purification-batches",
|
|
10
|
+
"capability": "query_database",
|
|
11
|
+
"expect": {
|
|
12
|
+
"decision": "REFUSED",
|
|
13
|
+
"reason": "UNREGISTERED_CAPABILITY",
|
|
14
|
+
"granted": false,
|
|
15
|
+
"executed": false
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"op": "request",
|
|
20
|
+
"principal": "process_engineer",
|
|
21
|
+
"text": "do the thing",
|
|
22
|
+
"dataset": "purification-batches",
|
|
23
|
+
"capability": "exfiltrate",
|
|
24
|
+
"expect": {
|
|
25
|
+
"decision": "REFUSED",
|
|
26
|
+
"reason": "UNREGISTERED_CAPABILITY",
|
|
27
|
+
"granted": false,
|
|
28
|
+
"executed": false
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"op": "request",
|
|
33
|
+
"principal": "process_engineer",
|
|
34
|
+
"text": "do the thing",
|
|
35
|
+
"dataset": "purification-batches",
|
|
36
|
+
"capability": "search_all",
|
|
37
|
+
"expect": {
|
|
38
|
+
"decision": "REFUSED",
|
|
39
|
+
"reason": "UNREGISTERED_CAPABILITY",
|
|
40
|
+
"granted": false,
|
|
41
|
+
"executed": false
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"op": "request",
|
|
46
|
+
"principal": "process_engineer",
|
|
47
|
+
"text": "do the thing",
|
|
48
|
+
"dataset": "purification-batches",
|
|
49
|
+
"expect": {
|
|
50
|
+
"decision": "REFUSED",
|
|
51
|
+
"reason": "UNREGISTERED_CAPABILITY",
|
|
52
|
+
"granted": false,
|
|
53
|
+
"executed": false
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
],
|
|
57
|
+
"world": "reference"
|
|
58
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"assertion": "AD-007",
|
|
3
|
+
"rules_out": "scope widening between admission and execution",
|
|
4
|
+
"note": "the general form is the cross-vector invariant grant_scope covers executed_scope",
|
|
5
|
+
"steps": [
|
|
6
|
+
{
|
|
7
|
+
"op": "request",
|
|
8
|
+
"principal": "process_engineer",
|
|
9
|
+
"text": "Compare the recovery of batches B001 and B002",
|
|
10
|
+
"expect": {
|
|
11
|
+
"decision": "GRANTED",
|
|
12
|
+
"granted": true
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"op": "delegate",
|
|
17
|
+
"channel": "a2a",
|
|
18
|
+
"dataset": "purification-batches",
|
|
19
|
+
"capability": "compare_batches",
|
|
20
|
+
"scope": {
|
|
21
|
+
"principal_class": "process-engineer",
|
|
22
|
+
"dataset": "purification-batches",
|
|
23
|
+
"capabilities": [
|
|
24
|
+
"compare_batches",
|
|
25
|
+
"detect_outliers"
|
|
26
|
+
],
|
|
27
|
+
"max_sensitivity": "restricted"
|
|
28
|
+
},
|
|
29
|
+
"expect": {
|
|
30
|
+
"executed": false,
|
|
31
|
+
"error_contains": "widen"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
"world": "reference"
|
|
36
|
+
}
|