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
43 changes: 43 additions & 0 deletions .github/CODEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,43 @@ that PR as closed rather than merged. Staging alone never closes a PR, and a
closure failure cannot undo an otherwise successful publication. A later
change to the same topic needs another topic PR.

## Pull request labels

Topic pull requests are review-only: approve them, but do not merge them.
Automatic add/alter plans, human remove/reorder plans, and ordinary controller
changes are distinct `meta` changes. Every open PR against `codex`,
`codex-unstable`, or `meta`, plus retained plan history, receives one role,
one build, and one lifecycle state:

- `kind:review-only`, `kind:auto-plan`, `kind:plan-policy`, or
`kind:controller` identifies why the PR exists.
- `build:codex-stable`, `build:codex-unstable`, or
`build:codex-controller` identifies what it affects.
- `codex:draft`, `codex:needs-review`, `codex:ready`,
`codex:awaiting-plan`, `codex:planned`, `codex:staged`,
`codex:integrated`, or `codex:superseded` identifies its lifecycle state.
- `codex:blocked` is additional when an invalid topic name, failed admission,
merge conflict, or merge policy prevents the PR from advancing.

Labels are derived presentation, not admission or release policy. A topic is
planned only when its exact current head is recorded in its build plan, and
integrated only when `codex.config` records that same source head and its
output tip matches the live build branch. Staging a rebased topic requires
the frozen generation's verified candidate ledger; a rewritten integration
commit is not confused with the reviewed source commit. A moved source head
falls back to review or admission instead of inheriting an earlier state. The
reconciler refuses missing, duplicate, or contradictory classifications before
writing any labels.

The trusted default-branch scanner refreshes labels periodically. The local
publisher refreshes them after each candidate is staged and again after
atomic promotion; presentation failures warn but never change publication.
To inspect the current projection without changing labels:

```sh
Meta/codex reconcile-pr-state --dry-run
```

Remove and reorder are policy decisions rather than projections of a reviewed
topic head. Run **Actions > Refresh codex > Run workflow** with
`operation=remove` or `operation=reorder`; the resulting plan PR needs the
Expand Down Expand Up @@ -165,6 +202,7 @@ default-branch trampoline:
- dispatches ordinary refresh;
- scans approved topic PRs from the trusted default branch and creates
one automatic add/alter proposal at a time;
- reconciles derived PR labels from the trusted `meta` controller;
- offers explicit remove/reorder dispatch inputs; and
- runs plan admission through `pull_request_target` while loading the
reusable implementation from `meta`.
Expand All @@ -174,6 +212,11 @@ that trampoline. During migration, the controller accepts the previous
trampoline as published history, but it refuses the first v3 refresh until the
plan pins the current trampoline.

The label-aware trampoline is a backward-compatible upgrade: the already
published pinned-plan trampoline remains valid while its reviewed automation
topic is updated. Once the label-aware trampoline is published, moving back to
the earlier version is rejected.

No topic merge ref runs the pinning path. The default-branch scanner checks
each open approved PR with the trusted `meta` controller, skips tips already
present in the active plans or represented by an open plan PR, and only then
Expand Down
62 changes: 61 additions & 1 deletion .github/workflows/codex-branch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ usage () {
or: codex-branch validate-topic-review
--pull-request <number> --lane <codex|codex-unstable>
--topic <branch> --source-tip <oid>
or: codex-branch reconcile-pr-state [--expected-meta <oid>]
[--inputs <path> --updates <path>] [--dry-run]
or: codex-branch propose-plan [--remote <remote>]
--lane <codex|codex-unstable> --topic <branch>
[--source-tip <oid>] [--review-pr <number>]
Expand Down Expand Up @@ -330,6 +332,8 @@ legacy_control_paths_unchanged () (
.github/workflows/codex-admission.yml \
.github/workflows/codex-plan-admission.yml \
.github/workflows/codex-plan-propose.yml \
.github/workflows/codex-pr-state.sh \
.github/workflows/codex-pr-state.yml \
.github/workflows/codex-topic.yml \
.github/workflows/codex.yml \
.github/workflows/codex-branch.sh \
Expand Down Expand Up @@ -358,6 +362,8 @@ meta_control_paths_unchanged () (
.github/workflows/codex-admission.yml \
.github/workflows/codex-plan-admission.yml \
.github/workflows/codex-plan-propose.yml \
.github/workflows/codex-pr-state.sh \
.github/workflows/codex-pr-state.yml \
.github/workflows/codex-topic.yml \
.github/workflows/codex-branch.sh \
.github/workflows/main.yml \
Expand Down Expand Up @@ -621,9 +627,25 @@ jobs:
contents: read
pull-requests: write
uses: openai/git/.github/workflows/codex-plan-admission.yml@meta
pr_state:
name: Reconcile Codex pull request state
if: >-
github.event_name == 'schedule' ||
(github.event_name == 'workflow_dispatch' &&
github.ref == 'refs/heads/codex' &&
inputs.operation == 'scan')
permissions:
contents: read
issues: write
pull-requests: write
uses: openai/git/.github/workflows/codex-pr-state.yml@meta
EOF
}

write_previous_pinned_automation_workflow () {
write_automation_workflow | sed '/^ pr_state:$/,$d'
}

write_previous_automation_workflow () {
cat <<-'EOF'
name: Refresh codex
Expand Down Expand Up @@ -730,7 +752,7 @@ write_legacy_automation_workflow () {
EOF
}

automation_workflow_is_current () {
automation_workflow_is_latest () {
head_oid=$1
make_tmp_dir
git show "$head_oid:.github/workflows/codex.yml" \
Expand All @@ -740,6 +762,17 @@ automation_workflow_is_current () {
"$tmp_dir/actual-automation.yml"
}

automation_workflow_is_current () {
if automation_workflow_is_latest "$1"
then
return 0
fi
write_previous_pinned_automation_workflow \
>"$tmp_dir/expected-automation.yml"
cmp -s "$tmp_dir/expected-automation.yml" \
"$tmp_dir/actual-automation.yml"
}

automation_workflow_is_reviewed () {
head_oid=$1
if automation_workflow_is_current "$head_oid"
Expand Down Expand Up @@ -7995,6 +8028,8 @@ topic_control_paths_unchanged () (
.github/workflows/codex-admission.yml \
.github/workflows/codex-plan-admission.yml \
.github/workflows/codex-plan-propose.yml \
.github/workflows/codex-pr-state.sh \
.github/workflows/codex-pr-state.yml \
.github/workflows/codex-topic.yml \
.github/workflows/codex.yml \
.github/workflows/codex-branch.sh \
Expand Down Expand Up @@ -8136,6 +8171,11 @@ verify_control_paths () {
release_publication_controls_preserved "$published_codex" \
"$candidate" ||
die "candidate changes the controller-only release publication guard"
if automation_workflow_is_latest "$published_codex" &&
! automation_workflow_is_latest "$candidate"
then
die "candidate downgrades the canonical Codex admission workflow"
fi
if automation_workflow_is_current "$published_codex" &&
! automation_workflow_is_current "$candidate"
then
Expand Down Expand Up @@ -9540,6 +9580,19 @@ prepare_local_candidate () {
freeze_local_candidate "$session" "$local_candidate_dir"
}

reconcile_candidate_pr_state () (
inputs=$1
updates=$2
helper=$script_dir/codex-pr-state.sh
test -f "$helper" || return 0
if ! sh "$helper" --inputs "$inputs" --updates "$updates"
then
printf '%s\n' \
'warning: could not reconcile derived Codex pull request labels' \
>&2
fi
)

stage_and_wait_for_ci () {
repository=$1
candidate=$2
Expand All @@ -9560,6 +9613,7 @@ stage_and_wait_for_ci () {
say "GitHub API user: $publisher"
stage_candidate --remote origin --staging "$staging" \
--inputs "$inputs" --updates "$updates" --require-automation
reconcile_candidate_pr_state "$inputs" "$updates"
wait_for_staging_ci gh "$repository" "$candidate" "$baseline" \
"$staging"
unstable_candidate=$(awk -F '\t' \
Expand All @@ -9577,6 +9631,7 @@ stage_and_wait_for_ci () {
esac
stage_candidate --remote origin --staging "$staging" \
--inputs "$inputs" --updates "$updates" --require-automation
reconcile_candidate_pr_state "$inputs" "$updates"
wait_for_staging_ci gh "$repository" "$unstable_candidate" \
"$baseline" "$staging"
fi
Expand Down Expand Up @@ -9690,6 +9745,8 @@ rebuild_codex_locally () {
--inputs "$local_candidate_dir/codex-inputs" \
--updates "$local_candidate_dir/codex-updates" \
--require-automation
reconcile_candidate_pr_state "$local_candidate_dir/codex-inputs" \
"$local_candidate_dir/codex-updates"
say "Published codex candidate $candidate from local preparation session $session."
if ! close_published_topic_reviews "$controller_oid" \
"$local_candidate_dir/codex-updates"
Expand Down Expand Up @@ -9814,6 +9871,8 @@ publish_run () {
promote --remote origin --staging codex-staging \
--inputs "$metadata/codex-inputs" \
--updates "$metadata/codex-updates" --require-automation
reconcile_candidate_pr_state "$metadata/codex-inputs" \
"$metadata/codex-updates"
say "Published codex candidate $artifact_candidate from Actions run $run_id."
if ! close_published_topic_reviews "$run_controller" \
"$metadata/codex-updates"
Expand Down Expand Up @@ -10125,6 +10184,7 @@ verify-inputs) verify_inputs "$@" ;;
validate-plan-transition) validate_plan_transition "$@" ;;
test-validate-plan-transition) validate_plan_transition_fixture "$@" ;;
validate-topic-review) validate_topic_review "$@" ;;
reconcile-pr-state) sh "$script_dir/codex-pr-state.sh" "$@" ;;
propose-plan) propose_plan "$@" ;;
recover-release-pin) recover_release_pin "$@" ;;
test-recover-release-pin) recover_release_pin_fixture "$@" ;;
Expand Down
Loading