From d49804bf84ca641591b6af6a45df0f87bb1ddfaa Mon Sep 17 00:00:00 2001 From: alexeybe1kin <210597588+alexeybe1kin@users.noreply.github.com> Date: Tue, 3 Nov 2026 13:45:18 +0200 Subject: [PATCH 1/4] fix: Line count undercounts files without trailing newline A 601-line file whose last line lacks a newline is counted as 600 and passes the LIMIT=600 check; likewise allowlisted files appear one line shorter than their true size, so a file that grew to ceiling+1 with no trailing newline is reported as at-ceiling and passes. Found by the GLM relay (systemgate__scripts__check_file_sizes.py_p0__bugs__0), confirmed by GLM votes (round 2). Regression test to add: Create a 601-line tracked source file without a trailing newline and verify the check reports it as over the LIMIT=600 limit. Co-Authored-By: Claude Opus 5.5 --- scripts/check_file_sizes.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/check_file_sizes.py b/scripts/check_file_sizes.py index 7c4d10d..543fb9c 100644 --- a/scripts/check_file_sizes.py +++ b/scripts/check_file_sizes.py @@ -39,7 +39,8 @@ def main() -> int: allowed = allowlist() problems = [] for path in tracked_sources(): - lines = Path(path).read_text(encoding="utf-8", errors="replace").count("\n") + text = Path(path).read_text(encoding="utf-8", errors="replace") + lines = len(text.splitlines()) ceiling = allowed.pop(path, None) if ceiling is None and lines > LIMIT: problems.append(f"{path}: {lines} lines, limit {LIMIT}. Split it by concern.") From e807c0917a9ced3989e6d7257ab0ec17279d68e1 Mon Sep 17 00:00:00 2001 From: alexeybe1kin <210597588+alexeybe1kin@users.noreply.github.com> Date: Tue, 3 Nov 2026 13:45:18 +0200 Subject: [PATCH 2/4] fix: Non-numeric SYSTEMGATE_PORT crashes at startup with unhandled ValueError SYSTEMGATE_PORT='80 40' or 'http' (e.g. a trailing space beyond strip, or a typo like '804O') -> int() raises ValueError inside get_settings(), crashing every caller with an unrelated traceback instead of a clear config error. Found by the GLM relay (systemgate__systemgate__config.py_p0__bugs__0), confirmed by GLM votes (round 2). Regression test to add: Set SYSTEMGATE_PORT to '804O' and '70000' and assert get_settings() raises a ConfigError with a message naming SYSTEMGATE_PORT, while '8040' still parses to 8040. Co-Authored-By: Claude Opus 5.5 --- systemgate/config.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/systemgate/config.py b/systemgate/config.py index 6977142..3936f16 100644 --- a/systemgate/config.py +++ b/systemgate/config.py @@ -22,13 +22,28 @@ def _value(name: str, default: str = "") -> str: return os.environ.get(name, default).strip() +class ConfigError(ValueError): + pass + + +def _port(name: str, default: str) -> int: + raw = _value(name, default) + try: + port = int(raw) + except ValueError: + raise ConfigError(f"invalid {name}: {raw!r} (expected integer 1-65535)") from None + if not 1 <= port <= 65535: + raise ConfigError(f"{name} out of range: {port} (expected 1-65535)") + return port + + def get_settings() -> Settings: root = Path(__file__).resolve().parents[1] data_dir = Path(_value("SYSTEMGATE_DATA_DIR", str(root / "data"))).expanduser().resolve() backup_root = Path(_value("SYSTEMGATE_BACKUP_ROOT", "/backups")).expanduser().resolve() return Settings( host=_value("SYSTEMGATE_HOST", "127.0.0.1"), - port=int(_value("SYSTEMGATE_PORT", "8040")), + port=_port("SYSTEMGATE_PORT", "8040"), data_dir=data_dir, admin_key=_value("SYSTEMGATE_ADMIN_KEY"), backup_root=backup_root, From bcdc4113367383125811b0851a48972e6d4d485f Mon Sep 17 00:00:00 2001 From: alexeybe1kin <210597588+alexeybe1kin@users.noreply.github.com> Date: Tue, 3 Nov 2026 13:45:18 +0200 Subject: [PATCH 3/4] fix: sparse=True does not avoid per-container Docker inspect calls docker-py's containers.list(sparse=True) returns containers whose attrs are not prefetched; accessing container.attrs triggers a reload/inspect per container (and in some versions yields an empty/partial dict lacking Names/Ports/State), so rows lose name/image/ports or N extra API round-trips occur, contradicting the comment at line 169 and possibly emitting 'container_identity_unavailable' for every container. Found by the GLM relay (systemgate__systemgate__runtime.py_p0__bugs__0), confirmed by GLM votes (round 2). Regression test to add: Assert that with a mocked docker client, containers.list is called without sparse=True and that a single container whose list-time attrs include Names/Image/State/Ports yields a row with name, image, status, and its port bindings without any per-container reload/inspect calls. Co-Authored-By: Claude Opus 5.5 --- systemgate/runtime.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/systemgate/runtime.py b/systemgate/runtime.py index bff848f..be5b763 100644 --- a/systemgate/runtime.py +++ b/systemgate/runtime.py @@ -155,7 +155,7 @@ def add(section, row): try: client = docker_factory() for index, container in enumerate( - client.containers.list(all=True, limit=limit + 1, sparse=True) + client.containers.list(all=True, limit=limit + 1) ): if index >= limit: sections["containers"]["truncated"] = True @@ -166,7 +166,6 @@ def add(section, row): if not isinstance(identity, str) or not identity or len(identity) > 128: error("containers", "container_identity_unavailable") continue - # Sparse Docker list metadata avoids per-container inspect/stats calls. names = attrs.get("Names") or [] add( "containers", From 6dbf9f667eaeeb0a10cf2bb7e23112dc2c24adc6 Mon Sep 17 00:00:00 2001 From: alexeybe1kin <210597588+alexeybe1kin@users.noreply.github.com> Date: Tue, 3 Nov 2026 13:45:45 +0200 Subject: [PATCH 4/4] review: keep sparse=True on the container list GLM's bcdc411 dropped sparse=True claiming it does not avoid inspect calls. It does: docker-py's non-sparse list calls containers.get (an inspect) per container, and returns the inspect shape, where Names, State and Ports do not exist as the code reads them. The test pins sparse=True. Co-Authored-By: Claude Opus 5.5 --- systemgate/runtime.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/systemgate/runtime.py b/systemgate/runtime.py index be5b763..bff848f 100644 --- a/systemgate/runtime.py +++ b/systemgate/runtime.py @@ -155,7 +155,7 @@ def add(section, row): try: client = docker_factory() for index, container in enumerate( - client.containers.list(all=True, limit=limit + 1) + client.containers.list(all=True, limit=limit + 1, sparse=True) ): if index >= limit: sections["containers"]["truncated"] = True @@ -166,6 +166,7 @@ def add(section, row): if not isinstance(identity, str) or not identity or len(identity) > 128: error("containers", "container_identity_unavailable") continue + # Sparse Docker list metadata avoids per-container inspect/stats calls. names = attrs.get("Names") or [] add( "containers",