From 9b85f32506009ad7624f46b76b959142259a8cdc Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 13 Oct 2026 13:26:10 +0900 Subject: [PATCH] fix(noema): fail closed on non-boolean repository visibility fallback --- .github/workflows/noema-review.yml | 2 +- ...st_noema_orchestrator_workflow_contract.py | 71 +++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/.github/workflows/noema-review.yml b/.github/workflows/noema-review.yml index 6c81d859e2..bcf19aacee 100644 --- a/.github/workflows/noema-review.yml +++ b/.github/workflows/noema-review.yml @@ -837,7 +837,7 @@ jobs: visibility="" for target_visibility_attempt in 1 2 3 4 5 6; do if visibility="$( - gh api "/repos/${TARGET_REPOSITORY}" --jq '.visibility // (if .private then "private" else "public" end)' + gh api "/repos/${TARGET_REPOSITORY}" --jq '.visibility // (if .private == true then "private" elif .private == false then "public" else "" end)' )"; then break fi diff --git a/tests/test_noema_orchestrator_workflow_contract.py b/tests/test_noema_orchestrator_workflow_contract.py index 7d88b8188a..1d557cc07e 100644 --- a/tests/test_noema_orchestrator_workflow_contract.py +++ b/tests/test_noema_orchestrator_workflow_contract.py @@ -878,3 +878,74 @@ def test_evidence_initialization_rejects_path_aliases_without_deletion(tmp_path: for path in evidence.iterdir(): if path.is_file() and not path.is_symlink() and path.stat().st_nlink == 1: assert path.read_text() == "old evidence\n" + + +@pytest.mark.parametrize( + "metadata,expected", + [ + pytest.param({"visibility": "public"}, "false", id="public"), + pytest.param({"visibility": "private"}, "true", id="private"), + pytest.param({"visibility": "internal"}, "true", id="internal"), + pytest.param({"private": True}, "true", id="legacy-true"), + pytest.param({"private": False}, "false", id="legacy-false"), + pytest.param({}, None, id="missing"), + pytest.param({"private": None}, None, id="null"), + pytest.param({"private": "false"}, None, id="string"), + pytest.param({"visibility": "unknown"}, None, id="unknown"), + pytest.param({"private": 0}, None, id="zero"), + pytest.param({"private": 1}, None, id="one"), + pytest.param({"private": []}, None, id="array"), + pytest.param({"private": {}}, None, id="object"), + pytest.param({"visibility": None, "private": False}, "false", id="null-visibility-boolean"), + pytest.param({"visibility": "unknown", "private": False}, None, id="unknown-overrides-fallback"), + ], +) +def test_noema_visibility_requires_explicit_visibility_or_boolean_fallback( + tmp_path: Path, metadata: dict, expected: str | None +) -> None: + """Execute the actual gh/jq visibility shell; malformed metadata must not route.""" + assert shutil.which("jq") is not None + script = textwrap.dedent( + workflow_step( + workflow_text("noema-review.yml"), + "Resolve Noema target repository visibility", + ).split(" run: |\n", 1)[1] + ) + fake_gh = tmp_path / "gh" + fake_gh.write_text( + '#!/usr/bin/env bash\nset -euo pipefail\n' + 'test "$1" = api\ntest "$2" = /repos/ContextualWisdomLab/example\n' + 'test "$3" = --jq\ntest "$#" = 4\n' + 'printf called >> "$FAKE_GH_CALLS"\n' + 'printf "%s" "$FAKE_METADATA" | jq -r "$4"\n', + encoding="utf-8", + ) + fake_gh.chmod(0o755) + output = tmp_path / "github-output" + calls = tmp_path / "gh-calls" + result = subprocess.run( + [shutil.which("bash") or "/bin/bash", "-c", script], + env={ + "PATH": f"{tmp_path}{os.pathsep}{os.environ.get('PATH', '')}", + "HOME": str(tmp_path), + "TMPDIR": str(tmp_path), + "GITHUB_OUTPUT": str(output), + "GH_TOKEN": "synthetic-review-token", + "TARGET_REPOSITORY": "ContextualWisdomLab/example", + "FAKE_METADATA": json.dumps(metadata), + "FAKE_GH_CALLS": str(calls), + }, + capture_output=True, + text=True, + check=False, + timeout=5, + ) + assert calls.read_text(encoding="utf-8") == "called" + assert "synthetic-review-token" not in result.stdout + result.stderr + if expected is None: + assert result.returncode != 0, (metadata, result.stdout) + assert not output.exists() + assert "visibility is missing or unsupported" in result.stdout + else: + assert result.returncode == 0, result.stderr + assert output.read_text(encoding="utf-8") == f"require_zdr={expected}\n"