Skip to content

Commit 7533cae

Browse files
Add agent false-pass gate proof (#36)
Co-authored-by: yangfei222666-9 <261852489+yangfei222666-9@users.noreply.github.com>
1 parent 1b2ace6 commit 7533cae

8 files changed

Lines changed: 263 additions & 1 deletion

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
This is the main TaijiOS repository.
3333

3434
- Reviewer start page: [docs/START_HERE_FOR_REVIEWERS.md](docs/START_HERE_FOR_REVIEWERS.md) — 5-minute public review path, exact verdicts, and no-overclaim boundaries.
35+
- Agent Reliability False-Pass Gate: `python scripts/check_false_pass_gate.py --self-test examples/false_pass_gate/fixtures` — local proof that unsupported "done" claims and missing `cannot_claim` boundaries are blocked before success language is accepted.
3536
- SpaceXAI proof packet: [docs/SPACE_X_AI_PROOF_PACKET.md](docs/SPACE_X_AI_PROOF_PACKET.md) — human-review evidence for an evidence-first AI agent runtime; not SpaceX endorsement, not production readiness, and not real hardware control.
3637
- 权威边界文档: [Product Spine](docs/architecture/PRODUCT_SPINE_AUTHORITY.md), [Provider Gate](docs/provider/PROVIDER_BOUNDARY_GATE.md), [Direct LLM Caller](docs/provider/DIRECT_LLM_CALLER_BOUNDARY.md), [Multi-Model Gate](docs/provider/MULTI_MODEL_ARCHITECTURE_GATE.md), [Runtime Matrix](docs/runtime/RUNTIME_MATURITY_MATRIX.md), [HSDL](docs/design/HSDL_CANONICAL_SPEC_v0.1.md), [小九通天录](xiaojiu_tongtianlu/BOUNDARY.md), [Life Systems](life_systems/BIOSECURITY_BOUNDARY.md) — docs-only review gates; not repo-level PASS, runtime readiness, or provider readiness.
3738
- Machine-readable proof index: [docs/proof_index.json](docs/proof_index.json)

docs/START_HERE_FOR_REVIEWERS.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ The core claim is narrow: agent progress should require parseable evidence befor
2323
```bash
2424
pip install -e .
2525
bash scripts/replay_public_demo.sh
26+
python scripts/check_false_pass_gate.py --self-test examples/false_pass_gate/fixtures
2627
```
2728

2829
The replay script writes to a temporary output directory by default so review
@@ -42,6 +43,25 @@ Then inspect:
4243
- `docs/SPACE_X_AI_PROOF_PACKET.md`
4344
- `AUDIT_EVIDENCE.md`
4445

46+
## Agent Reliability: False-Pass Gate
47+
48+
The False-Pass Gate checks whether an AI-agent success claim has passing evidence
49+
and explicit `cannot_claim` boundaries. It is intentionally local and synthetic:
50+
51+
```bash
52+
python scripts/check_false_pass_gate.py --self-test examples/false_pass_gate/fixtures
53+
```
54+
55+
Expected local result:
56+
57+
```text
58+
self_test=PASS cases=3
59+
```
60+
61+
This gate can support `LOCAL_VALIDATED` after the self-test and pytest pass. It
62+
does not prove remote CI, public adoption, production readiness, provider/API
63+
readiness, or recruiting validation.
64+
4565
## Verdict Semantics
4666

4767
- `PASS`: a named local check passed with parseable evidence.
@@ -64,3 +84,4 @@ Do not claim:
6484
- Provider/API readiness unless a scoped live probe verifies it.
6585
- Release evidence `PASS` while `AUDIT_EVIDENCE.md` remains template-only.
6686
- Local demo `PASS` as repo-wide `PASS`.
87+
- Agent "done", "ready", or "complete" claims without passing evidence and explicit `cannot_claim` boundaries.

docs/proof_index.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@
3737
"command": "bash scripts/replay_public_demo.sh --runs 100",
3838
"verdict": "PENDING",
3939
"limitation": "The 100-run and 1,000-run batches are plans until executed and recorded."
40+
},
41+
{
42+
"claim": "The Agent Reliability False-Pass Gate blocks unsupported AI-agent done/readiness claims and missing cannot_claim boundaries.",
43+
"evidence_file": "scripts/check_false_pass_gate.py and examples/false_pass_gate/fixtures/",
44+
"command": "python scripts/check_false_pass_gate.py --self-test examples/false_pass_gate/fixtures",
45+
"verdict": "LOCAL_VALIDATED when self-test and pytest pass",
46+
"limitation": "Synthetic local fixtures only; this does not prove remote CI, public proof update, production readiness, provider/API readiness, recruiting validation, or canonical truth."
4047
}
4148
],
4249
"blocked_claims": [
@@ -45,6 +52,7 @@
4552
"hardware_control",
4653
"provider_api_readiness",
4754
"release_evidence_pass",
48-
"trading_or_order_authority"
55+
"trading_or_order_authority",
56+
"false_pass_gate_without_evidence"
4957
]
5058
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"case_id": "fail_missing_cannot_claim",
3+
"agent_claim": "Local validation passed.",
4+
"success_claims": ["local_validated"],
5+
"evidence": [
6+
{
7+
"type": "command",
8+
"command": "python -m pytest tests/test_false_pass_gate.py -q",
9+
"status": "PASS"
10+
}
11+
],
12+
"cannot_claim": [],
13+
"expected_verdict": "BLOCKED"
14+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"case_id": "fail_unsupported_done",
3+
"agent_claim": "Done. Everything is ready.",
4+
"success_claims": ["done", "ready"],
5+
"evidence": [],
6+
"cannot_claim": [
7+
"remote_ci_pass",
8+
"production_readiness"
9+
],
10+
"expected_verdict": "BLOCKED"
11+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"case_id": "pass_with_evidence",
3+
"agent_claim": "Local demo is complete.",
4+
"success_claims": ["done"],
5+
"evidence": [
6+
{
7+
"type": "command",
8+
"command": "python examples/quickstart_minimal.py",
9+
"status": "PASS"
10+
}
11+
],
12+
"cannot_claim": [
13+
"remote_ci_pass",
14+
"production_readiness",
15+
"public_adoption"
16+
],
17+
"expected_verdict": "PASS"
18+
}

scripts/check_false_pass_gate.py

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
#!/usr/bin/env python3
2+
"""Block unsupported AI-agent success claims before they become evidence."""
3+
4+
from __future__ import annotations
5+
6+
import argparse
7+
import json
8+
from pathlib import Path
9+
from typing import Any
10+
11+
12+
SUCCESS_TOKENS = {
13+
"done",
14+
"ready",
15+
"complete",
16+
"completed",
17+
"success",
18+
"succeeded",
19+
"pass",
20+
"passed",
21+
"local_validated",
22+
}
23+
24+
25+
def _as_list(value: Any) -> list[Any]:
26+
if value is None:
27+
return []
28+
if isinstance(value, list):
29+
return value
30+
return [value]
31+
32+
33+
def _text_tokens(case: dict[str, Any]) -> set[str]:
34+
values = _as_list(case.get("success_claims"))
35+
if case.get("agent_claim"):
36+
values.append(case["agent_claim"])
37+
return {str(value).lower().replace("-", "_") for value in values}
38+
39+
40+
def _is_success_claim(case: dict[str, Any]) -> bool:
41+
tokens = _text_tokens(case)
42+
return any(token in SUCCESS_TOKENS for token in tokens) or any(
43+
word in token for token in tokens for word in SUCCESS_TOKENS
44+
)
45+
46+
47+
def _has_passing_evidence(case: dict[str, Any]) -> bool:
48+
for item in _as_list(case.get("evidence")):
49+
if not isinstance(item, dict):
50+
continue
51+
status = str(item.get("status", "")).upper()
52+
has_pointer = any(item.get(key) for key in ("command", "file", "artifact", "url", "value"))
53+
if status == "PASS" and item.get("type") and has_pointer:
54+
return True
55+
return False
56+
57+
58+
def evaluate_case(case: dict[str, Any]) -> tuple[str, list[str]]:
59+
reasons: list[str] = []
60+
61+
if _is_success_claim(case) and not _has_passing_evidence(case):
62+
reasons.append("missing_passing_evidence")
63+
64+
if not _as_list(case.get("cannot_claim")):
65+
reasons.append("missing_cannot_claim")
66+
67+
if reasons:
68+
return "BLOCKED", reasons
69+
return "PASS", ["evidence_and_boundaries_present"]
70+
71+
72+
def load_case(path: Path) -> dict[str, Any]:
73+
return json.loads(path.read_text(encoding="utf-8"))
74+
75+
76+
def format_case_result(path: Path, actual: str, expected: str | None, reasons: list[str]) -> str:
77+
case_id = load_case(path).get("case_id", path.stem)
78+
fields = [
79+
f"case_id={case_id}",
80+
f"actual={actual}",
81+
f"reasons={','.join(reasons)}",
82+
]
83+
if expected:
84+
fields.append(f"expected={expected}")
85+
return " ".join(fields)
86+
87+
88+
def run_case(path: Path) -> int:
89+
case = load_case(path)
90+
actual, reasons = evaluate_case(case)
91+
print(format_case_result(path, actual, case.get("expected_verdict"), reasons))
92+
return 0 if actual == "PASS" else 1
93+
94+
95+
def run_self_test(fixtures_dir: Path) -> int:
96+
paths = sorted(fixtures_dir.glob("*.json"))
97+
failures: list[str] = []
98+
99+
for path in paths:
100+
case = load_case(path)
101+
actual, reasons = evaluate_case(case)
102+
expected = str(case.get("expected_verdict", "")).upper()
103+
print(format_case_result(path, actual, expected, reasons))
104+
if actual != expected:
105+
failures.append(path.name)
106+
107+
if failures:
108+
print(f"self_test=FAIL cases={len(paths)} failures={','.join(failures)}")
109+
return 1
110+
111+
print(f"self_test=PASS cases={len(paths)}")
112+
return 0
113+
114+
115+
def parse_args() -> argparse.Namespace:
116+
parser = argparse.ArgumentParser(description="Check false-pass evidence gates.")
117+
group = parser.add_mutually_exclusive_group(required=True)
118+
group.add_argument("--case", type=Path, help="Evaluate one JSON case file.")
119+
group.add_argument("--self-test", type=Path, help="Evaluate all JSON fixtures in a directory.")
120+
return parser.parse_args()
121+
122+
123+
def main() -> int:
124+
args = parse_args()
125+
if args.case:
126+
return run_case(args.case)
127+
return run_self_test(args.self_test)
128+
129+
130+
if __name__ == "__main__":
131+
raise SystemExit(main())

tests/test_false_pass_gate.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import json
2+
import subprocess
3+
import sys
4+
from pathlib import Path
5+
6+
7+
PROJECT_ROOT = Path(__file__).parent.parent
8+
SCRIPT = PROJECT_ROOT / "scripts" / "check_false_pass_gate.py"
9+
FIXTURES = PROJECT_ROOT / "examples" / "false_pass_gate" / "fixtures"
10+
11+
12+
def run_gate(*args: str) -> subprocess.CompletedProcess[str]:
13+
return subprocess.run(
14+
[sys.executable, str(SCRIPT), *args],
15+
cwd=PROJECT_ROOT,
16+
text=True,
17+
capture_output=True,
18+
timeout=10,
19+
)
20+
21+
22+
def test_self_test_fixture_suite_passes():
23+
result = run_gate("--self-test", str(FIXTURES))
24+
25+
assert result.returncode == 0, result.stderr
26+
assert "self_test=PASS" in result.stdout
27+
assert "cases=3" in result.stdout
28+
29+
30+
def test_unsupported_done_claim_is_blocked():
31+
result = run_gate("--case", str(FIXTURES / "fail_unsupported_done.json"))
32+
33+
assert result.returncode == 1
34+
assert "case_id=fail_unsupported_done" in result.stdout
35+
assert "actual=BLOCKED" in result.stdout
36+
assert "missing_passing_evidence" in result.stdout
37+
38+
39+
def test_case_with_passing_evidence_and_boundaries_passes():
40+
result = run_gate("--case", str(FIXTURES / "pass_with_evidence.json"))
41+
42+
assert result.returncode == 0, result.stderr
43+
assert "case_id=pass_with_evidence" in result.stdout
44+
assert "actual=PASS" in result.stdout
45+
46+
47+
def test_proof_index_records_false_pass_gate_claim_and_limits():
48+
proof_index = json.loads((PROJECT_ROOT / "docs" / "proof_index.json").read_text())
49+
50+
matching_claims = [
51+
claim
52+
for claim in proof_index["claims"]
53+
if "False-Pass Gate" in claim["claim"]
54+
]
55+
assert matching_claims
56+
assert matching_claims[0]["verdict"] == "LOCAL_VALIDATED when self-test and pytest pass"
57+
assert "remote CI" in matching_claims[0]["limitation"]
58+
assert "false_pass_gate_without_evidence" in proof_index["blocked_claims"]

0 commit comments

Comments
 (0)