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
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: a993ea45-7f4f-4d48-9eea-27e2f709ca7c
openapi_spec_hash: 1f2d498f08bdd6dacffeb41cee8f44e5
openapi_transformed_spec_hash: 77075630a8655ccbb8d1f790f19f5731
config_hash: 7a8181d81e1b9225f8ee6f09171d712e
codegen_sha: 6f2038ca576a63929de017d9145a842c3a09beb1
codegen_hash: a761b2892108918f3ad49d4929a6b756fa023b3192025bbc2bbcf618dea6c13a
public_codegen_sha: 9fdcc5c60f9247fe11ec25aaa0f486033f738421
generation_id: 4dd013a4-3586-4b2e-8d90-dd4d6b6d8e18
openapi_spec_hash: 2c78c708cab402e8904f555026c78020
openapi_transformed_spec_hash: e513b495f24d8119b952521ffc783d86
config_hash: 26002a7ad4f9fdd7011b62a2da3511b1
codegen_sha: b9a21b51f252010a29b36f9dff4118593321d48c
codegen_hash: d7afe679f4861d8aec19f61fefe2c0659d30cc557923b3614ca142d5985d5a4f
public_codegen_sha: 889f63058deba0eb0a04a593a9cf771c0674972a
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 @@ -182,7 +182,7 @@ jobs:
if (!(typeof value === 'string' || Number.isSafeInteger(value)) ||
!/^[1-9][0-9]*$/.test(String(value))) throw new Error('Invalid review identity');
const identity = String(value);
if (review.state === 'PENDING' || !review.author) continue;
if (['PENDING', 'COMMENTED'].includes(review.state) || !review.author) continue;
const previous = latest.get(review.author.login);
if (!previous || BigInt(identity) > BigInt(previous.identity)) latest.set(review.author.login, {...review, identity});
}
Expand Down Expand Up @@ -344,6 +344,97 @@ jobs:
return;
}
main-change:
name: Invalidate next queue after main advances
if: github.event.workflow_run.event == 'push' && github.event.workflow_run.head_branch == 'main'
runs-on: ubuntu-latest
timeout-minutes: 5
concurrency:
group: castiron-budget-writes-${{ github.repository_id }}
cancel-in-progress: false
queue: max
permissions:
actions: write
contents: read
pull-requests: read
statuses: write
steps:
- name: Re-evaluate live next queue after main advances
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
// Main pushes only invalidate. The normal authenticated evaluator
// and serialized publisher are the only paths that restore success.
const repo = `${context.repo.owner}/${context.repo.repo}`;
const signal = context.payload.workflow_run;
const {data: run} = await github.rest.actions.getWorkflowRun({...context.repo, run_id: signal.id});
const {data: workflow} = await github.rest.actions.getWorkflow({
...context.repo, workflow_id: 'castiron-custom-code.yml',
});
if (!Number.isSafeInteger(run.workflow_id) || run.workflow_id !== workflow.id ||
workflow.path !== '.github/workflows/castiron-custom-code.yml') return;
if (run.repository.full_name !== repo || run.head_repository?.full_name !== repo ||
run.event !== 'push' || run.head_branch !== 'main' || run.status !== 'completed' ||
run.id !== signal.id || run.run_attempt !== signal.run_attempt || run.head_sha !== signal.head_sha ||
!/^[0-9a-f]{40}$/.test(run.head_sha) ||
run.path.split('@', 1)[0] !== '.github/workflows/castiron-custom-code.yml') {
throw new Error('Unexpected or superseded main signal');
}
const {data: main} = await github.rest.git.getRef({...context.repo, ref: 'heads/main'});
// A newer main push has its own signal. An old retry must not churn
// candidates already qualified against that newer snapshot.
if (run.head_sha !== main.object.sha) return;
const targets = new Set();
let cursor = null;
do {
const result = await github.graphql(`query($owner:String!,$name:String!,$branch:String!,$cursor:String) {
repository(owner:$owner,name:$name) { mergeQueue(branch:$branch) {
entries(first:100,after:$cursor) {
nodes { headCommit { oid } }
pageInfo { hasNextPage endCursor }
}
} }
}`, {owner: context.repo.owner, name: context.repo.repo, branch: 'next', cursor});
const page = result.repository.mergeQueue?.entries;
if (!page) break;
for (const entry of page.nodes) {
if (entry.headCommit === null) continue; // Unprepared entries have no statuses to invalidate.
const sha = entry.headCommit?.oid;
if (!/^[0-9a-f]{40}$/.test(sha || '')) throw new Error('Invalid live queue SHA');
targets.add(sha);
}
if (!page.pageInfo.hasNextPage) break;
const next = page.pageInfo.endCursor;
if (!next || next === cursor) throw new Error('Invalid queue pagination');
cursor = next;
} while (true);
const url = `${context.serverUrl}/${repo}/actions/runs/${context.runId}`;
// Invalidate the entire live next queue before slower rerun discovery.
// This also covers bootstraps whose policy pin is now stale. Ordinary
// pinned-policy candidates can regain success through normal checks.
for (const sha of targets) {
for (const name of ['Castiron next / budget-only change', 'Castiron next / custom-code budget']) {
await github.rest.repos.createCommitStatus({...context.repo, sha, context: name, state: 'pending',
description: `Main advanced; reevaluation pending. [main ${main.object.sha}]`, target_url: url});
}
}
for (const sha of targets) {
const runs = await github.paginate(github.rest.actions.listWorkflowRuns, {
...context.repo, workflow_id: 'castiron-custom-code.yml', head_sha: sha, event: 'merge_group', per_page: 100,
});
const matches = runs.filter(item => item.repository.full_name === repo &&
item.workflow_id === workflow.id && item.event === 'merge_group' && item.head_sha === sha &&
typeof item.head_branch === 'string' && item.head_branch.startsWith('gh-readonly-queue/next/') &&
item.path.split('@', 1)[0] === '.github/workflows/castiron-custom-code.yml' &&
Number.isSafeInteger(item.id) && item.id > 0);
matches.sort((a, b) => b.id - a.id);
// A running evaluation will check main at publication. Do not
// restart an older completed run when a newer one is in flight.
if (matches[0]?.status === 'completed') {
await github.rest.actions.reRunWorkflow({...context.repo, run_id: matches[0].id});
Comment thread
jbeckwith-oai marked this conversation as resolved.
}
}
compute:
name: Compute trusted custom-code report
if: contains(fromJSON('["pull_request", "merge_group"]'), github.event.workflow_run.event) && (github.event.workflow_run.path == '.github/workflows/castiron-custom-code.yml' || startsWith(github.event.workflow_run.path, '.github/workflows/castiron-custom-code.yml@'))
Expand All @@ -355,6 +446,7 @@ jobs:
pull-requests: read
outputs:
selected-policy-sha: ${{ steps.select-policy.outputs.policy_sha }}
authenticated-main-sha: ${{ steps.select-policy.outputs.authenticated_main_sha }}
main-snapshot: ${{ steps.select-policy.outputs.main_snapshot }}
selected-base-sha: ${{ steps.select-policy.outputs.base_sha }}
selected-head-sha: ${{ steps.select-policy.outputs.head_sha }}
Expand Down Expand Up @@ -393,9 +485,11 @@ jobs:
GIT_CONFIG_KEY_1: credential.https://github.com.helper
GIT_CONFIG_VALUE_1: '!gh auth git-credential'
run: |
trusted_main="$(git rev-parse HEAD)"
printf 'authenticated_main_sha=%s\n' "$trusted_main" >> "$GITHUB_OUTPUT"
python3 -I scripts/castiron/custom_code_budget.py select-policy \
--repository "$REPOSITORY" --event-path "$GITHUB_EVENT_PATH" \
--trusted-sha "$(git rev-parse HEAD)" \
--trusted-sha "$trusted_main" \
--repo "$RUNNER_TEMP/castiron-policy-objects.git" \
--out "$RUNNER_TEMP/castiron-policy-selection.json"
Expand Down Expand Up @@ -505,6 +599,7 @@ jobs:
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
SELECTED_POLICY_SHA: ${{ needs.compute.outputs.selected-policy-sha }}
AUTHENTICATED_MAIN_SHA: ${{ needs.compute.outputs.authenticated-main-sha }}
MAIN_SNAPSHOT: ${{ needs.compute.outputs.main-snapshot }}
SELECTED_BASE_SHA: ${{ needs.compute.outputs.selected-base-sha }}
SELECTED_HEAD_SHA: ${{ needs.compute.outputs.selected-head-sha }}
Expand Down Expand Up @@ -629,7 +724,7 @@ jobs:
if (!(typeof value === 'string' || Number.isSafeInteger(value)) ||
!/^[1-9][0-9]*$/.test(String(value))) throw new Error('Invalid review identity');
const identity = String(value);
if (review.state === 'PENDING' || !review.author) continue;
if (['PENDING', 'COMMENTED'].includes(review.state) || !review.author) continue;
const previous = latest.get(review.author.login);
if (!previous || BigInt(identity) > BigInt(previous.identity)) latest.set(review.author.login, {...review, identity});
}
Expand Down Expand Up @@ -680,6 +775,16 @@ jobs:
const {data: target} = await github.rest.git.getRef({...context.repo, ref: 'heads/next'});
fresh = fresh && base === target.object.sha;
}
// Main pushes invalidate next evaluations, even when their reviewed
// checker pin stays unchanged. Do not restore success from a run
// computed before that invalidation. This is the reporter checkout,
// not the independently reviewed (and possibly older) policy pin.
if (branch === 'next') {
const authenticatedMain = process.env.AUTHENTICATED_MAIN_SHA || '';
const {data: currentMain} = await github.rest.git.getRef({...context.repo, ref: 'heads/main'});
fresh = fresh && /^[0-9a-f]{40}$/.test(authenticatedMain) &&
currentMain.object.sha === authenticatedMain;
}
// Bootstrap may adopt only the still-current authenticated main tree.
// This value comes from our selector, never from candidate artifacts.
if (process.env.MAIN_SNAPSHOT) {
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/castiron-custom-code.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
name: Castiron custom code

on:
push:
branches: [main]
pull_request:
types: [opened, synchronize, reopened, ready_for_review, edited]
pull_request_review:
Expand All @@ -18,11 +20,11 @@ concurrency:
cancel-in-progress: false

env:
REPORTER_SHA256: c243015818640b1e7068605c5352fcb770d406202ee4e75316fe0ca85d06a1cc
REPORTER_SHA256: ddb15c9a162d26a6c9abf1b4fefcd80f2e685d9d255af729863258b7ecd08e2d

jobs:
queue-signal:
if: github.event_name == 'merge_group' || github.event_name == 'pull_request_review'
if: github.event_name == 'merge_group' || github.event_name == 'pull_request_review' || github.event_name == 'push'
runs-on: ubuntu-latest
timeout-minutes: 1
permissions: {}
Expand Down
26 changes: 22 additions & 4 deletions api_reference/openapi.transformed.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74734,7 +74734,8 @@ components:
required:
- mode
title: Prompt cache breakpoint
description: Marks the exact end of a reusable prompt prefix. The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block.
description: Marks the exact end of a reusable prompt prefix. When `ttl` is omitted, the breakpoint inherits the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block.
x-stainless-go-constant-constructor: true
ModerationMode:
type: string
enum:
Expand Down Expand Up @@ -74964,7 +74965,8 @@ components:
required:
- mode
title: Prompt cache breakpoint
description: Marks the exact end of a reusable prompt prefix. The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block.
description: Marks the exact end of a reusable prompt prefix. When `ttl` is omitted, the breakpoint inherits the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block.
x-stainless-go-constant-constructor: true
InputTextContent:
properties:
type:
Expand All @@ -74979,6 +74981,7 @@ components:
description: The text input to the model.
prompt_cache_breakpoint:
$ref: '#/components/schemas/PromptCacheBreakpointConfig'
x-stainless-go-constant-constructor: true
type: object
required:
- type
Expand Down Expand Up @@ -75254,6 +75257,7 @@ components:
description: The detail level of the image to be sent to the model. One of `high`, `low`, `auto`, or `original`. Defaults to `auto`.
prompt_cache_breakpoint:
$ref: '#/components/schemas/PromptCacheBreakpointConfig'
x-stainless-go-constant-constructor: true
type: object
required:
- type
Expand Down Expand Up @@ -75285,6 +75289,7 @@ components:
description: The detail level of the screenshot image to be sent to the model. One of `high`, `low`, `auto`, or `original`. Defaults to `auto`.
prompt_cache_breakpoint:
$ref: '#/components/schemas/PromptCacheBreakpointConfig'
x-stainless-go-constant-constructor: true
type: object
required:
- type
Expand Down Expand Up @@ -75322,6 +75327,7 @@ components:
The content of the file to be sent to the model.
prompt_cache_breakpoint:
$ref: '#/components/schemas/PromptCacheBreakpointConfig'
x-stainless-go-constant-constructor: true
file_url:
type: string
format: uri
Expand Down Expand Up @@ -77314,6 +77320,7 @@ components:
anyOf:
- $ref: '#/components/schemas/PromptCacheBreakpointParam'
- type: 'null'
x-stainless-go-constant-constructor: true
type: object
required:
- type
Expand Down Expand Up @@ -77351,6 +77358,7 @@ components:
anyOf:
- $ref: '#/components/schemas/PromptCacheBreakpointParam'
- type: 'null'
x-stainless-go-constant-constructor: true
type: object
required:
- type
Expand Down Expand Up @@ -77401,6 +77409,7 @@ components:
anyOf:
- $ref: '#/components/schemas/PromptCacheBreakpointParam'
- type: 'null'
x-stainless-go-constant-constructor: true
type: object
required:
- type
Expand Down Expand Up @@ -99861,6 +99870,7 @@ components:
The content of the file to be sent to the model.
prompt_cache_breakpoint:
$ref: '#/components/schemas/BetaPromptCacheBreakpointConfig'
x-stainless-go-constant-constructor: true
file_url:
type: string
format: uri
Expand Down Expand Up @@ -99898,7 +99908,8 @@ components:
required:
- mode
title: Prompt cache breakpoint
description: Marks the exact end of a reusable prompt prefix. The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block.
description: Marks the exact end of a reusable prompt prefix. When `ttl` is omitted, the breakpoint inherits the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block.
x-stainless-go-constant-constructor: true
BetaInputImageContent:
properties:
type:
Expand All @@ -99924,6 +99935,7 @@ components:
description: The detail level of the image to be sent to the model. One of `high`, `low`, `auto`, or `original`. Defaults to `auto`.
prompt_cache_breakpoint:
$ref: '#/components/schemas/BetaPromptCacheBreakpointConfig'
x-stainless-go-constant-constructor: true
type: object
required:
- type
Expand Down Expand Up @@ -99951,6 +99963,7 @@ components:
description: The text input to the model.
prompt_cache_breakpoint:
$ref: '#/components/schemas/BetaPromptCacheBreakpointConfig'
x-stainless-go-constant-constructor: true
type: object
required:
- type
Expand Down Expand Up @@ -101731,6 +101744,7 @@ components:
anyOf:
- $ref: '#/components/schemas/BetaPromptCacheBreakpointParam'
- type: 'null'
x-stainless-go-constant-constructor: true
type: object
required:
- type
Expand All @@ -101755,7 +101769,8 @@ components:
required:
- mode
title: Prompt cache breakpoint
description: Marks the exact end of a reusable prompt prefix. The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block.
description: Marks the exact end of a reusable prompt prefix. When `ttl` is omitted, the breakpoint inherits the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block.
x-stainless-go-constant-constructor: true
BetaDetailEnum:
type: string
enum:
Expand All @@ -101780,6 +101795,7 @@ components:
anyOf:
- $ref: '#/components/schemas/BetaPromptCacheBreakpointParam'
- type: 'null'
x-stainless-go-constant-constructor: true
type: object
required:
- type
Expand Down Expand Up @@ -101899,6 +101915,7 @@ components:
anyOf:
- $ref: '#/components/schemas/BetaPromptCacheBreakpointParam'
- type: 'null'
x-stainless-go-constant-constructor: true
type: object
required:
- type
Expand Down Expand Up @@ -104224,6 +104241,7 @@ components:
description: The detail level of the screenshot image to be sent to the model. One of `high`, `low`, `auto`, or `original`. Defaults to `auto`.
prompt_cache_breakpoint:
$ref: '#/components/schemas/BetaPromptCacheBreakpointConfig'
x-stainless-go-constant-constructor: true
type: object
required:
- type
Expand Down
16 changes: 10 additions & 6 deletions scripts/castiron/custom_code_budget.py
Original file line number Diff line number Diff line change
Expand Up @@ -737,11 +737,17 @@ def prepare_main_snapshot(
"""Create and verify an adoption commit without altering a ref, index or metadata."""
base, authenticated_main = map(report.require_sha, (base, authenticated_main))
strict = {"schema_version": 2, "max_custom_patch_lines": 0, "generated_files": "strict"}
if read_policy(repo, authenticated_main) != strict or "next_policy" in read_policy(repo, base):
main_policy = read_policy(repo, authenticated_main)
if {
key: value for key, value in main_policy.items() if key != "ratchet"
} != strict or "next_policy" in read_policy(repo, base):
raise ValueError("main snapshot preparation requires strict-zero main and unpinned next")
if policy.merge_base(repo, base, authenticated_main) != base:
raise ValueError("protected next must be an ancestor of authenticated main")
value = {**strict, "next_policy": {"repository": repository, "main_sha": authenticated_main}}
value = {
**main_policy,
"next_policy": {"repository": repository, "main_sha": authenticated_main},
}
blob = report.git(
repo,
"hash-object",
Expand Down Expand Up @@ -814,10 +820,8 @@ def prepare_next_baseline(repo: Path, repository: str, branch: str) -> dict[str,
if not report.REPOSITORY.fullmatch(repository) or not branch.startswith("codex/"):
raise ValueError("expected a repository and a new codex/ review branch")
report.git(repo, "check-ref-format", "--branch", branch)
if (
report.git(repo, "remote", "get-url", "origin").decode().strip()
!= f"https://github.com/{repository}.git"
):
origin = report.git(repo, "remote", "get-url", "origin").decode().strip()
if origin not in report.github_origin_urls(repository):
raise ValueError("preparation requires the public repository as origin")
root = f"repos/{repository}"
metadata = read_public_api(root)
Expand Down
Loading
Loading