substratum-cli 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.
- substratum_cli/README.md +51 -0
- substratum_cli/__init__.py +6 -0
- substratum_cli/__main__.py +7 -0
- substratum_cli/_vendor/__init__.py +0 -0
- substratum_cli/_vendor/_pro_test_derive.py +169 -0
- substratum_cli/account.py +62 -0
- substratum_cli/cli.py +185 -0
- substratum_cli/codes.py +114 -0
- substratum_cli/commands/__init__.py +0 -0
- substratum_cli/commands/apply.py +49 -0
- substratum_cli/commands/auth.py +37 -0
- substratum_cli/commands/commit.py +40 -0
- substratum_cli/commands/explain.py +63 -0
- substratum_cli/commands/failing.py +80 -0
- substratum_cli/commands/fix.py +48 -0
- substratum_cli/commands/history.py +15 -0
- substratum_cli/commands/init_cmd.py +155 -0
- substratum_cli/commands/replay.py +68 -0
- substratum_cli/commands/scaffold.py +71 -0
- substratum_cli/commands/show.py +18 -0
- substratum_cli/commands/undo.py +28 -0
- substratum_cli/commands/verify.py +193 -0
- substratum_cli/commands/write.py +42 -0
- substratum_cli/config.py +49 -0
- substratum_cli/diffs.py +106 -0
- substratum_cli/engine.py +154 -0
- substratum_cli/gitio.py +102 -0
- substratum_cli/langscan.py +110 -0
- substratum_cli/product_ops.py +639 -0
- substratum_cli/refusals.py +127 -0
- substratum_cli/remote.py +140 -0
- substratum_cli/render.py +332 -0
- substratum_cli/result.py +185 -0
- substratum_cli/run_store.py +162 -0
- substratum_cli/runid.py +69 -0
- substratum_cli/session.py +558 -0
- substratum_cli/style.py +128 -0
- substratum_cli-0.1.0.dist-info/METADATA +69 -0
- substratum_cli-0.1.0.dist-info/RECORD +42 -0
- substratum_cli-0.1.0.dist-info/WHEEL +5 -0
- substratum_cli-0.1.0.dist-info/entry_points.txt +2 -0
- substratum_cli-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""The closed-class refusal template registry. Clinical-instrument voice: terse, no first
|
|
2
|
+
person, no anthropomorphism. Every template produces all five mandatory elements including
|
|
3
|
+
at least one executable flip command -- enforced by test. `from_engine` is the single door
|
|
4
|
+
translating raw engine/gate codes into a CLI Refusal; unknown codes preserve the raw string
|
|
5
|
+
under GATE_PRECONDITION (refuse-on-unknown, never invent a template).
|
|
6
|
+
|
|
7
|
+
Two-owner flip discipline: YOUR-TESTS = the developer's actionable debt; SUBSTRATUM-COVERAGE
|
|
8
|
+
= our gap, never dressed as theirs.
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from typing import Optional
|
|
13
|
+
|
|
14
|
+
from . import codes
|
|
15
|
+
from .result import FlipCondition, NextCommand, Refusal
|
|
16
|
+
|
|
17
|
+
_PERMANENCE = "Retrying without changing inputs returns this exact refusal."
|
|
18
|
+
|
|
19
|
+
# owner per code (fixed): coverage/driver gaps are ours; test-shaped gaps are theirs.
|
|
20
|
+
_OWNER = {
|
|
21
|
+
codes.NO_GROUNDED_CANDIDATE: "SUBSTRATUM-COVERAGE",
|
|
22
|
+
codes.NO_DRIVER: "SUBSTRATUM-COVERAGE",
|
|
23
|
+
codes.AUTONOMY_LOCKED: "SUBSTRATUM-COVERAGE",
|
|
24
|
+
codes.GATE_PRECONDITION: "YOUR-TESTS",
|
|
25
|
+
codes.NO_WARRANT_TESTS: "YOUR-TESTS",
|
|
26
|
+
codes.HUNK_CONTEXT_STALE: "YOUR-TESTS",
|
|
27
|
+
codes.APPLY_STATE_DRIFT: "YOUR-TESTS",
|
|
28
|
+
codes.REPLAY_STATE_DRIFT: "YOUR-TESTS",
|
|
29
|
+
codes.UNDERDETERMINED_INTENT: "YOUR-TESTS",
|
|
30
|
+
codes.FLAKY_OR_VACUOUS_WARRANT: "YOUR-TESTS",
|
|
31
|
+
codes.FLAKY_TEST: "YOUR-TESTS",
|
|
32
|
+
codes.RUN_NOT_FOUND: "YOUR-TESTS",
|
|
33
|
+
codes.NO_ENGINE: "SUBSTRATUM-COVERAGE",
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _why(code: str, ctx: dict) -> str:
|
|
38
|
+
tgt = ctx.get("target", "the request")
|
|
39
|
+
return {
|
|
40
|
+
codes.NO_GROUNDED_CANDIDATE:
|
|
41
|
+
"no candidate in the current library grounds this request; coverage absent.",
|
|
42
|
+
codes.NO_DRIVER:
|
|
43
|
+
f"no gate runner or filetype driver applies to {tgt}.",
|
|
44
|
+
codes.AUTONOMY_LOCKED:
|
|
45
|
+
"this action requires an autonomy level above L1; it is locked in config.",
|
|
46
|
+
codes.GATE_PRECONDITION:
|
|
47
|
+
"the gate could not be executed cleanly (environment or harness).",
|
|
48
|
+
codes.NO_WARRANT_TESTS:
|
|
49
|
+
"verification requires at least one test to gate against; none supplied.",
|
|
50
|
+
codes.HUNK_CONTEXT_STALE:
|
|
51
|
+
"a diff hunk does not apply to the base; context has drifted.",
|
|
52
|
+
codes.APPLY_STATE_DRIFT:
|
|
53
|
+
"HEAD has moved since this run was produced; applying is unsafe.",
|
|
54
|
+
codes.REPLAY_STATE_DRIFT:
|
|
55
|
+
"repository state differs from the recorded run; replay cannot compare.",
|
|
56
|
+
codes.UNDERDETERMINED_INTENT:
|
|
57
|
+
"candidates were composed and every one failed the gate; the test does not "
|
|
58
|
+
"pin the intent uniquely.",
|
|
59
|
+
codes.FLAKY_OR_VACUOUS_WARRANT:
|
|
60
|
+
"the named tests pass at base in isolated replays; there is nothing to fix, "
|
|
61
|
+
"or the tests are nondeterministic.",
|
|
62
|
+
codes.FLAKY_TEST:
|
|
63
|
+
"the named test is on the quarantine list from the flake scan.",
|
|
64
|
+
codes.RUN_NOT_FOUND:
|
|
65
|
+
f"no stored run matches {tgt}.",
|
|
66
|
+
codes.NO_ENGINE:
|
|
67
|
+
"synthesis needs the engine, which is not available in this install; local commands "
|
|
68
|
+
"(summarize, verify, failing) still work offline.",
|
|
69
|
+
}.get(code, "refused.")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _flip_steps(code: str, ctx: dict) -> tuple[NextCommand, ...]:
|
|
73
|
+
tgt = ctx.get("target", "<target>")
|
|
74
|
+
if code in (codes.UNDERDETERMINED_INTENT, codes.NO_WARRANT_TESTS):
|
|
75
|
+
return (NextCommand("scaffold_test", tgt,
|
|
76
|
+
f"substratum scaffold-test {tgt}"),
|
|
77
|
+
NextCommand("rerun_fix", tgt, f"substratum fix {tgt}"))
|
|
78
|
+
if code == codes.NO_GROUNDED_CANDIDATE:
|
|
79
|
+
return (NextCommand("scaffold_test", tgt,
|
|
80
|
+
f"substratum scaffold-test {tgt} # pin the behavior; coverage grows with data"),)
|
|
81
|
+
if code == codes.NO_ENGINE:
|
|
82
|
+
return (NextCommand("login", tgt,
|
|
83
|
+
"substratum login # use the hosted engine (nothing but intent leaves your machine)"),
|
|
84
|
+
NextCommand("install_engine", tgt,
|
|
85
|
+
"pip install 'substratum-cli[engine]' # or run the engine locally"))
|
|
86
|
+
if code == codes.NO_DRIVER:
|
|
87
|
+
return (NextCommand("provide_test_command", tgt,
|
|
88
|
+
"substratum doctor # inspect detected harness / configure drivers"),)
|
|
89
|
+
if code in (codes.FLAKY_OR_VACUOUS_WARRANT, codes.FLAKY_TEST):
|
|
90
|
+
return (NextCommand("repro", tgt, f"substratum doctor # review quarantine for {tgt}"),)
|
|
91
|
+
if code in (codes.APPLY_STATE_DRIFT, codes.REPLAY_STATE_DRIFT):
|
|
92
|
+
return (NextCommand("show", tgt, f"substratum show {ctx.get('run_id','<run>')} # inspect the recorded state"),)
|
|
93
|
+
if code == codes.GATE_PRECONDITION:
|
|
94
|
+
return (NextCommand("repro", tgt, "substratum doctor # check harness, worktree, tmpdir"),)
|
|
95
|
+
if code == codes.AUTONOMY_LOCKED:
|
|
96
|
+
return (NextCommand("show", tgt, "substratum doctor # shows effective .substratum.toml"),)
|
|
97
|
+
if code == codes.HUNK_CONTEXT_STALE:
|
|
98
|
+
return (NextCommand("rerun_fix", tgt, "git rebase; then re-run verify"),)
|
|
99
|
+
return (NextCommand("show", tgt, f"substratum show {ctx.get('run_id','<run>')}"),)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def build(code: str, engine_code: str = "", ctx: Optional[dict] = None,
|
|
103
|
+
verified_before: tuple[str, ...] = ()) -> Refusal:
|
|
104
|
+
ctx = ctx or {}
|
|
105
|
+
owner = _OWNER.get(code, "SUBSTRATUM-COVERAGE")
|
|
106
|
+
return Refusal(
|
|
107
|
+
code=code, engine_code=engine_code or code, why=_why(code, ctx),
|
|
108
|
+
verified_before_refusing=verified_before,
|
|
109
|
+
flip=FlipCondition(owner=owner, steps=_flip_steps(code, ctx)),
|
|
110
|
+
permanence=_PERMANENCE,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def from_engine(engine_code: str, ctx: Optional[dict] = None,
|
|
115
|
+
verified_before: tuple[str, ...] = ()) -> Refusal:
|
|
116
|
+
"""The single mapping door: raw engine/gate code -> a complete Refusal."""
|
|
117
|
+
cli_code = codes.map_engine_code(engine_code)
|
|
118
|
+
return build(cli_code, engine_code=engine_code, ctx=ctx, verified_before=verified_before)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def all_codes_have_flips() -> bool:
|
|
122
|
+
"""The CI guard: every closed refusal code yields a complete refusal with an executable flip."""
|
|
123
|
+
for code in codes.REFUSAL_CODES:
|
|
124
|
+
r = build(code, ctx={"target": "x", "run_id": "sub_x"})
|
|
125
|
+
if not r.is_complete() or not any(s.command for s in r.flip.steps):
|
|
126
|
+
return False
|
|
127
|
+
return True
|
substratum_cli/remote.py
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""The remote transport: when logged in, the thin client sends fix/write/clarify to the hosted
|
|
2
|
+
engine (Fly) and renders the SAME SubstratumResult it would locally. The 6 GB library + the engine
|
|
3
|
+
never ship; only focal snippets (fix) or the intent+signature (write) transit, over TLS. Stdlib
|
|
4
|
+
`urllib` only -- the client stays dependency-free. Login is the GitHub device flow (no secret ships;
|
|
5
|
+
the OAuth client-id is fetched from our server so it can rotate)."""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import time
|
|
10
|
+
import urllib.error
|
|
11
|
+
import urllib.request
|
|
12
|
+
|
|
13
|
+
from . import account
|
|
14
|
+
|
|
15
|
+
_GH_DEVICE_CODE = "https://github.com/login/device/code"
|
|
16
|
+
_GH_ACCESS_TOKEN = "https://github.com/login/oauth/access_token"
|
|
17
|
+
_UA = "substratum-cli"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class RemoteError(Exception):
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def enabled() -> bool:
|
|
25
|
+
"""Remote mode is active iff the user is logged in (a token is present)."""
|
|
26
|
+
return account.is_logged_in()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _post_json(url: str, payload: dict, headers: dict | None = None, timeout: int = 30) -> dict:
|
|
30
|
+
data = json.dumps(payload).encode("utf-8")
|
|
31
|
+
req = urllib.request.Request(url, data=data, method="POST")
|
|
32
|
+
req.add_header("Content-Type", "application/json")
|
|
33
|
+
req.add_header("Accept", "application/json")
|
|
34
|
+
req.add_header("User-Agent", _UA)
|
|
35
|
+
for k, v in (headers or {}).items():
|
|
36
|
+
req.add_header(k, v)
|
|
37
|
+
try:
|
|
38
|
+
with urllib.request.urlopen(req, timeout=timeout) as r:
|
|
39
|
+
return json.loads(r.read().decode("utf-8") or "{}")
|
|
40
|
+
except urllib.error.HTTPError as e:
|
|
41
|
+
body = ""
|
|
42
|
+
try:
|
|
43
|
+
body = e.read().decode("utf-8")
|
|
44
|
+
except Exception:
|
|
45
|
+
pass
|
|
46
|
+
raise RemoteError(f"server returned {e.code}: {body[:200]}")
|
|
47
|
+
except urllib.error.URLError as e:
|
|
48
|
+
raise RemoteError(f"could not reach {url} ({e.reason})")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _get_json(url: str, headers: dict | None = None, timeout: int = 20) -> dict:
|
|
52
|
+
req = urllib.request.Request(url, method="GET")
|
|
53
|
+
req.add_header("Accept", "application/json")
|
|
54
|
+
req.add_header("User-Agent", _UA)
|
|
55
|
+
for k, v in (headers or {}).items():
|
|
56
|
+
req.add_header(k, v)
|
|
57
|
+
with urllib.request.urlopen(req, timeout=timeout) as r:
|
|
58
|
+
return json.loads(r.read().decode("utf-8") or "{}")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _auth_headers() -> dict:
|
|
62
|
+
return {"Authorization": f"Bearer {account.token()}"}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# ---- the engine calls (server returns the SAME SubstratumResult / candidate shapes) --------------
|
|
66
|
+
def server_info() -> dict:
|
|
67
|
+
"""GET /api/cli/version -> {engine_version, min_client, github_client_id}."""
|
|
68
|
+
return _get_json(account.server() + "/api/cli/version")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def write(intent: str, signature: str, into: str | None = None,
|
|
72
|
+
prefer_purpose: str | None = None) -> dict:
|
|
73
|
+
"""POST /api/cli/write -> a full SubstratumResult dict (server does synth + intrinsic verify)."""
|
|
74
|
+
return _post_json(account.server() + "/api/cli/write",
|
|
75
|
+
{"intent": intent, "signature": signature, "into": into,
|
|
76
|
+
"prefer_purpose": prefer_purpose}, _auth_headers())
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def clarify(intent: str, signature: str) -> dict:
|
|
80
|
+
"""POST /api/cli/clarify -> {options: [...]} (needs the library)."""
|
|
81
|
+
return _post_json(account.server() + "/api/cli/clarify",
|
|
82
|
+
{"intent": intent, "signature": signature}, _auth_headers())
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def candidate(test_ids: list, focal: dict) -> dict:
|
|
86
|
+
"""POST /api/cli/candidate -> {status, diff, provenance, refuse_reason}. The server SYNTHESIZES;
|
|
87
|
+
the client gates the returned diff locally, so the user's code + tests never leave the machine."""
|
|
88
|
+
return _post_json(account.server() + "/api/cli/candidate",
|
|
89
|
+
{"test_ids": list(test_ids), "focal": focal}, _auth_headers())
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# ---- GitHub device-flow login --------------------------------------------------------------------
|
|
93
|
+
def login() -> dict:
|
|
94
|
+
"""Run the GitHub device flow, exchange for a Substratum token, and persist it. Returns
|
|
95
|
+
{github_login}. Raises RemoteError with a clear message on any failure."""
|
|
96
|
+
info = server_info()
|
|
97
|
+
client_id = info.get("github_client_id")
|
|
98
|
+
if not client_id:
|
|
99
|
+
raise RemoteError("the server is not configured for login yet (no GitHub client id)")
|
|
100
|
+
dev = _post_json(_GH_DEVICE_CODE, {"client_id": client_id, "scope": "read:user"})
|
|
101
|
+
user_code = dev.get("user_code")
|
|
102
|
+
verify_uri = dev.get("verification_uri", "https://github.com/login/device")
|
|
103
|
+
device_code = dev.get("device_code")
|
|
104
|
+
interval = int(dev.get("interval", 5))
|
|
105
|
+
expires = int(dev.get("expires_in", 900))
|
|
106
|
+
if not (user_code and device_code):
|
|
107
|
+
raise RemoteError("GitHub did not return a device code")
|
|
108
|
+
|
|
109
|
+
print(f"\n Open {verify_uri} and enter code: {user_code}\n")
|
|
110
|
+
deadline = expires
|
|
111
|
+
waited = 0
|
|
112
|
+
gh_token = ""
|
|
113
|
+
while waited < deadline:
|
|
114
|
+
time.sleep(interval)
|
|
115
|
+
waited += interval
|
|
116
|
+
try:
|
|
117
|
+
r = _post_json(_GH_ACCESS_TOKEN, {
|
|
118
|
+
"client_id": client_id, "device_code": device_code,
|
|
119
|
+
"grant_type": "urn:ietf:params:oauth:grant-type:device_code"})
|
|
120
|
+
except RemoteError:
|
|
121
|
+
continue
|
|
122
|
+
if r.get("access_token"):
|
|
123
|
+
gh_token = r["access_token"]
|
|
124
|
+
break
|
|
125
|
+
err = r.get("error")
|
|
126
|
+
if err == "authorization_pending":
|
|
127
|
+
continue
|
|
128
|
+
if err == "slow_down":
|
|
129
|
+
interval += int(r.get("interval", 5))
|
|
130
|
+
continue
|
|
131
|
+
raise RemoteError(f"login failed: {err or 'unknown error'}")
|
|
132
|
+
if not gh_token:
|
|
133
|
+
raise RemoteError("login timed out; run `substratum login` again")
|
|
134
|
+
|
|
135
|
+
exch = _post_json(account.server() + "/api/cli/auth/exchange", {"github_token": gh_token})
|
|
136
|
+
tok = exch.get("token")
|
|
137
|
+
if not tok:
|
|
138
|
+
raise RemoteError("the server did not issue a token")
|
|
139
|
+
account.save(token=tok, github_login=exch.get("github_login", ""), server=account.server())
|
|
140
|
+
return {"github_login": exch.get("github_login", "")}
|
substratum_cli/render.py
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
"""Human rendering in the Sovereign Seal design (see style.py). Line 1 is always the exact
|
|
2
|
+
parseable grammar `<VERDICT> run=<id> [code=<CODE>]`; the body is a verdict-tinted rail closed
|
|
3
|
+
by a foot seal, uppercase channel labels down a grey gutter, gold for the next executable move.
|
|
4
|
+
Color law: green proven, cyan refused, gold next-action, grey chrome; red is tool-fault only.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
from . import codes
|
|
11
|
+
from . import style as S
|
|
12
|
+
from .result import SubstratumResult
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _diff_file(diff: str) -> str:
|
|
16
|
+
"""The file a diff touches (for the frame label). Prefers the +++ target, then the git header."""
|
|
17
|
+
import re
|
|
18
|
+
m = re.search(r"^\+\+\+ b/(.+)$", diff, re.M) or re.search(r"^diff --git a/\S+ b/(.+)$", diff, re.M)
|
|
19
|
+
return m.group(1).strip() if m else ""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def render_human(res: SubstratumResult, stream=None, color: str | None = None) -> str:
|
|
23
|
+
stream = stream or sys.stdout
|
|
24
|
+
c = S.use_color(stream, color)
|
|
25
|
+
p = S.Painter(c)
|
|
26
|
+
vcol = S.verdict_color(res.verdict)
|
|
27
|
+
rail = p(S.g("rail"), vcol)
|
|
28
|
+
W = S.width() # everything (wraps, truncation) derives from the pane width
|
|
29
|
+
out: list[str] = []
|
|
30
|
+
|
|
31
|
+
def body(label: str, text: str, lab_col: str = S.LABEL, txt_col: str = "") -> None:
|
|
32
|
+
if text == "": # a section header: no ljust padding, no trailing whitespace
|
|
33
|
+
out.append(f"{rail} {p(label, lab_col)}")
|
|
34
|
+
else:
|
|
35
|
+
out.append(f"{rail} {p(label.ljust(8), lab_col)} {p(text, txt_col) if txt_col else text}")
|
|
36
|
+
|
|
37
|
+
def bare() -> None:
|
|
38
|
+
out.append(rail)
|
|
39
|
+
|
|
40
|
+
# ---- line 1: the grammar, colored by verdict (bold verdict token) ----
|
|
41
|
+
head = res.summary
|
|
42
|
+
if c:
|
|
43
|
+
vb = (S.PROVEN_B if res.verdict in (codes.VERIFIED, codes.PASS)
|
|
44
|
+
else S.REFUSED_B if res.verdict in (codes.REFUSED, codes.UNVERIFIABLE)
|
|
45
|
+
else S.FAILC_B if res.verdict == codes.FAIL else vcol)
|
|
46
|
+
head = head.replace(res.verdict, p(res.verdict, vb), 1)
|
|
47
|
+
out.append(head)
|
|
48
|
+
|
|
49
|
+
# ---- streamed gate hero (proven runs) ----
|
|
50
|
+
if res.gate and res.gate.base_status:
|
|
51
|
+
arrow = p(S.g("arrow"), S.GOLD)
|
|
52
|
+
tests = ", ".join(res.gate.test_ids[:3]) or "warrant tests"
|
|
53
|
+
# color law: base FAIL is EXPECTED (chrome, never red); patched PASS is the proof (green)
|
|
54
|
+
base_c = p(res.gate.base_status, S.CHROME)
|
|
55
|
+
patched_c = p(res.gate.patched_status,
|
|
56
|
+
S.PROVEN if res.gate.patched_status == "PASS" else S.FAULT)
|
|
57
|
+
body("GATE", f"{tests}: base {base_c} {arrow} patched {patched_c}")
|
|
58
|
+
if res.gate.impacted:
|
|
59
|
+
im = res.gate.impacted
|
|
60
|
+
body("IMPACT", f"{im.get('tests_run','?')} tests {S.g('dot')} "
|
|
61
|
+
f"{im.get('regressions','?')} regressions")
|
|
62
|
+
|
|
63
|
+
# ---- verified fix / write: the change + provenance + next moves ----
|
|
64
|
+
if res.verdict in (codes.VERIFIED, codes.PASS) and res.diff and res.operation in ("fix", "write"):
|
|
65
|
+
bare()
|
|
66
|
+
fw = min(W, 82)
|
|
67
|
+
iw = fw - 4
|
|
68
|
+
diff_lines = []
|
|
69
|
+
for ln in res.diff.rstrip("\n").splitlines():
|
|
70
|
+
t = S.ellipsize(ln, iw) # keep long code lines inside the box (full diff in `show`)
|
|
71
|
+
if ln.startswith("+") and not ln.startswith("+++"):
|
|
72
|
+
diff_lines.append(p(t, S.PROVEN))
|
|
73
|
+
elif ln.startswith("-") and not ln.startswith("---"):
|
|
74
|
+
diff_lines.append(p(t, S.CHROME))
|
|
75
|
+
elif ln.startswith("@@"):
|
|
76
|
+
diff_lines.append(p(t, S.GOLD))
|
|
77
|
+
else:
|
|
78
|
+
diff_lines.append(p(t, S.CHROME))
|
|
79
|
+
out.extend(S.frame(diff_lines, label=_diff_file(res.diff) or "change", width=fw, color=c))
|
|
80
|
+
if res.provenance and res.provenance.entries:
|
|
81
|
+
bare()
|
|
82
|
+
body("SOURCE", p(" " + S.g("dot") + " ", S.CHROME).join(res.provenance.entries[:4]),
|
|
83
|
+
lab_col=S.GOLD)
|
|
84
|
+
if res.data and res.data.get("note"):
|
|
85
|
+
body("", p(res.data["note"], S.CHROME))
|
|
86
|
+
out.append(p(S.g("foot") + S.g("bar") * 2, vcol))
|
|
87
|
+
rid = res.run_id
|
|
88
|
+
body_g = lambda lab, cmd: out.append( # noqa: E731
|
|
89
|
+
f" {p(lab.ljust(6), S.LABEL)} {p(cmd, S.GOLD)}")
|
|
90
|
+
if res.operation in ("fix", "write"):
|
|
91
|
+
body_g("apply", f"substratum apply {rid}")
|
|
92
|
+
body_g("proof", f"substratum show {rid}")
|
|
93
|
+
body_g("replay", f"substratum replay {rid} {p(S.g('arrow'), S.GOLD)} byte-identical")
|
|
94
|
+
|
|
95
|
+
# ---- apply: the write moment -- show what landed + how to undo (never silent) ----
|
|
96
|
+
if res.operation == "apply" and res.verdict in (codes.VERIFIED, codes.PASS):
|
|
97
|
+
applied = list((res.data or {}).get("applied") or getattr(res, "files", ()) or ())
|
|
98
|
+
body("APPLIED", f"{len(applied)} file{'' if len(applied) == 1 else 's'} written",
|
|
99
|
+
lab_col=S.PROVEN)
|
|
100
|
+
for f in applied[:12]:
|
|
101
|
+
out.append(f"{rail} {p(S.g('ok'), S.PROVEN)} {f}")
|
|
102
|
+
out.append(p(S.g("foot") + S.g("bar") * 2, vcol))
|
|
103
|
+
if applied:
|
|
104
|
+
undo = "git checkout -- " + " ".join(applied[:3]) + (" …" if len(applied) > 3 else "")
|
|
105
|
+
out.append(f" {p('undo'.ljust(6), S.LABEL)} {p(undo, S.GOLD)}")
|
|
106
|
+
|
|
107
|
+
# ---- per-hunk table (verify mixed diff) ----
|
|
108
|
+
if res.hunks:
|
|
109
|
+
bare()
|
|
110
|
+
for h in res.hunks:
|
|
111
|
+
hv = p(h.verdict, S.verdict_color(h.verdict))
|
|
112
|
+
body("HUNK", f"{h.path} {S.g('dot')} {hv}{(' ' + S.g('dot') + ' ' + h.code) if h.code else ''}")
|
|
113
|
+
out.append(p(S.g("foot") + S.g("bar") * 2, vcol))
|
|
114
|
+
|
|
115
|
+
# ---- FAIL: the gate ran and the change did NOT pass -- show the captured failure + the move ----
|
|
116
|
+
if res.verdict == codes.FAIL and not res.hunks:
|
|
117
|
+
exc = ""
|
|
118
|
+
if res.gate:
|
|
119
|
+
exc = getattr(res.gate, "patched_excerpt", "") or getattr(res.gate, "base_excerpt", "")
|
|
120
|
+
if exc:
|
|
121
|
+
bare()
|
|
122
|
+
body("OUTPUT", "from the gate run", lab_col=S.FAILC)
|
|
123
|
+
for ln in exc.rstrip("\n").splitlines()[-8:]:
|
|
124
|
+
out.append(f"{rail} {p(ln, S.CHROME)}")
|
|
125
|
+
out.append(p(S.g("foot") + S.g("bar") * 2, vcol))
|
|
126
|
+
out.append(f" {p('note'.ljust(6), S.LABEL)} "
|
|
127
|
+
f"{p('the change does not satisfy the warrant; revise and re-verify', S.CHROME)}")
|
|
128
|
+
|
|
129
|
+
# ---- init: the Coverage Contract ----
|
|
130
|
+
if res.operation == "init" and res.data and "coverage_contract" in res.data:
|
|
131
|
+
cc = res.data["coverage_contract"]
|
|
132
|
+
body("HARNESS", cc.get("harness") or "none detected")
|
|
133
|
+
body("GATED", "", lab_col=S.PROVEN)
|
|
134
|
+
for r in cc.get("gated", [])[:16]:
|
|
135
|
+
lang = f" [{r['lang']}]" if r.get("lang") else ""
|
|
136
|
+
out.append(f"{rail} {r['ext']}{lang}: {r['files']} files {S.g('dot')} {r['lines']} lines")
|
|
137
|
+
if cc.get("no_driver"):
|
|
138
|
+
body("REFUSES", "by name", lab_col=S.REFUSED)
|
|
139
|
+
for r in cc["no_driver"][:12]:
|
|
140
|
+
out.append(f"{rail} {p(r['ext'] + ': ' + r['refusal'], S.REFUSED)}")
|
|
141
|
+
if res.data.get("air_gap"):
|
|
142
|
+
body("AIR-GAP", f"{res.data['air_gap']['sockets_opened']} sockets opened "
|
|
143
|
+
f"({res.data['air_gap']['attestation']})")
|
|
144
|
+
if res.data.get("flake"):
|
|
145
|
+
body("FLAKE", f"suite stable across {res.data['flake']['rounds']} rounds: "
|
|
146
|
+
f"{res.data['flake']['stable']}")
|
|
147
|
+
out.append(p(S.g("foot") + S.g("bar") * 2, vcol))
|
|
148
|
+
|
|
149
|
+
if res.operation == "doctor" and res.data and "checks" in res.data:
|
|
150
|
+
for k, v in res.data["checks"].items():
|
|
151
|
+
label = k.replace("_", " ").upper() # full label, not mid-word truncated
|
|
152
|
+
if isinstance(v, dict):
|
|
153
|
+
val = " " + S.g("dot") + " ".join(f"{kk} {vv}" for kk, vv in v.items())
|
|
154
|
+
else:
|
|
155
|
+
val = str(v)
|
|
156
|
+
ok = any(t in val.lower() for t in ("ok", "yes", "warm", "found",
|
|
157
|
+
"0 socket", "verified", "pass", "clean"))
|
|
158
|
+
body(label, p(val, S.PROVEN if ok else S.CHROME))
|
|
159
|
+
out.append(p(S.g("foot") + S.g("bar") * 2, vcol))
|
|
160
|
+
|
|
161
|
+
if res.operation == "replay" and res.data:
|
|
162
|
+
bi = res.data.get("byte_identical"); sha = res.data.get("sha", "")
|
|
163
|
+
body("REPLAY", p(f"byte-identical: {'yes' if bi else 'no'}"
|
|
164
|
+
+ (f" (sha {sha[:12]})" if sha else ""), S.PROVEN if bi else S.FAULT))
|
|
165
|
+
out.append(p(S.g("foot") + S.g("bar") * 2, vcol))
|
|
166
|
+
|
|
167
|
+
if res.operation == "explain" and res.data and "manifest" in res.data:
|
|
168
|
+
m = res.data["manifest"]
|
|
169
|
+
body("GRAIN", m.get("grain", ""))
|
|
170
|
+
for e in m.get("provenance", [])[:8]:
|
|
171
|
+
out.append(f"{rail} {p(e, S.GOLD)}")
|
|
172
|
+
out.append(p(S.g("foot") + S.g("bar") * 2, vcol))
|
|
173
|
+
|
|
174
|
+
if res.operation == "attest" and res.data:
|
|
175
|
+
body("BUNDLE", res.data.get("bundle", ""))
|
|
176
|
+
body("EGRESS", res.data.get("manifest", {}).get("egress", ""))
|
|
177
|
+
out.append(p(S.g("foot") + S.g("bar") * 2, vcol))
|
|
178
|
+
|
|
179
|
+
# ---- failing: the failing tests discovered by running the repo's own suite ----
|
|
180
|
+
if res.operation == "failing" and res.data:
|
|
181
|
+
f = res.data.get("failing") or []
|
|
182
|
+
if f:
|
|
183
|
+
body("FAILING", f"{len(f)} failing test{'' if len(f) == 1 else 's'}", lab_col=S.FAILC)
|
|
184
|
+
bare()
|
|
185
|
+
for nid in f[:20]:
|
|
186
|
+
out.append(f"{rail} {p(S.g('wall'), S.FAILC)} {S.ellipsize(nid, W - 6)}")
|
|
187
|
+
if len(f) > 20:
|
|
188
|
+
out.append(f"{rail} {p(f'… and {len(f) - 20} more', S.CHROME)}")
|
|
189
|
+
out.append(p(S.g("foot") + S.g("bar") * 2, vcol))
|
|
190
|
+
out.append(f" {p('fix'.ljust(6), S.LABEL)} {p('substratum fix ' + f[0], S.GOLD)}")
|
|
191
|
+
elif res.data.get("all_pass"):
|
|
192
|
+
body("TESTS", "all tests pass — nothing to fix", lab_col=S.PROVEN)
|
|
193
|
+
out.append(p(S.g("foot") + S.g("bar") * 2, vcol))
|
|
194
|
+
else:
|
|
195
|
+
body("TESTS", "the suite did not run cleanly", lab_col=S.CHROME)
|
|
196
|
+
for ln in (res.data.get("excerpt") or "").splitlines()[-6:]:
|
|
197
|
+
out.append(f"{rail} {p(S.ellipsize(ln, W - 4), S.CHROME)}")
|
|
198
|
+
out.append(p(S.g("foot") + S.g("bar") * 2, vcol))
|
|
199
|
+
|
|
200
|
+
# ---- runs / log: the proof store made visible (history + conversion ledger) ----
|
|
201
|
+
if res.operation == "runs" and res.data:
|
|
202
|
+
led = res.data.get("ledger") or {}
|
|
203
|
+
refn, conv = led.get("refusals", 0), led.get("converted", 0)
|
|
204
|
+
if refn:
|
|
205
|
+
body("LEDGER", f"{conv} of {refn} refusals later converted to VERIFIED", lab_col=S.GOLD)
|
|
206
|
+
runs = res.data.get("runs") or []
|
|
207
|
+
if not runs:
|
|
208
|
+
body("RUNS", "no runs yet — try fix or verify")
|
|
209
|
+
else:
|
|
210
|
+
bare()
|
|
211
|
+
for rn in runs:
|
|
212
|
+
vc = S.verdict_color(rn.get("verdict", ""))
|
|
213
|
+
vv = p(rn.get("verdict", "").ljust(8), vc)
|
|
214
|
+
target = S.ellipsize(rn.get("target", ""), max(10, W - 34))
|
|
215
|
+
tail = f" {p(target, S.CHROME)}" if target else ""
|
|
216
|
+
out.append(f"{rail} {vv} {p(rn.get('run_id', ''), S.LABEL)} "
|
|
217
|
+
f"{rn.get('operation', '').ljust(6)}{tail}")
|
|
218
|
+
out.append(p(S.g("foot") + S.g("bar") * 2, vcol))
|
|
219
|
+
|
|
220
|
+
# ---- undo: the inverse of apply (files restored) ----
|
|
221
|
+
if res.operation == "undo" and res.verdict in (codes.VERIFIED, codes.PASS):
|
|
222
|
+
rev = list((res.data or {}).get("reverted") or [])
|
|
223
|
+
body("REVERTED", f"{len(rev)} file{'' if len(rev) == 1 else 's'} restored", lab_col=S.PROVEN)
|
|
224
|
+
for f in rev[:12]:
|
|
225
|
+
out.append(f"{rail} {p(S.g('ok'), S.PROVEN)} {f}")
|
|
226
|
+
out.append(p(S.g("foot") + S.g("bar") * 2, vcol))
|
|
227
|
+
|
|
228
|
+
# ---- commit: the proof-carrying commit ----
|
|
229
|
+
if res.operation == "commit" and res.verdict in (codes.VERIFIED, codes.PASS):
|
|
230
|
+
d = res.data or {}
|
|
231
|
+
nf = len(d.get("files", []))
|
|
232
|
+
body("COMMITTED", f"{d.get('sha', '')} {S.g('dot')} {nf} file{'' if nf == 1 else 's'}",
|
|
233
|
+
lab_col=S.PROVEN)
|
|
234
|
+
if d.get("subject"):
|
|
235
|
+
body("SUBJECT", d["subject"])
|
|
236
|
+
out.append(p(S.g("foot") + S.g("bar") * 2, vcol))
|
|
237
|
+
|
|
238
|
+
if res.operation == "scaffold_test" and res.data:
|
|
239
|
+
if res.diff:
|
|
240
|
+
bare()
|
|
241
|
+
for ln in res.diff.rstrip("\n").splitlines():
|
|
242
|
+
out.append(f"{rail} {p(ln, S.CHROME)}")
|
|
243
|
+
out.append(p(S.g("foot") + S.g("bar") * 2, vcol))
|
|
244
|
+
out.append(f" {p('next'.ljust(6), S.LABEL)} {p(res.data.get('next', ''), S.GOLD)}")
|
|
245
|
+
|
|
246
|
+
# ---- summarize: the codebase in plain English, across every serviced language ----
|
|
247
|
+
if res.operation == "summarize" and res.data and "summary" in res.data:
|
|
248
|
+
sm = res.data["summary"]
|
|
249
|
+
about = sm.get("about")
|
|
250
|
+
if about and (about.get("name") or about.get("tagline") or about.get("blurb")):
|
|
251
|
+
import textwrap as _tw
|
|
252
|
+
fw = min(W, 74)
|
|
253
|
+
inner = []
|
|
254
|
+
if about.get("name"):
|
|
255
|
+
inner.append(p(about["name"], S.PROVEN_B))
|
|
256
|
+
for field in ("tagline", "blurb"):
|
|
257
|
+
if about.get(field):
|
|
258
|
+
for w in _tw.wrap(about[field], fw - 4)[:4]:
|
|
259
|
+
inner.append(w)
|
|
260
|
+
srcs = about.get("sources") or []
|
|
261
|
+
if srcs:
|
|
262
|
+
inner.append(p("grounded in " + (" " + S.g("dot") + " ").join(srcs), S.CHROME))
|
|
263
|
+
out.extend(S.frame(inner, label="ABOUT", width=fw, color=c)) # a light 'card' for the synopsis
|
|
264
|
+
bare()
|
|
265
|
+
langs = sm.get("languages") or {}
|
|
266
|
+
if sm.get("total_code_files") and langs:
|
|
267
|
+
brk = " ".join(f"{n} {k}" for k, n in sorted(langs.items(), key=lambda x: -x[1]))
|
|
268
|
+
plus = " +" if sm.get("truncated_files") else ""
|
|
269
|
+
body("CODEBASE", f"{sm['total_code_files']}{plus} files {S.g('dot')} {brk}")
|
|
270
|
+
parts = [f"{sm['functions']} functions"]
|
|
271
|
+
if sm.get("methods"):
|
|
272
|
+
parts.append(f"{sm['methods']} methods")
|
|
273
|
+
if sm.get("types"):
|
|
274
|
+
parts.append(f"{sm['types']} types")
|
|
275
|
+
body("SCOPE", (" " + S.g("dot") + " ").join(parts))
|
|
276
|
+
if sm.get("types_described"):
|
|
277
|
+
bare()
|
|
278
|
+
body("TYPES", "", lab_col=S.GOLD)
|
|
279
|
+
for d in sm["types_described"]:
|
|
280
|
+
eng = d.get("english") or d.get("symbol")
|
|
281
|
+
tag = f" [{d['lang']}]" if d.get("lang") else ""
|
|
282
|
+
meta = S.ellipsize(S.g('dot') + ' ' + d['symbol'] + ' ' + d['file'],
|
|
283
|
+
max(14, W - 5 - len(eng) - len(tag)))
|
|
284
|
+
out.append(f"{rail} {p(eng, '')}{p(tag, S.CHROME)} {p(meta, S.LABEL)}")
|
|
285
|
+
if sm.get("described"):
|
|
286
|
+
bare()
|
|
287
|
+
shown, trunc = sm.get("functions_shown", len(sm["described"])), sm.get("functions_truncated", 0)
|
|
288
|
+
lab = "FUNCTIONS" if not trunc else f"FUNCTIONS (top {shown} of {sm['functions']})"
|
|
289
|
+
body(lab, "", lab_col=S.GOLD)
|
|
290
|
+
for d in sm["described"]:
|
|
291
|
+
eng = d.get("english") or d.get("symbol")
|
|
292
|
+
tag = f" [{d['lang']}]" if d.get("lang") else ""
|
|
293
|
+
meta = S.ellipsize(S.g('dot') + ' ' + d['symbol'] + ' ' + d['file'],
|
|
294
|
+
max(14, W - 5 - len(eng) - len(tag)))
|
|
295
|
+
out.append(f"{rail} {p(eng, '')}{p(tag, S.CHROME)} {p(meta, S.LABEL)}")
|
|
296
|
+
out.append(p(S.g("foot") + S.g("bar") * 2, vcol))
|
|
297
|
+
return "\n".join(out) + "\n"
|
|
298
|
+
|
|
299
|
+
# ---- the clarification ASK (ambiguous bare intent, plain-English options) ----
|
|
300
|
+
if res.data and res.data.get("clarify"):
|
|
301
|
+
cl = res.data["clarify"]
|
|
302
|
+
body("ASK", cl.get("question", "Which did you mean?"), lab_col=S.GOLD)
|
|
303
|
+
bare()
|
|
304
|
+
fw = min(W, 74)
|
|
305
|
+
inner = [p(str(i), S.GOLD) + " " + S.ellipsize(o.get("text", ""), fw - 8)
|
|
306
|
+
for i, o in enumerate(cl.get("options", [])[:6], 1)]
|
|
307
|
+
out.extend(S.frame(inner, label="options", width=fw, color=c))
|
|
308
|
+
intq, sigq = cl.get("intent", ""), cl.get("sig", "")
|
|
309
|
+
out.append(f" {p('pick'.ljust(6), S.LABEL)} "
|
|
310
|
+
f"{p(f'in a session, type the number', S.GOLD)}")
|
|
311
|
+
out.append(f" {p('or'.ljust(6), S.LABEL)} "
|
|
312
|
+
f"{p(f'write \"{intq}\" --sig \"{sigq}\" --mean \"<option>\"', S.GOLD)}")
|
|
313
|
+
return "\n".join(out) + "\n"
|
|
314
|
+
|
|
315
|
+
# ---- the Diligence Report (refusal) ----
|
|
316
|
+
if res.verdict in (codes.REFUSED, codes.UNVERIFIABLE) and res.refusal:
|
|
317
|
+
r = res.refusal
|
|
318
|
+
if r.verified_before_refusing:
|
|
319
|
+
body("CHECKED", "diligence performed before refusing")
|
|
320
|
+
for line in r.verified_before_refusing:
|
|
321
|
+
out.append(f"{rail} {p(S.g('ok'), S.PROVEN)} {line}")
|
|
322
|
+
bare()
|
|
323
|
+
body("WHY", r.why, txt_col="")
|
|
324
|
+
bare()
|
|
325
|
+
body("FLIP", f"owner: {p(r.flip.owner, S.GOLD if r.flip.owner == 'YOUR-TESTS' else S.REFUSED)}")
|
|
326
|
+
for i, step in enumerate(r.flip.steps, 1):
|
|
327
|
+
out.append(f"{rail} {p(str(i), S.GOLD)} {p(step.command, S.GOLD)}")
|
|
328
|
+
bare()
|
|
329
|
+
body("STATE", p(r.permanence, S.CHROME))
|
|
330
|
+
out.append(p(S.g("foot") + S.g("bar") * 2, vcol))
|
|
331
|
+
|
|
332
|
+
return "\n".join(out) + "\n"
|