-
Notifications
You must be signed in to change notification settings - Fork 1.2k
[WRONG BRANCH] promote dev to main for the v2.37.0 release #3037
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ce883a5
bd21269
c2778ca
73eb88b
df8b388
f5a625c
6f75616
1031b6f
870a2ad
afc1cc5
7666f7d
4bdc0f6
4180067
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,170 @@ | ||
| name: Dev version bump | ||
|
|
||
| # When a release publishes, open a pull request that moves `dev` past the published | ||
| # version. Without this, `dev` keeps carrying a version that is at or behind a released | ||
| # one, and `tests/release-version-line.test.ts` fails on `dev` and on every pull request | ||
| # opened against it - inherited red a contributor cannot fix from their own diff. | ||
| # | ||
| # That has been repaired by hand four times: 32529c2b2, e4a85d134, 076ad3036, befcac3e1. | ||
| # The second of those ADDED the detector and two more repairs followed it, so more | ||
| # visibility was never the missing piece; a prepared change was. | ||
| # | ||
| # WHAT THIS DOES NOT DO. It does not push to `dev`. It opens a pull request and a human | ||
| # merges it, because ruleset `Protect dev` requires an approving review and code-owner | ||
| # sign-off that a bot cannot supply. Until that merge the red persists. This converts a | ||
| # forgotten chore into a queued, reviewable change - not into an automatic repair. | ||
| # | ||
| # A `release` event resolves this workflow file from the repository DEFAULT branch | ||
| # (`main`), not from `dev` - the same trap documented in cleanup-closed-pr-branches.yml. | ||
| # So merging this file to `dev` installs it but arms nothing; it first fires after an | ||
| # ordinary dev -> main promotion carries it there. | ||
| # | ||
| # There is deliberately no `workflow_dispatch`: a branch-selected manual run executes | ||
| # THAT branch body with `contents: write`. Re-drive a missed run by running | ||
| # `bun scripts/bump-dev-version.ts <released> package.json` locally and opening the pull | ||
| # request normally. | ||
| on: | ||
| release: | ||
| types: [published] | ||
|
|
||
| permissions: {} | ||
|
|
||
| concurrency: | ||
| group: dev-version-bump | ||
| cancel-in-progress: false | ||
|
|
||
| jobs: | ||
| open-bump-pr: | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| # Push the new codex/dev-version-* branch. Ruleset `Protect dev` covers only | ||
| # refs/heads/dev, so the bump branch is unprotected and this token cannot | ||
| # bypass dev review. It is the ruleset that keeps this job off dev, not the | ||
| # permission name. | ||
| contents: write | ||
| # Open the pull request. | ||
| pull-requests: write | ||
| steps: | ||
| - name: Checkout dev | ||
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 | ||
| with: | ||
| ref: dev | ||
| # Tags are load-bearing, not decoration: the freeness gate below is a bun | ||
| # test that reads the local tag set, and release-version-line.test.ts | ||
| # returns EARLY on an empty set. A shallow checkout would make that gate | ||
| # silently vacuous instead of failing loudly. | ||
| fetch-depth: 0 | ||
| # Do NOT set persist-credentials: false here as the read-only workflows do. | ||
| # This job has to push its bump branch. | ||
|
|
||
| # The repository-owned composite action, not a hand-pinned setup-bun SHA: it | ||
| # resolves the Bun version from package.json so the runtime SOT stays in one | ||
| # place. An independently pinned action here would drift from every other job. | ||
| - name: Setup project Bun | ||
| uses: ./.github/actions/setup-project-bun | ||
|
|
||
| - name: Install dependencies | ||
| run: bun install --frozen-lockfile | ||
|
|
||
| - name: Decide the version dev should carry | ||
| id: decide | ||
| env: | ||
| RELEASED_VERSION: ${{ github.event.release.tag_name }} | ||
| run: | | ||
| set -euo pipefail | ||
| bun scripts/bump-dev-version.ts "${RELEASED_VERSION}" package.json | ||
|
|
||
| - name: Prove the chosen version is unused | ||
| if: ${{ steps.decide.outputs.changed == 'true' }} | ||
| # The script decides the candidate from the released version SHAPE, which is all | ||
| # a pure function can see. Whether that candidate is actually FREE is a property | ||
| # of the tag set, so it is settled here by the detector that already owns the | ||
| # question. If this fails, no pull request is opened and the job goes red asking | ||
| # for a human decision - which is the correct outcome, not a fallback. | ||
| run: bun test tests/release-version-line.test.ts | ||
|
|
||
| - name: Open the bump pull request | ||
| if: ${{ steps.decide.outputs.changed == 'true' }} | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| NEXT_VERSION: ${{ steps.decide.outputs.version }} | ||
| RELEASED_VERSION: ${{ github.event.release.tag_name }} | ||
| run: | | ||
| set -euo pipefail | ||
|
|
||
| branch="codex/dev-version-${NEXT_VERSION}" | ||
|
|
||
| # Idempotent: a second publish, a re-run, or a manual repair must not turn a | ||
| # successful release into a red job. | ||
| # | ||
| # Check the PULL REQUEST as well as the branch, not just the branch. A security | ||
| # review caught that: an open bump pull request whose head branch was deleted | ||
| # leaves the branch check passing, so the job would recreate the branch and then | ||
| # fail on `gh pr create` with "already exists" — turning a successful release red | ||
| # for a repair that was already queued. | ||
| open_prs="$(gh pr list --base dev --head "${branch}" --state open --json number --jq 'length')" | ||
| if [ "${open_prs}" != "0" ]; then | ||
| echo "::notice::a bump pull request for ${branch} is already open; nothing to do" | ||
| exit 0 | ||
| fi | ||
|
|
||
| # An existing branch is NOT terminal. If a previous run pushed the branch and then | ||
| # failed at `gh pr create`, exiting here would leave the repair permanently unqueued | ||
| # while every rerun reports success - the exact failure mode a reviewer caught. So | ||
| # reuse the branch and fall through to pull-request creation instead. | ||
| if git ls-remote --exit-code --heads origin "${branch}" >/dev/null 2>&1; then | ||
| echo "::notice::${branch} exists without an open pull request; validating it" | ||
| git fetch origin "${branch}" | ||
|
|
||
| # Fail closed on unexpected content. The branch carries the bot's own one-line | ||
| # bump, so anything else on it means a human or another job is using that name and | ||
| # this job must not push to it or open a pull request from it. | ||
| changed_files="$(git diff --name-only "origin/dev...origin/${branch}")" | ||
| if [ "${changed_files}" != "package.json" ]; then | ||
| echo "::error::${branch} touches unexpected files: ${changed_files:-<none>}" | ||
| exit 1 | ||
| fi | ||
| branch_version="$(git show "origin/${branch}:package.json" | node -p "JSON.parse(require('fs').readFileSync(0,'utf8')).version")" | ||
| if [ "${branch_version}" != "${NEXT_VERSION}" ]; then | ||
| echo "::error::${branch} carries ${branch_version}, expected ${NEXT_VERSION}" | ||
| exit 1 | ||
| fi | ||
| git checkout -B "${branch}" "origin/${branch}" | ||
| else | ||
| git config user.name "github-actions[bot]" | ||
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | ||
| git checkout -b "${branch}" | ||
| git add package.json | ||
| git commit -m "fix(release): move dev to ${NEXT_VERSION} after ${RELEASED_VERSION}" | ||
| git push origin "${branch}" | ||
| fi | ||
|
|
||
| gh pr create \ | ||
| --base dev \ | ||
| --head "${branch}" \ | ||
| --title "fix(release): move dev to ${NEXT_VERSION} after ${RELEASED_VERSION}" \ | ||
| --body "$(cat <<BODY | ||
| ## Summary | ||
|
|
||
| \`${RELEASED_VERSION}\` published, so \`dev\` would otherwise keep a version at or | ||
| behind a released one and \`tests/release-version-line.test.ts\` would fail on | ||
| \`dev\` and on every pull request opened against it. This moves \`dev\` to | ||
| \`${NEXT_VERSION}\`. | ||
|
|
||
| Opened automatically by \`.github/workflows/dev-version-bump.yml\`. The same | ||
| repair was previously done by hand in 32529c2b2, e4a85d134, 076ad3036, and | ||
| befcac3e1. | ||
|
|
||
| ## Verification | ||
|
|
||
| \`bun test tests/release-version-line.test.ts\` ran against this exact tree | ||
| before the pull request was opened; the workflow refuses to open one if the | ||
| chosen version collides with a published release. | ||
|
|
||
| ## Checklist | ||
|
|
||
| - [x] Scope stays focused and avoids unrelated cleanup. | ||
| - [x] Docs or release notes were updated when needed. | ||
| - [x] Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults. | ||
| BODY | ||
| )" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -73,6 +73,21 @@ when a maintainer steps down. | |
| - Direct pushes are reserved for maintainer-owned integration work, urgent repairs, or incident | ||
| recovery. The same CI and documentation requirements still apply. | ||
| - Promotion from `dev` to `main` and npm releases is maintainer-controlled. | ||
| - **Closing out a release includes moving `dev`'s version line forward.** A published | ||
| release leaves `dev` carrying a version at or behind it, and | ||
| `tests/release-version-line.test.ts` then fails on `dev` and on every pull request | ||
| opened against it — red that contributors inherit and cannot fix from their own diff. | ||
| This was repaired by hand four times (`32529c2b2`, `e4a85d134`, `076ad3036`, | ||
| `befcac3e1`) before it was automated. | ||
|
|
||
| `.github/workflows/dev-version-bump.yml` now opens that bump as a pull request when a | ||
| release publishes. Merging it is part of closing the release; a bot cannot, because | ||
| `Protect dev` requires an approving review and code-owner sign-off. Two caveats worth | ||
| knowing: the workflow runs from the DEFAULT branch, so it only fires once it has been | ||
| promoted to `main`; and a pull request opened with `GITHUB_TOKEN` does not start | ||
| `pull_request` workflows, so the bump pull request arrives without CI. To re-drive a | ||
| missed run by hand: `bun scripts/bump-dev-version.ts <released-version> package.json`, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Run the tag-aware gate during manual recovery. The command at line 89 computes a candidate without reading tags. A delayed re-drive can therefore write a version that is already published. The workflow prevents this by running Require the same test to pass before opening the manual PR. - missed run by hand: `bun scripts/bump-dev-version.ts <released-version> package.json`,
- then open the pull request normally.
+ missed run by hand: `bun scripts/bump-dev-version.ts <released-version> package.json`,
+ then run `bun test tests/release-version-line.test.ts`. Open the pull request only
+ if that test passes.🤖 Prompt for AI Agents |
||
| then open the pull request normally. | ||
|
|
||
| ## The retired `dev2-go` line | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,139 @@ | ||
| # Kiro post-final-answer tool calls — measurement and root cause | ||
|
|
||
| Reported symptom, twice: routed through Kiro, the agent keeps issuing tool calls | ||
| after its final response has already been delivered. | ||
|
|
||
| ## Hosts measured | ||
|
|
||
| | Host | Proxy | Version | Checkout | Kiro attempt rows | | ||
| | --- | --- | --- | --- | --- | | ||
| | local (this machine) | PID 99470, port 10100 | 2.36.0 | primary source checkout | 4080 | | ||
| | `macmini-cf` | PID 96671, port 10100 | 2.35.0 | `~/opencodex` | 0 | | ||
|
|
||
| `macmini-cf` carries no Kiro attempt diagnostics at all, so every behavioral | ||
| row below comes from the local 2.36.0 proxy. The remote host is one release | ||
| behind and is not the reporting surface. | ||
|
|
||
| ## What the attempt rows say | ||
|
|
||
| `ocx:kiro:attempt_complete` over the local log, bucketed: | ||
|
|
||
| | Count | mode | sawText | sawRealTool | completionCalls | stopReason | | ||
| | --- | --- | --- | --- | --- | --- | | ||
| | 2643 | required | true | true | 0 | TOOL_USE | | ||
| | 1400 | required | false | true | 0 | TOOL_USE | | ||
| | 23 | required | true | false | 1 | TOOL_USE | | ||
| | 10 | disabled | true | false | 0 | END_TURN | | ||
| | 2 | required | false | false | 1 | TOOL_USE | | ||
| | 1 | required | true | false | 0 | END_TURN | | ||
| | 1 | text_fallback | false | false | 1 | TOOL_USE | | ||
|
|
||
| 4069 of 4080 attempts ran in `required` mode and every one of them ended with | ||
| upstream `stopReason: TOOL_USE`. Only 25 attempts ever called the private | ||
| completion tool. The model overwhelmingly prefers another tool call to the | ||
| completion channel. | ||
|
|
||
| ## What is NOT the cause | ||
|
|
||
| Two candidate mechanisms were ruled out with evidence rather than reading. | ||
|
|
||
| Replayed history is not the cause. 532 client rollouts under | ||
| `~/.codex/sessions/2026/08/{29,30}` were scanned for a `final_answer` message | ||
| followed by a tool call with no intervening user turn. What actually follows a | ||
| recorded `final_answer`: END 478, user message 131, developer message 5, tool | ||
| call 0. The client never replays a post-answer tool call. | ||
|
|
||
| The delivered-answer local terminal is not broken. Two live probes against the | ||
| running proxy replayed a closed turn — once with `phase: "final_answer"` | ||
| echoed, once without it, matching real Codex traffic — and both returned | ||
| `output: []` with `end_turn: true` and added zero upstream Kiro requests. | ||
| The guard added in `b557a8140`/`68eaf45d8` works. | ||
|
|
||
| It has simply never been needed: `~/.opencodex/usage.jsonl` holds 25042 Kiro | ||
| rows with zero `localTerminalReason` and zero `locallyAnswered`. Real turns | ||
| never arrive already closed, because the client ends the turn itself. So the | ||
| defect lives inside a live turn, not across turns. | ||
|
|
||
| ## Rejected first hypothesis | ||
|
|
||
| The first diagnosis was that the model calls the completion tool, waits for a | ||
| tool result that never arrives, and then calls another tool. An independent | ||
| read-only audit refuted it with the parser: `flushOpen` consumes a valid | ||
| completion call and records `completionAnswer` without emitting any tool-call | ||
| event, the stream end yields the answer as `final_answer` followed by | ||
| `done(endTurn: true)`, and `parseKiroStream` returns without another request. | ||
| A completion call therefore terminates locally inside one inference; there is | ||
| no later inference in which the model could "keep going". Mixed | ||
| completion-plus-real-tool output in one inference also fails closed before any | ||
| answer is delivered. | ||
|
|
||
| That refutation is correct, and it narrows the defect rather than dissolving it: | ||
| the problem is not what happens AFTER a completion call, it is that the model | ||
| mostly never makes one. | ||
|
|
||
| ## Root cause | ||
|
|
||
| The private completion tool is advertised to the model as an ordinary tool. | ||
|
|
||
| A source probe (`buildKiroPayload` with an `exec`/`wait` catalog) renders the | ||
| wire tool names as `["exec","wait","codex_kiro_final_answer"]` and injects: | ||
|
|
||
| > Valid tool names for this turn are exactly \`exec\`, \`wait\`, | ||
| > \`codex_kiro_final_answer\`. These listed names are the complete top-level | ||
| > tool-call surface for this turn. | ||
|
|
||
| That sentence comes from the shared, provider-agnostic nudge in | ||
| `src/adapters/tool-catalog-nudge.ts`, which knows nothing about completion | ||
| semantics. It cannot distinguish the proxy's private terminal channel from | ||
| `exec`, and the same nudge closes with: | ||
|
|
||
| > Count a tool call only after its tool result returns. | ||
|
|
||
| `KIRO_COMPLETION_INSTRUCTIONS` is the only text that describes the completion | ||
| tool, and it never contradicts that: | ||
|
|
||
| > When tools are available, ordinary assistant text is mid-task commentary and | ||
| > does not end the turn. Continue using tools after progress updates. When the | ||
| > task is fully complete and no more tool calls are needed, call | ||
| > `codex_kiro_final_answer` exactly once with the complete user-facing final | ||
| > answer in `answer`. Do not provide the final answer as ordinary assistant | ||
| > text. | ||
|
|
||
| Every sentence there is about WHEN to call it. Nothing marks it as different in | ||
| kind from `exec`, and nothing states what happens after. So the model holds a | ||
| contract in which the terminal channel is one more ordinary tool it may defer | ||
| while it keeps working — and the generic nudge's "count a tool call only after | ||
| its tool result returns" applies to it as uniformly as to everything else. | ||
|
|
||
| The failure that follows is one of SELECTION, not sequencing. Across 4069 | ||
| required-mode attempts the completion tool was chosen 25 times: 0.6%. The model | ||
| keeps emitting finished prose as commentary and calling ordinary tools instead | ||
| of completing through the channel built for it. | ||
|
|
||
| That is what the user sees. Measured over 1116 Kiro turns in the same two days | ||
| of client rollouts: 626 turns ended through the completion channel, 462 ended | ||
| on a tool call, and 28 ended with answer-shaped commentary prose and no | ||
| completion call at all. Those 28 are answers the model had already finished | ||
| writing — they open with "Done.", "완료", "머지까지 끝났습니다", "All ten items are | ||
| done" — delivered as mid-task commentary, which by the proxy's own contract | ||
| "does not end the turn". Three of them are followed by 4, 10, and 12 further | ||
| tool calls after the closing summary was already on screen. | ||
|
|
||
| The missing terminal distinction is the leading mechanism behind that measured | ||
| selection failure: the terminal channel is advertised as an ordinary, deferrable | ||
| tool, and nothing tells the model that this is the one call that ends the turn. | ||
| It is a defect in the proxy's own injected text, not a client bug and not a | ||
| stream-parsing bug. Causality is not claimed as proven — establishing it | ||
| requires a live post-change comparison of the same selection rate, which this | ||
| unit records as the follow-up measurement rather than asserting up front. | ||
|
|
||
| ## Fix direction | ||
|
|
||
| State terminal semantics where the model reads them: calling the completion | ||
| tool ENDS the turn, returns no tool result, and nothing may follow it. The | ||
| completion tool's own schema description is the load-bearing site — it travels | ||
| with the tool the nudge enumerates — with the prose contract kept consistent. | ||
|
|
||
| Removing the tool from the enumeration is not an option: the nudge states that | ||
| names mentioned only in instructions are not callable, so an unlisted | ||
| completion tool would be a tool the model is told not to call. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Reset the generated
package.jsonbefore switching branches.Line 75 modifies
package.json. If a prior bump branch has the expected version but differs from currentdevin another package field,git checkout -Brefuses to overwrite the local modification. The workflow then fails instead of creating the missing pull request.After the remote-branch validation, discard the local generated change before the checkout.
Proposed fix
if [ "${branch_version}" != "${NEXT_VERSION}" ]; then echo "::error::${branch} carries ${branch_version}, expected ${NEXT_VERSION}" exit 1 fi + git restore --source=HEAD --staged --worktree package.json git checkout -B "${branch}" "origin/${branch}"📝 Committable suggestion
🤖 Prompt for AI Agents