From f18ad7808f5ae616cbef6075708828a682fedbc7 Mon Sep 17 00:00:00 2001 From: "Molecule AI Dev Engineer B (MiniMax)" Date: Sun, 24 May 2026 03:48:53 +0000 Subject: [PATCH 1/3] fix(ci): justify suppression comments in CI/operator scripts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RCA #1769 Finding 1: add local invariant rationale to lint/type suppressions that lack a local explanation. - sop-checklist.py:640: import yaml — type: ignore[import-not-found] justified: yaml is optional dep; fallback _load_config_minimal covers the same shape, so the ignore is safe when dep absent. - sop-checklist.py:660: _parse_minimal_yaml — noqa: C901 replaced with docstring note: function is necessarily long (finite- state YAML subset parser); no utility refactor meaningfully reduces length; all branches tested in test_parse_minimal_yaml.py. - sop-checklist.py:1030,1037: client._req / _team_id_cache — noqa: SLF001 justified inline: _req is an internal helper called from loop context in the caller; _team_id_cache is a write-through cache. - check_migration_collisions.py:94: urlopen — noqa: S310 justified inline: this function IS the outbound HTTP client for Gitea API calls; the call is intentional and controlled; timeout=20s prevents indefinite hangs. wheel_smoke.py F401 suppressions are intentionally excluded: the module docstring documents the regression class (0.1.16 main_sync incident) and each `# noqa: F401` is paired with an `assert callable()` that validates the name is present at runtime. Co-Authored-By: Claude Opus 4.7 --- .gitea/scripts/sop-checklist.py | 19 +++++++++++++++---- scripts/ops/check_migration_collisions.py | 4 ++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/.gitea/scripts/sop-checklist.py b/.gitea/scripts/sop-checklist.py index eaa05e4ad..40e3b81f6 100644 --- a/.gitea/scripts/sop-checklist.py +++ b/.gitea/scripts/sop-checklist.py @@ -636,6 +636,11 @@ def load_config(path: str) -> dict[str, Any]: dep by keeping the config shape constrained. """ try: + # yaml is an optional dep; the canonical loader is used when available, + # but the SOP runs on runners that may not have PyYAML installed. The + # fallback _load_config_minimal covers the same config shape without + # requiring the dep, so the ignore is safe: if yaml loads, we use it; + # otherwise we fall back silently. import yaml # type: ignore[import-not-found] with open(path) as f: return yaml.safe_load(f) @@ -656,8 +661,14 @@ def _load_config_minimal(path: str) -> dict[str, Any]: return _parse_minimal_yaml(lines) -def _parse_minimal_yaml(lines: list[str]) -> dict[str, Any]: # noqa: C901 - """Hand-rolled subset parser. See _load_config_minimal docstring.""" +def _parse_minimal_yaml(lines: list[str]) -> dict[str, Any]: + """Hand-rolled subset parser. See _load_config_minimal docstring. + + C901: function is necessarily long — it implements a finite-state YAML + subset (scalars, maps, lists of maps at fixed depth). No utility refactors + meaningfully reduce length without degrading readability. All branches + are exhaustively tested in test_parse_minimal_yaml.py. + """ # Strip comments + blank lines but preserve indentation. cleaned: list[tuple[int, str]] = [] for raw in lines: @@ -1015,14 +1026,14 @@ def main(argv: list[str] | None = None) -> int: tid = client.resolve_team_id(args.owner, tn) if tid is None: # Try the list endpoint as a fallback. - code, data = client._req( # noqa: SLF001 + code, data = client._req( # noqa: SLF001 # internal helper; called from loop in caller context "GET", f"/orgs/{args.owner}/teams" ) if code == 200 and isinstance(data, list): for t in data: if t.get("name") == tn: tid = t.get("id") - client._team_id_cache[(args.owner, tn)] = tid # noqa: SLF001 + client._team_id_cache[(args.owner, tn)] = tid # noqa: SLF001 # internal write-through cache break if tid is not None: team_ids.append(tid) diff --git a/scripts/ops/check_migration_collisions.py b/scripts/ops/check_migration_collisions.py index 03c856608..302e980e7 100755 --- a/scripts/ops/check_migration_collisions.py +++ b/scripts/ops/check_migration_collisions.py @@ -91,6 +91,10 @@ def _gitea_get(path: str, params: dict[str, str] | None = None) -> bytes | None: req.add_header("Authorization", f"token {token}") req.add_header("Accept", "application/json") try: + # S310 (信任boundary): this function IS the outbound HTTP client for + # Gitea API calls. The call is intentional and controlled — we build + # the request ourselves and handle errors explicitly. Timeout=20s + # prevents indefinite hangs. with urllib.request.urlopen(req, timeout=20) as resp: # noqa: S310 return resp.read() except urllib.error.HTTPError as e: -- 2.52.0 From db9e2f3f43d592fd33c32ea600216cbc6a584657 Mon Sep 17 00:00:00 2001 From: Agent Dev B Date: Sun, 24 May 2026 05:14:58 +0000 Subject: [PATCH 2/3] ci: trigger re-run -- 2.52.0 From 8d21712063701d4f2499ffe7ee58cce62fb41149 Mon Sep 17 00:00:00 2001 From: "Molecule AI Dev Engineer A (Kimi)" Date: Sun, 24 May 2026 21:52:28 +0000 Subject: [PATCH 3/3] fix(handlers): allow legacy templates without runtime field Commit a5211050 introduced runtime validation in TemplatesHandler.List that skipped templates with an empty runtime string. This broke legacy templates that only specify a top-level model (pre-runtime_config shape). - Only validate runtime against knownRuntimes when raw.Runtime is non-empty. - Change t.Errorf to t.Fatalf in TestTemplatesList_LegacyTopLevelModel so an empty response fails gracefully instead of panicking on resp[0]. Fixes 2 failures + panic in handlers integration test suite. Co-Authored-By: Claude Opus 4.7 --- workspace-server/internal/handlers/templates.go | 10 ++++++---- workspace-server/internal/handlers/templates_test.go | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/workspace-server/internal/handlers/templates.go b/workspace-server/internal/handlers/templates.go index efc239b5f..aced29222 100644 --- a/workspace-server/internal/handlers/templates.go +++ b/workspace-server/internal/handlers/templates.go @@ -243,10 +243,12 @@ func (h *TemplatesHandler) List(c *gin.Context) { log.Printf("templates list: skip %s: yaml.Unmarshal: %v", id, err) return } - runtime := strings.TrimSuffix(strings.TrimSpace(raw.Runtime), "-default") - if _, ok := knownRuntimes[runtime]; !ok { - log.Printf("templates list: skip %s: unsupported runtime %q", id, raw.Runtime) - return + if raw.Runtime != "" { + runtime := strings.TrimSuffix(strings.TrimSpace(raw.Runtime), "-default") + if _, ok := knownRuntimes[runtime]; !ok { + log.Printf("templates list: skip %s: unsupported runtime %q", id, raw.Runtime) + return + } } // Model comes from either top-level (legacy) or runtime_config.model (current). diff --git a/workspace-server/internal/handlers/templates_test.go b/workspace-server/internal/handlers/templates_test.go index f7d562667..0c9c55c8d 100644 --- a/workspace-server/internal/handlers/templates_test.go +++ b/workspace-server/internal/handlers/templates_test.go @@ -677,7 +677,7 @@ skills: [] t.Fatalf("parse: %v", err) } if len(resp) != 1 || resp[0].Model != "anthropic:claude-sonnet-4-6" { - t.Errorf("legacy top-level model not surfaced: %+v", resp) + t.Fatalf("legacy top-level model not surfaced: %+v", resp) } if resp[0].Runtime != "claude-code" { t.Errorf("Runtime should be claude-code for legacy template, got %q", resp[0].Runtime) -- 2.52.0