[WRONG BRANCH] release: promote verified 2.52.0-preview.20260912 product tree to preview - #4399
Conversation
chore(release): open dev at 2.52.0 before releasing 2.51.0
…a-base fix(zai): route coding quota probes by canonical base
…lared bare tool (#4264) * fix(tools): normalize invented default. namespace back to declared bare tool * fix(responses): normalize default namespace to declared bare tool and track bare provenance - Downstream identity normalization in Responses relay: rewrite provider-invented default. prefix or namespace: "default" back to declared bare tool for SSE streams (added, done, terminal completed/incomplete snapshots) and non-streaming JSON responses, preserving all item fields (id, call_id, arguments). - Bare tool provenance tracking: collectDeclaredBareWireToolNames collects top-level and builtin functions namespace declarations that do not carry . or __, preventing declarations like foo__view_image from authorizing default.view_image or { namespace: "default", name: "view_image" }. - Shared normalization helper: update normalizeDeclaredToolName and guard helper docstrings to clarify default namespace normalization boundary beyond code-mode exec helpers. - Test coverage in tests/responses/responses-undeclared-tool-guard.test.ts covering bare wire collection, SSE stream item rewriting, terminal snapshot rewriting, JSON rewriting, negative controls for declared namespaced tools, and preservation of explicitly declared default. and default__ tools. * test(responses): cover 2-turn continuation caller relay and normalize function_call_arguments.done --------- Co-authored-by: chilung <b0423031@gmail.com>
…ete-unavailable fix(claude): allow deleting unavailable routes in desktop profile
* fix(claude): allow deleting unavailable routes in desktop profile
* test(claude): cover rejecting modifications to unavailable routes
* fix(tools): normalize invented default. namespace back to declared bare tool
* fix(responses): normalize default namespace to declared bare tool and track bare provenance
- Downstream identity normalization in Responses relay: rewrite provider-invented
default. prefix or namespace: "default" back to declared bare tool for SSE streams
(added, done, terminal completed/incomplete snapshots) and non-streaming JSON
responses, preserving all item fields (id, call_id, arguments).
- Bare tool provenance tracking: collectDeclaredBareWireToolNames collects top-level
and builtin functions namespace declarations that do not carry . or __, preventing
declarations like foo__view_image from authorizing default.view_image or
{ namespace: "default", name: "view_image" }.
- Shared normalization helper: update normalizeDeclaredToolName and guard helper
docstrings to clarify default namespace normalization boundary beyond code-mode
exec helpers.
- Test coverage in tests/responses/responses-undeclared-tool-guard.test.ts covering
bare wire collection, SSE stream item rewriting, terminal snapshot rewriting, JSON
rewriting, negative controls for declared namespaced tools, and preservation of
explicitly declared default. and default__ tools.
* test(responses): cover 2-turn continuation caller relay and normalize function_call_arguments.done
* fix(catalog): register the canonical Antigravity discovery RPC
google-antigravity declared liveModels but no modelDiscovery spec, so isRegistryModelDiscoveryUrl rejected its own canonical CCA URL and the TUN Fake-IP exception never applied. Relative path keeps allowBaseUrlOverride bases custom; the resolved URL is byte-identical to what buildModelsRequest already sent.
---------
Co-authored-by: chilung <b0423031@gmail.com>
…me flow-style refusals (#4268) * fix(tools): normalize invented default. namespace back to declared bare tool * fix(responses): normalize default namespace to declared bare tool and track bare provenance - Downstream identity normalization in Responses relay: rewrite provider-invented default. prefix or namespace: "default" back to declared bare tool for SSE streams (added, done, terminal completed/incomplete snapshots) and non-streaming JSON responses, preserving all item fields (id, call_id, arguments). - Bare tool provenance tracking: collectDeclaredBareWireToolNames collects top-level and builtin functions namespace declarations that do not carry . or __, preventing declarations like foo__view_image from authorizing default.view_image or { namespace: "default", name: "view_image" }. - Shared normalization helper: update normalizeDeclaredToolName and guard helper docstrings to clarify default namespace normalization boundary beyond code-mode exec helpers. - Test coverage in tests/responses/responses-undeclared-tool-guard.test.ts covering bare wire collection, SSE stream item rewriting, terminal snapshot rewriting, JSON rewriting, negative controls for declared namespaced tools, and preservation of explicitly declared default. and default__ tools. * test(responses): cover 2-turn continuation caller relay and normalize function_call_arguments.done * fix(catalog): register the canonical Antigravity discovery RPC google-antigravity declared liveModels but no modelDiscovery spec, so isRegistryModelDiscoveryUrl rejected its own canonical CCA URL and the TUN Fake-IP exception never applied. Relative path keeps allowBaseUrlOverride bases custom; the resolved URL is byte-identical to what buildModelsRequest already sent. * fix(integrations): apply DSH into an empty providers container and name flow-style refusals locatePath refused both shapes a DSH-managed file has before any provider exists: a valueless providers: key (parses as null, so isPlainRecord rejected it) and providers: {} (no plain block key, so the missing-key branch never ran). Adopt both, and report a populated flow container as its own cause instead of blaming comments that are not in the file. --------- Co-authored-by: chilung <b0423031@gmail.com>
* feat(cli): route ocx login codex to the Codex account-pool login ocx login codex|chatgpt|openai answered with the ~90-provider usage wall, which never contains the word the user typed, because the Codex pool lives behind ocx account login. Route those three spellings to that flow and name it in the usage wall, ocx help and the registry entry. * docs(cli): document the ocx login codex route and fold the review findings Audit round 1 ran four independent grok-4.6 agents over the plan and the diff. Folded: redact bare leftovers in the account login parser now that ocx login reaches it, replace a flags-survive test that could not fail with one that reads the POST body, name openai-apikey in the wall and registry details since ocx login openai no longer shows the provider list, guard a future key-provider id collision, assert the production wall and the non-Codex path. Docs: the stale ocx login chatgpt form is replaced across the English guide, seven locales and both CLI reference pages.
Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>
…lly (#4276) * docs(structure): restructure the maintainer SOT and gate it mechanically structure/ was 13 numbered files and 457KB that nothing verified. 04_transports-and-sidecars.md alone was 1,860 lines, two docs shared the number 09, 95 [Decision Log] blocks sat inline in contract text, and four paths the docs named had already been moved or deleted. No test read the folder, so none of that could be caught. Sections move verbatim into 27 topic docs, at most one directory deep, ordered by structure/manifest.json instead of by filename. The NN_ prefix is what produced the 09 collision and made splitting a doc cost a renumber; ordering now has one author. The 95 decision records move to structure/decisions/ADR-NNNN-*.md, each linked from exactly one owning doc, so a doc states the contract that holds now and links the reasoning rather than carrying it inline. scripts/structure-ssot.ts is the gate, blocked into CI by tests/ci-workflows/structure-ssot.test.ts. It fails on a doc missing from the manifest, a numeric prefix, a doc over the 600-line budget, an unresolvable link, a backticked repository path this tree does not have, an inline [Decision Log], a decision record with zero or two owners, a hole in record numbering, an invariant whose test is gone, and a src/ area no doc claims. INDEX.md is generated by bun run structure:index and compared byte for byte, so the reading order and the source-ownership table cannot contradict the manifest. The nine invariants in overview.md now carry INV-* ids bound to the test that holds them, and each of those tests names its id back, so splitting or renaming the test fails the gate instead of silently unbinding the invariant. structure/AGENTS.md carries the rules the gate enforces. Fixed in passing, each found by the new gate: gui/src/pages/CodexAuth.tsx (now codex-set-multiauth.tsx) and two devlog units documented under _plan after they closed into _fin. * docs(structure): make the SSOT gate survive its own review Three read-only review passes over the previous commit found the gate claiming more than it checked, and one paragraph lost in the split. Fixed here rather than shipped. Content: clients/integrations.md lost its opening contract paragraph, the only sentence stating the subsystem's reversibility promise. The split preserved preambles for docs migrated whole and dropped them for docs assembled from a section list; that paragraph is restored. Ownership: the map claimed every source area had exactly one owning doc. That is false in this repository - src/server/ is genuinely described by the management-API doc, the Responses transport doc and the Images doc - and the gate could not see it, because collision detection compared exact strings while every real overlap was a nesting. The model is now many-docs-per-area, the manifest lists what each doc actually names, and the five areas no doc names at all are recorded with reasons instead of being assigned to a doc that never mentions them. Invariants: five of nine were bound to tests that do not enforce them. INV-AGENT-01 named the picker-order band test while the five-featured rule lives in catalog-full-picker-order.test.ts; INV-AUTH-01 named a file whose nearest assertions run one credential against both planes, while server-management-auth.test.ts has a describe block for the separation itself. Those two are rebound, INV-RESTORE-01 is narrowed to the path its test covers, and INV-HOME-01 and INV-SLUG-01 have no honest test in this repository, so they are stated without a binding and recorded in grace.unboundInvariants. Naming a test that would pass while the rule was violated is worse than admitting the gap. Gate: repository paths are resolved against the git index rather than the filesystem, because existsSync cannot tell a tracked file from untracked local leftovers and is case-insensitive on Windows and case-sensitive on Linux CI. Decision records are no longer checked against the present tree - a record describes a past one. Fenced blocks are stripped before scanning. Link anchors are validated. Record numbering requires uniqueness but no longer contiguity, which would have made two branches collide on merge. Owners are counted per doc, not per mention. Leading digits are rejected in one rule instead of two, so 01-overview.md fails like 01_overview.md. Inline decision-log detection is case-insensitive and also catches the bullet template. The budget counts content lines and drops a grace entry once the doc is back under it. Tests: the suite proved one rule and shipped ten unproven. It now drives every rule red once against a synthetic tree, including the three cases that must NOT fire - a stale path inside a decision record, a path inside a fenced example, and a doc naming one record twice. * docs(structure): carry the Z.ai quota section into the new layout dev added a Z.ai quota-destination section to 05_gui-and-management-api.md while this branch was open. Rebase rename-detection moved it into gui-and-management-api.md and brought its inline [Decision Log] with it, which the gate refused - the first thing it caught that was not planted. The reasoning is now ADR-0096, linked from the section that owns it.
Records the live-probe evidence that disproves the native/external diagnosis, maps the two real causes, and locks the slice order. wp2 replaces an instrumented build with a request-shape lever that reaches the existing expanded finalize grace, so the experiment runs on the shipped binary.
Follow-up to #4276. CodeRabbit posted twelve findings on that PR; these are the ones that were still true against the merged code. The gate was claiming more than it checked, again, in four places: - A fragment-only link was skipped entirely, and one was already broken: structure/subagents.md pointed at #ultra-reasoning-level, a heading that moved to catalog.md during the split. Fragment targets now resolve against their own document, which found it immediately. - Link targets were resolved with existsSync while backticked paths went through the index. That is the per-machine split verdict this module exists to remove, on the reference class the split churned hardest. - A documents entry was accepted because the path existed, not because the doc said anything about it, so the map could claim coverage the prose did not have. A claim now has to be backed by a path the doc actually names. - Decision-record ownership was inferred from any occurrence of the filename in raw text. A record path inside a fenced example counted as a second owner, and an orphaned record whose owner link was deleted still looked owned. Ownership is now the > Decision record: link, read fence-stripped. Also: the filesystem fallback no longer applies when the git index is readable, so untracked local leftovers cannot satisfy a check that CI will fail; the manifest goes through a validating loader so a malformed file is a failure line instead of an uncaught stack trace; a missing overview.md is a failure rather than silence across every invariant binding; and backticked paths rooted at any tracked top-level entry are checked, not just the ten directories that were hardcoded. One finding is answered rather than implemented. Validating every filename-shaped token would reject the runtime files these docs legitimately name - config.toml, models_cache.json, ocx.pid - which live in a user's home, not in this repository. The boundary is now stated in structure/AGENTS.md, and root documents stay covered because a reference like MAINTAINERS.md is written as a link, and links are checked. Docs: the client-integration rationale left inline in adapters/registry.md moves into ADR-0093 with its evidence intact. All 96 records are retitled to say what they are - the heading names the section a record was extracted from, not the decision it contains, and a title that reads like a decision name while being a section name sends maintainers to the wrong record. src/AGENTS.md now says every applicable doc is updated, not one. Six new negative cases, including the two that must NOT fire: a bare filename is not a repository path, and a record named in prose is not an owner.
…roadmap Removes the native-gate edit from branch A because conversationCheckpointUpdate is liveness-only, so arrival order is not content coverage. Downgrades the grace experiment to positive-only because client-tool-suspend elapsedMs is turn-relative and cannot witness which grace branch ran. Appends wp2b and wp5.
Adds a narrow noJsonSchemaModels contract that downgrades json_schema to json_object on the Zen gateway DeepSeek routes, stops arming the Go thinking-budget wire for models with no ladder, and pins both with structural parity guards. Maintainer self-integration into dev per MAINTAINERS.md: exact head 340bc24, 27 checks, 0 failing or pending.
…e stream finalizeAfterDrain reaches finalizeTurnEvents which sets state.terminated, and returns [] for a terminated state, so draining before re-arming would leave the retry returning early without cancelCursorRun. Mirror its guards instead of calling it.
…diff applies cleanly
…point claim 000_plan.md rests on the absence of conversationCheckpointUpdate among a tool turn frames. Record the actual 33-frame sequence, and state plainly that the 7ms window between suspend and cancel makes this an absence of opportunity rather than evidence of absence.
A second review, run separately from the agent that wrote the previous commit, found two ways the gate was still wider than its prose and one it had newly opened. Deriving the top-level path set from the tracked tree meant a reference became INVISIBLE exactly when its directory disappeared. go/ is the live case: retired and untracked, so every remaining go/ mention had silently stopped being checked, and the absentPaths exemption that documents its absence had become unreachable. The set now unions in the roots this repository has or used to have. The mention-backed ownership check accepted the area name itself, which a table of directory names satisfies - 32 of 105 claims rested on exactly that, including nearly every claim in runtime.md. The check is unchanged; the prose is, because the honest description is that it catches an invented claim and does not prove the doc says anything useful. The manifest loader validated that grace arrays were arrays, never their elements, so a bare string where an object belongs still threw a TypeError from inside the checks. Element shapes are validated now. Source-area enumeration read the filesystem while every path resolved through the index, so an untracked scratch directory under src/ produced a failure CI could not reproduce; it reads the index too. Decision-record ownership matched on basename, so a link outside decisions/ could claim a record it did not point at. Also corrected in the rules file: the decision-log check finds two literal markers, not all inline reasoning - the rationale moved out of registry.md last commit contained neither, which is the proof; root files are checked directly rather than only through links; and the index comparison normalises line endings rather than being byte-for-byte. Five new cases, two of which build a real git repository, because every prior negative case ran in a plain temp directory where the index branch - the one the module argues hardest for - was never reached. One drives the case-variant verdict, one rejects an untracked leftover.
…stant parallel_tool_calls is local-only and never protobuf-encoded, so three arms at 12 tools isolate the finalize grace. 50ms yields no checkpoint frame; 1500ms yields one and 2742 captured bytes; reproduced both directions.
…asurement ocx debug provider logs is a 500-line ring buffer, so line-count windowing silently returned zero for every arm once it filled. Reading a fixed tail restores the signal: 1500ms captures a post-toolCallStarted checkpoint of 3036 bytes, 50ms captures nothing. wp2 LATE stands; the usedTokens:0 pre-tool hypothesis is refuted by arrival order.
…ponses path Two /v1/responses turns with previous_response_id share conversationHash cursor_cdbed7dcc and the second reports mode checkpoint rather than full-replay. C2 is chat-completions-specific and closes without a patch; the surviving defect is the client-tool turn.
…n actually sends A client-tool turn suspends before turnEnded, and upstream sends that turns checkpoint right after toolCallStarted - after the 50ms drain grace, so it was cancelled away and every such turn full-replayed with cached_tokens 0. Measured live with the tool catalog held constant: 50ms captures nothing, 1500ms captures 3036 bytes. Extend once, bounded, before finalizeAfterDrain (which terminates the event state), and fire early from the capture site so a turn that does send a checkpoint pays arrival latency instead of the whole window. The native wire-model gate is untouched: capturedAfterClientTool proves arrival, not coverage.
…capture fix(cursor): capture the conversation checkpoint a suspended tool turn actually sends
…4282) Splits the DeepSeek thinking set by who serves the route (first-party deepseek-flash vs gateway deepseek-v4.1-flash), removes the retired deepseek-v4-pro from every static roster, and excludes it from the routed catalog on live-discovery providers where deleting a row would strip capabilities instead of the model. Carries the Command Code effort ladders from #4258. Maintainer integration into dev per MAINTAINERS.md with all checks passing at the exact head.
…dence docs: retain cache delivery evidence and correct closure attribution
…sh-integration-fixtures
dev has not had a finished CI run since e432cf5, and the one push run that was allowed to complete (f9815da) concluded failure, so dev is red rather than merely unverified. This unit plans the path back: repair CI, sweep the 79-commit window for regressions, then promote preview and main. Records the resolved release mechanics so later cycles do not re-derive them: promotion is a pull request because the preview and main rulesets give admin a pull_request bypass only, the prerelease is 2.52.0-preview.20260912, the stable is 2.52.0, and dev must move to 2.53.0 before the stable can publish.
The "unreadable preimages" integration test created its denial with
spyOn(fs, "readFileSync") on the child's require("node:fs") handle, but
inject-coordination.ts binds readFileSync as an ESM named import, so
readOrNull and captureCodexPreImages never saw the mock. Every assertion in
that test ran against an undenied filesystem, which is why it fails identically
as test 4/4 on Linux and macos 2/2 on macOS with matchedReads: 0.
Replace the mock with chmod 0 so the EACCES is a real permission, and report an
unreadable field that proves the precondition before the later assertions
depend on it. The capture, compensation, outcome, and preservation expectations
are unchanged. Windows chmod only toggles the read-only bit and root ignores
mode 0, so both skip.
…on-fixtures fix: complete Cline registration and stabilize integration fixtures
Eight read-only lanes covered every non-merge commit between the last green dev and the merge that re-greened it. Six came back clean, one found only the help text count #4390 had already fixed, and one found a real routing regression. #4368 gave a quota refusal its own avoidance window, and pool candidates honour it, but the main account reaches the candidate list through a separate re-insertion block that never checks it. The same commit also promises that an operator clearing the cooldown or naming the account overrules the avoidance, and neither path does. All three were re-derived against source by a second reviewer before being written down.
#4368 split a quota refusal into a capped cooldown and a longer window recording what the refusal actually announced. Pool candidates honour that window. The main login never did: it is not in config.codexAccounts, so it reaches selection only through a separate re-insertion branch that checked the soft avoid and not the quota avoidance. With the cooldown capped at fifteen minutes and the window running up to six hours, a user with the main login plus a pool saw exactly the failure #4368 was written to stop — the pool stayed avoided while the account that refused came back and won the next pick on a weekly bar a burst limit never touches. The same commit states that an operator clearing the cooldown or naming the account overrules the avoidance. Neither did. A reset-derived refusal writes only the scoped health entry, and resetCodexRoutingForManualSelection read the account-wide map and returned early when it was empty, so naming the account cleared nothing in the case that produced the avoidance. clearCodexAccountCooldown carried quotaAvoidUntil through in its rest spread, which left the operator escape hatch not escaping while automatic probe recovery deliberately drops the same field. Found by a regression sweep over the window between the last green dev and the merge that re-greened it, and re-derived against source by an independent reviewer before any change was made.
The routing change alters shared Codex selection and manual cooldown recovery, and structure/INDEX.md maps src/codex/ to this document, so the maintainer SSOT has to describe it: the window binds the main alias on the same terms as an added account, and both operator escapes clear it from the scoped entries a reset-derived refusal actually writes.
fix(codex): apply a quota refusal's avoidance window to the main login
clearCodexAccountCooldown returned before it looked at quotaAvoidUntil whenever cooldownUntil had already passed. The two durations have different lengths on purpose — the cooldown caps at fifteen minutes, the announced window runs up to six hours — so the moment an operator actually reaches for this escape hatch is usually after the cooldown lapsed and only the window is still keeping the account out of rotation. The call was a no-op there, and the dashboard reported cleared: false while selection kept passing the account over for hours. Treat a live avoidance window as its own reason to act, and keep the existing contract otherwise: failure counters and softAvoid still survive.
…psed fix(codex): let clear-cooldown lift an avoidance the cooldown outlived
#4397 made a live avoidance window its own reason for the clear-cooldown operation to act, which changes the operator-facing contract. structure/INDEX.md maps src/codex/ to this document, so the SSOT has to say that a lapsed cooldown with a live window still reports a clear, and that an account with neither still reports no change.
docs(structure): record what clear-cooldown does to a lapsed window
…view The product tree is dev at 392e182, whose last CI-bearing head f2f1783 passed the full hosted matrix. Only package.json differs, carrying the preview channel version. dev had drifted 79 commits past its last finished CI run and the one run that was allowed to complete had failed, so this promotion follows a repair and a regression sweep rather than an ordinary cut: #4390 fixed an integration test that never denied anything and had been red on dev since #4342, and #4396/#4397/#4398 closed four gaps in the quota-avoidance contract #4368 introduced, including the main login never honouring the window its own refusal announced.
|
Important Review skippedToo many files! This PR contains 821 files, which is 521 over the limit of 300. To get a review, reduce the PR to 300 files or fewer by splitting it into smaller PRs or changing its base branch. Usage-priced reviews support at most 300 files. ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Advanced Run ID: ⛔ Files ignored due to path filters (15)
📒 Files selected for processing (821)
You can disable this status message by setting the Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
✅ Deterministic PR hygiene checks passed. |
⏳ DRAFT
What to do
Its title has been prefixed with |
리뷰 · 우선순위 77 / 80이 PR은 새 기능을 넣는 작업이 아니다. 이미 지금 왜 지금 올리냐면, 버전 이름은 게이트 패턴도 이전 preview 승격(#4273, #4010, #4116 등)과 같다. 경로 base= 메인테이너의 판단이 필요한 지점
너의 추천 이 댓글은 grok-bot이 작성했습니다 |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8d7e24e7d2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (pathname === "/api/grok/reset-coupons/consume") { | ||
| if (req.method !== "POST") { | ||
| return jsonResponse({ error: "Method not allowed" }, 405, req, config); | ||
| } |
There was a problem hiding this comment.
Require dashboard-session consent before redeeming coupons
The new redemption endpoint accepts any caller that passed the general management-auth gate and never checks ctx.principal/hasBrowserSessionEvidence, unlike the GitHub identity-spending route. An agent or process holding only the admin token can therefore consume an irreversible account coupon directly; the CLI's --yes flag is also machine-answerable. Require dashboard-session evidence before this action and document the consent boundary in AGENTS_INSTALL.md.
AGENTS.md reference: AGENTS.md:L174-L176
Useful? React with 👍 / 👎.
| let resolvedTokenId = requestedTokenId; | ||
| if (!resolvedTokenId) { | ||
| try { | ||
| const remaining = await getGrokRemainingResets({ accessToken: tokenSnapshot.accessToken }); |
There was a problem hiding this comment.
Persist the selected coupon before redeeming it
When tokenId is omitted—which the documented API and CLI allow—the ledger is opened with no token, then this branch selects a coupon without durably attaching it to the open operation. If the process dies after upstream redeems that coupon but before recordGrokResetCouponSettlement, retrying the same operationId is classified as execute, fetches the next remaining coupon, and can consume a second credit. Persist the resolved token before the irreversible RPC and resume only that recorded token on an open-operation retry.
Useful? React with 👍 / 👎.
| if (Object.keys(ledger.operations).length >= MAX_GROK_RESET_COUPON_OPERATION_IDS) { | ||
| return { kind: "capacity", operationId: identity.operationId }; | ||
| } |
There was a problem hiding this comment.
Prune the coupon ledger before enforcing its capacity
Once the ledger reaches 256 entries, this early return prevents both pruning and lookup of existing operations. Consequently, even after all settled entries pass the 30-day retention window, no subsequent open can reach writeGrokCouponLedger to remove them, leaving redemption permanently capacity-blocked; retries of already-settled operation IDs also stop replaying. Prune expired entries first and check an existing operation before rejecting a genuinely new ID.
Useful? React with 👍 / 👎.
| {}, | ||
| deps, | ||
| ); | ||
| printData(result, wantsJson); |
There was a problem hiding this comment.
Return a failing CLI status for replayed redemption failures
When the server replays a durably recorded failure, it intentionally responds HTTP 200 with a code such as redeem_failed; runtimeRequest therefore succeeds, and this unconditional printData lets runCliAction return exit code 0. Automation retrying ocx account grok-reset-coupons --consume --operation-id … will report success despite no coupon being redeemed. Inspect the settled response code and throw or otherwise return a nonzero CLI status for non-redeemed outcomes.
Useful? React with 👍 / 👎.
|
Maintainer-controlled promotion to the preview channel. Exact head
Local tests, build, typecheck and install: NOT RUN under the standing restriction. The preview push-event run is the actual release gate and has to pass before anything publishes. |
Summary
2.52.0-preview.20260912. The merged tree is byte-identical to dev at392e182a00except for thepackage.jsonversion line, which carries the preview channel version.spyOn(fs, "readFileSync")while production bindsreadFileSyncas an ESM named import, so it had been failing on dev since fix(history): contain paginated metadata writes and preserve restore artifacts #4342 merged red.Verification
f2f1783ae0: success. That is the last CI-bearing dev head;392e182a00adds a documentation-only commit that workflow path filters exclude.4b6849cf12: success.devlog/_plan/260912_release_regression_train/025_sweep_results.md: eight lanes, six clean, one already-fixed minor, one blocking finding that is now landed.c1af92596a: a merge of dev into preview withpackage.jsonresolved to the channel version.enforce-targetis expected to reject this PR because it targetspreviewrather thandev. That is the promotion exception in the branch policy, not a defect.Checklist