Conversation
Greptile SummaryThis PR stabilises Windows CI by making three targeted categories of fixes: (1) moving
Confidence Score: 4/5
Prompt To Fix All With AIThis is a comment left during a code review.
Path: src/infra/gaxios-fetch-compat.ts
Line: 268-271
Comment:
**Concurrent callers during `"installing"` resolve immediately**
When `installState === "installing"`, any concurrent `await installGaxiosFetchCompat()` call short-circuits and returns a resolved `Promise<void>` before installation actually finishes. Code that follows the `await` in those concurrent callers may run against an unpatched gaxios prototype.
This is benign for the current sequential startup callers (`entry.ts`, `index.ts`), but the guard silently drops the wait contract for any future caller that does:
```ts
await installGaxiosFetchCompat();
// assumes gaxios is patched here – not guaranteed if a concurrent install is in flight
const result = await new Gaxios().request(...);
```
A simple future-proof alternative is to cache the in-flight Promise so all concurrent callers coalesce onto it:
```ts
let installPromise: Promise<void> | null = null;
export function installGaxiosFetchCompat(): Promise<void> {
if (installState === "installed" || installState === "shimmed") return Promise.resolve();
if (typeof globalThis.fetch !== "function") return Promise.resolve();
installPromise ??= (async () => {
installState = "installing";
try {
// ... existing body
} catch (err) {
installState = "not-installed";
installPromise = null;
throw err;
}
})();
return installPromise;
}
```
Not blocking the PR given current callers are sequential, but worth addressing before this module is used in any concurrent context.
How can I resolve this? If you propose a fix, please make it concise.Last reviewed commit: 81ef12c |
| import json | ||
| import os | ||
|
|
||
| with open(os.environ["GITHUB_EVENT_PATH"], "r", encoding="utf-8") as fh: | ||
| event = json.load(fh) | ||
|
|
||
| if os.environ["GITHUB_EVENT_NAME"] == "push": | ||
| print(event["before"]) | ||
| else: | ||
| print(event["pull_request"]["base"]["sha"]) | ||
| PY | ||
| )" | ||
|
|
There was a problem hiding this comment.
Heredoc terminator will never be matched
The Python code and its heredoc terminator (PY) sit at 12 spaces of YAML indentation, while the surrounding shell body (e.g. set -euo pipefail, BASE="$(") sits at 10 spaces. Because run: | is a YAML literal block scalar that strips those 10 spaces of common indentation, the shell actually receives the terminator as PY (2 leading spaces), not PY at column 0.
For a <<'PY' heredoc (no - modifier), bash only recognises a terminator that is exactly at column 0 with no surrounding whitespace. PY is never matched, so bash reads through the rest of the script looking for the terminator and eventually hits EOF, producing an "unexpected end of file" error that breaks the entire secrets job.
There are two problems that need to be fixed:
-
The terminator indentation – the
PYline (and the whole Python body) must be placed at the same indentation level as the surrounding bash code (10 spaces in the raw YAML) so that after YAML stripping they appear at column 0. -
Python top-level indentation – after YAML stripping the Python lines all start with 2 spaces, which Python 3 rejects with
IndentationError: unexpected indenton the very firstimportstatement.
The existing iOS job (lines ~725-798 in the same file) uses the correct pattern: the Python body and its PY terminator are at the same YAML indentation as the surrounding shell code, not at a deeper level.
A corrected version of this block:
BASE="$(
python - <<'PY'
import json
import os
with open(os.environ["GITHUB_EVENT_PATH"], "r", encoding="utf-8") as fh:
event = json.load(fh)
if os.environ["GITHUB_EVENT_NAME"] == "push":
print(event["before"])
else:
print(event["pull_request"]["base"]["sha"])
PY
)"
(The Python source lines and the PY terminator must be flush against the left margin of the YAML run: block — i.e., at the same raw indentation as set -euo pipefail.)
Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/ci.yml
Line: 463-475
Comment:
**Heredoc terminator will never be matched**
The Python code and its heredoc terminator (`PY`) sit at 12 spaces of YAML indentation, while the surrounding shell body (e.g. `set -euo pipefail`, `BASE="$("`) sits at 10 spaces. Because `run: |` is a YAML literal block scalar that strips those 10 spaces of common indentation, the shell actually receives the terminator as ` PY` (2 leading spaces), not `PY` at column 0.
For a `<<'PY'` heredoc (no `-` modifier), bash only recognises a terminator that is **exactly** at column 0 with no surrounding whitespace. ` PY` is never matched, so bash reads through the rest of the script looking for the terminator and eventually hits EOF, producing an "unexpected end of file" error that breaks the entire `secrets` job.
There are two problems that need to be fixed:
1. **The terminator indentation** – the `PY` line (and the whole Python body) must be placed at the same indentation level as the surrounding bash code (10 spaces in the raw YAML) so that after YAML stripping they appear at column 0.
2. **Python top-level indentation** – after YAML stripping the Python lines all start with 2 spaces, which Python 3 rejects with `IndentationError: unexpected indent` on the very first `import` statement.
The existing iOS job (lines ~725-798 in the same file) uses the correct pattern: the Python body and its `PY` terminator are at the **same** YAML indentation as the surrounding shell code, not at a deeper level.
A corrected version of this block:
```
BASE="$(
python - <<'PY'
import json
import os
with open(os.environ["GITHUB_EVENT_PATH"], "r", encoding="utf-8") as fh:
event = json.load(fh)
if os.environ["GITHUB_EVENT_NAME"] == "push":
print(event["before"])
else:
print(event["pull_request"]["base"]["sha"])
PY
)"
```
(The Python source lines and the `PY` terminator must be flush against the left margin of the YAML `run:` block — i.e., at the same raw indentation as `set -euo pipefail`.)
How can I resolve this? If you propose a fix, please make it concise.| export async function installGaxiosFetchCompat(): Promise<void> { | ||
| if (installState !== "not-installed" || typeof globalThis.fetch !== "function") { | ||
| return; | ||
| } |
There was a problem hiding this comment.
Concurrent callers during
"installing" resolve immediately
When installState === "installing", any concurrent await installGaxiosFetchCompat() call short-circuits and returns a resolved Promise<void> before installation actually finishes. Code that follows the await in those concurrent callers may run against an unpatched gaxios prototype.
This is benign for the current sequential startup callers (entry.ts, index.ts), but the guard silently drops the wait contract for any future caller that does:
await installGaxiosFetchCompat();
// assumes gaxios is patched here – not guaranteed if a concurrent install is in flight
const result = await new Gaxios().request(...);A simple future-proof alternative is to cache the in-flight Promise so all concurrent callers coalesce onto it:
let installPromise: Promise<void> | null = null;
export function installGaxiosFetchCompat(): Promise<void> {
if (installState === "installed" || installState === "shimmed") return Promise.resolve();
if (typeof globalThis.fetch !== "function") return Promise.resolve();
installPromise ??= (async () => {
installState = "installing";
try {
// ... existing body
} catch (err) {
installState = "not-installed";
installPromise = null;
throw err;
}
})();
return installPromise;
}Not blocking the PR given current callers are sequential, but worth addressing before this module is used in any concurrent context.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/infra/gaxios-fetch-compat.ts
Line: 268-271
Comment:
**Concurrent callers during `"installing"` resolve immediately**
When `installState === "installing"`, any concurrent `await installGaxiosFetchCompat()` call short-circuits and returns a resolved `Promise<void>` before installation actually finishes. Code that follows the `await` in those concurrent callers may run against an unpatched gaxios prototype.
This is benign for the current sequential startup callers (`entry.ts`, `index.ts`), but the guard silently drops the wait contract for any future caller that does:
```ts
await installGaxiosFetchCompat();
// assumes gaxios is patched here – not guaranteed if a concurrent install is in flight
const result = await new Gaxios().request(...);
```
A simple future-proof alternative is to cache the in-flight Promise so all concurrent callers coalesce onto it:
```ts
let installPromise: Promise<void> | null = null;
export function installGaxiosFetchCompat(): Promise<void> {
if (installState === "installed" || installState === "shimmed") return Promise.resolve();
if (typeof globalThis.fetch !== "function") return Promise.resolve();
installPromise ??= (async () => {
installState = "installing";
try {
// ... existing body
} catch (err) {
installState = "not-installed";
installPromise = null;
throw err;
}
})();
return installPromise;
}
```
Not blocking the PR given current callers are sequential, but worth addressing before this module is used in any concurrent context.
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 81ef12c609
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if (installState !== "not-installed" || typeof globalThis.fetch !== "function") { | ||
| return; |
There was a problem hiding this comment.
Await in-progress gaxios compat install
installGaxiosFetchCompat now returns immediately for any state other than not-installed, including installing. Because the function sets installState = "installing" before awaiting loadGaxiosConstructor(), a second concurrent caller can await installGaxiosFetchCompat() and continue before the adapter patch is applied, so its first gaxios calls may run with unpatched behavior (no compat fetch/proxy dispatcher translation). Keep an in-flight install promise and await it when state is installing instead of returning early.
Useful? React with 👍 / 👎.
81ef12c to
25a1f49
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 25a1f49c9c
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| providers = resolvePluginProviders({ | ||
| config, | ||
| env: process.env, | ||
| }); |
There was a problem hiding this comment.
Keep contract expectations independent from plugin resolver
This test now builds both the expected contract set and the wizard/model-picker outputs from resolvePluginProviders, so a regression in provider resolution (for example, a bundled provider being dropped by loader/config changes) would make both sides shrink together and still pass. In this file the stated goal is to assert coverage of every registered provider contract, so deriving expectations from the same resolver path removes that protection and can let real wizard coverage regressions ship undetected.
Useful? React with 👍 / 👎.
|
Please don't make PRs for test failures on main. The team is aware of those and will handle them directly on the codebase, not only fixing the tests but also investigating what the root cause is. Having to sift through test-fix-PRs (including some that have been out of date for weeks...) on top of that doesn't help. There are already way too many PRs for humans to manage; please don't make the flood worse. Thank you. |
Summary
mainstayed red on the Windows shards, masking real regressions and slowing follow-up work.Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
User-visible / Behavior Changes
None.
Security Impact (required)
NoNoNoNoNoYes, explain risk + mitigation:Repro + Verification
Environment
Steps
Expected
Actual
Evidence
Human Verification (required)
What you personally verified (not just CI), and how:
pnpm vitest run src/cron/isolated-agent.auth-profile-propagation.test.ts src/cron/isolated-agent.subagent-model.test.tspnpm vitest run src/plugins/bundle-mcp.test.ts src/plugins/marketplace.test.ts src/infra/provider-usage.auth.normalizes-keys.test.tsRUNNER~1) vs canonical path handling in assertionsReview Conversations
Compatibility / Migration
YesNoNoFailure Recovery (if this breaks)
ce67a582b5Risks and Mitigations
mainWindows CI run.