Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .castiron-ratchet.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"schema_version": 1,
"max_custom_patch_lines": 10000
}
14 changes: 7 additions & 7 deletions .castiron.stats.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
schema_version: 1
generation_id: ddf51c0b-5978-487f-be21-e379112a927a
openapi_spec_hash: a85edbfc22ff719d064bce2705c7394e
openapi_transformed_spec_hash: f8e7644df5aee22dfcd0ea2b70942054
config_hash: 85382dd94c503b5d225adc7636a77c9f
codegen_sha: 310aa46b5b69a9e4a3d0dd78fa47797d6cae2746
codegen_hash: 5188f6aac875d009719a2f3702c2824068b7ce0780fe001f8a791735d8212a8b
public_codegen_sha: 0d3e70da47bb645fbfd0dd16fdd37ea10ee981f5
generation_id: a346904e-ba2c-4683-831d-a452ad321113
openapi_spec_hash: 92700e1a33a4f6174a01b46648c8186a
openapi_transformed_spec_hash: e37bbe0f04caa6093f1cb5d65d23ad03
config_hash: d13c582815d0db08c374985806be7352
codegen_sha: 035b803d39b5d87e9ea7624fdf638f58ff6343e9
codegen_hash: 015182335504f95ee327a5096cdb3c6046b558c54ddac2ddb9d6b526be4b536d
public_codegen_sha: e36749421d1be152d016bc41a8850bbdd9de0e63
111 changes: 108 additions & 3 deletions .github/workflows/castiron-custom-code-comment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ concurrency:
jobs:
compute:
name: Compute trusted custom-code report
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.path == '.github/workflows/castiron-custom-code.yml'
if: contains(fromJSON('["pull_request", "merge_group"]'), github.event.workflow_run.event) && github.event.workflow_run.path == '.github/workflows/castiron-custom-code.yml'
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
Expand All @@ -26,15 +26,20 @@ jobs:
number: ${{ steps.report.outputs.number }}
artifact-id: ${{ steps.artifact.outputs.artifact-id }}
artifact-run-attempt: ${{ github.run_attempt }}
base-sha: ${{ steps.budget.outputs.base_sha }}
head-sha: ${{ steps.budget.outputs.head_sha }}
isolation: ${{ steps.budget.outputs.isolation }}
budget: ${{ steps.budget.outputs.budget }}
steps:
- name: Check out the trusted reporter
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
ref: ${{ github.workflow_sha }}
ref: main
persist-credentials: false

- name: Compute from the current pull request Git objects
id: report
if: github.event.workflow_run.event == 'pull_request'
env:
GH_TOKEN: ${{ github.token }}
GIT_CONFIG_COUNT: '2'
Expand Down Expand Up @@ -66,10 +71,110 @@ jobs:
if-no-files-found: error
retention-days: 7

# The prior step's report was computed here from
# Git objects by main's reporter, not downloaded from the candidate run.
- name: Evaluate main's custom-code budget
id: budget
if: always()
continue-on-error: true # A budget failure must not suppress the existing report comment.
env:
GH_TOKEN: ${{ github.token }}
REPOSITORY: ${{ github.repository }}
SOURCE_EVENT: ${{ github.event.workflow_run.event }}
GIT_CONFIG_COUNT: '2'
GIT_CONFIG_KEY_0: credential.helper
GIT_CONFIG_VALUE_0: ''
GIT_CONFIG_KEY_1: credential.https://github.com.helper
GIT_CONFIG_VALUE_1: '!gh auth git-credential'
run: |
trusted_sha=$(git rev-parse HEAD)
reuse=()
if [[ "$SOURCE_EVENT" == pull_request ]]; then
reuse=(--trusted-report-dir "$RUNNER_TEMP/castiron-custom-code")
fi
python3 -I scripts/castiron/custom_code_budget.py github \
--repository "$REPOSITORY" --event-path "$GITHUB_EVENT_PATH" \
--trusted-sha "$trusted_sha" --repo "$RUNNER_TEMP/castiron-objects.git" \
"${reuse[@]}" --out "$RUNNER_TEMP/custom-code-budget"

- name: Add the budget to the run summary
if: always()
run: |
if test -f "$RUNNER_TEMP/custom-code-budget/summary.md"; then
cat "$RUNNER_TEMP/custom-code-budget/summary.md" >> "$GITHUB_STEP_SUMMARY"
fi

- name: Upload trusted budget measurements
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: custom-code-budget-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ runner.temp }}/custom-code-budget/
if-no-files-found: error
retention-days: 7

budget-status:
name: Publish custom-code budget checks
needs: compute
if: always() && !cancelled() && needs.compute.result != 'skipped'
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
actions: read
pull-requests: read
statuses: write
steps:
# This workflow definition is from main. No candidate checkout/artifacts.
- name: Publish exact-head statuses after checking freshness
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
env:
BASE_SHA: ${{ needs.compute.outputs.base-sha }}
HEAD_SHA: ${{ needs.compute.outputs.head-sha }}
ISOLATION_RESULT: ${{ needs.compute.outputs.isolation }}
BUDGET_RESULT: ${{ needs.compute.outputs.budget }}
with:
script: |
const event = context.payload.workflow_run;
const {data: run} = await github.rest.actions.getWorkflowRun({...context.repo, run_id: event.id});
if (run.head_sha !== event.head_sha || run.run_attempt !== event.run_attempt ||
run.status !== 'completed' || run.path.split('@', 1)[0] !== '.github/workflows/castiron-custom-code.yml' ||
run.repository.full_name !== `${context.repo.owner}/${context.repo.repo}`) return;
const head = run.head_sha;
if (!/^[0-9a-f]{40}$/.test(head)) throw new Error('Invalid candidate SHA');
const {data: main} = await github.rest.git.getRef({...context.repo, ref: 'heads/main'});
const base = main.object.sha;
if (run.event === 'pull_request') {
const pulls = run.pull_requests.length ? run.pull_requests : await github.paginate(
github.rest.repos.listPullRequestsAssociatedWithCommit, {...context.repo, commit_sha: head});
const current = [];
for (const pull of pulls) {
const {data: pr} = await github.rest.pulls.get({...context.repo, pull_number: pull.number});
if (pr.state === 'open' && pr.head.sha === head && pr.base.sha === base &&
pr.base.ref === 'main' && pr.base.repo.full_name === `${context.repo.owner}/${context.repo.repo}`) current.push(pr);
}
if (current.length !== 1) return;
} else if (run.event !== 'merge_group' || !run.head_branch.startsWith('gh-readonly-queue/main/')) {
return;
}
const fresh = base === process.env.BASE_SHA && head === process.env.HEAD_SHA;
const url = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
for (const [name, result] of [
['Castiron / budget-only change', process.env.ISOLATION_RESULT],
['Castiron / custom-code budget', process.env.BUDGET_RESULT],
]) {
const state = fresh && result === 'success' ? 'success' : 'failure';
const description = !fresh ? 'Evaluation unavailable or base changed; rerun against current main.'
: state === 'success' ? 'Passed against main policy. See the trusted run summary.'
: 'Budget check failed. See the trusted run summary.';
await github.rest.repos.createCommitStatus({...context.repo, sha: head, context: name,
state, description, target_url: url});
}

comment:
name: Update custom-code comment
needs: compute
if: always() && !cancelled() && (needs.compute.result == 'failure' || needs.compute.outputs.number != '')
if: always() && !cancelled() && github.event.workflow_run.event == 'pull_request' && (needs.compute.result == 'failure' || needs.compute.outputs.number != '')
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
Expand Down
20 changes: 16 additions & 4 deletions .github/workflows/castiron-custom-code.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,32 @@ name: Castiron custom code

on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
types: [opened, synchronize, reopened, ready_for_review, edited]
# Notify the existing trusted handler for queue candidates.
merge_group:
types: [checks_requested]

permissions:
contents: read

concurrency:
group: castiron-custom-code-${{ github.event.pull_request.number }}
group: castiron-custom-code-${{ github.event.pull_request.number || github.event.merge_group.head_ref }}
cancel-in-progress: false

env:
REPORTER_SHA256: ac48ca88e9f7ad57195038157e99f055c0cd3dac8de856e4d102dca807766d4a

jobs:
queue-signal:
if: github.event_name == 'merge_group'
runs-on: ubuntu-latest
timeout-minutes: 1
permissions: {}
steps:
- run: echo 'The workflow_run handler on main evaluates this candidate independently.'

report:
if: github.event_name == 'pull_request'
name: Castiron / baseline consistency
runs-on: ubuntu-latest
timeout-minutes: 10
Expand Down Expand Up @@ -44,8 +56,8 @@ jobs:
- name: Verify the reporter matches its generated workflow
run: printf '%s %s\n' "$REPORTER_SHA256" scripts/castiron/custom_code_report.py | sha256sum --check --strict

- name: Test hash mismatch and snapshot isolation
run: python3 -m unittest discover -s scripts/castiron -p test_custom_code_report.py
- name: Test snapshot isolation and the custom-code budget
run: python3 -m unittest discover -s scripts/castiron -p 'test_custom_code*.py'

- name: Validate the codegen hash and report custom code
env:
Expand Down
10 changes: 10 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ changing generated files. Handwritten policy, automation, tests, and examples
should remain small and should not alter exported SDK APIs unless the change
explicitly requires it.

## Custom-code budget

Follow [the custom-code guidance](scripts/castiron/CUSTOM_CODE.md). Budget changes
belong in a separate PR containing only `.castiron-ratchet.json`, with an explicit justification
in the PR description. Increases require a **human approving review** before merging.
Agents may investigate and draft proposals, but must not approve budget increases
(including through a human's credentials) or bypass the gate. Do not weaken
counting, broaden exclusions, or alter generation metadata to make a change pass.
The checker and effective budget come from main, not the PR. Keep default CODEOWNERS.

## Security requirements for coding agents

- Never commit real API or admin keys, bearer tokens, webhook secrets, cloud
Expand Down
17 changes: 17 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,23 @@ Most of the SDK is generated code. Modifications to code will be persisted betwe
result in merge conflicts between manual patches and changes from the generator. The generator will never
modify the contents of the `src/openai/lib/` and `examples/` directories.

## Custom-code budget

The custom-code budget counts additions plus deletions in the remaining patch
against verified generated output. `.castiron-ratchet.json` defines this repository's
ceiling. CI uses the checker and budget on main, not the PR's proposed versions.

Budget changes must be in a separate PR modifying **only `.castiron-ratchet.json`**.
Justify the current usage, proposed ceiling, and why fixing generation is not
appropriate in the PR description. Increases require a **human approving review**
and must merge before an SDK change relies on them. Agents may draft proposals,
but must not approve increases or bypass the gate. Keep default CODEOWNERS.
Lower the ceiling after cleanup while retaining headroom; decreases must still
fit the measured usage.

See [custom-code technical details](scripts/castiron/CUSTOM_CODE.md) for accounting,
local checks, trusted CI, and activation instructions.

## Security requirements for contributions

- Never commit API or admin keys, bearer tokens, webhook secrets, cloud
Expand Down
87 changes: 36 additions & 51 deletions api_reference/openapi.transformed.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33071,18 +33071,14 @@ components:
nullable: true
description: |
Allows to set transparency for the background of the generated image(s).
This parameter is only supported for GPT image models that support
transparent backgrounds. Must be one of `transparent`, `opaque`, or
`auto` (default value). When `auto` is used, the model will
automatically determine the best background for the image.

`gpt-image-2` and `gpt-image-2-2026-04-21` do not support
transparent backgrounds. Requests with `background` set to
`transparent` will return an error for these models; use `opaque` or
`auto` instead.

If `transparent`, the output format needs to support transparency,
so it should be set to either `png` (default value) or `webp`.
Must be one of `transparent`, `opaque`, or `auto` (default value). When
`auto` is used, the model will automatically determine the best
background for the image.

Transparent backgrounds are available for supported GPT Image models.
For `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in
preview. When using `transparent`, set the output format to `png` or
`webp`.
model:
anyOf:
- type: string
Expand Down Expand Up @@ -33310,18 +33306,14 @@ components:
nullable: true
description: |
Allows to set transparency for the background of the generated image(s).
This parameter is only supported for GPT image models that support
transparent backgrounds. Must be one of `transparent`, `opaque`, or
`auto` (default value). When `auto` is used, the model will
automatically determine the best background for the image.

`gpt-image-2` and `gpt-image-2-2026-04-21` do not support
transparent backgrounds. Requests with `background` set to
`transparent` will return an error for these models; use `opaque` or
`auto` instead.

If `transparent`, the output format needs to support transparency,
so it should be set to either `png` (default value) or `webp`.
Must be one of `transparent`, `opaque`, or `auto` (default value). When
`auto` is used, the model will automatically determine the best
background for the image.

Transparent backgrounds are available for supported GPT Image models.
For `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in
preview. When using `transparent`, set the output format to `png` or
`webp`.
style:
type: string
enum:
Expand Down Expand Up @@ -35512,7 +35504,7 @@ components:
- type: 'null'
default: auto
example: transparent
description: Background behavior for generated image output.
description: Set the background of the generated image output. Transparent backgrounds are available for supported GPT Image models. For `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`, set the output format to `png` or `webp`.
stream:
anyOf:
- type: boolean
Expand Down Expand Up @@ -39198,18 +39190,14 @@ components:
- auto
description: |
Allows to set transparency for the background of the generated image(s).
This parameter is only supported for GPT image models that support
transparent backgrounds. Must be one of `transparent`, `opaque`, or
`auto` (default value). When `auto` is used, the model will
automatically determine the best background for the image.

`gpt-image-2` and `gpt-image-2-2026-04-21` do not support
transparent backgrounds. Requests with `background` set to
`transparent` will return an error for these models; use `opaque` or
`auto` instead.

If `transparent`, the output format needs to support transparency,
so it should be set to either `png` (default value) or `webp`.
Must be one of `transparent`, `opaque`, or `auto` (default value). When
`auto` is used, the model will automatically determine the best
background for the image.

Transparent backgrounds are available for supported GPT Image models.
For `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in
preview. When using `transparent`, set the output format to `png` or
`webp`.
default: auto
input_fidelity:
anyOf:
Expand Down Expand Up @@ -51417,8 +51405,9 @@ components:
RealtimeTranslationServerEventSessionOutputAudioDelta:
type: object
description: |
Returned when translated output audio is available. Output audio deltas are
200 ms frames of PCM16 audio.
Returned when translated output audio is available. The `delta` contains a
PCM16 audio chunk whose length can vary. Clients should decode and queue the
complete delta instead of assuming a fixed byte or sample count.
properties:
event_id:
type: string
Expand Down Expand Up @@ -68577,18 +68566,14 @@ components:
- auto
description: |
Allows to set transparency for the background of the generated image(s).
This parameter is only supported for GPT image models that support
transparent backgrounds. Must be one of `transparent`, `opaque`, or
`auto` (default value). When `auto` is used, the model will
automatically determine the best background for the image.

`gpt-image-2` and `gpt-image-2-2026-04-21` do not support
transparent backgrounds. Requests with `background` set to
`transparent` will return an error for these models; use `opaque` or
`auto` instead.

If `transparent`, the output format needs to support transparency,
so it should be set to either `png` (default value) or `webp`.
Must be one of `transparent`, `opaque`, or `auto` (default value). When
`auto` is used, the model will automatically determine the best
background for the image.

Transparent backgrounds are available for supported GPT Image models.
For `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in
preview. When using `transparent`, set the output format to `png` or
`webp`.
default: auto
input_fidelity:
anyOf:
Expand Down
Loading
Loading