From 26fd0998768a9a7251800803b2a83a0e9a3c29b5 Mon Sep 17 00:00:00 2001 From: jbeckwith-oai <269036200+jbeckwith-oai@users.noreply.github.com> Date: Tue, 20 Oct 2026 05:20:13 +0000 Subject: [PATCH] chore: refresh API-key constraints and Castiron reporting Castiron-Internal-PR: https://github.com/openai/openai-cli-internal/pull/204 Castiron-Source-SHA: 35409113cc03a3c5397e29037af9f473826daef2 Castiron-Public-Base-SHA: 7639dab16a033ef6335cbb386b990b2ff6098d80 --- .castiron.stats.yml | 12 ++--- .../castiron-custom-code-comment.yml | 10 +++-- .github/workflows/castiron-custom-code.yml | 2 +- api_reference/openapi.transformed.yml | 36 +++++---------- scripts/castiron/custom_code_report.py | 21 ++++++++- .../castiron/fixtures/github_publisher.cjs | 2 +- scripts/castiron/generated-files.json | 14 +++--- scripts/castiron/test_custom_code_github.py | 29 ++++++++++++ .../castiron/test_custom_code_publication.py | 27 ++++++++++- scripts/castiron/test_custom_code_report.py | 45 +++++++++++++++++++ 10 files changed, 153 insertions(+), 45 deletions(-) diff --git a/.castiron.stats.yml b/.castiron.stats.yml index 4e8582fe..4c5769d4 100644 --- a/.castiron.stats.yml +++ b/.castiron.stats.yml @@ -1,8 +1,8 @@ schema_version: 1 -generation_id: 08df1715-e12d-4fb7-9f39-36a5177cb268 -openapi_spec_hash: f056027f327e6dc07798c27da39f1916 -openapi_transformed_spec_hash: ccbb3610f02d7b6473dfbb3ac3f7c0dd +generation_id: dfeb3d3b-06be-408a-88c2-f7e51d0931fb +openapi_spec_hash: 9c46c85a2ff35b78d9eeba9e1a285981 +openapi_transformed_spec_hash: 5ff561cf4786af67b36a5c5b6fb00ce8 config_hash: 65d162cc4be98e6e2cf07658e30d1170 -codegen_sha: 5749e9a81a33bc7cd25f7a6dda0a8186f4339e22 -codegen_hash: 13a3dd32d06c51dda8572c82c3ec12c5ede2b83e8edaf720cf07048209132189 -public_codegen_sha: 9edf54e2d708c12eb47df1ca228d28400e4dd335 +codegen_sha: 9b3da60e57bf7da65ef2a94c76ef395262f7c9d0 +codegen_hash: b555a476f1b902a2dab0f13b019d2c8e017079e2b544a33b3ce9cd3caac5d871 +public_codegen_sha: 4109ce39f7e1dced610768c6f58b520d85f636c4 diff --git a/.github/workflows/castiron-custom-code-comment.yml b/.github/workflows/castiron-custom-code-comment.yml index 43f3a1da..11588b31 100644 --- a/.github/workflows/castiron-custom-code-comment.yml +++ b/.github/workflows/castiron-custom-code-comment.yml @@ -168,9 +168,13 @@ jobs: if (pr.state === 'open' && pr.head.sha === head && ['main', 'next'].includes(pr.base.ref) && pr.base.repo.full_name === `${context.repo.owner}/${context.repo.repo}`) current.push(pr); } - if (current.length !== 1) return; - branch = current[0].base.ref; - number = current[0].number; + // Match compute's preference for required main checks when + // fallback discovery finds an advisory next PR for the same head. + const main = current.filter(pr => pr.base.ref === 'main'); + const selected = main.length ? main : current; + if (selected.length !== 1) return; + branch = selected[0].base.ref; + number = selected[0].number; } else { const match = run.head_branch.match(/^gh-readonly-queue\/(main|next)\//); if (run.event !== 'merge_group' || !match) return; diff --git a/.github/workflows/castiron-custom-code.yml b/.github/workflows/castiron-custom-code.yml index 0fb4c3c7..be0288f8 100644 --- a/.github/workflows/castiron-custom-code.yml +++ b/.github/workflows/castiron-custom-code.yml @@ -16,7 +16,7 @@ concurrency: cancel-in-progress: false env: - REPORTER_SHA256: 157ed30ac9d54d20fe5f7e625dc8ee5958beb91cc7e30aaa97cfba8fb3e06e21 + REPORTER_SHA256: 4dc056b460d688cbe3035bd9649820f77e489424d0c9c48bd3f19ed4dfb7e9b4 jobs: queue-signal: diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml index bfb99c9c..46dfb8b4 100644 --- a/api_reference/openapi.transformed.yml +++ b/api_reference/openapi.transformed.yml @@ -36936,6 +36936,11 @@ webhooks: components: schemas: UpdateProjectApiKeyBody: + anyOf: + - required: + - scopes + - required: + - expires_in_seconds properties: expires_in_seconds: type: integer @@ -87321,13 +87326,13 @@ components: type: string minLength: 0 description: The public ID of the environment. - container_size: - anyOf: - - $ref: '#/components/schemas/ContainerSizeResource' - - type: 'null' - description: The effective CPU and memory tier, or null when unknown or outside the public tiers. - x-stainless-skip: - - cli + files: + type: array + items: + $ref: '#/components/schemas/HostedEnvironmentFileResource' + minItems: 0 + maxItems: 50 + description: Files available in the environment, excluding their contents. packages: $ref: '#/components/schemas/EnvironmentPackagesResource' description: Packages installed in the environment. @@ -87359,13 +87364,6 @@ components: minItems: 0 maxItems: 2000 description: Plugins installed in the environment, excluding their archive contents. - files: - type: array - items: - $ref: '#/components/schemas/HostedEnvironmentFileResource' - minItems: 0 - maxItems: 50 - description: Files available in the environment, excluding their contents. required: - type - id @@ -88074,11 +88072,6 @@ components: minLength: 0 maxLength: 64 description: A reusable hosted template applied before inline session configuration. Omitted fields inherit the template; network overrides cannot broaden its policy. - container_size: - $ref: '#/components/schemas/ContainerSizeParam' - description: The hosted container size. Omission selects the medium tier. - x-stainless-skip: - - cli required: - type additionalProperties: false @@ -93483,11 +93476,6 @@ components: minLength: 1 maxLength: 1 pattern: ^[0-9A-D*#]$ - client_event_id: - description: The event_id of the client command, when supplied. - type: string - x-stainless-skip: - - cli required: - type - event_id diff --git a/scripts/castiron/custom_code_report.py b/scripts/castiron/custom_code_report.py index 21906f5a..02054ac8 100644 --- a/scripts/castiron/custom_code_report.py +++ b/scripts/castiron/custom_code_report.py @@ -452,18 +452,31 @@ def resolve_checkpoint( if not isinstance(decoded, dict): raise ReportError("invalid codegen checkpoint metadata") metadata = cast(dict[str, Any], decoded) + namespace = "" + if "schema_version" in metadata: + if ( + set(metadata) != {"schema_version", "release_target", "record"} + or metadata["schema_version"] != "castiron-checkpoint/v2" + or metadata["release_target"] != "next" + or not isinstance(metadata["record"], dict) + ): + raise ReportError("invalid versioned checkpoint envelope") + metadata = cast(dict[str, Any], metadata["record"]) + namespace = "castiron/next/" + if {"release_target", "schema_version", "record"}.intersection(metadata): + raise ReportError("checkpoint target metadata must use the versioned envelope") if metadata.get("generation_id") != stats["generation_id"]: raise ReportError("codegen checkpoint generation ID does not match stats") branch = metadata.get("source_branch") if not isinstance(branch, str) or not branch: raise ReportError("codegen checkpoint has no source branch") - ref = f"refs/heads/codegen/{branch}" + ref = f"refs/heads/codegen/{namespace}{branch}" git(repo, "check-ref-format", ref) tracking = f"refs/castiron/report/{hashlib.sha256(ref.encode()).hexdigest()}" if fetch: git(repo, "fetch", "--quiet", "--no-tags", "origin", f"+{ref}:{tracking}") else: - tracking = f"refs/remotes/origin/codegen/{branch}" + tracking = f"refs/remotes/origin/codegen/{namespace}{branch}" git(repo, "merge-base", "--is-ancestor", commit, tracking) actual = hash_codegen_commit(repo, commit) expected = stats.get("codegen_hash") @@ -807,6 +820,10 @@ def current_pull(repository: str, run: dict[str, Any]) -> tuple[int, str] | None and pull["base"]["ref"] in TARGET_BRANCHES ): current.append((number, pull["base"]["ref"])) + # Fallback discovery can find both targets for one head. Main's required + # checks take precedence over next's advisory report; keep same-target + # ambiguity fail-closed. Explicit run associations still limit discovery. + current = [pull for pull in current if pull[1] == "main"] or current if len(current) > 1: raise ReportError("workflow run has multiple current pull requests") return current[0] if current else None diff --git a/scripts/castiron/fixtures/github_publisher.cjs b/scripts/castiron/fixtures/github_publisher.cjs index 9f16565b..8c549a65 100644 --- a/scripts/castiron/fixtures/github_publisher.cjs +++ b/scripts/castiron/fixtures/github_publisher.cjs @@ -6,7 +6,7 @@ const writes = []; const github = { rest: { actions: { getWorkflowRun: async () => ({ data: data.run }) }, - pulls: { get: async () => ({ data: data.current_sequence?.shift() || data.current }), list: 'pulls' }, + pulls: { get: async ({ pull_number }) => ({ data: data.current_sequence?.shift() || data.pulls_by_number?.[pull_number] || data.current }), list: 'pulls' }, git: { getRef: async ({ ref }) => ({ data: { diff --git a/scripts/castiron/generated-files.json b/scripts/castiron/generated-files.json index e9e0f727..2044ae36 100644 --- a/scripts/castiron/generated-files.json +++ b/scripts/castiron/generated-files.json @@ -3,11 +3,11 @@ "files": { ".github/workflows/castiron-custom-code-comment.yml": { "mode": "100644", - "sha256": "02b1c497461fcaf604e1389e7dcab06cfcca01635a8cb2e92f8769245a2aaead" + "sha256": "f0a54911a96ae55a6a9c71ed7e6b259b0da84642dd1716b68e75a7123519728c" }, ".github/workflows/castiron-custom-code.yml": { "mode": "100644", - "sha256": "ccd67760a8cfcc17ebdf5c8df7e330ee7be5199cb62232afdf9035fea68bbc2f" + "sha256": "867db0c0cb67312a7682236b2ff04f2ccf611ad9e307a2ee650b3a997191f289" }, "generated/commands/adminorganizationadminapikey.go": { "mode": "100644", @@ -715,7 +715,7 @@ }, "scripts/castiron/custom_code_report.py": { "mode": "100644", - "sha256": "157ed30ac9d54d20fe5f7e625dc8ee5958beb91cc7e30aaa97cfba8fb3e06e21" + "sha256": "4dc056b460d688cbe3035bd9649820f77e489424d0c9c48bd3f19ed4dfb7e9b4" }, "scripts/castiron/custom_code_test_support.py": { "mode": "100644", @@ -723,7 +723,7 @@ }, "scripts/castiron/fixtures/github_publisher.cjs": { "mode": "100644", - "sha256": "fc80b1aff054f8a2ea16fc6bd9cff5ed4e4a48eebc7f3af8427947796fb8d865" + "sha256": "4f5475e905818875e37d686bf03553b55219ac05e30957628d4aaa834fced202" }, "scripts/castiron/ruff.toml": { "mode": "100644", @@ -735,15 +735,15 @@ }, "scripts/castiron/test_custom_code_github.py": { "mode": "100644", - "sha256": "aa170458b1b58523df78073a96ba8c58b75fce03206ff7d62d99b29791c6ca39" + "sha256": "338d54bf6b53a44b555dc7baa778c76af499b06959caf0339142f1b4293edd92" }, "scripts/castiron/test_custom_code_publication.py": { "mode": "100644", - "sha256": "99b266761f427d77990e68ce2fdea4b5f1a5b175612b3a9f4355c094939130ca" + "sha256": "1d93757664a6ffd26f21df2a4c07ac635393f5da1ab0283df75bad66faa2ef2e" }, "scripts/castiron/test_custom_code_report.py": { "mode": "100644", - "sha256": "56a748c56ccf137aee0e5997dd29484157d5566bf844cc4969270dd26fa38cbd" + "sha256": "8cb4add28b7e6acb4f8b3844073d33e7a95d8a554c4caf47bd1e09da86a3bd04" } } } diff --git a/scripts/castiron/test_custom_code_github.py b/scripts/castiron/test_custom_code_github.py index 3c04d207..a96162fd 100644 --- a/scripts/castiron/test_custom_code_github.py +++ b/scripts/castiron/test_custom_code_github.py @@ -62,6 +62,35 @@ def __call__(self, method: str, path: str, payload: dict[str, Any] | None = None class GitHubReportTests(GitTestCase): + def test_same_head_targets_preserve_required_main_discovery(self) -> None: + head = "b" * 40 + run = source_run(head, "pull_request") + run["pull_requests"] = [] + main: dict[str, Any] = { + "state": "open", + "head": {"sha": head}, + "base": {"ref": "main", "repo": {"full_name": REPOSITORY}}, + } + next_pull: dict[str, Any] = {**main, "base": {**main["base"], "ref": "next"}} + for numbers in ([3, 4], [4, 3]): + def api_response(_method: str, path: str, numbers: list[int] = numbers) -> Any: + if "/commits/" in path: + return [{"number": number} for number in numbers] + return {"3": main, "4": next_pull}[path.rsplit("/", 1)[1]] + + with ( + self.subTest(numbers=numbers), + mock.patch.object(report, "api", side_effect=api_response), + ): + self.assertEqual(report.current_pull(REPOSITORY, run), (3, "main")) + run["pull_requests"] = [{"number": 4}] + self.assertEqual(report.current_pull(REPOSITORY, run), (4, "next")) + run["pull_requests"] = [] + next_pull["base"]["ref"] = "main" + with self.assertRaisesRegex(report.ReportError, "multiple current"): + report.current_pull(REPOSITORY, run) + next_pull["base"]["ref"] = "next" + def test_trusted_report_recomputes_pr_output_in_a_bare_repository(self) -> None: generated, _ = self.baseline() content_hash = report.hash_codegen_commit(self.repo, generated) diff --git a/scripts/castiron/test_custom_code_publication.py b/scripts/castiron/test_custom_code_publication.py index f8137165..475cc7dc 100644 --- a/scripts/castiron/test_custom_code_publication.py +++ b/scripts/castiron/test_custom_code_publication.py @@ -326,6 +326,7 @@ def publish( queue_absent: bool = False, queue_pages: list[dict[str, Any]] | None = None, retarget_during_publication: bool = False, + same_head_next_pr: bool = False, ) -> list[dict[str, Any]]: base, head = "a" * 40, "b" * 40 run = source_run(head, event_name) @@ -369,6 +370,12 @@ def publish( "PUBLISH_ATTEMPT": "1", }, } + if same_head_next_pr: + current = payload["current"] + payload["pulls_by_number"] = { + 3: current, + 4: {**current, "number": 4, "base": {**current["base"], "ref": "next"}}, + } if retarget_during_publication: current = payload["current"] payload["current_sequence"] = [ @@ -397,6 +404,22 @@ def test_next_cannot_overwrite_main_results(self) -> None: result = self.publish(event_name=event, branch="next", previous_statuses=main) self.assertEqual(result, []) + def test_same_head_next_pr_does_not_block_required_main_statuses(self) -> None: + for numbers in ([3, 4], [4, 3]): + with self.subTest(numbers=numbers): + results = self.publish( + run_overrides={"pull_requests": []}, + fallback_pulls=[{"number": number} for number in numbers], + same_head_next_pr=True, + failed_budget=True, + ) + self.assertEqual([r["state"] for r in results], ["success", "failure"]) + self.assertTrue(all(r["sha"] == "b" * 40 for r in results)) + self.assertEqual( + self.publish(run_overrides={"pull_requests": [{"number": 4}]}, same_head_next_pr=True), + [], + ) + def test_next_queue_never_authorizes_against_a_stale_main_policy(self) -> None: for changed in (False, True): with self.subTest(policy_changed=changed): @@ -413,7 +436,9 @@ def test_main_statuses_bind_target_policy_and_queue_membership(self) -> None: for policy_changed, queue_absent in ((True, False), (False, True)): with self.subTest(policy_changed=policy_changed, queue_absent=queue_absent): result = self.publish( - event_name="merge_group", policy_changed=policy_changed, queue_absent=queue_absent + event_name="merge_group", + policy_changed=policy_changed, + queue_absent=queue_absent, ) self.assertEqual([r["state"] for r in result], ["failure", "failure"]) self.assertEqual(self.publish(retarget_during_publication=True), []) diff --git a/scripts/castiron/test_custom_code_report.py b/scripts/castiron/test_custom_code_report.py index d483c31c..6317d00c 100644 --- a/scripts/castiron/test_custom_code_report.py +++ b/scripts/castiron/test_custom_code_report.py @@ -166,6 +166,51 @@ def test_stats_symlink_is_rejected(self) -> None: with self.assertRaisesRegex(report.ReportError, "regular Git file"): report.read_stats(self.repo, self.commit()) + def test_next_checkpoint_uses_its_namespaced_ref(self) -> None: + generated, base = self.baseline() + self.git("checkout", "--detach", generated) + metadata = { + "schema_version": "castiron-checkpoint/v2", + "release_target": "next", + "record": {"generation_id": GENERATION, "source_branch": "test"}, + } + encoded = base64.b64encode(json.dumps(metadata).encode()).decode() + checkpoint = self.commit(f"codegen\n\nGeneration metadata: {encoded}") + ref = "refs/remotes/origin/codegen/castiron/next/test" + self.git("update-ref", ref, checkpoint) + stats = report.read_stats(self.repo, base) | { + "codegen_sha": checkpoint, + "codegen_hash": report.hash_codegen_commit(self.repo, checkpoint), + } + result = report.resolve_checkpoint(self.repo, stats, fetch=False, require_hash=True) + self.assertEqual(result["commit"], checkpoint) + self.git("update-ref", "refs/remotes/origin/codegen/test", checkpoint) + self.git("update-ref", "-d", ref) + with self.assertRaises(report.ReportError): + report.resolve_checkpoint(self.repo, stats, fetch=False, require_hash=True) + + def test_invalid_checkpoint_envelopes_are_rejected(self) -> None: + _, base = self.baseline() + record = {"generation_id": GENERATION, "source_branch": "test"} + envelope = { + "schema_version": "castiron-checkpoint/v2", + "release_target": "next", + "record": record, + } + for metadata in ( + envelope | {"schema_version": "unknown"}, + envelope | {"release_target": "main"}, + envelope | {"extra": True}, + envelope | {"record": record | {"release_target": "next"}}, + record | {"release_target": "next"}, + ): + with self.subTest(metadata=metadata): + encoded = base64.b64encode(json.dumps(metadata).encode()).decode() + checkpoint = self.commit(f"codegen\n\nGeneration metadata: {encoded}") + stats = report.read_stats(self.repo, base) | {"codegen_sha": checkpoint} + with self.assertRaisesRegex(report.ReportError, "envelope"): + report.resolve_checkpoint(self.repo, stats, fetch=False, require_hash=True) + def test_public_snapshot_rejects_private_governance_and_unpublished_paths(self) -> None: entry = report.Entry(b"100644", "a" * 40) blocked = [