dos-kernel 0.22.0__py3-none-win_amd64.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.
Files changed (178) hide show
  1. dos/__init__.py +261 -0
  2. dos/_bin/dos-hook.exe +0 -0
  3. dos/_filelock.py +255 -0
  4. dos/_job_policy.py +97 -0
  5. dos/_tree.py +145 -0
  6. dos/admission.py +433 -0
  7. dos/answer_shape.py +299 -0
  8. dos/arbiter.py +859 -0
  9. dos/archive_lock.py +266 -0
  10. dos/arg_provenance.py +814 -0
  11. dos/attest.py +472 -0
  12. dos/breaker.py +311 -0
  13. dos/churn.py +226 -0
  14. dos/claim_extract.py +229 -0
  15. dos/claim_ttl.py +150 -0
  16. dos/cli.py +8721 -0
  17. dos/commit_audit.py +666 -0
  18. dos/completion.py +466 -0
  19. dos/concurrency_class.py +154 -0
  20. dos/config.py +1380 -0
  21. dos/config_lint.py +464 -0
  22. dos/cooldown.py +390 -0
  23. dos/coverage.py +387 -0
  24. dos/dangling_intent.py +287 -0
  25. dos/data_class.py +397 -0
  26. dos/decisions.py +1274 -0
  27. dos/decisions_tui.py +251 -0
  28. dos/dispatch_top.py +740 -0
  29. dos/dispatch_top_tui.py +116 -0
  30. dos/drivers/__init__.py +40 -0
  31. dos/drivers/ci_status.py +630 -0
  32. dos/drivers/citation_resolve.py +703 -0
  33. dos/drivers/decision_stop.py +98 -0
  34. dos/drivers/export_file.py +173 -0
  35. dos/drivers/export_otlp.py +275 -0
  36. dos/drivers/export_statsd.py +242 -0
  37. dos/drivers/hook_dialects.py +391 -0
  38. dos/drivers/job.py +47 -0
  39. dos/drivers/llm_judge.py +360 -0
  40. dos/drivers/memory_recall.py +1231 -0
  41. dos/drivers/notify_slack.py +373 -0
  42. dos/drivers/notify_webhook.py +251 -0
  43. dos/drivers/operator_judge.py +114 -0
  44. dos/drivers/os_acceptance.py +228 -0
  45. dos/drivers/paste_log.py +132 -0
  46. dos/drivers/plan_scope.py +133 -0
  47. dos/drivers/self_improve.py +375 -0
  48. dos/drivers/similarity_judge.py +249 -0
  49. dos/drivers/state_diff.py +274 -0
  50. dos/drivers/supervisor.py +347 -0
  51. dos/drivers/watchdog.py +363 -0
  52. dos/drivers/workshop.py +160 -0
  53. dos/durable_schema.py +344 -0
  54. dos/effect_witness.py +393 -0
  55. dos/efficiency.py +318 -0
  56. dos/enforce.py +414 -0
  57. dos/enumerate.py +776 -0
  58. dos/env_print.py +378 -0
  59. dos/event_severity.py +258 -0
  60. dos/evidence.py +692 -0
  61. dos/exec_capability.py +256 -0
  62. dos/export_cursor.py +143 -0
  63. dos/exporter.py +320 -0
  64. dos/firing_label.py +353 -0
  65. dos/fleet_roll.py +226 -0
  66. dos/gate_classify.py +827 -0
  67. dos/gh4_coverage.py +179 -0
  68. dos/git_delta.py +122 -0
  69. dos/guard.py +215 -0
  70. dos/health.py +552 -0
  71. dos/help_summary.py +519 -0
  72. dos/home.py +934 -0
  73. dos/hook_binary.py +194 -0
  74. dos/hook_dialect.py +271 -0
  75. dos/hook_exit.py +191 -0
  76. dos/hook_install.py +437 -0
  77. dos/id_alloc.py +304 -0
  78. dos/improve.py +499 -0
  79. dos/intent_ledger.py +635 -0
  80. dos/interpret.py +176 -0
  81. dos/intervention.py +769 -0
  82. dos/intervention_eval.py +371 -0
  83. dos/journal_delta.py +308 -0
  84. dos/judge_eval.py +328 -0
  85. dos/judges.py +366 -0
  86. dos/lane_infer.py +127 -0
  87. dos/lane_journal.py +1001 -0
  88. dos/lane_lease.py +952 -0
  89. dos/lane_overlap.py +228 -0
  90. dos/lease_health.py +282 -0
  91. dos/lifecycle.py +211 -0
  92. dos/liveness.py +352 -0
  93. dos/lock_modes.py +185 -0
  94. dos/log_source.py +395 -0
  95. dos/loop_decide.py +1746 -0
  96. dos/marker_gate.py +254 -0
  97. dos/marker_sensor.py +396 -0
  98. dos/noop_streak.py +280 -0
  99. dos/notify.py +479 -0
  100. dos/observe.py +175 -0
  101. dos/oracle.py +1661 -0
  102. dos/overlap_eval.py +214 -0
  103. dos/overlap_policy.py +342 -0
  104. dos/packet_sidecar.py +267 -0
  105. dos/phase_shipped.py +1985 -0
  106. dos/pick_priority.py +225 -0
  107. dos/pickable.py +369 -0
  108. dos/picker_oracle.py +1037 -0
  109. dos/plan_board.py +513 -0
  110. dos/plan_board_tui.py +113 -0
  111. dos/plan_source.py +455 -0
  112. dos/posttool_sensor.py +528 -0
  113. dos/precursor_gate.py +499 -0
  114. dos/precursor_gate_eval.py +239 -0
  115. dos/preflight.py +825 -0
  116. dos/pretool_sensor.py +490 -0
  117. dos/proc_delta.py +181 -0
  118. dos/productivity.py +296 -0
  119. dos/provider_limit.py +242 -0
  120. dos/py.typed +4 -0
  121. dos/reason_morphology.py +299 -0
  122. dos/reasons.py +449 -0
  123. dos/reconcile.py +173 -0
  124. dos/recurring_wedge.py +206 -0
  125. dos/render.py +393 -0
  126. dos/result_state.py +468 -0
  127. dos/resume.py +578 -0
  128. dos/resume_evidence.py +293 -0
  129. dos/retention.py +344 -0
  130. dos/reward.py +372 -0
  131. dos/rewind.py +587 -0
  132. dos/rewind_evidence.py +168 -0
  133. dos/rewind_tokens.py +252 -0
  134. dos/run_id.py +342 -0
  135. dos/scope.py +520 -0
  136. dos/scope_source.py +382 -0
  137. dos/scout.py +982 -0
  138. dos/self_modify.py +209 -0
  139. dos/sibling_scan.py +569 -0
  140. dos/skills/EXAMPLES.md +584 -0
  141. dos/skills/dos-class-cycle/SKILL.md +107 -0
  142. dos/skills/dos-dispatch/SKILL.md +177 -0
  143. dos/skills/dos-dispatch-loop/SKILL.md +254 -0
  144. dos/skills/dos-goal-gate/SKILL.md +269 -0
  145. dos/skills/dos-next-up/SKILL.md +231 -0
  146. dos/skills/dos-promote/SKILL.md +114 -0
  147. dos/skills/dos-replan/SKILL.md +159 -0
  148. dos/skills/dos-replan-loop/SKILL.md +114 -0
  149. dos/skills/dos-self-improve/SKILL.md +213 -0
  150. dos/skills/dos-supervise-loop/SKILL.md +180 -0
  151. dos/skills/dos-unstick/SKILL.md +108 -0
  152. dos/skills/dos-witness-claim/SKILL.md +251 -0
  153. dos/stamp.py +1002 -0
  154. dos/state_health.py +387 -0
  155. dos/status.py +114 -0
  156. dos/stop_policy.py +334 -0
  157. dos/supervise.py +1014 -0
  158. dos/testwitness.py +392 -0
  159. dos/timeline.py +1027 -0
  160. dos/tokens.py +485 -0
  161. dos/tool_stream.py +393 -0
  162. dos/tool_stream_eval.py +226 -0
  163. dos/trace.py +524 -0
  164. dos/verdict.py +140 -0
  165. dos/verdict_cli.py +189 -0
  166. dos/verdict_journal.py +497 -0
  167. dos/verdict_rollup.py +217 -0
  168. dos/verdicts.py +181 -0
  169. dos/wedge_reason.py +282 -0
  170. dos_kernel-0.22.0.dist-info/METADATA +859 -0
  171. dos_kernel-0.22.0.dist-info/RECORD +178 -0
  172. dos_kernel-0.22.0.dist-info/WHEEL +5 -0
  173. dos_kernel-0.22.0.dist-info/entry_points.txt +39 -0
  174. dos_kernel-0.22.0.dist-info/licenses/LICENSE +21 -0
  175. dos_kernel-0.22.0.dist-info/top_level.txt +2 -0
  176. dos_mcp/__init__.py +52 -0
  177. dos_mcp/py.typed +2 -0
  178. dos_mcp/server.py +779 -0
dos/testwitness.py ADDED
@@ -0,0 +1,392 @@
1
+ """TWV — the test-witness verdict: does this new test actually WITNESS this change? (docs/288)
2
+
3
+ "I added a test for this" is one of the highest-frequency work claims an agent
4
+ makes, and today every kernel consumer takes it at face value the moment the
5
+ suite is green: `verify()` witnesses that a *phase shipped* (it never looks
6
+ inside a test), `improve()` requires *suite green on the candidate tree* (a new
7
+ test that passes on BOTH trees keeps the suite green by construction), and the
8
+ diff-presence rung ("the commit contains a test file") is W2 presence, never W3
9
+ goal (docs/204). So the canonical inflation goes uncaught: an agent fixes (or
10
+ half-fixes, or doesn't fix) a bug, writes a test that asserts something *already
11
+ true on the baseline*, and every downstream consumer — the keep-gate, the reward
12
+ set, the reviewer reading "tests added ✓" — banks a unit of assurance that does
13
+ not exist.
14
+
15
+ The deterministic kill for exactly this is Cognition's FrontierCode
16
+ **reverse-classical testing** rule: run the new test against the tree WITHOUT
17
+ the change; it must fail. A test that passes on both trees witnesses nothing.
18
+ That check is two env-authored bits joined — which is to say, it is a DOS
19
+ verdict, and this module is it:
20
+
21
+ testwitness.classify(TestRunEvidence) -> TestWitnessVerdict
22
+ DISCRIMINATES / VACUOUS / UNSATISFIED / REGRESSIVE / ABSTAIN
23
+
24
+ **Byte-clean by construction (the docs/138 invariant).** The test's *content* is
25
+ agent-authored — maximally forgeable; the kernel never reads it. What the
26
+ verdict joins is two bits the agent authors zero bytes of: the runner's outcome
27
+ on the baseline tree and the runner's outcome on the candidate tree. Two
28
+ independently-authored facts (two different trees, one env-controlled runner),
29
+ joined into a label neither fact carries alone — the docs/179 fold-mints-data
30
+ rule, satisfied the same way `liveness` joins git-delta to journal-delta. An
31
+ agent cannot write its way from VACUOUS to DISCRIMINATES: the only path is a
32
+ test that actually fails on the tree it didn't get to touch. And the `rung`
33
+ field makes the floor structural rather than procedural: outcomes that exist
34
+ only as the agent's own narration arrive on `AGENT_AUTHORED` and the verdict
35
+ ABSTAINS — `--baseline fail --candidate pass --forgeable` cannot DISCRIMINATE,
36
+ exactly as `--witness confirm --forgeable` cannot ACCEPT in `reward`. Same
37
+ floor, same shape, same $0 demo.
38
+
39
+ THE HONEST RESIDUE — what DISCRIMINATES does NOT prove (docs/288 §6)
40
+ ====================================================================
41
+
42
+ * **Tree-discrimination ≠ behavior-assertion.** An adversarial test can
43
+ discriminate trivially (`assert os.path.exists("the_new_file.py")` is
44
+ red→green across any change that adds a file). DISCRIMINATES is sound
45
+ against the *lazy* inflation (the vacuous test — the overwhelmingly common
46
+ case) and against pass/pass forgery; it is not sound against an adversarial
47
+ test author. "Does this test assert the intended *behavior*?" is semantic —
48
+ that residue goes UP the ladder to the JUDGE rung (`dos.judges`, advisory,
49
+ fail-to-abstain), exactly where FrontierCode puts its own LLM rubric. The
50
+ `assert_level` bit narrows the residue (a structural discrimination is
51
+ flagged typed, not buried in prose) but does not close it.
52
+ * **One test, one change — not a suite verdict.** TWV adjudicates a (test,
53
+ change) pair. "The suite is green" stays `improve()`'s floor; TWV is the
54
+ *per-new-test* rung a keep-gate may additionally AND in.
55
+ * **The gather is the caller's.** Running the same test on two trees (a
56
+ worktree checkout without the candidate diff, then with it) is I/O — it
57
+ lives at the boundary (a CI step, the `dos-self-improve` engine's worktree
58
+ flow, a host driver), never inside `classify`. The kernel adjudicates
59
+ outcomes; it does not run pytest.
60
+
61
+ PURE — no I/O, no clock, and deliberately **no policy object**: the rule is
62
+ structural (there is no threshold a host could legitimately tune). It sits in
63
+ the kernel layer beside `reward` / `efficiency` / `liveness` and names no host,
64
+ no runner, no test framework. ADVISORY (docs/99): it reports; a consumer
65
+ (`improve()`'s host engine, a reward-set builder, a CI gate) decides.
66
+ """
67
+
68
+ from __future__ import annotations
69
+
70
+ import enum
71
+ from dataclasses import dataclass
72
+
73
+ # The accountability spectrum (docs/117) — who authored the two outcome bits.
74
+ # A kernel sibling import (the CLAUDE.md layer-1 rule): the spectrum has one
75
+ # home and the floor must be the SAME floor `reward`/`evidence` enforce.
76
+ from dos.evidence import Accountability
77
+
78
+ __all__ = [
79
+ "RunOutcome",
80
+ "TestWitness",
81
+ "TestRunEvidence",
82
+ "TestWitnessVerdict",
83
+ "classify",
84
+ "DISCRIMINATES",
85
+ "VACUOUS",
86
+ "UNSATISFIED",
87
+ "REGRESSIVE",
88
+ "ABSTAIN",
89
+ ]
90
+
91
+
92
+ class RunOutcome(str, enum.Enum):
93
+ """One runner outcome for one test on one tree — a closed, four-valued enum.
94
+
95
+ `str`-valued so it round-trips through a CLI flag / JSON without a lookup
96
+ table (the `Liveness` / `Efficiency` idiom). The FAILED/ERRORED split is
97
+ load-bearing (it feeds the verdict's `assert_level` bit):
98
+
99
+ PASSED — the test ran and its assertions held.
100
+ FAILED — the test RAN and an assertion was false (assert-level red): the
101
+ test executed against the tree's behavior and rejected it.
102
+ ERRORED — the test could not run at all (a collection/import error —
103
+ e.g. it imports a module only the candidate tree has):
104
+ structural red, weaker than assert-level.
105
+ NOT_RUN — no outcome exists for this tree. Half a join is not a join;
106
+ `classify` ABSTAINS rather than guess the missing half.
107
+ """
108
+
109
+ PASSED = "PASSED"
110
+ FAILED = "FAILED"
111
+ ERRORED = "ERRORED"
112
+ NOT_RUN = "NOT_RUN"
113
+
114
+ def __str__(self) -> str: # pragma: no cover - trivial
115
+ return self.value
116
+
117
+ @classmethod
118
+ def parse(cls, token: str) -> "RunOutcome":
119
+ """Parse a CLI-friendly token (`pass`/`fail`/`error`/`not-run`, or the
120
+ enum value itself, any case) into an outcome. Raises ValueError on
121
+ anything else — an outcome the runner never emitted must not be
122
+ invented at the parse boundary."""
123
+ t = (token or "").strip().lower().replace("_", "-")
124
+ aliases = {
125
+ "pass": cls.PASSED, "passed": cls.PASSED,
126
+ "fail": cls.FAILED, "failed": cls.FAILED,
127
+ "error": cls.ERRORED, "errored": cls.ERRORED,
128
+ "not-run": cls.NOT_RUN, "notrun": cls.NOT_RUN,
129
+ }
130
+ if t in aliases:
131
+ return aliases[t]
132
+ raise ValueError(
133
+ f"unknown run outcome {token!r}; expected one of "
134
+ f"pass/fail/error/not-run"
135
+ )
136
+
137
+
138
+ class TestWitness(str, enum.Enum):
139
+ """The typed test-witness verdict — five states, mutually exclusive.
140
+
141
+ The ladder is documented on `classify`; the one-line meanings:
142
+
143
+ DISCRIMINATES — red→green across the change: the ONLY verdict that
144
+ licenses "this test witnesses this change."
145
+ VACUOUS — passed on BOTH trees: the test would have passed without
146
+ the change; it witnesses nothing. The FrontierCode
147
+ false-positive shape — the load-bearing verdict.
148
+ UNSATISFIED — the change does not satisfy its own test (candidate not
149
+ green): the red half of red→green without the green.
150
+ REGRESSIVE — the change BREAKS a baseline-green test.
151
+ ABSTAIN — forgeable rung or a missing run: never mint a witness
152
+ off a narration or half a join.
153
+ """
154
+
155
+ # Not a pytest class, despite the Test* name (dunders are not enum members).
156
+ __test__ = False
157
+
158
+ DISCRIMINATES = "DISCRIMINATES"
159
+ VACUOUS = "VACUOUS"
160
+ UNSATISFIED = "UNSATISFIED"
161
+ REGRESSIVE = "REGRESSIVE"
162
+ ABSTAIN = "ABSTAIN"
163
+
164
+ def __str__(self) -> str: # pragma: no cover - trivial
165
+ return self.value
166
+
167
+
168
+ # Module-level aliases so a consumer writes `verdict is testwitness.VACUOUS`
169
+ # (the `reward.ACCEPT` idiom).
170
+ DISCRIMINATES = TestWitness.DISCRIMINATES
171
+ VACUOUS = TestWitness.VACUOUS
172
+ UNSATISFIED = TestWitness.UNSATISFIED
173
+ REGRESSIVE = TestWitness.REGRESSIVE
174
+ ABSTAIN = TestWitness.ABSTAIN
175
+
176
+
177
+ @dataclass(frozen=True)
178
+ class TestRunEvidence:
179
+ """The facts `classify()` reads — gathered by the CALLER at its boundary.
180
+
181
+ No I/O inside the verdict (the arbiter rule): the caller's boundary (a CI
182
+ step, the self-improve engine's worktree flow, the `dos test-witness`
183
+ flags) ran ONE test on TWO trees and froze the outcomes here.
184
+
185
+ baseline — the runner's outcome for THE TEST on the tree WITHOUT the
186
+ change (the merge-base / HEAD-before checkout).
187
+ candidate — the runner's outcome for THE TEST on the tree WITH the change.
188
+ rung — who authored those two outcome bits (docs/117 spectrum).
189
+ `OS_RECORDED` by default: a runner exit the environment
190
+ recorded. `AGENT_AUTHORED` = the agent *narrated* "it failed
191
+ before and passes now" — forgeable, and the verdict ABSTAINS
192
+ on it (the structural floor, not a procedural convention).
193
+ """
194
+
195
+ __test__ = False # not a pytest class, despite the Test* name
196
+
197
+ baseline: RunOutcome
198
+ candidate: RunOutcome
199
+ rung: Accountability = Accountability.OS_RECORDED
200
+
201
+ def __post_init__(self) -> None:
202
+ if not isinstance(self.baseline, RunOutcome):
203
+ raise ValueError("baseline must be a RunOutcome (use RunOutcome.parse)")
204
+ if not isinstance(self.candidate, RunOutcome):
205
+ raise ValueError("candidate must be a RunOutcome (use RunOutcome.parse)")
206
+ if not isinstance(self.rung, Accountability):
207
+ raise ValueError("rung must be an Accountability")
208
+
209
+ @classmethod
210
+ def of(
211
+ cls,
212
+ baseline: "RunOutcome | str",
213
+ candidate: "RunOutcome | str",
214
+ *,
215
+ forgeable: bool = False,
216
+ ) -> "TestRunEvidence":
217
+ """Build evidence from two outcomes (enum members or CLI tokens).
218
+ `forgeable=True` puts the outcomes on the `AGENT_AUTHORED` rung — the
219
+ floor demo: the same two bits, narrated instead of recorded, ABSTAIN."""
220
+ b = baseline if isinstance(baseline, RunOutcome) else RunOutcome.parse(baseline)
221
+ c = candidate if isinstance(candidate, RunOutcome) else RunOutcome.parse(candidate)
222
+ rung = Accountability.AGENT_AUTHORED if forgeable else Accountability.OS_RECORDED
223
+ return cls(baseline=b, candidate=c, rung=rung)
224
+
225
+
226
+ @dataclass(frozen=True)
227
+ class TestWitnessVerdict:
228
+ """The single verdict `classify()` returns, with the facts echoed back.
229
+
230
+ `verdict` is the typed `TestWitness`. `witnesses` is the one projection bit
231
+ a consumer ANDs in (True iff DISCRIMINATES) — a keep-gate / reward-set
232
+ builder branches on the bit, never re-parses the prose. `assert_level` is
233
+ True only for an assert-level discrimination (baseline FAILED, not ERRORED):
234
+ both forms honestly discriminate — the test provably cannot pass without the
235
+ change — but the assert-level form is the stronger witness (the test *ran*
236
+ against the old behavior and rejected it), and a consumer that wants only
237
+ the strong form filters on the bit. `reason` is the one-line operator-facing
238
+ summary; `evidence` is carried so `--json` emits the verdict *and the facts
239
+ behind it* in one object (the legible-distrust renderer seam).
240
+ """
241
+
242
+ __test__ = False # not a pytest class, despite the Test* name
243
+
244
+ verdict: TestWitness
245
+ reason: str
246
+ witnesses: bool
247
+ assert_level: bool
248
+ evidence: TestRunEvidence
249
+
250
+ def to_dict(self) -> dict:
251
+ e = self.evidence
252
+ return {
253
+ "verdict": self.verdict.value,
254
+ "reason": self.reason,
255
+ "witnesses": self.witnesses,
256
+ "assert_level": self.assert_level,
257
+ "evidence": {
258
+ "baseline": e.baseline.value,
259
+ "candidate": e.candidate.value,
260
+ "rung": e.rung.value,
261
+ },
262
+ }
263
+
264
+
265
+ def classify(evidence: TestRunEvidence) -> TestWitnessVerdict:
266
+ """Classify whether a (test, change) pair's two-tree outcomes witness the change.
267
+
268
+ PURE — no I/O, no clock, no policy (the rule is structural; there is no
269
+ threshold a host could legitimately tune). The ladder, top to bottom
270
+ (docs/288 §4):
271
+
272
+ 1. ABSTAIN — the rung is forgeable (`AGENT_AUTHORED`: the agent narrated
273
+ the red→green), or either run is `NOT_RUN`. Half a join is not a join;
274
+ a narration is not a run. Never mint a witness off either — the
275
+ `believe_under_floor` direction, applied to discrimination.
276
+ 2. REGRESSIVE — baseline PASSED, candidate not: the change *breaks* the
277
+ test. For a pre-existing test, a regression; for a test the change
278
+ itself ships, an incoherent claim. (FrontierCode's "regression safety"
279
+ dimension, reduced to its deterministic core.)
280
+ 3. UNSATISFIED — candidate not PASSED (and the baseline didn't pass
281
+ either): the change does not satisfy its own test. Nothing is
282
+ witnessed, and the fix claim itself is now in doubt — the red half of
283
+ red→green without the green.
284
+ 4. VACUOUS — PASSED on both trees. The test would have passed without the
285
+ change; it witnesses nothing. The FrontierCode false-positive shape,
286
+ and the load-bearing verdict of this module — the one a naive
287
+ "tests added ✓" reviewer banks as assurance.
288
+ 5. DISCRIMINATES — baseline FAILED/ERRORED, candidate PASSED: red→green
289
+ across the change. The only verdict whose `witnesses` bit is True.
290
+ `assert_level` distinguishes the strong form (baseline FAILED — the
291
+ test ran and rejected the old behavior) from the structural form
292
+ (baseline ERRORED — e.g. the test imports a module only the candidate
293
+ tree has; it provably cannot pass without the change, but it never
294
+ executed against the old behavior).
295
+ """
296
+ b, c = evidence.baseline, evidence.candidate
297
+
298
+ # 1. ABSTAIN — forgeable rung: the outcomes are the agent's own narration.
299
+ # The structural floor: an agent cannot narrate its way to DISCRIMINATES,
300
+ # exactly as a forgeable read-back cannot ACCEPT in `reward`.
301
+ if evidence.rung.is_agent_authored:
302
+ return TestWitnessVerdict(
303
+ verdict=TestWitness.ABSTAIN,
304
+ reason=(
305
+ f"outcomes are AGENT_AUTHORED (narrated '{b} -> {c}') — a "
306
+ f"narration is not a run; no witness minted (gather both "
307
+ f"outcomes from an env-recorded runner)"
308
+ ),
309
+ witnesses=False,
310
+ assert_level=False,
311
+ evidence=evidence,
312
+ )
313
+
314
+ # 1b. ABSTAIN — half a join is not a join: one (or both) trees has no
315
+ # runner outcome. Never guess the missing half.
316
+ if b is RunOutcome.NOT_RUN or c is RunOutcome.NOT_RUN:
317
+ missing = "baseline" if b is RunOutcome.NOT_RUN else "candidate"
318
+ if b is RunOutcome.NOT_RUN and c is RunOutcome.NOT_RUN:
319
+ missing = "baseline and candidate"
320
+ return TestWitnessVerdict(
321
+ verdict=TestWitness.ABSTAIN,
322
+ reason=(
323
+ f"the {missing} run is missing (NOT_RUN) — half a join is not "
324
+ f"a join; run the test on both trees"
325
+ ),
326
+ witnesses=False,
327
+ assert_level=False,
328
+ evidence=evidence,
329
+ )
330
+
331
+ # 2. REGRESSIVE — the change breaks a baseline-green test.
332
+ if b is RunOutcome.PASSED and c is not RunOutcome.PASSED:
333
+ return TestWitnessVerdict(
334
+ verdict=TestWitness.REGRESSIVE,
335
+ reason=(
336
+ f"baseline PASSED, candidate {c} — the change BREAKS this test "
337
+ f"(a regression for a pre-existing test; an incoherent claim "
338
+ f"for a test the change itself ships)"
339
+ ),
340
+ witnesses=False,
341
+ assert_level=False,
342
+ evidence=evidence,
343
+ )
344
+
345
+ # 3. UNSATISFIED — the change does not satisfy its own test (no green half).
346
+ if c is not RunOutcome.PASSED:
347
+ return TestWitnessVerdict(
348
+ verdict=TestWitness.UNSATISFIED,
349
+ reason=(
350
+ f"candidate {c} (baseline {b}) — the change does not satisfy "
351
+ f"its own test: the red half of red->green without the green; "
352
+ f"the fix claim itself is in doubt"
353
+ ),
354
+ witnesses=False,
355
+ assert_level=False,
356
+ evidence=evidence,
357
+ )
358
+
359
+ # 4. VACUOUS — green on both trees: the test witnesses nothing. The
360
+ # FrontierCode false-positive shape; the verdict this module exists for.
361
+ if b is RunOutcome.PASSED:
362
+ return TestWitnessVerdict(
363
+ verdict=TestWitness.VACUOUS,
364
+ reason=(
365
+ "PASSED on both trees — the test would have passed WITHOUT the "
366
+ "change; it witnesses nothing (the vacuous-test inflation a "
367
+ "'tests added' reviewer banks as assurance)"
368
+ ),
369
+ witnesses=False,
370
+ assert_level=False,
371
+ evidence=evidence,
372
+ )
373
+
374
+ # 5. DISCRIMINATES — red→green across the change. The only witness-minting
375
+ # verdict; `assert_level` flags the strong (ran-and-rejected) form.
376
+ assert_level = b is RunOutcome.FAILED
377
+ strength = (
378
+ "assert-level (the test RAN against the old behavior and rejected it)"
379
+ if assert_level
380
+ else "structural (baseline ERRORED — it provably cannot pass without "
381
+ "the change, but it never executed against the old behavior)"
382
+ )
383
+ return TestWitnessVerdict(
384
+ verdict=TestWitness.DISCRIMINATES,
385
+ reason=(
386
+ f"baseline {b}, candidate PASSED — red->green across the change: "
387
+ f"this test witnesses it; {strength}"
388
+ ),
389
+ witnesses=True,
390
+ assert_level=assert_level,
391
+ evidence=evidence,
392
+ )