agent-desktop/scripts/check_rust_comments.py
Lahfir 3f322728b4
feat!: implement Playwright-grade foundation contract
Settle the Playwright-grade reliability contract in agent-desktop-core
before the Windows/Linux adapters are built, so they inherit it instead
of redesigning it. Every command now observes, waits, verifies, and
reports honestly instead of firing blindly.

Highlights: capability-supertrait split of PlatformAdapter with
not_supported() defaults; canonical role/state vocabulary with live
`is --property visible`; display enumeration (`list-displays`) and honest
`--screen` with scale factor; truthful Automation permission; `native_id`
identity spine; window-id-first resolution; serializable `LocatorQuery`
with live `find`; default-on auto-wait before every ref action; three-way
`hit_test` occlusion gate; `scroll_into_view` in core; core accessible-name
precedence; typed `ActionStep` delivery tier; `ProcessState` and
`APP_UNRESPONSIVE`; `LaunchOptions`; baseline-diff desktop signals
(`wait --event`); typed clipboard (`Text`/`Image`/`FileUrls`); mouse
modifier chords and `mouse-wheel`. Hardened through a 35-reviewer pass with
independent validation and a green live e2e gate (109/0), plus a
head-vs-main performance comparison harness.

BREAKING CHANGE: default-on auto-wait changes the timing of every
previously-untouched ref-action call (bounded 5000 ms default; `--timeout-ms 0`
restores single-shot). `ENVELOPE_VERSION` is now `2.1` (adds the
`APP_UNRESPONSIVE` code and process state in error details). FFI ABI major
is `3` (append-only struct evolution; `wait --event` is intentionally not
exposed over FFI). The legacy string clipboard API is removed in favor of
typed content. `key-down`/`key-up` fail closed until daemon-owned held input
exists. `close-app` verifies termination and the osascript fallback path is
removed. `--text` matching is subtree containment: `find --text X --first`
returns the outermost matching container.
2026-07-20 00:21:38 -07:00

139 lines
4.3 KiB
Python

#!/usr/bin/env python3
import sys
from pathlib import Path
def raw_string_end(source, start):
prefix_length = 2 if source.startswith("br", start) else 1
if source[start : start + prefix_length] not in ("r", "br"):
return None
if start > 0 and (source[start - 1].isalnum() or source[start - 1] == "_"):
return None
cursor = start + prefix_length
hashes = 0
while cursor < len(source) and source[cursor] == "#":
hashes += 1
cursor += 1
if cursor >= len(source) or source[cursor] != '"':
return None
marker = '"' + "#" * hashes
end = source.find(marker, cursor + 1)
return len(source) if end < 0 else end + len(marker)
def quoted_string_end(source, start):
prefix_length = 2 if source.startswith('b"', start) else 1
if source[start : start + prefix_length] not in ('"', 'b"'):
return None
if prefix_length == 2 and start > 0 and (
source[start - 1].isalnum() or source[start - 1] == "_"
):
return None
cursor = start + prefix_length
escaped = False
while cursor < len(source):
character = source[cursor]
if escaped:
escaped = False
elif character == "\\":
escaped = True
elif character == '"':
return cursor + 1
cursor += 1
return len(source)
def block_comment_end(source, start):
cursor = start + 2
depth = 1
while cursor < len(source) and depth:
if source.startswith("/*", cursor):
depth += 1
cursor += 2
elif source.startswith("*/", cursor):
depth -= 1
cursor += 2
else:
cursor += 1
return cursor
def forbidden_comments(source):
findings = []
cursor = 0
line = 1
code_on_line = False
while cursor < len(source):
raw_end = raw_string_end(source, cursor)
if raw_end is not None:
segment = source[cursor:raw_end]
line += segment.count("\n")
if "\n" in segment:
code_on_line = bool(segment.rsplit("\n", 1)[-1].strip())
else:
code_on_line = True
cursor = raw_end
continue
string_end = quoted_string_end(source, cursor)
if string_end is not None:
segment = source[cursor:string_end]
line += segment.count("\n")
if "\n" in segment:
code_on_line = bool(segment.rsplit("\n", 1)[-1].strip())
else:
code_on_line = True
cursor = string_end
continue
if source.startswith("//", cursor):
is_doc = source.startswith("///", cursor) or source.startswith("//!", cursor)
if not is_doc:
kind = "end-of-line comment" if code_on_line else "non-doc line comment"
findings.append((line, kind))
end = source.find("\n", cursor + 2)
if end < 0:
break
cursor = end
continue
if source.startswith("/*", cursor):
is_doc = source.startswith("/**", cursor) or source.startswith("/*!", cursor)
if not is_doc:
findings.append((line, "block comment"))
end = block_comment_end(source, cursor)
line += source[cursor:end].count("\n")
code_on_line = False if "\n" in source[cursor:end] else code_on_line
cursor = end
continue
character = source[cursor]
if character == "\n":
line += 1
code_on_line = False
elif not character.isspace():
code_on_line = True
cursor += 1
return findings
def check_path(path):
if not path.is_file():
return []
source = path.read_text(encoding="utf-8")
if "@generated" in "\n".join(source.splitlines()[:5]):
return []
return forbidden_comments(source)
def main():
failed = False
for raw_path in sys.stdin.buffer.read().split(b"\0"):
if not raw_path:
continue
path = Path(raw_path.decode())
for line, kind in check_path(path):
print(f"{path}:{line}: {kind}s are forbidden", file=sys.stderr)
failed = True
raise SystemExit(1 if failed else 0)
if __name__ == "__main__":
main()