codereview-loop 0.13.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.
- codereview/__init__.py +6 -0
- codereview/__main__.py +4 -0
- codereview/acceptance.py +649 -0
- codereview/agentsmd.py +259 -0
- codereview/bench.py +881 -0
- codereview/claims.py +182 -0
- codereview/cli.py +2927 -0
- codereview/core.py +9070 -0
- codereview/crash.py +247 -0
- codereview/discovery.py +289 -0
- codereview/disputes.py +749 -0
- codereview/diversity.py +183 -0
- codereview/evidence.py +1023 -0
- codereview/feedback.py +416 -0
- codereview/github.py +286 -0
- codereview/hardening.py +45 -0
- codereview/herdrhost.py +373 -0
- codereview/hooks.py +261 -0
- codereview/intake.py +208 -0
- codereview/mcp_tools.py +455 -0
- codereview/mcp_transport.py +272 -0
- codereview/notify.py +237 -0
- codereview/pilot.py +549 -0
- codereview/policy.py +1086 -0
- codereview/probe.py +534 -0
- codereview/providers.py +1681 -0
- codereview/record_schema.py +904 -0
- codereview/registry.py +294 -0
- codereview/risk.py +368 -0
- codereview/sarif.py +309 -0
- codereview/seatstate.py +398 -0
- codereview/setup_wizard.py +355 -0
- codereview/support.py +328 -0
- codereview/techlead.py +1041 -0
- codereview/tiering.py +265 -0
- codereview/userconfig.py +307 -0
- codereview_loop-0.13.0.dist-info/METADATA +502 -0
- codereview_loop-0.13.0.dist-info/RECORD +44 -0
- codereview_loop-0.13.0.dist-info/WHEEL +5 -0
- codereview_loop-0.13.0.dist-info/entry_points.txt +3 -0
- codereview_loop-0.13.0.dist-info/licenses/LICENSE +21 -0
- codereview_loop-0.13.0.dist-info/top_level.txt +2 -0
- reviewloop/__init__.py +6 -0
- reviewloop/__main__.py +6 -0
codereview/claims.py
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Builder-claim verification for the run record — Epic C4.
|
|
3
|
+
|
|
4
|
+
A builder's work report asserts things: "I ran the tests and
|
|
5
|
+
they passed." The panel cannot run anything, so until now those
|
|
6
|
+
assertions entered a verification round as the builder's word.
|
|
7
|
+
This module checks the checkable part of them — the commands
|
|
8
|
+
the report claims were run, with the outcomes it claims they
|
|
9
|
+
had — and records the result in the run record's
|
|
10
|
+
`claim_verification` section.
|
|
11
|
+
|
|
12
|
+
The machinery is the Epic C evidence runner's, composed, never
|
|
13
|
+
duplicated: `evidence.parse_claims` is the one parser of claim
|
|
14
|
+
forms (a backtick-quoted command with an outcome on the same
|
|
15
|
+
line, or a labelled `Ran:` / `Tests:` / `Command:` line), and
|
|
16
|
+
`evidence.verify_claims` re-runs each claim through the C5
|
|
17
|
+
sandbox (throwaway copy of the tree, allow-listed environment,
|
|
18
|
+
no network, timeouts, process-group kill). There is no second
|
|
19
|
+
parser and no second executor in this module; what it adds is
|
|
20
|
+
the record-facing contract around them:
|
|
21
|
+
|
|
22
|
+
- **Statuses.** Each extracted claim ends `verified` (the
|
|
23
|
+
re-run matches the claim), `mismatch` (the re-run contradicts
|
|
24
|
+
the claim — the claim is downgraded: recorded as not
|
|
25
|
+
standing, with claimed vs actual outcomes stated), or
|
|
26
|
+
`unverifiable` (the re-run could not be performed — the
|
|
27
|
+
sandbox itself failed). A line that is *shaped* like a claim
|
|
28
|
+
— it presents a command in backticks, or carries a claim
|
|
29
|
+
label — but does not parse to an unambiguous command +
|
|
30
|
+
outcome is recorded as `unparsed`: never guessed at, never
|
|
31
|
+
executed. Ordinary prose is not a claim attempt and is not
|
|
32
|
+
recorded at all.
|
|
33
|
+
- **Boundary, stated plainly.** Claim verification is evidence
|
|
34
|
+
recorded in the run record. It is NOT a gate input: no claim
|
|
35
|
+
status changes the verdict, a finding, or any other gate
|
|
36
|
+
input, and nothing in the gate reads this section. A mismatch
|
|
37
|
+
downgrades the *claim*, never the gate — the gate consumes
|
|
38
|
+
configured evidence through the C3 rule (`evidence.
|
|
39
|
+
gate_effect`) and the panel's findings, exactly as before.
|
|
40
|
+
The section's own `boundary` field repeats this in every
|
|
41
|
+
record that carries it.
|
|
42
|
+
- **Never raises.** A malformed report, a missing tree, a
|
|
43
|
+
sandbox failure — every failure is a recorded outcome
|
|
44
|
+
(`unverifiable`, with the reason), never an exception. A
|
|
45
|
+
run whose builder report cannot be claim-checked still
|
|
46
|
+
produces its record.
|
|
47
|
+
|
|
48
|
+
The standalone surface predates this module and keeps its own
|
|
49
|
+
vocabulary (`codereview evidence --claims report.md`, statuses
|
|
50
|
+
confirmed / downgraded / error); the mapping to this section's
|
|
51
|
+
vocabulary happens here, in one place.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
from __future__ import annotations
|
|
55
|
+
|
|
56
|
+
from . import evidence as _evidence
|
|
57
|
+
from .core import redact_secrets
|
|
58
|
+
|
|
59
|
+
SCHEMA = "codereview.claims/1"
|
|
60
|
+
|
|
61
|
+
BOUNDARY = (
|
|
62
|
+
"Claim verification is evidence recorded in the run "
|
|
63
|
+
"record. It is not a gate input: no claim status changes "
|
|
64
|
+
"the verdict, a finding, or any other gate input. A "
|
|
65
|
+
"mismatch downgrades the claim, never the gate.")
|
|
66
|
+
|
|
67
|
+
# The evidence runner's statuses, in this section's vocabulary.
|
|
68
|
+
_STATUS_MAP = {"confirmed": "verified", "downgraded": "mismatch",
|
|
69
|
+
"error": "unverifiable"}
|
|
70
|
+
|
|
71
|
+
STATUSES = ("verified", "mismatch", "unverifiable", "unparsed")
|
|
72
|
+
|
|
73
|
+
_UNPARSED_TEXT_CHARS = 200
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _unparsed_entries(text, parsed_lines):
|
|
77
|
+
"""Claim-shaped lines the parser did not extract, as
|
|
78
|
+
`unparsed` entries. A line is claim-shaped when it presents
|
|
79
|
+
a command in a backtick span or carries one of the claim
|
|
80
|
+
labels — the two forms `evidence.parse_claims` recognises.
|
|
81
|
+
The span form is checked first, mirroring the parser's own
|
|
82
|
+
precedence. Everything else is prose, not a claim attempt,
|
|
83
|
+
and is left unrecorded."""
|
|
84
|
+
entries = []
|
|
85
|
+
for lineno, line in enumerate(text.splitlines(), start=1):
|
|
86
|
+
if lineno in parsed_lines:
|
|
87
|
+
continue
|
|
88
|
+
if _evidence._SPAN_RE.search(line):
|
|
89
|
+
reason = ("command presented without an unambiguous "
|
|
90
|
+
"claimed outcome — not extracted, not run")
|
|
91
|
+
elif _evidence._CLAIM_LABEL_RE.match(line):
|
|
92
|
+
reason = ("labelled line did not yield an "
|
|
93
|
+
"unambiguous command and outcome — not "
|
|
94
|
+
"extracted, not run")
|
|
95
|
+
else:
|
|
96
|
+
continue
|
|
97
|
+
entries.append({
|
|
98
|
+
"line": lineno,
|
|
99
|
+
"status": "unparsed",
|
|
100
|
+
"text": redact_secrets(line.strip())[
|
|
101
|
+
:_UNPARSED_TEXT_CHARS],
|
|
102
|
+
"reason": reason})
|
|
103
|
+
return entries
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def verify_report_claims(report_text, source_dir, timeout=None,
|
|
107
|
+
environ=None):
|
|
108
|
+
"""Verify a builder work report's claimed commands against
|
|
109
|
+
`source_dir` and return the run record's
|
|
110
|
+
`claim_verification` section — or None when there is no
|
|
111
|
+
report to check (None / empty / whitespace-only), so a run
|
|
112
|
+
without a builder report carries no section at all.
|
|
113
|
+
|
|
114
|
+
Never raises. `timeout` is per claimed command and defaults
|
|
115
|
+
to the evidence runner's own bound — one timeout discipline
|
|
116
|
+
for every command the sandbox executes (C5)."""
|
|
117
|
+
if report_text is None:
|
|
118
|
+
return None
|
|
119
|
+
if isinstance(report_text, bytes):
|
|
120
|
+
report_text = report_text.decode("utf-8", "replace")
|
|
121
|
+
if not isinstance(report_text, str):
|
|
122
|
+
return {"schema": SCHEMA, "boundary": BOUNDARY,
|
|
123
|
+
"claims": [],
|
|
124
|
+
"summary": {"total": 0, "verified": 0,
|
|
125
|
+
"mismatch": 0, "unverifiable": 0,
|
|
126
|
+
"unparsed": 0},
|
|
127
|
+
"detail": "builder report was not text "
|
|
128
|
+
f"({type(report_text).__name__}); "
|
|
129
|
+
"no claims could be checked"}
|
|
130
|
+
if not report_text.strip():
|
|
131
|
+
return None
|
|
132
|
+
if timeout is None:
|
|
133
|
+
timeout = _evidence.DEFAULT_TIMEOUT
|
|
134
|
+
try:
|
|
135
|
+
parsed = _evidence.parse_claims(report_text)
|
|
136
|
+
except Exception: # noqa: BLE001 — recorded below, not raised
|
|
137
|
+
parsed = []
|
|
138
|
+
entries = []
|
|
139
|
+
try:
|
|
140
|
+
# P2-07: hand over the claims parsed above — the
|
|
141
|
+
# report text is parsed exactly once per verification.
|
|
142
|
+
result = _evidence.verify_claims(
|
|
143
|
+
report_text, source_dir, timeout=timeout,
|
|
144
|
+
environ=environ, claims=parsed)
|
|
145
|
+
for claim in result.get("claims") or []:
|
|
146
|
+
entries.append({
|
|
147
|
+
"line": claim.get("line"),
|
|
148
|
+
"command": claim.get("command"),
|
|
149
|
+
"claimed": claim.get("claimed"),
|
|
150
|
+
"claimed_exit_code": claim.get(
|
|
151
|
+
"claimed_exit_code"),
|
|
152
|
+
"status": _STATUS_MAP.get(claim.get("status"),
|
|
153
|
+
"unverifiable"),
|
|
154
|
+
"actual_exit_code": claim.get("actual_exit_code"),
|
|
155
|
+
"duration_s": claim.get("duration_s"),
|
|
156
|
+
"output_sha256": claim.get("output_sha256"),
|
|
157
|
+
"detail": claim.get("detail") or ""})
|
|
158
|
+
except Exception as exc: # noqa: BLE001 — a verification that
|
|
159
|
+
# could not run at all leaves every extracted claim
|
|
160
|
+
# unverifiable, with the reason, and the run continues.
|
|
161
|
+
for claim in parsed:
|
|
162
|
+
entries.append({
|
|
163
|
+
"line": claim.get("line"),
|
|
164
|
+
"command": claim.get("command"),
|
|
165
|
+
"claimed": claim.get("claimed"),
|
|
166
|
+
"claimed_exit_code": claim.get(
|
|
167
|
+
"claimed_exit_code"),
|
|
168
|
+
"status": "unverifiable",
|
|
169
|
+
"actual_exit_code": None,
|
|
170
|
+
"duration_s": None,
|
|
171
|
+
"output_sha256": None,
|
|
172
|
+
"detail": f"claim verification could not run: "
|
|
173
|
+
f"{exc}"})
|
|
174
|
+
parsed_lines = {c.get("line") for c in parsed}
|
|
175
|
+
entries.extend(_unparsed_entries(report_text, parsed_lines))
|
|
176
|
+
entries.sort(key=lambda e: e.get("line") or 0)
|
|
177
|
+
summary = {"total": len(entries)}
|
|
178
|
+
for status in STATUSES:
|
|
179
|
+
summary[status] = sum(1 for e in entries
|
|
180
|
+
if e["status"] == status)
|
|
181
|
+
return {"schema": SCHEMA, "boundary": BOUNDARY,
|
|
182
|
+
"claims": entries, "summary": summary}
|